mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-04 18:41:51 +00:00
cleanup AssetsLoader a bit
This commit is contained in:
parent
ab3c1e1b0c
commit
ce068790c1
6 changed files with 86 additions and 80 deletions
|
|
@ -1,5 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/stringutil.hpp"
|
||||
#include "util/ObjectsKeeper.hpp"
|
||||
#include "graphics/core/TextureAnimation.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
|
@ -11,10 +15,6 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "util/stringutil.hpp"
|
||||
#include "util/ObjectsKeeper.hpp"
|
||||
#include "graphics/core/TextureAnimation.hpp"
|
||||
|
||||
class Assets;
|
||||
|
||||
enum class AssetType {
|
||||
|
|
@ -31,12 +31,12 @@ enum class AssetType {
|
|||
|
||||
namespace assetload {
|
||||
/// @brief final work to do in the main thread
|
||||
using postfunc = std::function<void(Assets*)>;
|
||||
using postfunc = std::function<void(Assets&)>;
|
||||
|
||||
using setupfunc = std::function<void(const Assets*)>;
|
||||
using setupfunc = std::function<void(const Assets&)>;
|
||||
|
||||
template <class T>
|
||||
void assets_setup(const Assets*);
|
||||
void assets_setup(const Assets&);
|
||||
|
||||
class error : public std::runtime_error {
|
||||
AssetType type;
|
||||
|
|
@ -145,7 +145,7 @@ public:
|
|||
|
||||
void setup() {
|
||||
for (auto& setupFunc : setupFuncs) {
|
||||
setupFunc(this);
|
||||
setupFunc(*this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,8 +155,8 @@ public:
|
|||
};
|
||||
|
||||
template <class T>
|
||||
void assetload::assets_setup(const Assets* assets) {
|
||||
if (auto mapPtr = assets->getMap<T>()) {
|
||||
void assetload::assets_setup(const Assets& assets) {
|
||||
if (auto mapPtr = assets.getMap<T>()) {
|
||||
for (const auto& entry : **mapPtr) {
|
||||
static_cast<T*>(entry.second.get())->setup();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ void AssetsLoader::loadNext() {
|
|||
try {
|
||||
aloader_func loader = getLoader(entry.tag);
|
||||
auto postfunc =
|
||||
loader(this, paths, entry.filename, entry.alias, entry.config);
|
||||
postfunc(&assets);
|
||||
loader(*this, paths, entry.filename, entry.alias, entry.config);
|
||||
postfunc(assets);
|
||||
} catch (const parsing_error& err) {
|
||||
error = err.errorLog();
|
||||
} catch (const std::runtime_error& err) {
|
||||
|
|
@ -361,17 +361,17 @@ const ResPaths& AssetsLoader::getPaths() const {
|
|||
}
|
||||
|
||||
class LoaderWorker : public util::Worker<aloader_entry, assetload::postfunc> {
|
||||
AssetsLoader* loader;
|
||||
AssetsLoader& loader;
|
||||
public:
|
||||
LoaderWorker(AssetsLoader* loader) : loader(loader) {
|
||||
LoaderWorker(AssetsLoader& loader) : loader(loader) {
|
||||
}
|
||||
|
||||
assetload::postfunc operator()(const aloader_entry& entry
|
||||
) override {
|
||||
aloader_func loadfunc = loader->getLoader(entry.tag);
|
||||
aloader_func loadfunc = loader.getLoader(entry.tag);
|
||||
return loadfunc(
|
||||
loader,
|
||||
loader->getPaths(),
|
||||
loader.getPaths(),
|
||||
entry.filename,
|
||||
entry.alias,
|
||||
entry.config
|
||||
|
|
@ -383,8 +383,8 @@ std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone, int maxWorkers) {
|
|||
auto pool =
|
||||
std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>(
|
||||
"assets-loader-pool",
|
||||
[=]() { return std::make_unique<LoaderWorker>(this); },
|
||||
[this](const assetload::postfunc& func) { func(&assets); },
|
||||
[=]() { return std::make_unique<LoaderWorker>(*this); },
|
||||
[this](const assetload::postfunc& func) { func(assets); },
|
||||
maxWorkers
|
||||
);
|
||||
pool->setOnComplete(std::move(onDone));
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ struct ModelCfg : AssetCfg {
|
|||
|
||||
using aloader_func = std::function<
|
||||
assetload::
|
||||
postfunc(AssetsLoader*, const ResPaths&, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
|
||||
postfunc(AssetsLoader&, const ResPaths&, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
|
||||
|
||||
struct aloader_entry {
|
||||
AssetType tag;
|
||||
|
|
@ -99,6 +99,8 @@ class AssetsLoader {
|
|||
void processPreloadConfigs(const Content* content);
|
||||
public:
|
||||
AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths);
|
||||
AssetsLoader(const AssetsLoader&) = delete;
|
||||
|
||||
void addLoader(AssetType tag, aloader_func func);
|
||||
|
||||
/// @brief Enqueue asset load
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ static debug::Logger logger("assetload-funcs");
|
|||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static bool load_animation(
|
||||
Assets* assets,
|
||||
static bool load_animated_texture(
|
||||
Assets& assets,
|
||||
const ResPaths& paths,
|
||||
const std::string& atlasName,
|
||||
const std::string& directory,
|
||||
|
|
@ -45,7 +45,7 @@ static bool load_animation(
|
|||
);
|
||||
|
||||
assetload::postfunc assetload::texture(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
|
|
@ -54,12 +54,12 @@ assetload::postfunc assetload::texture(
|
|||
auto actualFile = paths.find(filename + ".png");
|
||||
try {
|
||||
std::shared_ptr<ImageData> image(imageio::read(actualFile));
|
||||
return [name, image, actualFile](auto assets) {
|
||||
assets->store(Texture::from(image.get()), name);
|
||||
return [name, image, actualFile](auto& assets) {
|
||||
assets.store(Texture::from(image.get()), name);
|
||||
};
|
||||
} catch (const std::runtime_error& err) {
|
||||
logger.error() << actualFile.string() << ": " << err.what();
|
||||
return [](auto) {};
|
||||
return [](auto&) {};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ static auto read_program(const ResPaths& paths, const std::string& filename) {
|
|||
}
|
||||
|
||||
assetload::postfunc assetload::shader(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
|
|
@ -99,8 +99,8 @@ assetload::postfunc assetload::shader(
|
|||
io::path vertexFile = paths.find(filename + ".glslv");
|
||||
io::path fragmentFile = paths.find(filename + ".glslf");
|
||||
|
||||
return [=](auto assets) {
|
||||
assets->store(
|
||||
return [=](auto& assets) {
|
||||
assets.store(
|
||||
Shader::create(
|
||||
{vertexFile.string(), vertex},
|
||||
{fragmentFile.string(), fragment}
|
||||
|
|
@ -111,7 +111,7 @@ assetload::postfunc assetload::shader(
|
|||
}
|
||||
|
||||
assetload::postfunc assetload::posteffect(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
|
|
@ -131,7 +131,7 @@ assetload::postfunc assetload::posteffect(
|
|||
std::string vertexSource = std::move(vertex.code);
|
||||
std::string fragmentSource = std::move(fragment.code);
|
||||
|
||||
return [=](auto assets) {
|
||||
return [=](auto& assets) {
|
||||
auto program = Shader::create(
|
||||
{effectFile.string(), vertexSource},
|
||||
{effectFile.string(), fragmentSource}
|
||||
|
|
@ -140,7 +140,7 @@ assetload::postfunc assetload::posteffect(
|
|||
if (settings) {
|
||||
advanced = dynamic_cast<const PostEffectCfg*>(settings.get())->advanced;
|
||||
}
|
||||
assets->store(
|
||||
assets.store(
|
||||
std::make_shared<PostEffect>(advanced, std::move(program), params),
|
||||
name
|
||||
);
|
||||
|
|
@ -160,7 +160,7 @@ static bool append_atlas(AtlasBuilder& atlas, const io::path& file) {
|
|||
}
|
||||
|
||||
assetload::postfunc assetload::atlas(
|
||||
AssetsLoader* loader,
|
||||
AssetsLoader& loader,
|
||||
const ResPaths& paths,
|
||||
const std::string& directory,
|
||||
const std::string& name,
|
||||
|
|
@ -171,13 +171,13 @@ assetload::postfunc assetload::atlas(
|
|||
for (const auto& file : paths.listdir(directory)) {
|
||||
if (!imageio::is_read_supported(file.extension()))
|
||||
continue;
|
||||
loader->add(
|
||||
loader.add(
|
||||
AssetType::TEXTURE,
|
||||
directory + "/" + file.stem(),
|
||||
name + "/" + file.stem()
|
||||
);
|
||||
}
|
||||
return [](auto){};
|
||||
return [](auto&){};
|
||||
}
|
||||
AtlasBuilder builder;
|
||||
for (const auto& file : paths.listdir(directory)) {
|
||||
|
|
@ -186,17 +186,17 @@ assetload::postfunc assetload::atlas(
|
|||
}
|
||||
std::set<std::string> names = builder.getNames();
|
||||
Atlas* atlas = builder.build(ATLAS_EXTRUSION, false).release();
|
||||
return [=](auto assets) {
|
||||
return [=](Assets& assets) {
|
||||
atlas->prepare();
|
||||
assets->store(std::unique_ptr<Atlas>(atlas), name);
|
||||
assets.store(std::unique_ptr<Atlas>(atlas), name);
|
||||
for (const auto& file : names) {
|
||||
load_animation(assets, paths, name, directory, file, atlas);
|
||||
load_animated_texture(assets, paths, name, directory, file, atlas);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
assetload::postfunc assetload::font(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
|
|
@ -206,15 +206,15 @@ assetload::postfunc assetload::font(
|
|||
auto ext = fs::path(filename).extension().string();
|
||||
if (ext == ".ttf" || ext == ".otf") {
|
||||
logger.info() << "loading vector font " << util::quote(filename);
|
||||
return [=](Assets* assets) {
|
||||
return [=](Assets& assets) {
|
||||
using FontFile = vector_fonts::FontFile;
|
||||
FontFile* fontFile;
|
||||
if ((fontFile = assets->get<FontFile>(filename)) == nullptr) {
|
||||
if ((fontFile = assets.get<FontFile>(filename)) == nullptr) {
|
||||
auto fontFilePtr = vector_fonts::load_font(paths.find(filename).string());
|
||||
fontFile = fontFilePtr.get();
|
||||
assets->store(fontFilePtr, filename);
|
||||
assets.store(fontFilePtr, filename);
|
||||
}
|
||||
assets->store(fontFile->createInstance(cfg ? cfg->size : 16), name);
|
||||
assets.store(fontFile->createInstance(cfg ? cfg->size : 16), name);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -230,24 +230,24 @@ assetload::postfunc assetload::font(
|
|||
pages->push_back(nullptr);
|
||||
}
|
||||
}
|
||||
return [=](auto assets) {
|
||||
assets->store(Font::createBitmapFont(std::move(*pages)), name);
|
||||
return [=](auto& assets) {
|
||||
assets.store(Font::createBitmapFont(std::move(*pages)), name);
|
||||
};
|
||||
}
|
||||
|
||||
assetload::postfunc assetload::layout(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths&,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& config
|
||||
) {
|
||||
return [=](auto assets) {
|
||||
return [=](auto& assets) {
|
||||
try {
|
||||
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
|
||||
size_t pos = name.find(':');
|
||||
auto prefix = name.substr(0, pos);
|
||||
assets->store(
|
||||
assets.store(
|
||||
UiDocument::read(
|
||||
*cfg->gui,
|
||||
cfg->env,
|
||||
|
|
@ -265,7 +265,7 @@ assetload::postfunc assetload::layout(
|
|||
};
|
||||
}
|
||||
assetload::postfunc assetload::sound(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
|
|
@ -307,17 +307,17 @@ assetload::postfunc assetload::sound(
|
|||
}
|
||||
|
||||
auto sound = baseSound.release();
|
||||
return [=](auto assets) {
|
||||
assets->store(std::unique_ptr<audio::Sound>(sound), name);
|
||||
return [=](auto& assets) {
|
||||
assets.store(std::unique_ptr<audio::Sound>(sound), name);
|
||||
};
|
||||
}
|
||||
|
||||
static void request_textures(AssetsLoader* loader, const model::Model& model) {
|
||||
static void request_textures(AssetsLoader& loader, const model::Model& model) {
|
||||
for (auto& mesh : model.meshes) {
|
||||
if (mesh.texture.find('$') == std::string::npos &&
|
||||
mesh.texture.find(':') == std::string::npos) {
|
||||
auto filename = TEXTURES_FOLDER + "/" + mesh.texture;
|
||||
loader->add(
|
||||
loader.add(
|
||||
AssetType::TEXTURE, filename, mesh.texture, nullptr
|
||||
);
|
||||
}
|
||||
|
|
@ -325,7 +325,7 @@ static void request_textures(AssetsLoader* loader, const model::Model& model) {
|
|||
}
|
||||
|
||||
assetload::postfunc assetload::model(
|
||||
AssetsLoader* loader,
|
||||
AssetsLoader& loader,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
|
|
@ -337,7 +337,9 @@ assetload::postfunc assetload::model(
|
|||
if (io::exists(path)) {
|
||||
auto bytes = io::read_bytes_buffer(path);
|
||||
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(path.string(), bytes));
|
||||
return [loader, name, cfg, modelVEC3=std::move(modelVEC3)](Assets* assets) {
|
||||
return [&loader, name, cfg, modelVEC3 = std::move(modelVEC3)](
|
||||
Assets& assets
|
||||
) {
|
||||
if (cfg && cfg->squashed) {
|
||||
model::Model fullModel;
|
||||
for (auto& entry : modelVEC3->models) {
|
||||
|
|
@ -347,7 +349,7 @@ assetload::postfunc assetload::model(
|
|||
fullModel.merge(std::move(model));
|
||||
}
|
||||
request_textures(loader, fullModel);
|
||||
assets->store(
|
||||
assets.store(
|
||||
std::make_unique<model::Model>(fullModel),
|
||||
name
|
||||
);
|
||||
|
|
@ -360,7 +362,7 @@ assetload::postfunc assetload::model(
|
|||
if (name != modelName) {
|
||||
fullName += "." + modelName;
|
||||
}
|
||||
assets->store(
|
||||
assets.store(
|
||||
std::make_unique<model::Model>(model.model),
|
||||
fullName
|
||||
);
|
||||
|
|
@ -374,9 +376,9 @@ assetload::postfunc assetload::model(
|
|||
auto text = io::read_string(path);
|
||||
try {
|
||||
auto model = obj::parse(path.string(), text).release();
|
||||
return [=](Assets* assets) {
|
||||
return [=, &loader](Assets& assets) {
|
||||
request_textures(loader, *model);
|
||||
assets->store(std::unique_ptr<model::Model>(model), name);
|
||||
assets.store(std::unique_ptr<model::Model>(model), name);
|
||||
};
|
||||
} catch (const parsing_error& err) {
|
||||
std::cerr << err.errorLog() << std::endl;
|
||||
|
|
@ -409,22 +411,22 @@ assetload::postfunc assetload::model(
|
|||
|
||||
if (vcmModel.parts.size() == 1 || (cfg && cfg->squashed)) {
|
||||
auto modelPtr = std::make_unique<model::Model>(std::move(vcmModel.squash())).release();
|
||||
return [=](Assets* assets) {
|
||||
return [=, &loader](Assets& assets) {
|
||||
auto model = std::unique_ptr<model::Model>(modelPtr);
|
||||
request_textures(loader, *model);
|
||||
assets->store(std::move(model), name);
|
||||
assets.store(std::move(model), name);
|
||||
logger.info() << "store model " << util::quote(name);
|
||||
};
|
||||
} else {
|
||||
auto vcmModelPtr = std::make_unique<vcm::VcmModel>(std::move(vcmModel)).release();
|
||||
return [=](Assets* assets) {
|
||||
return [=](Assets& assets) {
|
||||
auto vcmModel = std::unique_ptr<vcm::VcmModel>(vcmModelPtr);
|
||||
for (auto& [partName, model] : vcmModel->parts) {
|
||||
auto fullName = name + "." + partName;
|
||||
logger.info()
|
||||
<< "store model part " << util::quote(partName)
|
||||
<< " as " << util::quote(fullName);
|
||||
assets->store(
|
||||
assets.store(
|
||||
std::make_unique<model::Model>(std::move(model)),
|
||||
fullName
|
||||
);
|
||||
|
|
@ -433,7 +435,7 @@ assetload::postfunc assetload::model(
|
|||
bone->setModel(name + "." + bone->model.name);
|
||||
}
|
||||
logger.info() << "store skeleton " << util::quote(name);
|
||||
assets->store<rigging::SkeletonConfig>(
|
||||
assets.store<rigging::SkeletonConfig>(
|
||||
std::make_unique<rigging::SkeletonConfig>(
|
||||
std::move(*vcmModel->skeleton)
|
||||
),
|
||||
|
|
@ -448,13 +450,13 @@ assetload::postfunc assetload::model(
|
|||
}
|
||||
|
||||
assetload::postfunc assetload::skeleton(
|
||||
AssetsLoader* loader,
|
||||
AssetsLoader& loader,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
) {
|
||||
return [=](auto assets) {
|
||||
return [&loader, &paths, file, name](auto& assets) {
|
||||
auto path = paths.find(file + ".json");
|
||||
std::string text = io::read_string(path);
|
||||
auto skeleton = rigging::SkeletonConfig::parse(text, file, name);
|
||||
|
|
@ -465,12 +467,12 @@ assetload::postfunc assetload::skeleton(
|
|||
model = model.substr(0, pos);
|
||||
}
|
||||
if (!model.empty()) {
|
||||
loader->add(
|
||||
loader.add(
|
||||
AssetType::MODEL, MODELS_FOLDER + "/" + model, model
|
||||
);
|
||||
}
|
||||
}
|
||||
assets->store(std::move(skeleton), name);
|
||||
assets.store(std::move(skeleton), name);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -552,8 +554,8 @@ inline bool contains(
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool load_animation(
|
||||
Assets* assets,
|
||||
static bool load_animated_texture(
|
||||
Assets& assets,
|
||||
const ResPaths& paths,
|
||||
const std::string& atlasName,
|
||||
const std::string& directory,
|
||||
|
|
@ -591,10 +593,10 @@ static bool load_animation(
|
|||
auto animation = create_animation(
|
||||
srcAtlas.get(), dstAtlas, name, builder.getNames(), frameList
|
||||
);
|
||||
assets->store(
|
||||
assets.store(
|
||||
std::move(srcAtlas), atlasName + "/" + name + "_animation"
|
||||
);
|
||||
assets->store(animation);
|
||||
assets.store(animation);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -14,56 +14,56 @@ struct AssetCfg;
|
|||
/// @brief see AssetsLoader.h: aloader_func
|
||||
namespace assetload {
|
||||
postfunc texture(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc shader(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc atlas(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& directory,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc font(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& filename,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc layout(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc sound(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc model(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
const std::shared_ptr<AssetCfg>& settings
|
||||
);
|
||||
postfunc posteffect(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
|
|
@ -71,7 +71,7 @@ namespace assetload {
|
|||
);
|
||||
|
||||
postfunc skeleton(
|
||||
AssetsLoader*,
|
||||
AssetsLoader&,
|
||||
const ResPaths& paths,
|
||||
const std::string& file,
|
||||
const std::string& name,
|
||||
|
|
|
|||
|
|
@ -298,6 +298,8 @@ void scripting::on_content_load(Content* content) {
|
|||
} catch (const std::exception&) {
|
||||
lua::pushnil(L);
|
||||
lua::setglobal(L, "__vc_app");
|
||||
lua::pushnil(L);
|
||||
lua::setglobal(L, "__vc_internals");
|
||||
throw;
|
||||
}
|
||||
lua::pushnil(L);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue