2023-06-20 04:33:09 +00:00
|
|
|
#include "StarLuaRoot.hpp"
|
|
|
|
#include "StarAssets.hpp"
|
|
|
|
|
|
|
|
namespace Star {
|
|
|
|
|
|
|
|
LuaRoot::LuaRoot() {
|
|
|
|
auto& root = Root::singleton();
|
|
|
|
m_scriptCache = make_shared<ScriptCache>();
|
|
|
|
|
2023-07-04 09:27:16 +00:00
|
|
|
restart();
|
|
|
|
|
2023-06-20 04:33:09 +00:00
|
|
|
m_rootReloadListener = make_shared<CallbackListener>([cache = m_scriptCache]() {
|
|
|
|
cache->clear();
|
|
|
|
});
|
|
|
|
root.registerReloadListener(m_rootReloadListener);
|
|
|
|
|
|
|
|
m_storageDirectory = root.toStoragePath("lua");
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaRoot::~LuaRoot() {
|
2023-07-04 09:27:16 +00:00
|
|
|
shutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::loadScript(String const& assetPath) {
|
|
|
|
m_scriptCache->loadScript(*m_luaEngine, assetPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LuaRoot::scriptLoaded(String const& assetPath) const {
|
|
|
|
return m_scriptCache->scriptLoaded(assetPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::unloadScript(String const& assetPath) {
|
|
|
|
m_scriptCache->unloadScript(assetPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::restart() {
|
|
|
|
shutdown();
|
|
|
|
|
|
|
|
auto& root = Root::singleton();
|
|
|
|
|
|
|
|
m_luaEngine = LuaEngine::create(root.configuration()->get("safeScripts").toBool());
|
|
|
|
|
|
|
|
m_luaEngine->setRecursionLimit(root.configuration()->get("scriptRecursionLimit").toUInt());
|
|
|
|
m_luaEngine->setInstructionLimit(root.configuration()->get("scriptInstructionLimit").toUInt());
|
|
|
|
m_luaEngine->setProfilingEnabled(root.configuration()->get("scriptProfilingEnabled").toBool());
|
|
|
|
m_luaEngine->setInstructionMeasureInterval(root.configuration()->get("scriptInstructionMeasureInterval").toUInt());
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::shutdown() {
|
|
|
|
clearScriptCache();
|
|
|
|
|
|
|
|
if (!m_luaEngine)
|
|
|
|
return;
|
|
|
|
|
2023-06-20 04:33:09 +00:00
|
|
|
auto profile = m_luaEngine->getProfile();
|
|
|
|
if (!profile.empty()) {
|
|
|
|
profile.sort([](auto const& a, auto const& b) {
|
|
|
|
return a.totalTime > b.totalTime;
|
|
|
|
});
|
|
|
|
|
|
|
|
std::function<Json (LuaProfileEntry const&)> jsonFromProfileEntry = [&](LuaProfileEntry const& entry) -> Json {
|
|
|
|
JsonObject profile;
|
|
|
|
profile.set("function", entry.name.value("<function>"));
|
|
|
|
profile.set("scope", entry.nameScope.value("?"));
|
2023-06-27 10:23:44 +00:00
|
|
|
profile.set("source", strf("{}:{}", entry.source, entry.sourceLine));
|
2023-06-20 04:33:09 +00:00
|
|
|
profile.set("self", entry.selfTime);
|
|
|
|
profile.set("total", entry.totalTime);
|
|
|
|
List<LuaProfileEntry> calls;
|
|
|
|
for (auto p : entry.calls)
|
|
|
|
calls.append(*p.second);
|
|
|
|
profile.set("calls", calls.sorted([](auto const& a, auto const& b) { return a.totalTime > b.totalTime; }).transformed(jsonFromProfileEntry));
|
|
|
|
return profile;
|
|
|
|
};
|
|
|
|
|
|
|
|
String profileSummary = Json(profile.transformed(jsonFromProfileEntry)).repr(1);
|
|
|
|
|
|
|
|
if (!File::isDirectory(m_storageDirectory)) {
|
|
|
|
Logger::info("Creating lua storage directory");
|
|
|
|
File::makeDirectory(m_storageDirectory);
|
|
|
|
}
|
|
|
|
|
2023-06-27 10:23:44 +00:00
|
|
|
String filename = strf("{}.luaprofile", Time::printCurrentDateAndTime("<year>-<month>-<day>-<hours>-<minutes>-<seconds>-<millis>"));
|
2023-06-20 04:33:09 +00:00
|
|
|
String path = File::relativeTo(m_storageDirectory, filename);
|
2023-06-27 10:23:44 +00:00
|
|
|
Logger::info("Writing lua profile {}", filename);
|
2023-06-20 04:33:09 +00:00
|
|
|
File::writeFile(profileSummary, path);
|
|
|
|
}
|
|
|
|
|
2023-07-04 09:27:16 +00:00
|
|
|
m_luaEngine.reset();
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LuaContext LuaRoot::createContext(String const& script) {
|
|
|
|
return createContext(StringList{script});
|
|
|
|
}
|
|
|
|
|
|
|
|
LuaContext LuaRoot::createContext(StringList const& scriptPaths) {
|
|
|
|
auto newContext = m_luaEngine->createContext();
|
|
|
|
|
|
|
|
auto cache = m_scriptCache;
|
|
|
|
newContext.setRequireFunction([cache](LuaContext& context, LuaString const& module) {
|
|
|
|
if (!context.get("_SBLOADED").is<LuaTable>())
|
|
|
|
context.set("_SBLOADED", context.createTable());
|
|
|
|
auto t = context.get<LuaTable>("_SBLOADED");
|
|
|
|
if (!t.contains(module)) {
|
|
|
|
t.set(module, true);
|
|
|
|
cache->loadContextScript(context, module.toString());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-02-14 14:39:35 +00:00
|
|
|
auto handleIndex = newContext.handleIndex();
|
|
|
|
auto engine = m_luaEngine.get();
|
|
|
|
newContext.set("loadstring", m_luaEngine->createFunction([engine,handleIndex](String const& source, Maybe<String> const& name, Maybe<LuaValue> const& env) -> LuaFunction {
|
2023-07-19 08:15:49 +00:00
|
|
|
String functionName = name ? strf("loadstring: {}", *name) : "loadstring";
|
2024-02-14 14:39:35 +00:00
|
|
|
return engine->createFunctionFromSource(handleIndex, source.utf8Ptr(), source.utf8Size(), functionName.utf8Ptr());
|
2023-07-18 15:16:22 +00:00
|
|
|
}));
|
|
|
|
|
2023-08-04 13:47:39 +00:00
|
|
|
auto assets = Root::singleton().assets();
|
|
|
|
|
|
|
|
for (auto const& scriptPath : scriptPaths) {
|
|
|
|
if (assets->assetExists(scriptPath))
|
|
|
|
cache->loadContextScript(newContext, scriptPath);
|
|
|
|
else
|
|
|
|
Logger::error("Script '{}' does not exist", scriptPath);
|
|
|
|
}
|
2023-06-20 04:33:09 +00:00
|
|
|
|
2023-07-04 09:27:16 +00:00
|
|
|
for (auto const& callbackPair : m_luaCallbacks)
|
|
|
|
newContext.setCallbacks(callbackPair.first, callbackPair.second);
|
|
|
|
|
2023-06-20 04:33:09 +00:00
|
|
|
return newContext;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::collectGarbage(Maybe<unsigned> steps) {
|
2023-07-04 09:27:16 +00:00
|
|
|
if (m_luaEngine)
|
|
|
|
m_luaEngine->collectGarbage(steps);
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::setAutoGarbageCollection(bool autoGarbageColleciton) {
|
2023-07-04 09:27:16 +00:00
|
|
|
if (m_luaEngine)
|
|
|
|
m_luaEngine->setAutoGarbageCollection(autoGarbageColleciton);
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::tuneAutoGarbageCollection(float pause, float stepMultiplier) {
|
2023-07-04 09:27:16 +00:00
|
|
|
if (m_luaEngine)
|
|
|
|
m_luaEngine->tuneAutoGarbageCollection(pause, stepMultiplier);
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t LuaRoot::luaMemoryUsage() const {
|
2023-07-04 09:27:16 +00:00
|
|
|
return m_luaEngine ? m_luaEngine->memoryUsage() : 0;
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t LuaRoot::scriptCacheMemoryUsage() const {
|
2023-07-04 09:27:16 +00:00
|
|
|
return m_luaEngine ? m_scriptCache->memoryUsage() : 0;
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::clearScriptCache() const {
|
|
|
|
return m_scriptCache->clear();
|
|
|
|
}
|
|
|
|
|
2023-07-04 09:27:16 +00:00
|
|
|
void LuaRoot::addCallbacks(String const& groupName, LuaCallbacks const& callbacks) {
|
|
|
|
m_luaCallbacks[groupName] = callbacks;
|
|
|
|
}
|
|
|
|
|
2023-06-20 04:33:09 +00:00
|
|
|
LuaEngine& LuaRoot::luaEngine() const {
|
|
|
|
return *m_luaEngine;
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::ScriptCache::loadScript(LuaEngine& engine, String const& assetPath) {
|
|
|
|
auto assets = Root::singleton().assets();
|
|
|
|
RecursiveMutexLocker locker(mutex);
|
|
|
|
scripts[assetPath] = engine.compile(*assets->bytes(assetPath), assetPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LuaRoot::ScriptCache::scriptLoaded(String const& assetPath) const {
|
|
|
|
RecursiveMutexLocker locker(mutex);
|
|
|
|
return scripts.contains(assetPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::ScriptCache::unloadScript(String const& assetPath) {
|
|
|
|
RecursiveMutexLocker locker(mutex);
|
|
|
|
scripts.remove(assetPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::ScriptCache::clear() {
|
|
|
|
RecursiveMutexLocker locker(mutex);
|
|
|
|
scripts.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LuaRoot::ScriptCache::loadContextScript(LuaContext& context, String const& assetPath) {
|
|
|
|
RecursiveMutexLocker locker(mutex);
|
|
|
|
if (!scriptLoaded(assetPath))
|
|
|
|
loadScript(context.engine(), assetPath);
|
|
|
|
context.load(scripts.get(assetPath));
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t LuaRoot::ScriptCache::memoryUsage() const {
|
|
|
|
RecursiveMutexLocker locker(mutex);
|
|
|
|
size_t total = 0;
|
|
|
|
for (auto const& p : scripts)
|
|
|
|
total += p.second.size();
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|