mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-04-21 10:28:44 -05:00
Add the ability for LuaLibrary%s to reference script files that will be executed (closing #328)
Create documentation for Lua scripts Add Lua script to check if a file exists
This commit is contained in:
+1
-1
Submodule ext/ghoul updated: 56bd0eb34a...a8489bba6c
@@ -55,8 +55,24 @@ struct LuaLibrary {
|
||||
};
|
||||
/// The name of the library
|
||||
std::string name;
|
||||
/// The list of all functions for this library
|
||||
/// The list of all C-based callback functions for this library
|
||||
std::vector<Function> functions;
|
||||
/// A list of script files that are executed for each Lua state
|
||||
std::vector<std::string> scripts;
|
||||
|
||||
/// This struct contains information about a function or constant that is defined in
|
||||
/// a Lua script
|
||||
struct Documentation {
|
||||
/// The name of the function/variable
|
||||
std::string name;
|
||||
/// The description of the parameters for a function
|
||||
std::string parameter;
|
||||
/// The description of the function/variable
|
||||
std::string description;
|
||||
};
|
||||
/// The list of documentations will be populated automatically by parsing the Lua
|
||||
/// scripts
|
||||
std::vector<Documentation> documentations;
|
||||
|
||||
/// Comparison function that compares two LuaLibrary%s name
|
||||
bool operator<(const LuaLibrary& rhs) const;
|
||||
|
||||
@@ -101,8 +101,8 @@ public:
|
||||
static std::string OpenSpaceLibraryName;
|
||||
|
||||
private:
|
||||
bool registerLuaLibrary(lua_State* state, const LuaLibrary& library);
|
||||
void addLibraryFunctions(lua_State* state, const LuaLibrary& library, bool replace);
|
||||
bool registerLuaLibrary(lua_State* state, LuaLibrary& library);
|
||||
void addLibraryFunctions(lua_State* state, LuaLibrary& library, bool replace);
|
||||
|
||||
bool isLibraryNameAllowed(lua_State* state, const std::string& name);
|
||||
|
||||
@@ -112,7 +112,8 @@ private:
|
||||
std::string generateJson() const override;
|
||||
|
||||
ghoul::lua::LuaState _state;
|
||||
std::set<LuaLibrary> _registeredLibraries;
|
||||
std::vector<LuaLibrary> _registeredLibraries;
|
||||
|
||||
|
||||
//sync variables
|
||||
std::mutex _mutex;
|
||||
|
||||
@@ -111,4 +111,14 @@ globebrowsing::cache::MemoryAwareTileCache* GlobeBrowsingModule::tileCache() {
|
||||
return _tileCache.get();
|
||||
}
|
||||
|
||||
scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const {
|
||||
return {
|
||||
"globebrowsing",
|
||||
{},
|
||||
{
|
||||
"${MODULE_GLOBEBROWSING}/scripts/layer_support.lua"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace openspace
|
||||
|
||||
@@ -38,14 +38,18 @@ namespace cache {
|
||||
|
||||
class GlobeBrowsingModule : public OpenSpaceModule {
|
||||
public:
|
||||
static const std::string name;
|
||||
|
||||
GlobeBrowsingModule();
|
||||
|
||||
globebrowsing::cache::MemoryAwareTileCache* tileCache();
|
||||
|
||||
static const std::string name;
|
||||
|
||||
scripting::LuaLibrary luaLibrary() const override;
|
||||
|
||||
protected:
|
||||
void internalInitialize() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<globebrowsing::cache::MemoryAwareTileCache> _tileCache;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
openspace.globebrowsing.documentation = {
|
||||
{
|
||||
Name = "createTextureLayers",
|
||||
Arguments = "table",
|
||||
Documentation = "Creates a table used in the 'ColorLayers', 'GrayScaleLayers', or 'GrayScaleColorOverlays' of a RenderableGlobe."
|
||||
},
|
||||
{
|
||||
Name = "createHeightLayers",
|
||||
Arguments = "table",
|
||||
Documentation = "Creates a table used in the 'HeightLayers' of a RenderableGlobe."
|
||||
}
|
||||
}
|
||||
|
||||
-- Creates a table used in the 'ColorLayers', 'GrayScaleLayers', or 'GrayScaleColorOverlays'
|
||||
-- of a RenderableGlobe
|
||||
-- Usage:
|
||||
-- table.unpack(openspace.globebrowsing.createTextureLayers(p))
|
||||
-- where p is an array that contains tables with 'Name' and 'Texture' values
|
||||
openspace.globebrowsing.createTextureLayers = function (patches)
|
||||
result = {}
|
||||
for k,v in pairs(patches) do
|
||||
table.insert(result, { Name = v["Name"], FilePath = v["Texture"] })
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Creates a table used in the 'HeightLayers' of a RenderableGlobe
|
||||
-- Usage:
|
||||
-- table.unpack(openspace.globebrowsing.openspace.globebrowsing.createHeightLayers(p))
|
||||
-- where p is an array that contains tables with 'Name' and 'Height' values
|
||||
openspace.globebrowsing.createHeightLayers = function (patches)
|
||||
result = {}
|
||||
for k,v in pairs(patches) do
|
||||
table.insert(result, { Name = v["Name"], FilePath = v["Height"], TilePixelSize = 90, DoPreProcessing = true })
|
||||
end
|
||||
return result
|
||||
end
|
||||
@@ -311,7 +311,7 @@ void RenderEngine::deinitialize() {
|
||||
}
|
||||
|
||||
void RenderEngine::updateScene() {
|
||||
const Time& currentTime = OsEng.timeManager().time();
|
||||
const Time& currentTime = OsEng.timeManager().time();
|
||||
_scene->update({
|
||||
{ glm::dvec3(0), glm::dmat3(1), 1.0 },
|
||||
currentTime,
|
||||
@@ -485,9 +485,9 @@ void RenderEngine::renderShutdownInformation(float timer, float fullTime) {
|
||||
}
|
||||
|
||||
void RenderEngine::postDraw() {
|
||||
Time& currentTime = OsEng.timeManager().time();
|
||||
Time& currentTime = OsEng.timeManager().time();
|
||||
if (currentTime.timeJumped()) {
|
||||
currentTime.setTimeJumped(false);
|
||||
currentTime.setTimeJumped(false);
|
||||
}
|
||||
|
||||
if (_shouldTakeScreenshot) {
|
||||
|
||||
+109
-48
@@ -76,8 +76,8 @@ void ScriptEngine::initialize() {
|
||||
addBaseLibrary();
|
||||
LDEBUG("Initializing Lua state");
|
||||
initializeLuaState(_state);
|
||||
LDEBUG("Remapping Print functions");
|
||||
remapPrintFunction();
|
||||
//LDEBUG("Remapping Print functions");
|
||||
//remapPrintFunction();
|
||||
}
|
||||
|
||||
void ScriptEngine::deinitialize() {}
|
||||
@@ -88,7 +88,7 @@ void ScriptEngine::initializeLuaState(lua_State* state) {
|
||||
lua_setglobal(state, OpenSpaceLibraryName.c_str());
|
||||
|
||||
LDEBUG("Add OpenSpace modules");
|
||||
for (const LuaLibrary& lib : _registeredLibraries) {
|
||||
for (LuaLibrary& lib : _registeredLibraries) {
|
||||
registerLuaLibrary(state, lib);
|
||||
}
|
||||
}
|
||||
@@ -106,7 +106,8 @@ void ScriptEngine::addLibrary(LuaLibrary library) {
|
||||
if (it == _registeredLibraries.end()) {
|
||||
// If not, we can add it after we sorted it
|
||||
std::sort(library.functions.begin(), library.functions.end(), sortFunc);
|
||||
_registeredLibraries.insert(std::move(library));
|
||||
_registeredLibraries.push_back(std::move(library));
|
||||
std::sort(_registeredLibraries.begin(), _registeredLibraries.end());
|
||||
}
|
||||
else {
|
||||
// otherwise, we merge the libraries
|
||||
@@ -127,15 +128,21 @@ void ScriptEngine::addLibrary(LuaLibrary library) {
|
||||
"' has been defined twice");
|
||||
return;
|
||||
}
|
||||
else
|
||||
else {
|
||||
merged.functions.push_back(fun);
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& script : library.scripts) {
|
||||
merged.scripts.push_back(script);
|
||||
}
|
||||
|
||||
_registeredLibraries.erase(it);
|
||||
|
||||
// Sort the merged library before inserting it
|
||||
std::sort(merged.functions.begin(), merged.functions.end(), sortFunc);
|
||||
_registeredLibraries.insert(std::move(merged));
|
||||
_registeredLibraries.push_back(std::move(merged));
|
||||
std::sort(_registeredLibraries.begin(), _registeredLibraries.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,13 +352,11 @@ bool ScriptEngine::isLibraryNameAllowed(lua_State* state, const std::string& nam
|
||||
return result;
|
||||
}
|
||||
|
||||
void ScriptEngine::addLibraryFunctions(lua_State* state, const LuaLibrary& library, bool replace) {
|
||||
assert(state);
|
||||
void ScriptEngine::addLibraryFunctions(lua_State* state, LuaLibrary& library, bool replace) {
|
||||
ghoul_assert(state, "State must not be nullptr");
|
||||
for (LuaLibrary::Function p : library.functions) {
|
||||
if (!replace) {
|
||||
//ghoul::lua::logStack(_state);
|
||||
lua_getfield(state, -1, p.name.c_str());
|
||||
//ghoul::lua::logStack(_state);
|
||||
const bool isNil = lua_isnil(state, -1);
|
||||
if (!isNil) {
|
||||
LERROR("Function name '" << p.name << "' was already assigned");
|
||||
@@ -359,13 +364,49 @@ void ScriptEngine::addLibraryFunctions(lua_State* state, const LuaLibrary& libra
|
||||
}
|
||||
lua_pop(state, 1);
|
||||
}
|
||||
//ghoul::lua::logStack(_state);
|
||||
lua_pushstring(state, p.name.c_str());
|
||||
//ghoul::lua::logStack(_state);
|
||||
lua_pushcfunction(state, p.function);
|
||||
//ghoul::lua::logStack(_state);
|
||||
lua_settable(state, TableOffset);
|
||||
//ghoul::lua::logStack(_state);
|
||||
}
|
||||
|
||||
for (const std::string& script : library.scripts) {
|
||||
// First we run the script to set its values in the current state
|
||||
ghoul::lua::runScriptFile(state, absPath(script));
|
||||
|
||||
library.documentations.clear();
|
||||
// Then, we extract the documentation information from the file
|
||||
lua_pushstring(state, "documentation");
|
||||
lua_gettable(state, -2);
|
||||
if (lua_isnil(state, -1)) {
|
||||
LERROR(
|
||||
"Module '" << library.name << "' did not provide a documentation in " <<
|
||||
"script file '" << script << "'");
|
||||
}
|
||||
else {
|
||||
lua_pushnil(state);
|
||||
while (lua_next(state, -2)) {
|
||||
lua_pushstring(state, "Name");
|
||||
lua_gettable(state, -2);
|
||||
const std::string name = lua_tostring(state, -1);
|
||||
lua_pop(state, 1);
|
||||
|
||||
lua_pushstring(state, "Arguments");
|
||||
lua_gettable(state, -2);
|
||||
const std::string arguments = lua_tostring(state, -1);
|
||||
lua_pop(state, 1);
|
||||
|
||||
lua_pushstring(state, "Documentation");
|
||||
lua_gettable(state, -2);
|
||||
const std::string documentation = lua_tostring(state, -1);
|
||||
lua_pop(state, 1);
|
||||
|
||||
lua_pop(state, 1);
|
||||
|
||||
library.documentations.push_back({ name, arguments, documentation });
|
||||
|
||||
}
|
||||
lua_pop(state, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,6 +463,12 @@ void ScriptEngine::addBaseLibrary() {
|
||||
"Returns the absolute path to the passed path, resolving path tokens as "
|
||||
"well as resolving relative paths"
|
||||
},
|
||||
{
|
||||
"fileExists",
|
||||
&luascriptfunctions::fileExists,
|
||||
"string",
|
||||
"Checks whether the provided file exists."
|
||||
},
|
||||
{
|
||||
"setPathToken",
|
||||
&luascriptfunctions::setPathToken,
|
||||
@@ -476,42 +523,37 @@ void ScriptEngine::remapPrintFunction() {
|
||||
//ghoul::lua::logStack(_state);
|
||||
}
|
||||
|
||||
bool ScriptEngine::registerLuaLibrary(lua_State* state, const LuaLibrary& library) {
|
||||
bool ScriptEngine::registerLuaLibrary(lua_State* state, LuaLibrary& library) {
|
||||
ghoul_assert(state, "State must not be nullptr");
|
||||
|
||||
if (library.functions.empty()) {
|
||||
LERROR("Lua library '" << library.name << "' does not have any functions");
|
||||
return false;
|
||||
}
|
||||
|
||||
//ghoul::lua::logStack(_state);
|
||||
lua_getglobal(state, OpenSpaceLibraryName.c_str());
|
||||
//ghoul::lua::logStack(_state);
|
||||
if (library.name.empty()) {
|
||||
//ghoul::lua::logStack(_state);
|
||||
addLibraryFunctions(state, library, true);
|
||||
//ghoul::lua::logStack(_state);
|
||||
lua_pop(state, 1);
|
||||
//ghoul::lua::logStack(_state);
|
||||
}
|
||||
else {
|
||||
const bool allowed = isLibraryNameAllowed(state, library.name);
|
||||
if (!allowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//ghoul::lua::logStack(_state);
|
||||
|
||||
lua_pushstring(state, library.name.c_str());
|
||||
//ghoul::lua::logStack(_state);
|
||||
lua_newtable(state);
|
||||
//ghoul::lua::logStack(_state);
|
||||
addLibraryFunctions(state, library, false);
|
||||
lua_settable(state, TableOffset);
|
||||
//ghoul::lua::logStack(_state);
|
||||
|
||||
//_registeredLibraries.insert(library);
|
||||
//_registeredLibraries.push_back(library);
|
||||
// We need to first create the table and then retrieve it as the table will
|
||||
// probably be used by scripts already
|
||||
|
||||
// Add the table
|
||||
lua_pushstring(state, library.name.c_str());
|
||||
lua_newtable(state);
|
||||
lua_settable(state, TableOffset);
|
||||
|
||||
// Retrieve the table
|
||||
lua_pushstring(state, library.name.c_str());
|
||||
lua_gettable(state, -2);
|
||||
|
||||
// Add the library functions into the table
|
||||
addLibraryFunctions(state, library, false);
|
||||
|
||||
// Pop the table
|
||||
lua_pop(state, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -522,11 +564,21 @@ std::vector<std::string> ScriptEngine::allLuaFunctions() const {
|
||||
for (const LuaLibrary& library : _registeredLibraries) {
|
||||
for (const LuaLibrary::Function& function : library.functions) {
|
||||
std::string total = "openspace.";
|
||||
if (!library.name.empty())
|
||||
if (!library.name.empty()) {
|
||||
total += library.name + ".";
|
||||
}
|
||||
total += function.name;
|
||||
result.push_back(std::move(total));
|
||||
}
|
||||
|
||||
for (const LuaLibrary::Documentation& doc : library.documentations) {
|
||||
std::string total = "openspace.";
|
||||
if (!library.name.empty()) {
|
||||
total += library.name + ".";
|
||||
}
|
||||
total += doc.name;
|
||||
result.push_back(std::move(total));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -554,10 +606,23 @@ std::string ScriptEngine::generateJson() const {
|
||||
json << "\"arguments\": \"" << f.argumentText << "\", ";
|
||||
json << "\"help\": \"" << f.helpText << "\"";
|
||||
json << "}";
|
||||
if (&f != &l.functions.back()) {
|
||||
if (&f != &l.functions.back() || !l.documentations.empty()) {
|
||||
json << ",";
|
||||
}
|
||||
}
|
||||
|
||||
for (const LuaLibrary::Documentation& doc : l.documentations) {
|
||||
json << "{";
|
||||
json << "\"name\": \"" << doc.name << "\", ";
|
||||
json << "\"arguments\": \"" << doc.parameter<< "\", ";
|
||||
json << "\"help\": \"" << doc.description<< "\"";
|
||||
json << "}";
|
||||
if (&doc != &l.documentations.back()) {
|
||||
json << ",";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
json << "]}";
|
||||
|
||||
}
|
||||
@@ -624,15 +689,15 @@ bool ScriptEngine::writeLog(const std::string& script) {
|
||||
}
|
||||
|
||||
void ScriptEngine::presync(bool isMaster) {
|
||||
if (!isMaster) return;
|
||||
if (!isMaster) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mutex.lock();
|
||||
|
||||
if (!_queuedScripts.empty()) {
|
||||
_currentSyncedScript = _queuedScripts.back().first;
|
||||
bool remoteScripting = _queuedScripts.back().second;
|
||||
|
||||
|
||||
//Not really a received script but the master also needs to run the script...
|
||||
_receivedScripts.push_back(_currentSyncedScript);
|
||||
_queuedScripts.pop_back();
|
||||
@@ -640,11 +705,8 @@ void ScriptEngine::presync(bool isMaster) {
|
||||
if (OsEng.parallelConnection().isHost() && remoteScripting) {
|
||||
OsEng.parallelConnection().sendScript(_currentSyncedScript);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_mutex.unlock();
|
||||
|
||||
}
|
||||
|
||||
void ScriptEngine::encode(SyncBuffer* syncBuffer) {
|
||||
@@ -682,13 +744,12 @@ void ScriptEngine::postsync(bool) {
|
||||
}
|
||||
|
||||
void ScriptEngine::queueScript(const std::string &script, ScriptEngine::RemoteScripting remoteScripting){
|
||||
if (script.empty())
|
||||
if (script.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mutex.lock();
|
||||
|
||||
_queuedScripts.insert(_queuedScripts.begin(), std::make_pair(script, remoteScripting));
|
||||
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
@@ -226,6 +226,30 @@ int setPathToken(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup LuaScripts
|
||||
* walkDirectory(string, bool, bool):
|
||||
* Walks a directory and returns the contents of the directory as absolute paths. The
|
||||
* first argument is the path of the directory that should be walked, the second argument
|
||||
* determines if the walk is recursive and will continue in contained directories. The
|
||||
* default value for this parameter is "false". The third argument determines whether the
|
||||
* table that is returned is sorted. The default value for this parameter is "false".
|
||||
*/
|
||||
int fileExists(lua_State* L) {
|
||||
const int nArguments = lua_gettop(L);
|
||||
if (nArguments != 1) {
|
||||
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
|
||||
}
|
||||
|
||||
const std::string file = luaL_checkstring(L, -1);
|
||||
const bool e = FileSys.fileExists(absPath(file));
|
||||
lua_pushboolean(
|
||||
L,
|
||||
e ? 1 : 0
|
||||
);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup LuaScripts
|
||||
* walkDirectory(string, bool, bool):
|
||||
|
||||
Reference in New Issue
Block a user