asset onLoad scripts can add and patch assets now
This commit is contained in:
parent
13f91aa195
commit
7eec15098e
@ -14,6 +14,7 @@ SET (star_base_HEADERS
|
|||||||
StarCellularLiquid.hpp
|
StarCellularLiquid.hpp
|
||||||
StarConfiguration.hpp
|
StarConfiguration.hpp
|
||||||
StarDirectoryAssetSource.hpp
|
StarDirectoryAssetSource.hpp
|
||||||
|
StarMemoryAssetSource.hpp
|
||||||
StarMixer.hpp
|
StarMixer.hpp
|
||||||
StarPackedAssetSource.hpp
|
StarPackedAssetSource.hpp
|
||||||
StarVersion.hpp
|
StarVersion.hpp
|
||||||
@ -27,6 +28,7 @@ SET (star_base_SOURCES
|
|||||||
StarCellularLighting.cpp
|
StarCellularLighting.cpp
|
||||||
StarConfiguration.cpp
|
StarConfiguration.cpp
|
||||||
StarDirectoryAssetSource.cpp
|
StarDirectoryAssetSource.cpp
|
||||||
|
StarMemoryAssetSource.cpp
|
||||||
StarMixer.cpp
|
StarMixer.cpp
|
||||||
StarPackedAssetSource.cpp
|
StarPackedAssetSource.cpp
|
||||||
StarVersionOptionParser.cpp
|
StarVersionOptionParser.cpp
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "StarTime.hpp"
|
#include "StarTime.hpp"
|
||||||
#include "StarDirectoryAssetSource.hpp"
|
#include "StarDirectoryAssetSource.hpp"
|
||||||
#include "StarPackedAssetSource.hpp"
|
#include "StarPackedAssetSource.hpp"
|
||||||
|
#include "StarMemoryAssetSource.hpp"
|
||||||
#include "StarJsonBuilder.hpp"
|
#include "StarJsonBuilder.hpp"
|
||||||
#include "StarJsonExtra.hpp"
|
#include "StarJsonExtra.hpp"
|
||||||
#include "StarJsonPatch.hpp"
|
#include "StarJsonPatch.hpp"
|
||||||
@ -108,18 +109,54 @@ Assets::Assets(Settings settings, StringList assetSources) {
|
|||||||
m_assetSources = std::move(assetSources);
|
m_assetSources = std::move(assetSources);
|
||||||
|
|
||||||
auto luaEngine = LuaEngine::create();
|
auto luaEngine = LuaEngine::create();
|
||||||
auto decorateLuaContext = [this](LuaContext& context) {
|
auto decorateLuaContext = [this](LuaContext& context, MemoryAssetSourcePtr mySource) {
|
||||||
context.setCallbacks("sb", LuaBindings::makeUtilityCallbacks());
|
context.setCallbacks("sb", LuaBindings::makeUtilityCallbacks());
|
||||||
LuaCallbacks callbacks;
|
LuaCallbacks callbacks;
|
||||||
callbacks.registerCallbackWithSignature<StringSet, String>("byExtension", bind(&Assets::scanExtension, this, _1));
|
callbacks.registerCallbackWithSignature<StringSet, String>("byExtension", bind(&Assets::scanExtension, this, _1));
|
||||||
callbacks.registerCallbackWithSignature<Json, String>("json", bind(&Assets::json, this, _1));
|
callbacks.registerCallbackWithSignature<Json, String>("json", bind(&Assets::json, this, _1));
|
||||||
|
|
||||||
callbacks.registerCallback("bytes", [this](String const& path) -> String {
|
callbacks.registerCallback("bytes", [this](String const& path) -> String {
|
||||||
auto assetBytes = bytes(path);
|
auto assetBytes = bytes(path);
|
||||||
return String(assetBytes->ptr(), assetBytes->size());
|
return String(assetBytes->ptr(), assetBytes->size());
|
||||||
});
|
});
|
||||||
|
|
||||||
callbacks.registerCallback("scan", [this](Maybe<String> const& a, Maybe<String> const& b) -> StringList {
|
callbacks.registerCallback("scan", [this](Maybe<String> const& a, Maybe<String> const& b) -> StringList {
|
||||||
return b ? scan(a.value(), *b) : scan(a.value());
|
return b ? scan(a.value(), *b) : scan(a.value());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
callbacks.registerCallback("add", [this, &mySource](LuaEngine& engine, String const& path, LuaValue const& data) -> bool {
|
||||||
|
ByteArray bytes;
|
||||||
|
if (auto str = engine.luaMaybeTo<String>(data))
|
||||||
|
bytes = ByteArray(str->utf8Ptr(), str->utf8Size());
|
||||||
|
else {
|
||||||
|
auto json = engine.luaTo<Json>(data).repr();
|
||||||
|
bytes = ByteArray(json.utf8Ptr(), json.utf8Size());
|
||||||
|
}
|
||||||
|
return mySource->set(path, bytes);
|
||||||
|
});
|
||||||
|
|
||||||
|
callbacks.registerCallback("patch", [this, &mySource](String const& path, String const& patchPath) -> bool {
|
||||||
|
if (auto file = m_files.ptr(path)) {
|
||||||
|
if (mySource->contains(patchPath)) {
|
||||||
|
file->patchSources.append(make_pair(patchPath, mySource));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (auto asset = m_files.ptr(patchPath)) {
|
||||||
|
file->patchSources.append(make_pair(patchPath, asset->source));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
callbacks.registerCallback("erase", [this, &mySource](String const& path) -> bool {
|
||||||
|
bool erased = m_files.erase(path);
|
||||||
|
if (erased)
|
||||||
|
m_filesByExtension[AssetPath::extension(path).toLower()].erase(path);
|
||||||
|
return erased;
|
||||||
|
});
|
||||||
|
|
||||||
context.setCallbacks("assets", callbacks);
|
context.setCallbacks("assets", callbacks);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -131,37 +168,46 @@ Assets::Assets(Settings settings, StringList assetSources) {
|
|||||||
else
|
else
|
||||||
source = std::make_shared<PackedAssetSource>(sourcePath);
|
source = std::make_shared<PackedAssetSource>(sourcePath);
|
||||||
|
|
||||||
m_assetSourcePaths.add(sourcePath, source);
|
auto addSource = [&](String const& sourcePath, AssetSourcePtr source) {
|
||||||
|
m_assetSourcePaths.add(sourcePath, source);
|
||||||
|
|
||||||
for (auto const& filename : source->assetPaths()) {
|
for (auto const& filename : source->assetPaths()) {
|
||||||
if (filename.contains(AssetsPatchSuffix, String::CaseInsensitive)) {
|
if (filename.contains(AssetsPatchSuffix, String::CaseInsensitive)) {
|
||||||
if (filename.endsWith(AssetsPatchSuffix, String::CaseInsensitive)) {
|
if (filename.endsWith(AssetsPatchSuffix, String::CaseInsensitive)) {
|
||||||
auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsPatchSuffix));
|
auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsPatchSuffix));
|
||||||
if (auto p = m_files.ptr(targetPatchFile))
|
if (auto p = m_files.ptr(targetPatchFile))
|
||||||
p->patchSources.append({filename, source});
|
p->patchSources.append({filename, source});
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
if (filename.endsWith(AssetsPatchSuffix + toString(i), String::CaseInsensitive)) {
|
if (filename.endsWith(AssetsPatchSuffix + toString(i), String::CaseInsensitive)) {
|
||||||
auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsPatchSuffix) + 1);
|
auto targetPatchFile = filename.substr(0, filename.size() - strlen(AssetsPatchSuffix) + 1);
|
||||||
if (auto p = m_files.ptr(targetPatchFile))
|
if (auto p = m_files.ptr(targetPatchFile))
|
||||||
p->patchSources.append({filename, source});
|
p->patchSources.append({filename, source});
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto& descriptor = m_files[filename];
|
||||||
|
descriptor.sourceName = filename;
|
||||||
|
descriptor.source = source;
|
||||||
|
m_filesByExtension[AssetPath::extension(filename).toLower()].insert(filename);
|
||||||
}
|
}
|
||||||
auto& descriptor = m_files[filename];
|
};
|
||||||
descriptor.sourceName = filename;
|
addSource(sourcePath, source);
|
||||||
descriptor.source = source;
|
|
||||||
m_filesByExtension[AssetPath::extension(filename).toLower()].insert(filename);
|
|
||||||
}
|
|
||||||
auto metadata = source->metadata();
|
auto metadata = source->metadata();
|
||||||
if (auto scripts = metadata.ptr("scripts")) {
|
if (auto scripts = metadata.ptr("scripts")) {
|
||||||
if (auto onLoad = scripts->optArray("onLoad")) {
|
if (auto onLoad = scripts->optArray("onLoad")) {
|
||||||
|
JsonObject memoryMetadata{
|
||||||
|
{"name", strf("{}.onLoad", metadata.value("name", File::baseName(sourcePath)))}
|
||||||
|
};
|
||||||
|
auto memoryAssets = make_shared<MemoryAssetSource>(memoryMetadata);
|
||||||
Logger::info("Running onLoad scripts {}", *onLoad);
|
Logger::info("Running onLoad scripts {}", *onLoad);
|
||||||
try {
|
try {
|
||||||
auto context = luaEngine->createContext();
|
auto context = luaEngine->createContext();
|
||||||
decorateLuaContext(context);
|
decorateLuaContext(context, memoryAssets);
|
||||||
for (auto& jPath : *onLoad) {
|
for (auto& jPath : *onLoad) {
|
||||||
auto path = jPath.toString();
|
auto path = jPath.toString();
|
||||||
auto script = source->read(path);
|
auto script = source->read(path);
|
||||||
@ -171,6 +217,8 @@ Assets::Assets(Settings settings, StringList assetSources) {
|
|||||||
catch (LuaException const& e) {
|
catch (LuaException const& e) {
|
||||||
Logger::error("Exception while running onLoad scripts from asset source '{}': {}", sourcePath, e.what());
|
Logger::error("Exception while running onLoad scripts from asset source '{}': {}", sourcePath, e.what());
|
||||||
}
|
}
|
||||||
|
if (!memoryAssets->empty())
|
||||||
|
addSource(memoryMetadata.get("name").toString(), memoryAssets);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
86
source/base/StarMemoryAssetSource.cpp
Normal file
86
source/base/StarMemoryAssetSource.cpp
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#include "StarMemoryAssetSource.hpp"
|
||||||
|
#include "StarDataStreamDevices.hpp"
|
||||||
|
#include "StarDataStreamExtra.hpp"
|
||||||
|
#include "StarSha256.hpp"
|
||||||
|
|
||||||
|
namespace Star {
|
||||||
|
|
||||||
|
MemoryAssetSource::MemoryAssetSource(JsonObject metadata) : m_metadata(metadata) {}
|
||||||
|
|
||||||
|
JsonObject MemoryAssetSource::metadata() const {
|
||||||
|
return m_metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringList MemoryAssetSource::assetPaths() const {
|
||||||
|
return m_files.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
IODevicePtr MemoryAssetSource::open(String const& path) {
|
||||||
|
struct AssetReader : public IODevice {
|
||||||
|
AssetReader(ByteArrayPtr assetData, String name) : assetData(assetData), name(name) { setMode(IOMode::Read); }
|
||||||
|
|
||||||
|
size_t read(char* data, size_t len) override {
|
||||||
|
len = min<StreamOffset>(len, StreamOffset(assetData->size()) - assetPos);
|
||||||
|
assetData->copyTo(data, len);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t write(char const*, size_t) override {
|
||||||
|
throw IOException("Assets IODevices are read-only");
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamOffset size() override { return assetData->size(); }
|
||||||
|
StreamOffset pos() override { return assetPos; }
|
||||||
|
|
||||||
|
String deviceName() const override { return name; }
|
||||||
|
|
||||||
|
bool atEnd() override {
|
||||||
|
return assetPos >= assetData->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void seek(StreamOffset p, IOSeek mode) override {
|
||||||
|
if (mode == IOSeek::Absolute)
|
||||||
|
assetPos = p;
|
||||||
|
else if (mode == IOSeek::Relative)
|
||||||
|
assetPos = clamp<StreamOffset>(assetPos + p, 0, assetData->size());
|
||||||
|
else
|
||||||
|
assetPos = clamp<StreamOffset>(assetPos - p, 0, assetData->size());
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayPtr assetData;
|
||||||
|
StreamOffset assetPos;
|
||||||
|
String name;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto p = m_files.ptr(path);
|
||||||
|
if (!p)
|
||||||
|
throw AssetSourceException::format("Requested file '{}' does not exist in memory", path);
|
||||||
|
|
||||||
|
return make_shared<AssetReader>(*p, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemoryAssetSource::empty() const {
|
||||||
|
return m_files.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemoryAssetSource::contains(String const& path) const {
|
||||||
|
return m_files.contains(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemoryAssetSource::erase(String const& path) {
|
||||||
|
return m_files.erase(path) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MemoryAssetSource::set(String const& path, ByteArray data) {
|
||||||
|
return m_files.emplace(path, make_shared<ByteArray>(std::move(data))).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArray MemoryAssetSource::read(String const& path) {
|
||||||
|
auto p = m_files.ptr(path);
|
||||||
|
if (!p)
|
||||||
|
throw AssetSourceException::format("Requested file '{}' does not exist in memory", path);
|
||||||
|
else
|
||||||
|
return *p->get(); // this is a double indirection, and that freaking sucks!!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
source/base/StarMemoryAssetSource.hpp
Normal file
29
source/base/StarMemoryAssetSource.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "StarAssetSource.hpp"
|
||||||
|
#include "StarIODevice.hpp"
|
||||||
|
|
||||||
|
namespace Star {
|
||||||
|
|
||||||
|
STAR_CLASS(MemoryAssetSource);
|
||||||
|
|
||||||
|
class MemoryAssetSource : public AssetSource {
|
||||||
|
public:
|
||||||
|
MemoryAssetSource(JsonObject metadata = JsonObject());
|
||||||
|
|
||||||
|
JsonObject metadata() const override;
|
||||||
|
StringList assetPaths() const override;
|
||||||
|
|
||||||
|
IODevicePtr open(String const& path) override;
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
bool contains(String const& path) const;
|
||||||
|
bool erase(String const& path);
|
||||||
|
bool set(String const& path, ByteArray data);
|
||||||
|
ByteArray read(String const& path) override;
|
||||||
|
private:
|
||||||
|
JsonObject m_metadata;
|
||||||
|
StringMap<ByteArrayPtr> m_files;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user