On-demand loading of Nasal modules.
Load a module whenever its /nasal/foo/enabled flag is set, even if it was disabled at start-up. Also expose a property if it was successfully loaded.
This commit is contained in:
parent
731e803223
commit
93180eb004
2 changed files with 99 additions and 53 deletions
|
@ -38,6 +38,31 @@
|
|||
|
||||
static FGNasalSys* nasalSys = 0;
|
||||
|
||||
// Listener class for loading Nasal modules on demand
|
||||
class FGNasalModuleListener : public SGPropertyChangeListener
|
||||
{
|
||||
public:
|
||||
FGNasalModuleListener(SGPropertyNode* node);
|
||||
|
||||
virtual void valueChanged(SGPropertyNode* node);
|
||||
|
||||
private:
|
||||
SGPropertyNode_ptr _node;
|
||||
};
|
||||
|
||||
FGNasalModuleListener::FGNasalModuleListener(SGPropertyNode* node) : _node(node)
|
||||
{
|
||||
}
|
||||
|
||||
void FGNasalModuleListener::valueChanged(SGPropertyNode*)
|
||||
{
|
||||
if (_node->getBoolValue("enabled",false)&&
|
||||
!_node->getBoolValue("loaded",true))
|
||||
{
|
||||
nasalSys->loadPropertyScripts(_node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read and return file contents in a single buffer. Note use of
|
||||
// stat() to get the file size. This is a win32 function, believe it
|
||||
|
@ -817,56 +842,73 @@ void FGNasalSys::loadPropertyScripts()
|
|||
SGPropertyNode* nasal = globals->get_props()->getNode("nasal");
|
||||
if(!nasal) return;
|
||||
|
||||
for(int i=0; i<nasal->nChildren(); i++) {
|
||||
for(int i=0; i<nasal->nChildren(); i++)
|
||||
{
|
||||
SGPropertyNode* n = nasal->getChild(i);
|
||||
bool is_loaded = false;
|
||||
|
||||
const char* module = n->getName();
|
||||
if(n->hasChild("module"))
|
||||
module = n->getStringValue("module");
|
||||
if (n->getBoolValue("enabled",true))
|
||||
{
|
||||
// allow multiple files to be specified within a single
|
||||
// Nasal module tag
|
||||
int j = 0;
|
||||
SGPropertyNode *fn;
|
||||
bool file_specified = false;
|
||||
while((fn = n->getChild("file", j)) != NULL) {
|
||||
file_specified = true;
|
||||
const char* file = fn->getStringValue();
|
||||
SGPath p(file);
|
||||
if (!p.isAbsolute() || !p.exists())
|
||||
{
|
||||
p = globals->resolve_maybe_aircraft_path(file);
|
||||
}
|
||||
loadModule(p, module);
|
||||
j++;
|
||||
}
|
||||
|
||||
const char* src = n->getStringValue("script");
|
||||
if(!n->hasChild("script")) src = 0; // Hrm...
|
||||
if(src)
|
||||
createModule(module, n->getPath().c_str(), src, strlen(src));
|
||||
|
||||
if(!file_specified && !src)
|
||||
{
|
||||
// module no longer exists - clear the archived "enable" flag
|
||||
n->setAttribute(SGPropertyNode::USERARCHIVE,false);
|
||||
SGPropertyNode* node = n->getChild("enabled",0,false);
|
||||
if (node)
|
||||
node->setAttribute(SGPropertyNode::USERARCHIVE,false);
|
||||
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
|
||||
"no <file> or <script> defined in " <<
|
||||
"/nasal/" << module);
|
||||
}
|
||||
else
|
||||
is_loaded = true;
|
||||
}
|
||||
n->setBoolValue("loaded",is_loaded);
|
||||
loadPropertyScripts(n);
|
||||
}
|
||||
}
|
||||
|
||||
// Loads the scripts found under /nasal in the global tree
|
||||
void FGNasalSys::loadPropertyScripts(SGPropertyNode* n)
|
||||
{
|
||||
bool is_loaded = false;
|
||||
|
||||
const char* module = n->getName();
|
||||
if(n->hasChild("module"))
|
||||
module = n->getStringValue("module");
|
||||
if (n->getBoolValue("enabled",true))
|
||||
{
|
||||
// allow multiple files to be specified within a single
|
||||
// Nasal module tag
|
||||
int j = 0;
|
||||
SGPropertyNode *fn;
|
||||
bool file_specified = false;
|
||||
bool ok=true;
|
||||
while((fn = n->getChild("file", j)) != NULL) {
|
||||
file_specified = true;
|
||||
const char* file = fn->getStringValue();
|
||||
SGPath p(file);
|
||||
if (!p.isAbsolute() || !p.exists())
|
||||
{
|
||||
p = globals->resolve_maybe_aircraft_path(file);
|
||||
}
|
||||
ok &= loadModule(p, module);
|
||||
j++;
|
||||
}
|
||||
|
||||
const char* src = n->getStringValue("script");
|
||||
if(!n->hasChild("script")) src = 0; // Hrm...
|
||||
if(src)
|
||||
createModule(module, n->getPath().c_str(), src, strlen(src));
|
||||
|
||||
if(!file_specified && !src)
|
||||
{
|
||||
// module no longer exists - clear the archived "enable" flag
|
||||
n->setAttribute(SGPropertyNode::USERARCHIVE,false);
|
||||
SGPropertyNode* node = n->getChild("enabled",0,false);
|
||||
if (node)
|
||||
node->setAttribute(SGPropertyNode::USERARCHIVE,false);
|
||||
|
||||
SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
|
||||
"no <file> or <script> defined in " <<
|
||||
"/nasal/" << module);
|
||||
}
|
||||
else
|
||||
is_loaded = ok;
|
||||
}
|
||||
else
|
||||
{
|
||||
SGPropertyNode* enable = n->getChild("enabled");
|
||||
if (enable)
|
||||
{
|
||||
FGNasalModuleListener* listener = new FGNasalModuleListener(n);
|
||||
enable->addChangeListener(listener, false);
|
||||
}
|
||||
}
|
||||
n->setBoolValue("loaded",is_loaded);
|
||||
}
|
||||
|
||||
// Logs a runtime error, with stack trace, to the FlightGear log stream
|
||||
void FGNasalSys::logError(naContext context)
|
||||
{
|
||||
|
@ -884,7 +926,7 @@ void FGNasalSys::logError(naContext context)
|
|||
// Reads a script file, executes it, and places the resulting
|
||||
// namespace into the global namespace under the specified module
|
||||
// name.
|
||||
void FGNasalSys::loadModule(SGPath file, const char* module)
|
||||
bool FGNasalSys::loadModule(SGPath file, const char* module)
|
||||
{
|
||||
int len = 0;
|
||||
char* buf = readfile(file.c_str(), &len);
|
||||
|
@ -892,25 +934,26 @@ void FGNasalSys::loadModule(SGPath file, const char* module)
|
|||
SG_LOG(SG_NASAL, SG_ALERT,
|
||||
"Nasal error: could not read script file " << file.c_str()
|
||||
<< " into module " << module);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
createModule(module, file.c_str(), buf, len);
|
||||
bool ok = createModule(module, file.c_str(), buf, len);
|
||||
delete[] buf;
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Parse and run. Save the local variables namespace, as it will
|
||||
// become a sub-object of globals. The optional "arg" argument can be
|
||||
// used to pass an associated property node to the module, which can then
|
||||
// be accessed via cmdarg(). (This is, for example, used by XML dialogs.)
|
||||
void FGNasalSys::createModule(const char* moduleName, const char* fileName,
|
||||
bool FGNasalSys::createModule(const char* moduleName, const char* fileName,
|
||||
const char* src, int len,
|
||||
const SGPropertyNode* cmdarg,
|
||||
int argc, naRef* args)
|
||||
{
|
||||
naRef code = parse(fileName, src, len);
|
||||
if(naIsNil(code))
|
||||
return;
|
||||
return false;
|
||||
|
||||
// See if we already have a module hash to use. This allows the
|
||||
// user to, for example, add functions to the built-in math
|
||||
|
@ -925,6 +968,7 @@ void FGNasalSys::createModule(const char* moduleName, const char* fileName,
|
|||
|
||||
call(code, argc, args, locals);
|
||||
hashset(_globals, moduleName, locals);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGNasalSys::deleteModule(const char* moduleName)
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
|
||||
// Loads a nasal script from an external file and inserts it as a
|
||||
// global module of the specified name.
|
||||
void loadModule(SGPath file, const char* moduleName);
|
||||
bool loadModule(SGPath file, const char* moduleName);
|
||||
|
||||
// Simple hook to run arbitrary source code. Returns a bool to
|
||||
// indicate successful execution. Does *not* return any Nasal
|
||||
|
@ -54,7 +54,7 @@ public:
|
|||
// Callbacks for command and timer bindings
|
||||
virtual bool handleCommand(const SGPropertyNode* arg);
|
||||
|
||||
void createModule(const char* moduleName, const char* fileName,
|
||||
bool createModule(const char* moduleName, const char* fileName,
|
||||
const char* src, int len, const SGPropertyNode* cmdarg=0,
|
||||
int argc=0, naRef*args=0);
|
||||
|
||||
|
@ -66,6 +66,7 @@ public:
|
|||
private:
|
||||
friend class FGNasalScript;
|
||||
friend class FGNasalListener;
|
||||
friend class FGNasalModuleListener;
|
||||
|
||||
//
|
||||
// FGTimer subclass for handling Nasal timer callbacks.
|
||||
|
@ -86,6 +87,7 @@ private:
|
|||
static int _listenerId;
|
||||
|
||||
void loadPropertyScripts();
|
||||
void loadPropertyScripts(SGPropertyNode* n);
|
||||
void loadScriptDirectory(simgear::Dir nasalDir);
|
||||
void addModule(string moduleName, simgear::PathList scripts);
|
||||
void hashset(naRef hash, const char* key, naRef val);
|
||||
|
|
Loading…
Reference in a new issue