experimental asset load scripts

This commit is contained in:
Kae 2024-03-15 21:28:11 +11:00
parent 696abcca71
commit 6fa0afd758
46 changed files with 256 additions and 141 deletions

View File

@ -275,6 +275,8 @@ if(STAR_COMPILER_GNU)
set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG -Ofast")
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Ofast")
set(BUILD_RPATH_USE_ORIGIN TRUE)
elseif(STAR_COMPILER_CLANG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wuninitialized -Wno-parentheses-equality -Wno-deprecated-declarations")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall -Wextra -Wuninitialized -Wno-parentheses-equality -Wno-deprecated-declarations")
@ -304,6 +306,8 @@ elseif(STAR_COMPILER_CLANG)
set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG -Ofast")
set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Ofast")
set(BUILD_RPATH_USE_ORIGIN TRUE)
elseif(STAR_COMPILER_MSVC)
# /MP - Multi-processor building
# /EHsc - Enable normal C++ exception handling
@ -513,7 +517,10 @@ set(STAR_EXTERN_INCLUDES ${PROJECT_SOURCE_DIR}/extern)
add_subdirectory(extern)
# Core support code, not specific to starbound.
set(STAR_CORE_INCLUDES ${PROJECT_SOURCE_DIR}/core)
set(STAR_CORE_INCLUDES
${PROJECT_SOURCE_DIR}/core
${PROJECT_SOURCE_DIR}/core/scripting
)
add_subdirectory(core)
# Less general purpose code than core that is available to both the game and

View File

@ -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();
}
}
}

View File

@ -187,7 +187,7 @@ public:
// Scans all assets for files with the given extension, which is specially
// indexed and much faster than a normal scan. Extension may contain leading
// '.' character or it may be omitted.
StringList scanExtension(String const& extension) const;
StringSet const& scanExtension(String const& extension) const;
// Get json asset with an optional sub-path. The sub-path portion of the
// path refers to a key in the top-level object, and may use dot notation
@ -201,6 +201,7 @@ public:
// Load all the given jsons using background processing.
void queueJsons(StringList const& paths) const;
void queueJsons(StringSet const& paths) const;
// Returns *either* an image asset or a sub-frame. Frame files are JSON
// descriptor files that reference a particular image and label separate
@ -212,6 +213,7 @@ public:
ImageConstPtr image(AssetPath const& path) const;
// Load images using background processing
void queueImages(StringList const& paths) const;
void queueImages(StringSet const& paths) const;
// Return the given image *if* it is already loaded, otherwise queue it for
// loading.
ImageConstPtr tryImage(AssetPath const& path) const;
@ -226,6 +228,7 @@ public:
AudioConstPtr audio(String const& path) const;
// Load audios using background processing
void queueAudios(StringList const& paths) const;
void queueAudios(StringSet const& paths) const;
// Return the given audio *if* it is already loaded, otherwise queue it for
// loading.
AudioConstPtr tryAudio(String const& path) const;
@ -249,6 +252,8 @@ private:
static FramesSpecification parseFramesSpecification(Json const& frameConfig, String path);
void queueAssets(List<AssetId> const& assetIds) const;
//Lock before calling!
void queueAsset(AssetId const& assetId) const;
shared_ptr<AssetData> tryAsset(AssetId const& id) const;
shared_ptr<AssetData> getAsset(AssetId const& id) const;
@ -317,7 +322,7 @@ private:
// Maps the source asset name to the source containing it
CaseInsensitiveStringMap<AssetFileDescriptor> m_files;
// Maps an extension to the files with that extension
CaseInsensitiveStringMap<StringList> m_filesByExtension;
CaseInsensitiveStringMap<StringSet> m_filesByExtension;
ByteArray m_digest;

View File

@ -127,6 +127,7 @@ SET (star_core_HEADERS
StarWorkerPool.hpp
StarXXHash.hpp
StarZSTDCompression.hpp
scripting/StarUtilityLuaBindings.hpp
)
SET (star_core_SOURCES
@ -183,6 +184,7 @@ SET (star_core_SOURCES
StarUuid.cpp
StarWorkerPool.cpp
StarZSTDCompression.cpp
scripting/StarUtilityLuaBindings.cpp
)
IF (STAR_SYSTEM_FAMILY_UNIX)

View File

@ -41,7 +41,7 @@ AssetPath AssetPath::split(String const& path) {
if (str[end] == ':') {
size_t beg = end + 1;
if (beg != str.size()) {
end = str.find_first_of("?", beg);
end = str.find_first_of('?', beg);
if (end == NPos && beg + 1 != str.size())
components.subPath.emplace(str.substr(beg));
else if (size_t len = end - beg)

View File

@ -407,7 +407,7 @@ bool String::hasCharOrWhitespace(Char c) const {
return hasChar(c);
}
String String::replace(String const& rplc, String const& val) const {
String String::replace(String const& rplc, String const& val, CaseSensitivity cs) const {
size_t index;
size_t sz = size();
size_t rsz = rplc.size();
@ -417,7 +417,7 @@ String String::replace(String const& rplc, String const& val) const {
if (rplc.empty())
return *this;
index = find(rplc);
index = find(rplc, 0, cs);
if (index == NPos)
return *this;
@ -431,7 +431,7 @@ String String::replace(String const& rplc, String const& val) const {
for (size_t i = 0; i < rsz; ++i)
++it;
size_t nindex = find(rplc, index);
size_t nindex = find(rplc, index, cs);
for (size_t i = index; i < nindex && i < sz; ++i)
ret.append(*it++);

View File

@ -148,7 +148,7 @@ public:
// whitespace.
bool hasCharOrWhitespace(Char c) const;
String replace(String const& rplc, String const& val) const;
String replace(String const& rplc, String const& val, CaseSensitivity cs = CaseSensitive) const;
String trimEnd(String const& chars = "") const;
String trimBeg(String const& chars = "") const;

View File

@ -1,12 +1,12 @@
#include "StarUtilityLuaBindings.hpp"
#include "StarJsonExtra.hpp"
#include "StarLuaGameConverters.hpp"
#include "StarUuid.hpp"
#include "StarRandom.hpp"
#include "StarPerlin.hpp"
#include "StarXXHash.hpp"
#include "StarLogging.hpp"
#include "StarInterpolation.hpp"
#include "StarLuaConverters.hpp"
namespace Star {

View File

@ -251,7 +251,6 @@ SET (star_game_HEADERS
scripting/StarStatusControllerLuaBindings.hpp
scripting/StarWorldLuaBindings.hpp
scripting/StarUniverseServerLuaBindings.hpp
scripting/StarUtilityLuaBindings.hpp
terrain/StarCacheSelector.hpp
terrain/StarConstantSelector.hpp
@ -489,7 +488,6 @@ SET (star_game_SOURCES
scripting/StarStatusControllerLuaBindings.cpp
scripting/StarWorldLuaBindings.cpp
scripting/StarUniverseServerLuaBindings.cpp
scripting/StarUtilityLuaBindings.cpp
terrain/StarCacheSelector.cpp
terrain/StarConstantSelector.cpp

View File

@ -10,7 +10,7 @@ AiDatabase::AiDatabase() {
auto assets = Root::singleton().assets();
auto config = assets->json("/ai/ai.config");
auto missions = assets->scanExtension("aimission");
auto& missions = assets->scanExtension("aimission");
assets->queueJsons(missions);
for (auto const& file : missions) {

View File

@ -151,12 +151,12 @@ BehaviorTree::BehaviorTree(String const& name, StringSet scripts, JsonObject con
BehaviorDatabase::BehaviorDatabase() {
auto assets = Root::singleton().assets();
StringList nodeFiles = assets->scanExtension("nodes");
auto& nodeFiles = assets->scanExtension("nodes");
assets->queueJsons(nodeFiles);
for (String const& file : nodeFiles) {
try {
Json nodes = assets->json(file);
for (auto node : nodes.toObject()) {
for (auto& node : nodes.toObject()) {
StringMap<NodeParameter> parameters;
for (auto p : node.second.getObject("properties", {}))
parameters.set(p.first, jsonToNodeParameter(p.second));
@ -174,7 +174,7 @@ BehaviorDatabase::BehaviorDatabase() {
}
}
auto behaviorFiles = assets->scanExtension("behavior");
auto& behaviorFiles = assets->scanExtension("behavior");
assets->queueJsons(behaviorFiles);
for (auto const& file : behaviorFiles) {
try {
@ -190,7 +190,7 @@ BehaviorDatabase::BehaviorDatabase() {
}
}
for (auto pair : m_configs) {
for (auto& pair : m_configs) {
if (!m_behaviors.contains(pair.first))
loadTree(pair.first);
}

View File

@ -15,9 +15,9 @@ BiomeDatabase::BiomeDatabase() {
// 'type' here is the extension of the file, and determines the selector type
auto scanFiles = [=](String const& type, ConfigMap& map) {
auto files = assets->scanExtension(type);
auto& files = assets->scanExtension(type);
assets->queueJsons(files);
for (auto path : files) {
for (auto& path : files) {
auto parameters = assets->json(path);
if (parameters.isNull())
continue;

View File

@ -7,10 +7,10 @@ namespace Star {
CodexDatabase::CodexDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("codex");
auto& files = assets->scanExtension("codex");
auto codexConfig = assets->json("/codex.config");
assets->queueJsons(files);
for (auto const& file : files) {
for (auto& file : files) {
try {
auto codexJson = assets->json(file);
codexJson = codexJson.set("icon",

View File

@ -23,9 +23,9 @@ Collectable::Collectable(String const& name, int order, String const& title, Str
CollectionDatabase::CollectionDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("collection");
auto& files = assets->scanExtension("collection");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
auto config = assets->json(file);
Collection collection;

View File

@ -18,9 +18,9 @@ DamageDatabase::DamageDatabase() {
m_elementalTypes.set(p.first, std::move(type));
}
auto files = assets->scanExtension("damage");
auto& files = assets->scanExtension("damage");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
auto config = assets->json(file);
String name = config.getString("kind");
if (m_damageKinds.contains(name))

View File

@ -5,8 +5,8 @@ namespace Star {
DanceDatabase::DanceDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("dance");
for (auto file : files) {
auto& files = assets->scanExtension("dance");
for (auto& file : files) {
try {
DancePtr dance = readDance(file);
m_dances[dance->name] = dance;

View File

@ -1309,7 +1309,7 @@ namespace Dungeon {
DungeonDefinitions::DungeonDefinitions() : m_paths(), m_cacheMutex(), m_definitionCache(DefinitionsCacheSize) {
auto assets = Root::singleton().assets();
for (auto file : assets->scan(".dungeon")) {
for (auto& file : assets->scan(".dungeon")) {
Json dungeon = assets->json(file);
m_paths.insert(dungeon.get("metadata").getString("name"), file);
}

View File

@ -12,7 +12,7 @@ namespace Star {
EffectSourceDatabase::EffectSourceDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("effectsource");
auto& files = assets->scanExtension("effectsource");
assets->queueJsons(files);
for (auto const& file : files) {
auto sourceConfig = make_shared<EffectSourceConfig>(assets->json(file));

View File

@ -552,7 +552,7 @@ ItemRecipe ItemDatabase::makeRecipe(List<ItemDescriptor> inputs, ItemDescriptor
void ItemDatabase::addItemSet(ItemType type, String const& extension) {
auto assets = Root::singleton().assets();
for (auto file : assets->scanExtension(extension)) {
for (auto& file : assets->scanExtension(extension)) {
ItemData data;
try {
auto config = assets->json(file);
@ -662,9 +662,9 @@ void ItemDatabase::addObjectItems() {
void ItemDatabase::scanRecipes() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("recipe");
auto& files = assets->scanExtension("recipe");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
try {
m_recipes.add(parseRecipe(assets->json(file)));
} catch (std::exception const& e) {

View File

@ -32,10 +32,10 @@ LiquidsDatabase::LiquidsDatabase() {
m_liquidNames["empty"] = EmptyLiquidId;
auto liquids = assets->scanExtension("liquid");
auto& liquids = assets->scanExtension("liquid");
assets->queueJsons(liquids);
for (auto file : liquids) {
for (auto& file : liquids) {
try {
auto liquidConfig = assets->json(file);

View File

@ -54,13 +54,13 @@ MaterialDatabase::MaterialDatabase() {
setMetaMaterial(matId, MaterialDatabase::MetaMaterialInfo{matName, matId, matCollisionKind, blocksLiquidFlow});
}
auto materials = assets->scanExtension("material");
auto mods = assets->scanExtension("matmod");
auto& materials = assets->scanExtension("material");
auto& mods = assets->scanExtension("matmod");
assets->queueJsons(materials);
assets->queueJsons(mods);
for (auto file : materials) {
for (auto& file : materials) {
try {
auto matConfig = assets->json(file);
@ -140,7 +140,7 @@ MaterialDatabase::MaterialDatabase() {
}
}
for (auto file : mods) {
for (auto& file : mods) {
try {
auto modConfig = assets->json(file);

View File

@ -11,10 +11,10 @@ namespace Star {
MonsterDatabase::MonsterDatabase() {
auto assets = Root::singleton().assets();
auto monsterTypes = assets->scanExtension("monstertype");
auto monsterParts = assets->scanExtension("monsterpart");
auto monsterSkills = assets->scanExtension("monsterskill");
auto monsterColors = assets->scanExtension("monstercolors");
auto& monsterTypes = assets->scanExtension("monstertype");
auto& monsterParts = assets->scanExtension("monsterpart");
auto& monsterSkills = assets->scanExtension("monsterskill");
auto& monsterColors = assets->scanExtension("monstercolors");
assets->queueJsons(monsterTypes);
assets->queueJsons(monsterParts);
@ -66,7 +66,7 @@ MonsterDatabase::MonsterDatabase() {
}
}
for (auto file : monsterParts) {
for (auto const& file : monsterParts) {
try {
auto config = assets->json(file);
if (config.isNull())
@ -91,7 +91,7 @@ MonsterDatabase::MonsterDatabase() {
}
}
for (auto file : monsterSkills) {
for (auto const& file : monsterSkills) {
try {
auto config = assets->json(file);
if (config.isNull())
@ -115,7 +115,7 @@ MonsterDatabase::MonsterDatabase() {
}
}
for (auto file : monsterColors) {
for (auto const& file : monsterColors) {
try {
auto config = assets->json(file);
if (config.isNull())

View File

@ -7,9 +7,9 @@ namespace Star {
PatternedNameGenerator::PatternedNameGenerator() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("namesource");
auto &files = assets->scanExtension("namesource");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
try {
auto sourceConfig = assets->json(file);
@ -24,7 +24,7 @@ PatternedNameGenerator::PatternedNameGenerator() {
}
auto profanityFilter = assets->json("/names/profanityfilter.config").toArray();
for (auto naughtyWord : profanityFilter)
for (auto& naughtyWord : profanityFilter)
m_profanityFilter.add(naughtyWord.toString().toLower());
}

View File

@ -17,9 +17,9 @@ namespace Star {
NpcDatabase::NpcDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("npctype");
auto& files = assets->scanExtension("npctype");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
try {
auto config = assets->json(file);
String typeName = config.getString("type");

View File

@ -309,9 +309,9 @@ List<ObjectOrientationPtr> ObjectDatabase::parseOrientations(String const& path,
ObjectDatabase::ObjectDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("object");
auto& files = assets->scanExtension("object");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
try {
String name = assets->json(file).getString("objectName");
if (m_paths.contains(name))

View File

@ -23,9 +23,9 @@ Particle ParticleConfig::instance() {
ParticleDatabase::ParticleDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("particle");
auto& files = assets->scanExtension("particle");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
auto particleConfig = make_shared<ParticleConfig>(assets->json(file));
if (m_configs.contains(particleConfig->kind()))
throw StarException(strf("Duplicate particle asset kind Name {}. configfile {}", particleConfig->kind(), file));

View File

@ -104,10 +104,10 @@ Json BushVariant::toJson() const {
PlantDatabase::PlantDatabase() {
auto assets = Root::singleton().assets();
auto stems = assets->scanExtension("modularstem");
auto foliages = assets->scanExtension("modularfoliage");
auto grasses = assets->scanExtension("grass");
auto bushes = assets->scanExtension("bush");
auto& stems = assets->scanExtension("modularstem");
auto& foliages = assets->scanExtension("modularfoliage");
auto& grasses = assets->scanExtension("grass");
auto& bushes = assets->scanExtension("bush");
assets->queueJsons(stems);
assets->queueJsons(foliages);
@ -115,22 +115,22 @@ PlantDatabase::PlantDatabase() {
assets->queueJsons(bushes);
try {
for (auto file : stems) {
for (auto& file : stems) {
auto config = assets->json(file);
m_treeStemConfigs.insert(config.getString("name"), Config{AssetPath::directory(file), config.toObject()});
}
for (auto file : foliages) {
for (auto& file : foliages) {
auto config = assets->json(file);
m_treeFoliageConfigs.insert(config.getString("name"), Config{AssetPath::directory(file), config.toObject()});
}
for (auto file : grasses) {
for (auto& file : grasses) {
auto config = assets->json(file);
m_grassConfigs.insert(config.getString("name"), Config{AssetPath::directory(file), config.toObject()});
}
for (auto file : bushes) {
for (auto& file : bushes) {
auto config = assets->json(file);
m_bushConfigs.insert(config.getString("name"), Config{AssetPath::directory(file), config.toObject()});
}

View File

@ -9,9 +9,9 @@ namespace Star {
ProjectileDatabase::ProjectileDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("projectile");
auto& files = assets->scanExtension("projectile");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
try {
auto projectileConfig = readConfig(file);
if (m_configs.contains(projectileConfig->typeName))

View File

@ -60,9 +60,9 @@ QuestTemplate::QuestTemplate(Json const& config) {
QuestTemplateDatabase::QuestTemplateDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("questtemplate");
auto& files = assets->scanExtension("questtemplate");
assets->queueJsons(files);
for (auto qt : files) {
for (auto& qt : files) {
auto questTemplate = make_shared<QuestTemplate>(assets->json(qt));
if (!m_templates.insert(questTemplate->templateId, questTemplate).second)
throw StarException(strf("Duplicate quest template '{}'", questTemplate->templateId));

View File

@ -13,8 +13,8 @@ EnumMap<RadioMessageType> const RadioMessageTypeNames{
RadioMessageDatabase::RadioMessageDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("radiomessages");
for (auto file : files) {
auto& files = assets->scanExtension("radiomessages");
for (auto& file : files) {
try {
Json messages = assets->json(file);
for (auto pair : messages.iterateObject()) {

View File

@ -697,11 +697,12 @@ StringList Root::scanForAssetSources(StringList const& directories, StringList c
StringList sourcePaths;
for (auto const& source : dependencySortedSources) {
auto path = File::convertDirSeparators(source->path);
if (source->name)
Logger::info("Root: Detected asset source named '{}' at '{}'", *source->name, source->path);
Logger::info("Root: Detected asset source named '{}' at '{}'", *source->name, path);
else
Logger::info("Root: Detected unnamed asset source at '{}'", source->path);
sourcePaths.append(source->path);
Logger::info("Root: Detected unnamed asset source at '{}'", path);
sourcePaths.append(path);
}
return sourcePaths;

View File

@ -113,7 +113,7 @@ SpawnProfile constructSpawnProfile(Json const& config, uint64_t seed) {
SpawnTypeDatabase::SpawnTypeDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("spawntypes");
auto& files = assets->scanExtension("spawntypes");
assets->queueJsons(files);
uint64_t seedMix = 0;
for (auto const& file : files) {

View File

@ -27,9 +27,9 @@ SpeciesOption::SpeciesOption()
SpeciesDatabase::SpeciesDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("species");
auto& files = assets->scanExtension("species");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
auto speciesDefinition = make_shared<SpeciesDefinition>(assets->json(file));
if (m_species.contains(speciesDefinition->kind()))
throw StarException(strf("Duplicate species asset with kind {}. configfile {}", speciesDefinition->kind(), file));

View File

@ -8,9 +8,9 @@ namespace Star {
StagehandDatabase::StagehandDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("stagehand");
auto& files = assets->scanExtension("stagehand");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
try {
auto config = assets->json(file);

View File

@ -6,12 +6,12 @@ namespace Star {
StatisticsDatabase::StatisticsDatabase() : m_cacheMutex(), m_eventCache() {
auto assets = Root::singleton().assets();
auto eventFiles = assets->scanExtension("event");
auto& eventFiles = assets->scanExtension("event");
assets->queueJsons(eventFiles);
auto achievementFiles = assets->scanExtension("achievement");
auto& achievementFiles = assets->scanExtension("achievement");
assets->queueJsons(achievementFiles);
for (auto file : eventFiles) {
for (auto& file : eventFiles) {
try {
String name = assets->json(file).getString("eventName");
if (m_eventPaths.contains(name))
@ -23,7 +23,7 @@ StatisticsDatabase::StatisticsDatabase() : m_cacheMutex(), m_eventCache() {
}
}
for (auto file : achievementFiles) {
for (auto& file : achievementFiles) {
try {
Json achievement = assets->json(file);
String name = achievement.getString("name");

View File

@ -7,9 +7,9 @@ namespace Star {
StatusEffectDatabase::StatusEffectDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("statuseffect");
auto& files = assets->scanExtension("statuseffect");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
auto uniqueEffect = parseUniqueEffect(assets->json(file), file);
if (m_uniqueEffects.contains(uniqueEffect.name))

View File

@ -121,15 +121,15 @@ Json StoredConfigFunction::get(double value) const {
FunctionDatabase::FunctionDatabase() {
auto assets = Root::singleton().assets();
auto functions = assets->scanExtension("functions");
auto sndFunctions = assets->scanExtension("2functions");
auto configFunctions = assets->scanExtension("configfunctions");
auto& functions = assets->scanExtension("functions");
auto& sndFunctions = assets->scanExtension("2functions");
auto& configFunctions = assets->scanExtension("configfunctions");
assets->queueJsons(functions);
assets->queueJsons(sndFunctions);
assets->queueJsons(configFunctions);
for (auto file : functions) {
for (auto& file : functions) {
for (auto const& functionPair : assets->json(file).iterateObject()) {
if (m_functions.contains(functionPair.first))
throw StarException(strf("Named Function '{}' defined twice, second time from {}", functionPair.first, file));
@ -137,7 +137,7 @@ FunctionDatabase::FunctionDatabase() {
}
}
for (auto file : sndFunctions) {
for (auto& file : sndFunctions) {
for (auto const& functionPair : assets->json(file).iterateObject()) {
if (m_functions2.contains(functionPair.first))
throw StarException(
@ -146,7 +146,7 @@ FunctionDatabase::FunctionDatabase() {
}
}
for (auto file : configFunctions) {
for (auto& file : configFunctions) {
for (auto const& tablePair : assets->json(file).iterateObject()) {
if (m_configFunctions.contains(tablePair.first))
throw StarException(

View File

@ -13,9 +13,9 @@ EnumMap<TechType> const TechTypeNames{
TechDatabase::TechDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("tech");
auto& files = assets->scanExtension("tech");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
auto tech = parseTech(assets->json(file), file);
if (m_tech.contains(tech.name))

View File

@ -15,9 +15,9 @@ bool Tenant::criteriaSatisfied(StringMap<unsigned> const& colonyTags) const {
TenantDatabase::TenantDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("tenant");
auto& files = assets->scanExtension("tenant");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
try {
String name = assets->json(file).getString("name");
if (m_paths.contains(name))

View File

@ -59,9 +59,9 @@ TerrainDatabase::TerrainDatabase() {
// 'type' here is the extension of the file, and determines the selector type
auto scanFiles = [this, assets](String const& type) {
auto files = assets->scanExtension(type);
auto& files = assets->scanExtension(type);
assets->queueJsons(files);
for (auto path : files) {
for (auto& path : files) {
auto parameters = assets->json(path);
auto name = parameters.getString("name");
if (m_terrainSelectors.contains(name))
@ -74,9 +74,9 @@ TerrainDatabase::TerrainDatabase() {
scanFiles(WormCaveSelector::Name);
scanFiles(RidgeBlocksSelector::Name);
auto files = assets->scanExtension("terrain");
auto& files = assets->scanExtension("terrain");
assets->queueJsons(files);
for (auto path : files) {
for (auto& path : files) {
auto parameters = assets->json(path);
auto name = parameters.getString("name");
auto type = parameters.getString("type");

View File

@ -13,13 +13,13 @@ namespace Star {
TreasureDatabase::TreasureDatabase() {
auto assets = Root::singleton().assets();
auto treasurePools = assets->scanExtension("treasurepools");
auto treasureChests = assets->scanExtension("treasurechests");
auto& treasurePools = assets->scanExtension("treasurepools");
auto& treasureChests = assets->scanExtension("treasurechests");
assets->queueJsons(treasurePools);
assets->queueJsons(treasureChests);
for (auto file : treasurePools) {
for (auto& file : treasurePools) {
for (auto const& pair : assets->json(file).iterateObject()) {
if (m_treasurePools.contains(pair.first))
throw TreasureException(strf("Duplicate TreasurePool config '{}' from file '{}'", pair.first, file));
@ -70,7 +70,7 @@ TreasureDatabase::TreasureDatabase() {
}
}
for (auto file : treasureChests) {
for (auto& file : treasureChests) {
for (auto const& pair : assets->json(file).iterateObject()) {
if (m_treasureChestSets.contains(pair.first))
throw TreasureException(strf("Duplicate TreasureChestSet config '{}' from file '{}'", pair.first, file));

View File

@ -8,9 +8,9 @@ namespace Star {
VehicleDatabase::VehicleDatabase() {
auto assets = Root::singleton().assets();
auto files = assets->scanExtension("vehicle");
auto& files = assets->scanExtension("vehicle");
assets->queueJsons(files);
for (auto file : files) {
for (auto& file : files) {
try {
auto config = assets->json(file);
String name = config.getString("name");

View File

@ -30,7 +30,6 @@ LuaCallbacks LuaBindings::makeRootCallbacks() {
auto root = Root::singletonPtr();
callbacks.registerCallbackWithSignature<StringList, String>("assetsByExtension", bind(RootCallbacks::assetsByExtension, root, _1));
callbacks.registerCallbackWithSignature<String, String>("assetData", bind(RootCallbacks::assetData, root, _1));
callbacks.registerCallbackWithSignature<Json, String>("assetJson", bind(RootCallbacks::assetJson, root, _1));
callbacks.registerCallbackWithSignature<Json, String, Json>("makeCurrentVersionedJson", bind(RootCallbacks::makeCurrentVersionedJson, root, _1, _2));
@ -63,6 +62,15 @@ LuaCallbacks LuaBindings::makeRootCallbacks() {
callbacks.registerCallbackWithSignature<Maybe<String>, String, Maybe<String>>("materialMiningSound", bind(RootCallbacks::materialMiningSound, root, _1, _2));
callbacks.registerCallbackWithSignature<Maybe<String>, String, Maybe<String>>("materialFootstepSound", bind(RootCallbacks::materialFootstepSound, root, _1, _2));
callbacks.registerCallback("assetsByExtension", [root](LuaEngine& engine, String const& extension) -> LuaTable {
auto& extensions = root->assets()->scanExtension(extension);
auto table = engine.createTable(extensions.size(), 0);
size_t i = 0;
for (auto& file : extensions)
table.set(++i, file);
return table;
});
callbacks.registerCallback("assetOrigin", [root](String const& path) -> Maybe<String> {
auto assets = root->assets();
if (auto descriptor = assets->assetDescriptor(path))
@ -242,10 +250,6 @@ LuaCallbacks LuaBindings::makeRootCallbacks() {
return callbacks;
}
StringList LuaBindings::RootCallbacks::assetsByExtension(Root* root, String const& extension) {
return root->assets()->scanExtension(extension);
}
String LuaBindings::RootCallbacks::assetData(Root* root, String const& path) {
auto bytes = root->assets()->bytes(path);
return String(bytes->ptr(), bytes->size());

View File

@ -12,7 +12,6 @@ namespace LuaBindings {
LuaCallbacks makeRootCallbacks();
namespace RootCallbacks {
StringList assetsByExtension(Root* root, String const& extension);
String assetData(Root* root, String const& path);
Json assetJson(Root* root, String const& path);
Json makeCurrentVersionedJson(Root* root, String const& identifier, Json const& content);

View File

@ -22,11 +22,11 @@ int main(int argc, char** argv) {
auto assets = Root::singleton().assets();
auto countWordsInType = [&](String const& type, function<int(Json const&)> countFunction, Maybe<function<bool(String const&)>> filterFunction = {}, Maybe<String> wordCountKey = {}) {
auto files = assets->scanExtension(type);
auto& files = assets->scanExtension(type).values();
if (filterFunction)
files.filter(*filterFunction);
assets->queueJsons(files);
for (auto path : files) {
for (auto& path : files) {
auto json = assets->json(path);
if (json.isNull())
continue;
@ -62,7 +62,7 @@ int main(int argc, char** argv) {
"activeitem",
"augment" };
for (auto itemFileType : itemFileTypes) {
for (auto& itemFileType : itemFileTypes) {
countWordsInType(itemFileType, [](Json const& json) {
int wordCount = 0;
wordCount += json.getString("shortdescription", "").split(" ").count();