Browse Source

Execute procedural Start() & Stop() functions automatically when a ScriptFile is loaded and unloaded. This allows live-reload of a whole script application.
Do not recreate Console & DebugHud if they already exist.

Lasse Öörni 13 years ago
parent
commit
2c96dff225

+ 2 - 0
Docs/Reference.dox

@@ -277,6 +277,8 @@ file->Execute("void MyFunction(int)", parameters); // Execute
 
 
 \ref ScriptFile::Execute "Execute()" also has an overload which takes a function pointer instead of querying by declaration. Using a pointer is naturally faster than a query, but also more risky: in case the ScriptFile resource is unloaded or reloaded, any function pointers will be invalidated.
 \ref ScriptFile::Execute "Execute()" also has an overload which takes a function pointer instead of querying by declaration. Using a pointer is naturally faster than a query, but also more risky: in case the ScriptFile resource is unloaded or reloaded, any function pointers will be invalidated.
 
 
+Note that when a script file loads itself, it automatically calls the function "void Start()" after successfully compiling, if it exists in the script. Likewise, before a script file is unloaded or reloaded, it calls the function "void Stop()" if defined.
+
 \section Scripting_Object Instantiating a script object
 \section Scripting_Object Instantiating a script object
 
 
 The component ScriptInstance can be used to instantiate a specific class from within a script file. After instantiation, the the script object can respond to scene updates, \ref Events "events" and \ref Serialization "serialization" much like a component written in C++ would do, if it has the appropriate methods implemented. For example:
 The component ScriptInstance can be used to instantiate a specific class from within a script file. After instantiation, the the script object can respond to scene updates, \ref Events "events" and \ref Serialization "serialization" much like a component written in C++ would do, if it has the appropriate methods implemented. For example:

+ 2 - 28
Engine/Core/Object.cpp

@@ -184,33 +184,7 @@ void Object::UnsubscribeFromAllEvents()
     }
     }
 }
 }
 
 
-void Object::UnsubscribeFromAllEventsWithUserData()
-{
-    EventHandler* handler = eventHandlers_.First();
-    EventHandler* previous = 0;
-    
-    while (handler)
-    {
-        if (handler->GetUserData())
-        {
-            if (handler->GetSender())
-                context_->RemoveEventReceiver(this, handler->GetSender(), handler->GetEventType());
-            else
-                context_->RemoveEventReceiver(this, handler->GetEventType());
-            
-            EventHandler* next = eventHandlers_.Next(handler);
-            eventHandlers_.Erase(handler, previous);
-            handler = next;
-        }
-        else
-        {
-            previous = handler;
-            handler = eventHandlers_.Next(handler);
-        }
-    }
-}
-
-void Object::UnsubscribeFromAllEventsExcept(const PODVector<StringHash>& exceptions)
+void Object::UnsubscribeFromAllEventsExcept(const PODVector<StringHash>& exceptions, bool needUserData)
 {
 {
     EventHandler* handler = eventHandlers_.First();
     EventHandler* handler = eventHandlers_.First();
     EventHandler* previous = 0;
     EventHandler* previous = 0;
@@ -219,7 +193,7 @@ void Object::UnsubscribeFromAllEventsExcept(const PODVector<StringHash>& excepti
     {
     {
         EventHandler* next = eventHandlers_.Next(handler);
         EventHandler* next = eventHandlers_.Next(handler);
         
         
-        if (!exceptions.Contains(handler->GetEventType()))
+        if ((!needUserData || handler->GetUserData()) && !exceptions.Contains(handler->GetEventType()))
         {
         {
             if (handler->GetSender())
             if (handler->GetSender())
                 context_->RemoveEventReceiver(this, handler->GetSender(), handler->GetEventType());
                 context_->RemoveEventReceiver(this, handler->GetSender(), handler->GetEventType());

+ 2 - 4
Engine/Core/Object.h

@@ -62,10 +62,8 @@ public:
     void UnsubscribeFromEvents(Object* sender);
     void UnsubscribeFromEvents(Object* sender);
     /// Unsubscribe from all events.
     /// Unsubscribe from all events.
     void UnsubscribeFromAllEvents();
     void UnsubscribeFromAllEvents();
-    /// Unsubscribe from all events with userdata defined in the handler.
-    void UnsubscribeFromAllEventsWithUserData();
-    /// Unsubscribe from all events except those listed.
-    void UnsubscribeFromAllEventsExcept(const PODVector<StringHash>& exceptions);
+    /// Unsubscribe from all events except those listed, and optionally only those with userdata (script registered events.)
+    void UnsubscribeFromAllEventsExcept(const PODVector<StringHash>& exceptions, bool needUserData);
     /// Send event to all subscribers.
     /// Send event to all subscribers.
     void SendEvent(StringHash eventType);
     void SendEvent(StringHash eventType);
     /// Send event with parameters to all subscribers.
     /// Send event with parameters to all subscribers.

+ 1 - 1
Engine/Engine/CoreAPI.cpp

@@ -677,7 +677,7 @@ static void UnsubscribeFromAllEvents()
 {
 {
     Object* listener = GetScriptContextEventListenerObject();
     Object* listener = GetScriptContextEventListenerObject();
     if (listener)
     if (listener)
-        listener->UnsubscribeFromAllEventsWithUserData();
+        listener->UnsubscribeFromAllEventsExcept(PODVector<StringHash>(), true);
 }
 }
 
 
 static Object* GetEventSender()
 static Object* GetEventSender()

+ 12 - 0
Engine/Engine/Engine.cpp

@@ -358,6 +358,12 @@ Console* Engine::CreateConsole()
 {
 {
     if (headless_ || !initialized_)
     if (headless_ || !initialized_)
         return 0;
         return 0;
+    
+    // Return existing console if possible
+    Console* console = GetSubsystem<Console>();
+    if (console)
+        return console;
+    
     context_->RegisterSubsystem(new Console(context_));
     context_->RegisterSubsystem(new Console(context_));
     return GetSubsystem<Console>();
     return GetSubsystem<Console>();
 }
 }
@@ -366,6 +372,12 @@ DebugHud* Engine::CreateDebugHud()
 {
 {
     if (headless_ || !initialized_)
     if (headless_ || !initialized_)
         return 0;
         return 0;
+    
+     // Return existing debug hud if possible
+    DebugHud* debugHud = GetSubsystem<DebugHud>();
+    if (debugHud)
+        return debugHud;
+    
     context_->RegisterSubsystem(new DebugHud(context_));
     context_->RegisterSubsystem(new DebugHud(context_));
     return GetSubsystem<DebugHud>();
     return GetSubsystem<DebugHud>();
 }
 }

+ 11 - 1
Engine/Script/ScriptFile.cpp

@@ -101,6 +101,11 @@ bool ScriptFile::Load(Deserializer& source)
     // Map script module to script resource with userdata
     // Map script module to script resource with userdata
     scriptModule_->SetUserData(this);
     scriptModule_->SetUserData(this);
     
     
+    // Execute start function if defined
+    asIScriptFunction* start = GetFunction("void Start()");
+    if (start)
+        Execute(start);
+    
     return true;
     return true;
 }
 }
 
 
@@ -511,6 +516,11 @@ void ScriptFile::ReleaseModule()
 {
 {
     if (scriptModule_)
     if (scriptModule_)
     {
     {
+        // Execute stop function if defined
+        asIScriptFunction* stop = GetFunction("void Stop()");
+        if (stop)
+            Execute(stop);
+        
         script_->ClearObjectTypeCache();
         script_->ClearObjectTypeCache();
         
         
         // Clear search caches and event handlers
         // Clear search caches and event handlers
@@ -518,7 +528,7 @@ void ScriptFile::ReleaseModule()
         validClasses_.Clear();
         validClasses_.Clear();
         functions_.Clear();
         functions_.Clear();
         methods_.Clear();
         methods_.Clear();
-        UnsubscribeFromAllEventsWithUserData();
+        UnsubscribeFromAllEventsExcept(PODVector<StringHash>(), true);
         
         
         // Remove the module
         // Remove the module
         scriptModule_->SetUserData(0);
         scriptModule_->SetUserData(0);

+ 1 - 1
Engine/Script/ScriptInstance.cpp

@@ -360,7 +360,7 @@ void ScriptInstance::ReleaseObject()
         PODVector<StringHash> exceptions;
         PODVector<StringHash> exceptions;
         exceptions.Push(E_RELOADSTARTED);
         exceptions.Push(E_RELOADSTARTED);
         exceptions.Push(E_RELOADFINISHED);
         exceptions.Push(E_RELOADFINISHED);
-        UnsubscribeFromAllEventsExcept(exceptions);
+        UnsubscribeFromAllEventsExcept(exceptions, false);
         subscribed_ = false;
         subscribed_ = false;
         
         
         ClearMethods();
         ClearMethods();

+ 18 - 9
Urho3D/Urho3D.cpp

@@ -95,25 +95,34 @@ void Run()
             return;
             return;
         }
         }
         
         
-        // Execute the Start function from the script file, then run the engine loop until exited
+        // When the script file is loaded, its Start() function will be executed
         // Hold a shared pointer to the script file to make sure it is not unloaded during runtime
         // Hold a shared pointer to the script file to make sure it is not unloaded during runtime
         engine->InitializeScripting();
         engine->InitializeScripting();
         SharedPtr<ScriptFile> scriptFile(context->GetSubsystem<ResourceCache>()->GetResource<ScriptFile>(scriptFileName));
         SharedPtr<ScriptFile> scriptFile(context->GetSubsystem<ResourceCache>()->GetResource<ScriptFile>(scriptFileName));
-        if (scriptFile && scriptFile->Execute("void Start()"))
+        bool success = false;
+        
+        if (scriptFile)
         {
         {
-            while (!engine->IsExiting())
-                engine->RunFrame();
-            
-            // Run the optional shutdown function
-            if (scriptFile->GetFunction("void Stop()"))
-                scriptFile->Execute("void Stop()");
+            // Do not run engine loop if script did not have a Start() function at all
+            if (scriptFile->GetFunction("void Start()"))
+            {
+                success = true;
+                while (!engine->IsExiting())
+                    engine->RunFrame();
+            }
+            else
+                context->GetSubsystem<Log>()->Write(LOG_ERROR, "Script " + scriptFileName + " did not have a Start() function");
         }
         }
-        else
+        
+        if (!success)
         {
         {
             engine->Exit(); // Close the rendering window
             engine->Exit(); // Close the rendering window
             ErrorDialog("Urho3D", context->GetSubsystem<Log>()->GetLastMessage());
             ErrorDialog("Urho3D", context->GetSubsystem<Log>()->GetLastMessage());
         }
         }
+        
+        // Release the script now to execute the Stop() function
         scriptFile.Reset();
         scriptFile.Reset();
+        context->GetSubsystem<ResourceCache>()->ReleaseResource(ScriptFile::GetTypeStatic(), scriptFileName, true);
     }
     }
     catch (std::bad_alloc&)
     catch (std::bad_alloc&)
     {
     {