Browse Source

Fixed crash that could occur when calling Game::exit() from a script function, due to the order of subsystem finalization and destruction.

sgrenier 13 years ago
parent
commit
4d42b51f2a
3 changed files with 60 additions and 27 deletions
  1. 32 24
      gameplay/src/Game.cpp
  2. 6 1
      gameplay/src/Game.h
  3. 22 2
      gameplay/src/ScriptController.cpp

+ 32 - 24
gameplay/src/Game.cpp

@@ -34,20 +34,7 @@ Game::Game()
 
 Game::~Game()
 {
-    if (_scriptListeners)
-    {
-        for (unsigned int i = 0; i < _scriptListeners->size(); i++)
-        {
-            SAFE_DELETE((*_scriptListeners)[i]);
-        }
-        SAFE_DELETE(_scriptListeners);
-    }
-
-    if (_scriptController)
-    {
-        _scriptController->finalize();
-        SAFE_DELETE(_scriptController);
-    }
+	SAFE_DELETE(_scriptController);
 
     // Do not call any virtual functions from the destructor.
     // Finalization is done from outside this class.
@@ -181,8 +168,21 @@ void Game::shutdown()
         GP_ASSERT(_aiController);
 
         Platform::signalShutdown();
+
+		// Call user finalize
         finalize();
 
+		// Shutdown scripting system first so that any objects allocated in script are released before our subsystems are released
+		_scriptController->finalizeGame();
+		if (_scriptListeners)
+		{
+			for (unsigned int i = 0; i < _scriptListeners->size(); i++)
+			{
+				SAFE_DELETE((*_scriptListeners)[i]);
+			}
+			SAFE_DELETE(_scriptListeners);
+		}
+		_scriptController->finalize();
 
         std::vector<Gamepad*>::iterator itr = _gamepads->begin();
         std::vector<Gamepad*>::iterator end = _gamepads->end();
@@ -194,8 +194,6 @@ void Game::shutdown()
         _gamepads->clear();
         SAFE_DELETE(_gamepads);
 
-        _scriptController->finalizeGame();
-
         _animationController->finalize();
         SAFE_DELETE(_animationController);
 
@@ -216,7 +214,7 @@ void Game::shutdown()
 
         SAFE_DELETE(_properties);
 
-        _state = UNINITIALIZED;
+		_state = UNINITIALIZED;
     }
 }
 
@@ -263,7 +261,11 @@ void Game::resume()
 
 void Game::exit()
 {
-    shutdown();
+	// Schedule a call to shutdown rather than calling it right away.
+	// This handles the case of shutting down the script system from
+	// within a script function (which can cause errors).
+	static ShutdownListener listener;
+	schedule(0, &listener);
 }
 
 void Game::frame()
@@ -276,6 +278,12 @@ void Game::frame()
         triggerGamepadEvents(); // Now that the game has been initialized, trigger any gamepad attached events.
     }
 
+	static double lastFrameTime = Game::getGameTime();
+	double frameTime = getGameTime();
+
+    // Fire time events to scheduled TimeListeners
+    fireTimeEvents(frameTime);
+
     if (_state == Game::RUNNING)
     {
         GP_ASSERT(_animationController);
@@ -284,17 +292,12 @@ void Game::frame()
         GP_ASSERT(_aiController);
 
         // Update Time.
-        static double lastFrameTime = Game::getGameTime();
-        double frameTime = getGameTime();
         float elapsedTime = (frameTime - lastFrameTime);
         lastFrameTime = frameTime;
 
         // Update the scheduled and running animations.
         _animationController->update(elapsedTime);
 
-        // Fire time events to scheduled TimeListeners
-        fireTimeEvents(frameTime);
-
         // Update the physics.
         _physicsController->update(elapsedTime);
 
@@ -325,7 +328,7 @@ void Game::frame()
             _frameLastFPS = getGameTime();
         }
     }
-    else
+	else if (_state == Game::PAUSED)
     {
         // Application Update.
         update(0);
@@ -613,4 +616,9 @@ void Game::triggerGamepadEvents()
     }
 }
 
+void Game::ShutdownListener::timeEvent(long timeDiff, void* cookie)
+{
+	Game::getInstance()->shutdown();
+}
+
 }

+ 6 - 1
gameplay/src/Game.h

@@ -25,8 +25,8 @@ class ScriptController;
  */
 class Game
 {
-
     friend class Platform;
+	friend class ShutdownListener;
 
 public:
     
@@ -606,6 +606,11 @@ private:
         std::string function;
     };
 
+	struct ShutdownListener : public TimeListener
+	{
+		void timeEvent(long timeDiff, void* cookie);
+	};
+
     /**
      * TimeEvent represents the event that is sent to TimeListeners as a result of calling Game::schedule().
      */

+ 22 - 2
gameplay/src/ScriptController.cpp

@@ -632,17 +632,34 @@ void ScriptController::initializeGame()
 void ScriptController::finalize()
 {
     if (_lua)
+	{
         lua_close(_lua);
+		_lua = NULL;
+	}
 }
 
 void ScriptController::finalizeGame()
 {
-    if (_callbacks[FINALIZE])
+	std::string finalizeCallback;
+	if (_callbacks[FINALIZE])
+		finalizeCallback = _callbacks[FINALIZE]->c_str();
+
+	// Remove any registered callbacks so they don't get called after shutdown
+	for (unsigned int i = 0; i < CALLBACK_COUNT; i++)
     {
-        executeFunction<void>(_callbacks[FINALIZE]->c_str());
+        SAFE_DELETE(_callbacks[i]);
+    }
+
+	// Fire script finalize callback
+    if (!finalizeCallback.empty())
+	{
+        executeFunction<void>(finalizeCallback.c_str());
     }
 
     // Perform a full garbage collection cycle.
+	// Note that this does NOT free any global variables declared in scripts, since 
+	// they are stored in the global state and are still referenced. Only after 
+	// closing the state (lua_close) will those variables be released.
     lua_gc(_lua, LUA_GCCOLLECT, 0);
 }
 
@@ -697,6 +714,9 @@ void ScriptController::gamepadEvent(Gamepad::GamepadEvent evt, Gamepad* gamepad)
 
 void ScriptController::executeFunctionHelper(int resultCount, const char* func, const char* args, va_list* list)
 {
+	if (!_lua)
+		return; // handles calling this method after script is finalized
+
     if (func == NULL)
     {
         GP_ERROR("Lua function name must be non-null.");