Script reloading support. ScriptInstances register themselves with ScriptFile so that on reload, objects can be deleted and recreated.

Automatic caching for ScriptFile::getFunction() into a std::map.
This commit is contained in:
Lasse Öörni 2011-01-26 21:00:16 +00:00
parent 2ebe2a36b4
commit 956cac7648
8 changed files with 73 additions and 28 deletions

View File

@ -180,7 +180,7 @@ void updateControls()
Entity@ playerEntity = gameScene.getEntity("ObjPlayer");
if (@playerEntity == null)
return;
ScriptInstance@ instance = playerEntity.getComponent("ScriptInstance");
GameObject@ object = cast<GameObject>(instance.getScriptObject());
object.setControls(playerControls);
@ -205,7 +205,7 @@ void updateCamera()
// Collide camera ray with static objects (collision mask 2)
Vector3 rayDir = (maxDist - minDist).getNormalized();
float rayDistance = cameraMaxDist - cameraMinDist + cameraSafetyDist;
array<PhysicsRaycastResult> result = gameScene.getPhysicsWorld().raycast(Ray(minDist, rayDir), rayDistance, 2);
array<PhysicsRaycastResult>@ result = gameScene.getPhysicsWorld().raycast(Ray(minDist, rayDir), rayDistance, 2);
if (result.length() > 0)
rayDistance = min(rayDistance, result[0].distance - cameraSafetyDist);

View File

@ -388,7 +388,6 @@ static CScriptArray* SceneGetEntitiesWithClass(const std::string& className, Sce
}
}
}
return vectorToHandleArray<Entity*>(result, "array<Entity@>");
}

View File

@ -28,6 +28,7 @@
#include "ResourceCache.h"
#include "ScriptEngine.h"
#include "ScriptFile.h"
#include "ScriptInstance.h"
#include "SharedArrayPtr.h"
#include "StringUtils.h"
@ -72,10 +73,17 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
{
PROFILE(Script_Load);
// Discard the previous module if there was one
// If script instances have created objects from this file, release them now
// Make a copy of the vector because the script instances will remove themselves from the member vector
std::vector<ScriptInstance*> instances = mScriptInstances;
for (std::vector<ScriptInstance*>::iterator i = instances.begin(); i != instances.end(); ++i)
(*i)->releaseObject();
// Discard the previous module if there was one, and clear search caches
mCompiled = false;
mAllIncludeFiles.clear();
mInterfaceFound.clear();
mCheckedClasses.clear();
mFunctions.clear();
setMemoryUse(0);
removeAllEventHandlers();
@ -101,6 +109,10 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
LOGINFO("Compiled script module " + getName());
mCompiled = true;
// Now let the script instances recreate their objects
for (std::vector<ScriptInstance*>::iterator i = instances.begin(); i != instances.end(); ++i)
(*i)->setScriptClass(this, (*i)->getClassName());
}
void ScriptFile::addEventHandler(StringHash eventType, const std::string& handlerName)
@ -262,8 +274,10 @@ asIScriptObject* ScriptFile::createObject(const std::string& className, asIScrip
// Ensure that the type implements the "ScriptObject" interface, so it can be returned also to script properly
bool found = false;
std::map<asIObjectType*, bool>::const_iterator i = mInterfaceFound.find(type);
if (i == mInterfaceFound.end())
std::map<asIObjectType*, bool>::const_iterator i = mCheckedClasses.find(type);
if (i != mCheckedClasses.end())
found = i->second;
else
{
unsigned numInterfaces = type->GetInterfaceCount();
for (unsigned j = 0; j < numInterfaces; ++j)
@ -275,10 +289,8 @@ asIScriptObject* ScriptFile::createObject(const std::string& className, asIScrip
break;
}
}
mInterfaceFound[type] = found;
mCheckedClasses[type] = found;
}
else
found = i->second;
if (!found)
{
LOGERROR("Script class " + className + " does not implement the ScriptObject interface");
@ -302,13 +314,19 @@ asIScriptObject* ScriptFile::createObject(const std::string& className, asIScrip
return obj;
}
asIScriptFunction* ScriptFile::getFunction(const std::string& declaration) const
asIScriptFunction* ScriptFile::getFunction(const std::string& declaration)
{
if (!mCompiled)
return 0;
std::map<std::string, asIScriptFunction*>::const_iterator i = mFunctions.find(declaration);
if (i != mFunctions.end())
return i->second;
int id = mScriptModule->GetFunctionIdByDecl(declaration.c_str());
return mScriptModule->GetFunctionDescriptorById(id);
asIScriptFunction* function = mScriptModule->GetFunctionDescriptorById(id);
mFunctions[declaration] = function;
return function;
}
asIScriptFunction* ScriptFile::getMethod(asIScriptObject* object, const std::string& declaration) const
@ -324,6 +342,23 @@ asIScriptFunction* ScriptFile::getMethod(asIScriptObject* object, const std::str
return mScriptModule->GetFunctionDescriptorById(id);
}
void ScriptFile::addScriptInstance(ScriptInstance* instance)
{
mScriptInstances.push_back(instance);
}
void ScriptFile::removeScriptInstance(ScriptInstance* instance)
{
for (std::vector<ScriptInstance*>::iterator i = mScriptInstances.begin(); i != mScriptInstances.end(); ++i)
{
if ((*i) == instance)
{
mScriptInstances.erase(i);
break;
}
}
}
void ScriptFile::addScriptSection(asIScriptEngine* engine, Deserializer& source, ResourceCache* cache)
{
unsigned dataSize = source.getSize();

View File

@ -33,6 +33,7 @@
#include <vector>
class ScriptEngine;
class ScriptInstance;
class Variant;
class asIObjectType;
class asIScriptContext;
@ -76,13 +77,18 @@ public:
ScriptEngine* getScriptEngine() const { return mScriptEngine; }
//! Return script module
asIScriptModule* getScriptModule() const { return mScriptModule; }
//! Return a function by declaration
asIScriptFunction* getFunction(const std::string& declaration) const;
//! Return a function by declaration. Will be stored to a search cache so that further searches should be faster
asIScriptFunction* getFunction(const std::string& declaration);
//! Return an object method by declaration
asIScriptFunction* getMethod(asIScriptObject* object, const std::string& declaration) const;
//! Return whether script compiled successfully
bool isCompiled() const { return mCompiled; }
//! Add script instance to keep track of in case of script reload
void addScriptInstance(ScriptInstance* instance);
//! Remove script instance to keep track of
void removeScriptInstance(ScriptInstance* instance);
private:
//! Add a script section, checking for includes recursively
void addScriptSection(asIScriptEngine* engine, Deserializer& source, ResourceCache* cache);
@ -102,7 +108,11 @@ private:
//! Encountered include files during script file loading
std::set<std::string> mAllIncludeFiles;
//! Search cache for checking whether script classes implement "ScriptObject" interface
std::map<asIObjectType*, bool> mInterfaceFound;
std::map<asIObjectType*, bool> mCheckedClasses;
//! Search cache for functions
std::map<std::string, asIScriptFunction*> mFunctions;
//! ScriptInstances that have created objects from this script file
std::vector<ScriptInstance*> mScriptInstances;
};
//! Get last script file that is executing or has executed script functions

View File

@ -275,7 +275,7 @@ bool ScriptInstance::setScriptClass(ScriptFile* scriptFile, const std::string& c
return false;
}
if ((scriptFile == mScriptFile) && (className == mClassName))
if ((mScriptObject) && (scriptFile == mScriptFile) && (className == mClassName))
return true;
releaseObject();
@ -293,6 +293,7 @@ bool ScriptInstance::setScriptClass(ScriptFile* scriptFile, const std::string& c
mScriptObject = mScriptFile->createObject(mClassName, mScriptContext);
if (mScriptObject)
{
mScriptFile->addScriptInstance(this);
objectToInstance[(void*)mScriptObject] = this;
getSupportedMethods();
if (mMethods[METHOD_START])
@ -365,6 +366,8 @@ void ScriptInstance::releaseObject()
mScriptObject->Release();
mScriptObject = 0;
mScriptFile->removeScriptInstance(this);
}
}

View File

@ -114,15 +114,15 @@ public:
asIScriptObject* getScriptObject() const { return mScriptObject; }
//! Return class name
const std::string& getClassName() const { return mClassName; }
//! Return whether object created and running
bool isRunning() const { return mScriptObject != 0; }
//! Return whether scripted updates and event handlers are enabled
bool isEnabled() const { return mEnabled; }
private:
//! Release object
void releaseObject();
private:
//! Clear supported methods
void clearMethods();
//! Check for supported methods

View File

@ -71,11 +71,14 @@ void Game::run()
{
init();
if (!mRunFrameFunction)
return;
while (!mEngine->isExiting())
mScriptFile->execute(mRunFrameFunction);
{
mScriptFile->execute("void runFrame()");
// Test reloading the ninja script
if (mEngine->getInput()->getKeyPress('R'))
mCache->reloadResource(mCache->getResource<ScriptFile>("Scripts/Ninja.as"));
}
}
void Game::init()
@ -112,10 +115,7 @@ void Game::init()
// 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()");
mScriptFile->execute(mInitFunction);
mScriptFile->execute("void init()");
}
void Game::createSkyPlaneModel()

View File

@ -50,8 +50,6 @@ private:
SharedPtr<Engine> mEngine;
SharedPtr<ResourceCache> mCache;
SharedPtr<ScriptFile> mScriptFile;
asIScriptFunction* mInitFunction;
asIScriptFunction* mRunFrameFunction;
};
#endif // GAME_H