Browse Source

Exposed Engine's runFrame() and associated sub-functions to script.
Moved ScriptTest scene init & frame iteration fully to script.
Added optional retained mode script engine logging. This is used for collecting errors during script module compile.

Lasse Öörni 15 years ago
parent
commit
044ef4d5a9

+ 14 - 5
Bin/Data/Scripts/NinjaSnowWar.as

@@ -13,13 +13,12 @@ float mouseSensitivity = 0.125;
 float cameraMinDist = 25;
 float cameraMinDist = 25;
 float cameraMaxDist = 500;
 float cameraMaxDist = 500;
 float cameraSafetyDist = 30;
 float cameraSafetyDist = 30;
+bool paused = false;
 
 
-void init(Scene@ scene)
+void init()
 {
 {
-    @gameScene = @scene;
-
     initAudio();
     initAudio();
-    loadScene();
+    initScene();
     createCamera();
     createCamera();
     createOverlays();
     createOverlays();
     spawnPlayer();
     spawnPlayer();
@@ -31,6 +30,14 @@ void init(Scene@ scene)
     subscribeToEvent("PostUpdate", "handlePostUpdate");
     subscribeToEvent("PostUpdate", "handlePostUpdate");
 }
 }
 
 
+void runFrame()
+{
+    engine.runFrame(gameScene, gameCamera, !paused);
+    
+    if (input.getKeyPress(KEY_ESCAPE))
+        engine.exit();
+}
+
 void initAudio()
 void initAudio()
 {
 {
     // Lower mastervolumes slightly
     // Lower mastervolumes slightly
@@ -42,8 +49,10 @@ void initAudio()
     song.play(0);
     song.play(0);
 }
 }
 
 
-void loadScene()
+void initScene()
 {
 {
+    @gameScene = engine.createScene("ScriptTest", BoundingBox(-100000.0, 100000.0), 8, true);
+
     File@ levelFile = cache.getFile("TestLevel.xml");
     File@ levelFile = cache.getFile("TestLevel.xml");
     gameScene.loadXML(levelFile);
     gameScene.loadXML(levelFile);
 }
 }

+ 4 - 1
Engine/Engine/RegisterEngine.cpp

@@ -389,6 +389,7 @@ static void registerEngine(asIScriptEngine* engine)
     engine->RegisterObjectType("Engine", 0, asOBJ_REF);
     engine->RegisterObjectType("Engine", 0, asOBJ_REF);
     engine->RegisterObjectBehaviour("Engine", asBEHAVE_ADDREF, "void f()", asMETHOD(Engine, addRef), asCALL_THISCALL);
     engine->RegisterObjectBehaviour("Engine", asBEHAVE_ADDREF, "void f()", asMETHOD(Engine, addRef), asCALL_THISCALL);
     engine->RegisterObjectBehaviour("Engine", asBEHAVE_RELEASE, "void f()", asMETHOD(Engine, releaseRef), asCALL_THISCALL);
     engine->RegisterObjectBehaviour("Engine", asBEHAVE_RELEASE, "void f()", asMETHOD(Engine, releaseRef), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Engine", "void runFrame(Scene@+, Camera@+, bool)", asMETHOD(Engine, runFrame), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "Scene@ createScene(const string& in, const BoundingBox& in, uint, bool)", asFUNCTION(EngineCreateScene), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Engine", "Scene@ createScene(const string& in, const BoundingBox& in, uint, bool)", asFUNCTION(EngineCreateScene), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Engine", "Client@+ createClient(const string& in)", asMETHOD(Engine, createClient), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "Client@+ createClient(const string& in)", asMETHOD(Engine, createClient), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "Server@+ createServer()", asMETHOD(Engine, createServer), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "Server@+ createServer()", asMETHOD(Engine, createServer), asCALL_THISCALL);
@@ -411,7 +412,9 @@ static void registerEngine(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Engine", "bool isInitialized() const", asMETHOD(Engine, isInitialized), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "bool isInitialized() const", asMETHOD(Engine, isInitialized), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "bool isExiting() const", asMETHOD(Engine, isExiting), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "bool isExiting() const", asMETHOD(Engine, isExiting), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "bool isHeadless() const", asMETHOD(Engine, isHeadless), asCALL_THISCALL);
     engine->RegisterObjectMethod("Engine", "bool isHeadless() const", asMETHOD(Engine, isHeadless), asCALL_THISCALL);
-    
+    engine->RegisterObjectMethod("Engine", "float getNextTimeStep()", asMETHOD(Engine, getNextTimeStep), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Engine", "void update(float, Scene@+, Camera@+, bool)", asMETHOD(Engine, update), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Engine", "void render()", asMETHOD(Engine, render), asCALL_THISCALL);
     engine->RegisterGlobalFunction("Engine@+ getEngine()", asFUNCTION(getEngine), asCALL_CDECL);
     engine->RegisterGlobalFunction("Engine@+ getEngine()", asFUNCTION(getEngine), asCALL_CDECL);
     engine->RegisterGlobalFunction("Engine@+ get_engine()", asFUNCTION(getEngine), asCALL_CDECL);
     engine->RegisterGlobalFunction("Engine@+ get_engine()", asFUNCTION(getEngine), asCALL_CDECL);
 }
 }

+ 44 - 18
Engine/Script/ScriptEngine.cpp

@@ -34,24 +34,10 @@
 
 
 #include "DebugNew.h"
 #include "DebugNew.h"
 
 
-void messageCallback(const asSMessageInfo *msg, void *param)
+void messageCallback(const asSMessageInfo* msg, void* param)
 {
 {
-    std::string message = std::string(msg->section) + " (" + toString(msg->row) + "," + toString(msg->col) + ") " + std::string(msg->message);
-    
-    switch (msg->type)
-    {
-    case asMSGTYPE_ERROR:
-        LOGERROR(message);
-        break;
-        
-    case asMSGTYPE_WARNING:
-        LOGWARNING(message);
-        break;
-        
-    default:
-        LOGINFO(message);
-        break;
-    }
+    ScriptEngine* engine = static_cast<ScriptEngine*>(param);
+    engine->logMessage(msg);
 }
 }
 
 
 ScriptEngine::ScriptEngine() :
 ScriptEngine::ScriptEngine() :
@@ -67,7 +53,7 @@ ScriptEngine::ScriptEngine() :
     mAngelScriptEngine->SetUserData(this);
     mAngelScriptEngine->SetUserData(this);
     mAngelScriptEngine->SetEngineProperty(asEP_USE_CHARACTER_LITERALS, true);
     mAngelScriptEngine->SetEngineProperty(asEP_USE_CHARACTER_LITERALS, true);
     mAngelScriptEngine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, true);
     mAngelScriptEngine->SetEngineProperty(asEP_ALLOW_UNSAFE_REFERENCES, true);
-    mAngelScriptEngine->SetMessageCallback(asFUNCTION(messageCallback), 0, asCALL_CDECL);
+    mAngelScriptEngine->SetMessageCallback(asFUNCTION(messageCallback), this, asCALL_CDECL);
     
     
     // Register the array and string types, but leave it for the script engine instantiator to install the rest of the API
     // Register the array and string types, but leave it for the script engine instantiator to install the rest of the API
     {
     {
@@ -140,3 +126,43 @@ void ScriptEngine::garbageCollect()
     
     
     mAngelScriptEngine->GarbageCollect(asGC_ONE_STEP);
     mAngelScriptEngine->GarbageCollect(asGC_ONE_STEP);
 }
 }
+
+void ScriptEngine::setLogMode(ScriptLogMode mode)
+{
+    mLogMode = mode;
+}
+
+void ScriptEngine::clearLogMessages()
+{
+    mLogMessages.clear();
+}
+
+void ScriptEngine::logMessage(const asSMessageInfo* msg)
+{
+    std::string message = std::string(msg->section) + " (" + toString(msg->row) + "," + toString(msg->col) + ") " +
+        std::string(msg->message);
+    
+    if (mLogMode == LOGMODE_IMMEDIATE)
+    {
+        switch (msg->type)
+        {
+        case asMSGTYPE_ERROR:
+            LOGERROR(message);
+            break;
+            
+        case asMSGTYPE_WARNING:
+            LOGWARNING(message);
+            break;
+            
+        default:
+            LOGINFO(message);
+            break;
+        }
+    }
+    else
+    {
+        // In retained mode, ignore info messages
+        if ((msg->type == asMSGTYPE_ERROR) || (msg->type == asMSGTYPE_WARNING))
+            mLogMessages += message + "\n";
+    }
+}

+ 23 - 1
Engine/Script/ScriptEngine.h

@@ -28,12 +28,20 @@
 
 
 class asIScriptEngine;
 class asIScriptEngine;
 class asIScriptContext;
 class asIScriptContext;
+struct asSMessageInfo;
+
+//! Script engine logging mode
+enum ScriptLogMode
+{
+    LOGMODE_IMMEDIATE = 0,
+    LOGMODE_RETAINED
+};
 
 
 //! Utilizes the AngelScript library for executing scripts
 //! Utilizes the AngelScript library for executing scripts
 class ScriptEngine : public RefCounted
 class ScriptEngine : public RefCounted
 {
 {
 public:
 public:
-    //! Construct. Create the AngelScript engine and register the application interface
+    //! Construct. Create the AngelScript engine and register string & array classes
     ScriptEngine();
     ScriptEngine();
     //! Destruct. Release the AngelScript engine
     //! Destruct. Release the AngelScript engine
     ~ScriptEngine();
     ~ScriptEngine();
@@ -44,17 +52,31 @@ public:
     bool execute(const std::string& line);
     bool execute(const std::string& line);
     //! Perform garbage collection
     //! Perform garbage collection
     void garbageCollect();
     void garbageCollect();
+    //! Set script engine logging mode, immediate is default
+    void setLogMode(ScriptLogMode mode);
+    //! Clear retained mode log messages
+    void clearLogMessages();
+    //! Log a message from the script engine
+    void logMessage(const asSMessageInfo* msg);
     
     
     //! Return the AngelScript engine
     //! Return the AngelScript engine
     asIScriptEngine* getAngelScriptEngine() const { return mAngelScriptEngine; }
     asIScriptEngine* getAngelScriptEngine() const { return mAngelScriptEngine; }
     //! Return immediate execution script context
     //! Return immediate execution script context
     asIScriptContext* getImmediateContext() const { return mImmediateContext; }
     asIScriptContext* getImmediateContext() const { return mImmediateContext; }
+    //! Return logging mode
+    ScriptLogMode getLogMode() const;
+    //! Return retained mode log messages
+    const std::string& getLogMessages() const { return mLogMessages; }
     
     
 private:
 private:
     //! AngelScript engine
     //! AngelScript engine
     asIScriptEngine* mAngelScriptEngine;
     asIScriptEngine* mAngelScriptEngine;
     //! Immediate execution script context
     //! Immediate execution script context
     asIScriptContext* mImmediateContext;
     asIScriptContext* mImmediateContext;
+    //! Script engine logging mode
+    ScriptLogMode mLogMode;
+    //! Retained mode log messages
+    std::string mLogMessages;
 };
 };
 
 
 #endif // SCRIPT_SCRIPTENGINE_H
 #endif // SCRIPT_SCRIPTENGINE_H

+ 10 - 3
Engine/Script/ScriptFile.cpp

@@ -88,9 +88,16 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
     // Add the initial section and check for includes
     // Add the initial section and check for includes
     addScriptSection(engine, source, cache);
     addScriptSection(engine, source, cache);
     
     
-    // Compile
-    if (mScriptModule->Build() < 0)
-        EXCEPTION("Failed to build script module " + getName());
+    // Compile. Set script engine logging to retained mode so that potential exceptions can show all error info
+    mScriptEngine->setLogMode(LOGMODE_RETAINED);
+    mScriptEngine->clearLogMessages();
+    int result = mScriptModule->Build();
+    mScriptEngine->setLogMode(LOGMODE_IMMEDIATE);
+    if (result < 0)
+    {
+        std::string errors = mScriptEngine->getLogMessages();
+        EXCEPTION("Failed to compile script module " + getName() + ":\n" + errors);
+    }
     
     
     LOGINFO("Compiled script module " + getName());
     LOGINFO("Compiled script module " + getName());
     mCompiled = true;
     mCompiled = true;

+ 0 - 1
Engine/Script/ScriptFile.h

@@ -41,7 +41,6 @@ class asIScriptFunction;
 class asIScriptModule;
 class asIScriptModule;
 class asIScriptObject;
 class asIScriptObject;
 
 
-
 //! A script file resource
 //! A script file resource
 class ScriptFile : public Resource, public ScriptEventListener
 class ScriptFile : public Resource, public ScriptEventListener
 {
 {

+ 11 - 22
Examples/ScriptTest/Game.cpp

@@ -47,8 +47,7 @@
 #include "DebugNew.h"
 #include "DebugNew.h"
 
 
 Game::Game(const std::vector<std::string>& arguments) :
 Game::Game(const std::vector<std::string>& arguments) :
-    mArguments(arguments),
-    mPaused(false)
+    mArguments(arguments)
 {
 {
     std::string userDir = getUserDocumentsDirectory();
     std::string userDir = getUserDocumentsDirectory();
     std::string applicationDir = userDir + "ScriptTest";
     std::string applicationDir = userDir + "ScriptTest";
@@ -64,6 +63,7 @@ Game::Game(const std::vector<std::string>& arguments) :
 Game::~Game()
 Game::~Game()
 {
 {
     // The scripts hold references to engine subsystems, not releasing them shows up as numerous memory leaks
     // The scripts hold references to engine subsystems, not releasing them shows up as numerous memory leaks
+    mScriptFile.reset();
     mCache->releaseResources(ShortStringHash("ScriptFile"), true);
     mCache->releaseResources(ShortStringHash("ScriptFile"), true);
 }
 }
 
 
@@ -71,19 +71,11 @@ void Game::run()
 {
 {
     init();
     init();
     
     
+    if (!mRunFrameFunction)
+        return;
+    
     while (!mEngine->isExiting())
     while (!mEngine->isExiting())
-    {
-        Entity* cameraEntity = mScene->getEntity("Camera");
-        Camera* camera = 0;
-        if (cameraEntity)
-            camera = cameraEntity->getComponent<Camera>();
-        
-        mEngine->runFrame(mScene, camera, !mPaused);
-        
-        Input* input = mEngine->getInput();
-        if (input->getKeyPress(KEY_ESCAPE))
-            mEngine->exit();
-    }
+        mScriptFile->execute(mRunFrameFunction);
 }
 }
 
 
 void Game::init()
 void Game::init()
@@ -104,8 +96,6 @@ void Game::init()
     
     
     mEngine->init("ScriptTest", mArguments);
     mEngine->init("ScriptTest", mArguments);
     mEngine->createScriptEngine();
     mEngine->createScriptEngine();
-    // Register the pause variable for script access
-    mEngine->getScriptEngine()->getAngelScriptEngine()->RegisterGlobalProperty("bool paused", &mPaused);
     
     
     mCache->addResourcePath(getSystemFontDirectory());
     mCache->addResourcePath(getSystemFontDirectory());
     
     
@@ -120,13 +110,12 @@ void Game::init()
     
     
     createSkyPlaneModel();
     createSkyPlaneModel();
     
     
-    mScene = mEngine->createScene("ScriptTest", BoundingBox(-100000.0f, 100000.f));
+    // Execute the rest of initialization, including scene creation, in script
+    mScriptFile = mCache->getResource<ScriptFile>("Scripts/NinjaSnowWar.as");
+    mInitFunction = mScriptFile->getFunction("void init()");
+    mRunFrameFunction = mScriptFile->getFunction("void runFrame()");
     
     
-    // Execute the rest of initialization in script
-    ScriptFile* script = mCache->getResource<ScriptFile>("Scripts/NinjaSnowWar.as");
-    std::vector<Variant> arguments;
-    arguments.push_back(Variant((void*)mScene.getPtr()));
-    script->execute("void init(Scene@)", 0, arguments);
+    mScriptFile->execute(mInitFunction);
 }
 }
 
 
 void Game::createSkyPlaneModel()
 void Game::createSkyPlaneModel()

+ 5 - 2
Examples/ScriptTest/Game.h

@@ -30,6 +30,8 @@
 class Engine;
 class Engine;
 class ResourceCache;
 class ResourceCache;
 class Scene;
 class Scene;
+class ScriptFile;
+class asIScriptFunction;
 
 
 class Game : public EventListener
 class Game : public EventListener
 {
 {
@@ -47,8 +49,9 @@ private:
     
     
     SharedPtr<Engine> mEngine;
     SharedPtr<Engine> mEngine;
     SharedPtr<ResourceCache> mCache;
     SharedPtr<ResourceCache> mCache;
-    SharedPtr<Scene> mScene;
-    bool mPaused;
+    SharedPtr<ScriptFile> mScriptFile;
+    asIScriptFunction* mInitFunction;
+    asIScriptFunction* mRunFrameFunction;
 };
 };
 
 
 #endif // GAME_H
 #endif // GAME_H