Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/base/StarAssets.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/base/StarAssets.cpp')
-rw-r--r--source/base/StarAssets.cpp161
1 files changed, 130 insertions, 31 deletions
diff --git a/source/base/StarAssets.cpp b/source/base/StarAssets.cpp
index de7bc51..2515b5e 100644
--- a/source/base/StarAssets.cpp
+++ b/source/base/StarAssets.cpp
@@ -17,33 +17,35 @@
#include "StarLexicalCast.hpp"
#include "StarSha256.hpp"
#include "StarDataStreamDevices.hpp"
+#include "StarLua.hpp"
+#include "StarUtilityLuaBindings.hpp"
namespace Star {
-static void validatePath(AssetPath const& components, bool canContainSubPath, bool canContainDirectives) {
- if (components.basePath.empty() || components.basePath[0] != '/')
- throw AssetException(strf("Path '{}' must be absolute", components.basePath));
+static void validateBasePath(std::string_view const& basePath) {
+ if (basePath.empty() || basePath[0] != '/')
+ throw AssetException(strf("Path '{}' must be absolute", basePath));
bool first = true;
bool slashed = true;
bool dotted = false;
- for (auto c : components.basePath) {
+ for (auto c : basePath) {
if (c == '/') {
if (!first) {
if (slashed)
- throw AssetException(strf("Path '{}' contains consecutive //, not allowed", components.basePath));
+ throw AssetException(strf("Path '{}' contains consecutive //, not allowed", basePath));
else if (dotted)
- throw AssetException(strf("Path '{}' '.' and '..' not allowed", components.basePath));
+ throw AssetException(strf("Path '{}' '.' and '..' not allowed", basePath));
}
slashed = true;
dotted = false;
} else if (c == ':') {
if (slashed)
- throw AssetException(strf("Path '{}' has ':' after directory", components.basePath));
+ throw AssetException(strf("Path '{}' has ':' after directory", basePath));
break;
} else if (c == '?') {
if (slashed)
- throw AssetException(strf("Path '{}' has '?' after directory", components.basePath));
+ throw AssetException(strf("Path '{}' has '?' after directory", basePath));
break;
} else {
slashed = false;
@@ -52,7 +54,11 @@ static void validatePath(AssetPath const& components, bool canContainSubPath, bo
first = false;
}
if (slashed)
- throw AssetException(strf("Path '{}' cannot be a file", components.basePath));
+ throw AssetException(strf("Path '{}' cannot be a file", basePath));
+}
+
+static void validatePath(AssetPath const& components, bool canContainSubPath, bool canContainDirectives) {
+ validateBasePath(components.basePath.utf8());
if (!canContainSubPath && components.subPath)
throw AssetException::format("Path '{}' cannot contain sub-path", components);
@@ -60,6 +66,32 @@ static void validatePath(AssetPath const& components, bool canContainSubPath, bo
throw AssetException::format("Path '{}' cannot contain directives", components);
}
+static void validatePath(StringView const& path, bool canContainSubPath, bool canContainDirectives) {
+ std::string_view const& str = path.utf8();
+
+ size_t end = str.find_first_of(":?");
+ auto basePath = str.substr(0, end);
+ validateBasePath(basePath);
+
+ bool subPath = false;
+ if (str[end] == ':') {
+ size_t beg = end + 1;
+ if (beg != str.size()) {
+ end = str.find_first_of('?', beg);
+ if (end == NPos && beg + 1 != str.size())
+ subPath = true;
+ else if (size_t len = end - beg)
+ subPath = true;
+ }
+ }
+
+ if (subPath)
+ throw AssetException::format("Path '{}' cannot contain sub-path", path);
+
+ if (end != NPos && str[end] == '?' && !canContainDirectives)
+ throw AssetException::format("Path '{}' cannot contain directives", path);
+}
+
Maybe<RectU> FramesSpecification::getRect(String const& frame) const {
if (auto alias = aliases.ptr(frame)) {
return frames.get(*alias);
@@ -75,6 +107,22 @@ Assets::Assets(Settings settings, StringList assetSources) {
m_stopThreads = false;
m_assetSources = std::move(assetSources);
+ auto luaEngine = LuaEngine::create();
+ auto decorateLuaContext = [this](LuaContext& context) {
+ context.setCallbacks("sb", LuaBindings::makeUtilityCallbacks());
+ LuaCallbacks callbacks;
+ callbacks.registerCallbackWithSignature<StringSet, String>("byExtension", bind(&Assets::scanExtension, this, _1));
+ callbacks.registerCallbackWithSignature<Json, String>("json", bind(&Assets::json, this, _1));
+ callbacks.registerCallback("bytes", [this](String const& path) -> String {
+ auto assetBytes = bytes(path);
+ return String(assetBytes->ptr(), assetBytes->size());
+ });
+ callbacks.registerCallback("scan", [this](Maybe<String> const& a, Maybe<String> const& b) -> StringList {
+ return b ? scan(a.value(), *b) : scan(a.value());
+ });
+ context.setCallbacks("assets", callbacks);
+ };
+
for (auto& sourcePath : m_assetSources) {
Logger::info("Loading assets from: '{}'", sourcePath);
AssetSourcePtr source;
@@ -105,6 +153,25 @@ Assets::Assets(Settings settings, StringList assetSources) {
auto& descriptor = m_files[filename];
descriptor.sourceName = filename;
descriptor.source = source;
+ m_filesByExtension[AssetPath::extension(filename).toLower()].insert(filename);
+ }
+ auto metadata = source->metadata();
+ if (auto scripts = metadata.ptr("scripts")) {
+ if (auto onLoad = scripts->optArray("onLoad")) {
+ Logger::info("Running onLoad scripts {}", *onLoad);
+ try {
+ auto context = luaEngine->createContext();
+ decorateLuaContext(context);
+ for (auto& jPath : *onLoad) {
+ auto path = jPath.toString();
+ auto script = source->read(path);
+ context.load(script, path);
+ }
+ }
+ catch (LuaException const& e) {
+ Logger::error("Exception while running onLoad scripts from asset source '{}': {}", sourcePath, e.what());
+ }
+ }
}
}
@@ -133,9 +200,6 @@ Assets::Assets(Settings settings, StringList assetSources) {
m_digest = digest.compute();
- for (auto const& filename : m_files.keys())
- m_filesByExtension[AssetPath::extension(filename).toLower()].append(filename);
-
int workerPoolSize = m_settings.workerPoolSize;
for (int i = 0; i < workerPoolSize; i++)
m_workerThreads.append(Thread::invoke("Assets::workerMain", mem_fn(&Assets::workerMain), this));
@@ -194,7 +258,9 @@ Maybe<String> Assets::assetSourcePath(AssetSourcePtr const& source) const {
StringList Assets::scan(String const& suffix) const {
if (suffix.beginsWith(".") && !suffix.substr(1).hasChar('.')) {
- return scanExtension(suffix);
+ return scanExtension(suffix).values();
+ } else if (suffix.empty()) {
+ return m_files.keys();
} else {
StringList result;
for (auto const& fileEntry : m_files) {
@@ -210,7 +276,7 @@ StringList Assets::scan(String const& suffix) const {
StringList Assets::scan(String const& prefix, String const& suffix) const {
StringList result;
if (suffix.beginsWith(".") && !suffix.substr(1).hasChar('.')) {
- StringList filesWithExtension = scanExtension(suffix);
+ StringSet filesWithExtension = scanExtension(suffix);
for (auto const& file : filesWithExtension) {
if (file.beginsWith(prefix, String::CaseInsensitive))
result.append(file);
@@ -225,11 +291,11 @@ StringList Assets::scan(String const& prefix, String const& suffix) const {
return result;
}
-StringList Assets::scanExtension(String const& extension) const {
- if (extension.beginsWith("."))
- return m_filesByExtension.value(extension.substr(1));
- else
- return m_filesByExtension.value(extension);
+const StringSet NullStringSet;
+
+StringSet const& Assets::scanExtension(String const& extension) const {
+ auto find = m_filesByExtension.find(extension.beginsWith(".") ? extension.substr(1) : extension);
+ return find != m_filesByExtension.end() ? find->second : NullStringSet;
}
Json Assets::json(String const& path) const {
@@ -255,6 +321,16 @@ void Assets::queueJsons(StringList const& paths) const {
}));
}
+void Assets::queueJsons(StringSet const& paths) const {
+ MutexLocker assetsLocker(m_assetsMutex);
+ for (String const& path : paths) {
+ auto components = AssetPath::split(path);
+ validatePath(components, true, false);
+
+ queueAsset(AssetId{AssetType::Json, {components.basePath, {}, {}}});
+ };
+}
+
ImageConstPtr Assets::image(AssetPath const& path) const {
validatePath(path, true, true);
@@ -270,6 +346,16 @@ void Assets::queueImages(StringList const& paths) const {
}));
}
+void Assets::queueImages(StringSet const& paths) const {
+ MutexLocker assetsLocker(m_assetsMutex);
+ for (String const& path : paths) {
+ auto components = AssetPath::split(path);
+ validatePath(components, true, true);
+
+ queueAsset(AssetId{AssetType::Image, std::move(components)});
+ };
+}
+
ImageConstPtr Assets::tryImage(AssetPath const& path) const {
validatePath(path, true, true);
@@ -296,13 +382,23 @@ AudioConstPtr Assets::audio(String const& path) const {
void Assets::queueAudios(StringList const& paths) const {
queueAssets(paths.transformed([](String const& path) {
- const auto components = AssetPath::split(path);
+ auto components = AssetPath::split(path);
validatePath(components, false, false);
return AssetId{AssetType::Audio, std::move(components)};
}));
}
+void Assets::queueAudios(StringSet const& paths) const {
+ MutexLocker assetsLocker(m_assetsMutex);
+ for (String const& path : paths) {
+ auto components = AssetPath::split(path);
+ validatePath(components, false, true);
+
+ queueAsset(AssetId{AssetType::Audio, std::move(components)});
+ };
+}
+
AudioConstPtr Assets::tryAudio(String const& path) const {
auto components = AssetPath::split(path);
validatePath(components, false, false);
@@ -490,17 +586,20 @@ FramesSpecification Assets::parseFramesSpecification(Json const& frameConfig, St
void Assets::queueAssets(List<AssetId> const& assetIds) const {
MutexLocker assetsLocker(m_assetsMutex);
- for (auto const& id : assetIds) {
- auto i = m_assetsCache.find(id);
- if (i != m_assetsCache.end()) {
- if (i->second)
- freshen(i->second);
- } else {
- auto j = m_queue.find(id);
- if (j == m_queue.end()) {
- m_queue[id] = QueuePriority::Load;
- m_assetsQueued.signal();
- }
+ for (auto const& id : assetIds)
+ queueAsset(id);
+}
+
+void Assets::queueAsset(AssetId const& assetId) const {
+ auto i = m_assetsCache.find(assetId);
+ if (i != m_assetsCache.end()) {
+ if (i->second)
+ freshen(i->second);
+ } else {
+ auto j = m_queue.find(assetId);
+ if (j == m_queue.end()) {
+ m_queue[assetId] = QueuePriority::Load;
+ m_assetsQueued.signal();
}
}
}