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:
parent
2ebe2a36b4
commit
956cac7648
@ -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);
|
||||
|
||||
|
@ -388,7 +388,6 @@ static CScriptArray* SceneGetEntitiesWithClass(const std::string& className, Sce
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return vectorToHandleArray<Entity*>(result, "array<Entity@>");
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -50,8 +50,6 @@ private:
|
||||
SharedPtr<Engine> mEngine;
|
||||
SharedPtr<ResourceCache> mCache;
|
||||
SharedPtr<ScriptFile> mScriptFile;
|
||||
asIScriptFunction* mInitFunction;
|
||||
asIScriptFunction* mRunFrameFunction;
|
||||
};
|
||||
|
||||
#endif // GAME_H
|
||||
|
Loading…
Reference in New Issue
Block a user