diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index a55e7a17..79ee123c 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,5 +1,5 @@ - \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7d518a1c..117e4c61 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -167,8 +167,6 @@ add_library(Zepha_Core lua/modules/Dimension.h lua/modules/mSetGui.h lua/modules/mStartGame.h - lua/modules/Register.cpp - lua/modules/Register.h lua/modules/SubgameModule.cpp lua/modules/SubgameModule.h lua/modules/Time.cpp @@ -176,7 +174,7 @@ add_library(Zepha_Core lua/register/CreateBlockModel.cpp lua/register/CreateBlockModel.h lua/register/RegisterBiomes.h - lua/register/RegisterBlocks.h + lua/register/RegisterBlock.h lua/register/RegisterItems.h lua/register/RegisterKeybinds.h lua/ServerLuaParser.cpp @@ -321,6 +319,6 @@ add_library(Zepha_Core world/ServerWorld.h world/World.cpp world/World.h - util/net/Address.cpp util/Bounds.cpp util/Bounds.h) + util/net/Address.cpp util/Bounds.cpp util/Bounds.h lua/register/CreateRegister.h lua/register/CreateRegister.cpp) target_include_directories(Zepha_Core PUBLIC .) \ No newline at end of file diff --git a/src/lua/LocalLuaParser.cpp b/src/lua/LocalLuaParser.cpp index 6afc35d3..71acbdd3 100644 --- a/src/lua/LocalLuaParser.cpp +++ b/src/lua/LocalLuaParser.cpp @@ -6,9 +6,8 @@ #include "client/Client.h" #include "ErrorFormatter.h" -#include "client/graph/Renderer.h" #include "register/RegisterItems.h" -#include "register/RegisterBlocks.h" +#include "register/RegisterBlock.h" #include "register/RegisterBiomes.h" #include "register/RegisterKeybinds.h" @@ -26,11 +25,13 @@ // Modules #include "modules/Time.h" -#include "modules/Register.h" #include "modules/Dimension.h" #include "modules/create_structure.h" +// Util +#include "lua/register/CreateRegister.h" + LocalLuaParser::LocalLuaParser(LocalSubgame& game): LuaParser(game), keybinds(this) {} void LocalLuaParser::init(WorldPtr world, PlayerPtr player, Client& client) { @@ -79,9 +80,30 @@ void LocalLuaParser::loadApi(WorldPtr world, PlayerPtr player) { // Modules modules.emplace_back(std::make_unique(Api::State::CLIENT, lua, core)); - modules.emplace_back(std::make_unique(Api::State::CLIENT, core, game, **world)); modules.emplace_back(std::make_unique(Api::State::CLIENT, core, game, **world)); + // Register + Api::Util::createRegister(lua, core, "mesh"); + Api::Util::createRegister(lua, core, "item"); + Api::Util::createRegister(lua, core, "block", + [&](const std::string& iden) { RegisterBlock::client(core, static_cast(game), iden); }); + Api::Util::createRegister(lua, core, "biome"); + Api::Util::createRegister(lua, core, "keybind"); + Api::Util::createRegister(lua, core, "blockmodel"); + Api::Util::createRegister(lua, core, "entity", nullptr, "entities"); + + // Define keybind variables + core["keys"] = lua.create_table(); + core["keycodes"] = lua.create_table(); + + for (unsigned short i = 0; i < 350; i++) { + auto key = ::Util::getKeyStr(i); + if (!key.empty()) { + core["keys"][key] = i; + core["keycodes"][i] = key; + } + } + bindModules(); Api::create_structure (lua, core); @@ -93,7 +115,7 @@ void LocalLuaParser::loadApi(WorldPtr world, PlayerPtr player) { void LocalLuaParser::registerDefs() { auto& local = static_cast(game); - RegisterBlocks ::client(core, local); +// RegisterBlocks ::client(core, local); RegisterItems ::client(core, local); RegisterBiomes ::client(core, local); RegisterKeybinds::client(core, keybinds); diff --git a/src/lua/ServerLuaParser.cpp b/src/lua/ServerLuaParser.cpp index 72f28050..4b2b6b20 100644 --- a/src/lua/ServerLuaParser.cpp +++ b/src/lua/ServerLuaParser.cpp @@ -3,35 +3,36 @@ // #include -#include #include "ServerLuaParser.h" #include "ErrorFormatter.h" #include "world/ServerWorld.h" #include "util/net/Serializer.h" -#include "register/RegisterItems.h" -#include "register/RegisterBiomes.h" -#include "register/RegisterBlocks.h" #include "world/player/ServerPlayer.h" +#include "lua/register/RegisterItems.h" +#include "lua/register/RegisterBiomes.h" +#include "lua/register/RegisterBlock.h" // Usertypes -#include "usertype/Target.h" -#include "usertype/Player.h" -#include "usertype/Entity.h" -#include "usertype/Inventory.h" -#include "usertype/Dimension.h" -#include "usertype/ItemStack.h" -#include "usertype/InventoryList.h" -#include "usertype/AnimationManager.h" +#include "lua/usertype/Target.h" +#include "lua/usertype/Player.h" +#include "lua/usertype/Entity.h" +#include "lua/usertype/Inventory.h" +#include "lua/usertype/Dimension.h" +#include "lua/usertype/ItemStack.h" +#include "lua/usertype/InventoryList.h" +#include "lua/usertype/AnimationManager.h" // Modules -#include "modules/Time.h" -#include "modules/Register.h" -#include "modules/Dimension.h" +#include "lua/modules/Time.h" +#include "lua/modules/Dimension.h" #include "modules/create_structure.h" +// Util +#include "lua/register/CreateRegister.h" + ServerLuaParser::ServerLuaParser(ServerSubgame& game) : LuaParser(game) {} void ServerLuaParser::init(WorldPtr world, const std::string& path) { @@ -109,9 +110,30 @@ void ServerLuaParser::loadApi(WorldPtr world) { // Modules modules.emplace_back(std::make_unique(Api::State::SERVER, lua, core)); - modules.emplace_back(std::make_unique(Api::State::SERVER, core, game, *world.s())); modules.emplace_back(std::make_unique(Api::State::SERVER, core, game, *world.s())); + // Register + Api::Util::createRegister(lua, core, "mesh"); + Api::Util::createRegister(lua, core, "item"); + Api::Util::createRegister(lua, core, "block", + [&](const std::string& iden) { RegisterBlock::server(core, static_cast(game), iden); }); + Api::Util::createRegister(lua, core, "biome"); + Api::Util::createRegister(lua, core, "keybind"); + Api::Util::createRegister(lua, core, "blockmodel"); + Api::Util::createRegister(lua, core, "entity", nullptr, "entities"); + + // Define keybind variables + core["keys"] = lua.create_table(); + core["keycodes"] = lua.create_table(); + + for (unsigned short i = 0; i < 350; i++) { + auto key = ::Util::getKeyStr(i); + if (!key.empty()) { + core["keys"][key] = i; + core["keycodes"][i] = key; + } + } + Api::create_structure (lua, core); bindModules(); @@ -123,7 +145,7 @@ void ServerLuaParser::loadApi(WorldPtr world) { void ServerLuaParser::registerDefs() { auto& server = static_cast(game); - RegisterBlocks::server(core, server); +// RegisterBlocks::server(core, server); RegisterItems ::server(core, server); RegisterBiomes::server(core, server); } @@ -141,7 +163,7 @@ sol::protected_function_result ServerLuaParser::errorCallback(sol::protected_fun std::string::size_type lineNumStart = errString.find(':', slash); if (lineNumStart != std::string::npos) throw "lineNumStart"; std::string::size_type lineNumEnd = errString.find(':', lineNumStart + 1); - if (lineNumStart != std::string::npos) throw "lineNumEnd"; + if (lineNumEnd != std::string::npos) throw "lineNumEnd"; std::string fileName = errString.substr(0, lineNumStart); int lineNum = std::stoi(errString.substr(lineNumStart + 1, lineNumEnd - lineNumStart - 1)); @@ -181,7 +203,8 @@ sol::protected_function_result ServerLuaParser::runFileSandboxed(const std::stri env["_FILE"] = f.path; env["_MODNAME"] = mod.config.name; - return lua.safe_script(f.file, env, std::bind(&ServerLuaParser::errorCallback, this, std::placeholders::_2), "@" + f.path, sol::load_mode::text); + return lua.safe_script(f.file, env, std::bind(&ServerLuaParser::errorCallback, this, + std::placeholders::_2), "@" + f.path, sol::load_mode::text); } throw std::runtime_error("Error opening \"" + file + "\", file not found."); } diff --git a/src/lua/ServerLuaParser.h b/src/lua/ServerLuaParser.h index 5c742c8d..7ea4d102 100644 --- a/src/lua/ServerLuaParser.h +++ b/src/lua/ServerLuaParser.h @@ -10,9 +10,9 @@ #include "ServerModHandler.h" -class ServerSubgame; class ServerWorld; class ServerPlayer; +class ServerSubgame; class ServerLuaParser : public LuaParser { public: diff --git a/src/lua/modules/Register.cpp b/src/lua/modules/Register.cpp deleted file mode 100644 index 22c5841d..00000000 --- a/src/lua/modules/Register.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// Created by aurailus on 2020-07-24. -// - -#include "Register.h" - -#include "../Lua.h" - -void Api::Module::Register::bind() { - - // Basic - - createRegisterFn("mesh"); - createRegisterFn("item"); - createRegisterFn("block"); - createRegisterFn("biome"); - createRegisterFn("blockmodel"); - - // Keybinds - - core["keys"] = lua.create_table(); - core["keycodes"] = lua.create_table(); - - for (unsigned short i = 0; i < 350; i++) { - auto key = Util::getKeyStr(i); - if (!key.empty()) { - core["keys"][key] = i; - core["keycodes"][i] = key; - } - } - - createRegisterFn("keybind"); - - // Entities - - core["registered_entities"] = lua.create_table(); - core.set_function("register_entity", [=](sol::this_environment env, std::string identifier, sol::table data) { - data["__index"] = data; - registerFn("registered_entities", static_cast(env), identifier, data); - }); -} - -void Api::Module::Register::createRegisterFn(const std::string& name, const std::string& table) { - std::string tableName = "registered_" + (table.empty() ? name + "s" : table); - - core[tableName] = lua.create_table(); - - core.set_function("register_" + name, [=](sol::this_environment env, std::string identifier, sol::table data) - { registerFn(tableName, static_cast(env), identifier, data); }); -} - -void Api::Module::Register::registerFn(const std::string& table, sol::environment env, const std::string& identifier, const sol::table& data) { - auto modName = env.get("_MODNAME"); - - if (identifier[0] != ':' && strncmp(identifier.data(), modName.data(), modName.length())) - throw std::runtime_error(identifier + " does not match calling mod name."); - - std::string iden = (identifier[0] == ':' ? modName + identifier : identifier); - unsigned int splitters = std::count_if(iden.begin(), iden.end(), [](char c) { return c == ':'; }); - - if (splitters > 2) throw std::runtime_error("Too many splitters in identifier " + iden + "."); - - core[table][iden] = data; -} diff --git a/src/lua/modules/Register.h b/src/lua/modules/Register.h deleted file mode 100644 index 38d433e4..00000000 --- a/src/lua/modules/Register.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// Created by aurailus on 2020-07-24. -// - -#pragma once - -#include - -#include "SubgameModule.h" - -namespace Api::Module { - class Register : public Api::Module::SubgameModule { - public: - using SubgameModule::SubgameModule; - void bind() override; - - protected: - void createRegisterFn(const std::string& name, const std::string& table = ""); - void registerFn(const std::string& table, sol::environment env, const std::string& identifier, const sol::table& data); - }; -} \ No newline at end of file diff --git a/src/lua/modules/mStartGame.h b/src/lua/modules/mStartGame.h index f56ea043..9d6493c2 100644 --- a/src/lua/modules/mStartGame.h +++ b/src/lua/modules/mStartGame.h @@ -11,8 +11,6 @@ namespace MenuApi { void start_game(Client& client, sol::table& core) { - //TODO: Don't hardcode the subgame - core.set_function("game_connect", [&](std::string address) { client.scene.setScene(std::make_unique(client, Address::fromString(address))); }); diff --git a/src/lua/register/CreateRegister.cpp b/src/lua/register/CreateRegister.cpp new file mode 100644 index 00000000..448f795a --- /dev/null +++ b/src/lua/register/CreateRegister.cpp @@ -0,0 +1,61 @@ + +#include "CreateRegister.h" + +#include "lua/Lua.h" + +namespace { + + /** + * Handles a register request from Lua. Accepts an identifier and a data table, + * adds it to the registered table, and calls the after function, if it exists. + * + * @param core - The core table, i.e. `_G['zepha']`. + * @param table - The register table, e.g `_G['registered_blocks']`. + * @param env - The sol::environment that the function is being called from. + * @param after - The function to execute after the element is added to the register table. + * @param identifier - The identifier of the element to add. + * @param data - The data table of the element to add. + */ + + void registerFn(sol::table& core, const std::string& table, sol::environment env, + std::function after, const std::string& identifier, const sol::table& data) { + + auto modName = env.get("_MODNAME"); + + if (identifier[0] != ':' && strncmp(identifier.data(), modName.data(), modName.length())) + throw std::runtime_error(identifier + " does not match calling mod name."); + + std::string iden = (identifier[0] == ':' ? modName + identifier : identifier); + unsigned int splitters = std::count_if(iden.begin(), iden.end(), [](char c) { return c == ':'; }); + + if (splitters > 2) throw std::runtime_error("Too many splitters in identifier " + iden + "."); + + core[table][iden] = data; + + if (after) after(iden); + } +} + + +/** + * Create a new register_* function in the core namespace and an associated registered_* table. + * The passed in function will be called after a new element has been inserted into the table using the + * generated function. + * + * @param lua - The Sol Lua State. + * @param core - The core table, i.e. `_G['zepha']`. + * @param name - The name of the element being registered. The name of the generated function will be 'register_[name]'. + * @param after - The function to run every time an element is registered. + * @param table - Optional, overrides the default table name and changes it to 'register_[table]'. + */ + +void Api::Util::createRegister(sol::state& lua, sol::table& core, + const std::string& name, std::function after, const std::string& table) { + + std::string tableName = "registered_" + (table.empty() ? name + "s" : table); + + core[tableName] = lua.create_table(); + + core.set_function("register_" + name, [=, &core](sol::this_environment env, std::string identifier, sol::table data) + { registerFn(core, tableName, static_cast(env), after, identifier, data); }); +} \ No newline at end of file diff --git a/src/lua/register/CreateRegister.h b/src/lua/register/CreateRegister.h new file mode 100644 index 00000000..8ed955df --- /dev/null +++ b/src/lua/register/CreateRegister.h @@ -0,0 +1,16 @@ +/* + * A Utility Function that creates register functions for Lua. + * + * - Auri, 03/11/20 + */ + +#pragma once + +#include +#include +#include + +namespace Api::Util { + void createRegister(sol::state& lua, sol::table& core, const std::string& name, + std::function after = nullptr, const std::string& table = ""); +} \ No newline at end of file diff --git a/src/lua/register/RegisterBlock.h b/src/lua/register/RegisterBlock.h new file mode 100644 index 00000000..e5fb300c --- /dev/null +++ b/src/lua/register/RegisterBlock.h @@ -0,0 +1,366 @@ +// +// Created by aurailus on 2020-01-10. +// + +#pragma once + +#include "lua/Lua.h" +#include "lua/Callback.h" +#include "game/def/ItemDef.h" +#include "game/LocalSubgame.h" +#include "game/def/BiomeDef.h" +#include "game/def/BlockDef.h" +#include "game/ServerSubgame.h" +#include "game/def/CraftItemDef.h" +#include "game/def/mesh/BlockModel.h" +#include "game/def/mesh/SelectionBox.h" +#include "game/atlas/LocalDefinitionAtlas.h" +#include "game/atlas/ServerDefinitionAtlas.h" + +namespace RegisterBlock { + + static std::vector parseBoxes(sol::table boxesTable) { + std::vector boxes{}; + + for (auto pair : boxesTable) { + if (!pair.second.is()) throw std::runtime_error("must be a table"); + sol::table table = pair.second; + + if (table.size() != 6) throw std::runtime_error("must contain exactly 6 elements"); + boxes.emplace_back(glm::vec3{table[1], table[2], table[3]}, glm::vec3{table[4], table[5], table[6]}); + } + + return boxes; + } + + static inline void getMeshPartTexture(std::string &texture, unsigned int &blendInd, std::string &blendMask) { + if (strncmp(texture.data(), "tint(", 5) == 0 && texture.find_last_of(')') != std::string::npos) { + // Biome tinting time + texture.erase(std::remove(texture.begin(), texture.end(), ' '), texture.end()); + + std::string::size_type paramsBegin = texture.find_first_of('('); + std::string::size_type paramsEnd = texture.find_last_of(')'); + + std::string paramsString = texture.substr(paramsBegin + 1, paramsEnd - paramsBegin - 1); + + std::vector params; + std::string::size_type pos = 0; + while ((pos = paramsString.find(',')) != std::string::npos) { + params.push_back(paramsString.substr(0, pos)); + paramsString.erase(0, pos + 1); + } + params.push_back(paramsString); + + if (params.size() < 2) throw std::runtime_error("Invalid biome tint values. Must have at least 2 params."); + + texture = params[1]; + blendInd = atoi(params[0].data()) + 1; //TODO: support multiple blend colors + blendMask = (params.size() >= 3 ? params[2] : ""); + } + } + + static std::pair + createBlockModel(sol::table blockTable, sol::table blockModels, TextureAtlas *atlas) { + // Get the specified block model + auto modelStr = blockTable.get_or("model", "default:cube"); + auto modelOpt = blockModels.get>(modelStr); + if (!modelOpt) throw std::runtime_error("Non-existent model \"" + modelStr + "\" specified"); + + sol::table modelTable = *modelOpt; + BlockModel model; + + // Apply basic properties + model.culls = blockTable.get_or("culls", true); + model.visible = blockTable.get_or("visible", true); + + // Convert textures and low-def textures into vectors + auto texturesOpt = blockTable.get>("textures"); + auto ldTexturesOpt = blockTable.get>("lowdef_textures"); + + if (!texturesOpt) throw std::runtime_error("Missing textures property"); + + std::vector textures; + for (auto pair : *texturesOpt) { + if (!pair.second.is()) throw std::runtime_error("textures table contains non-string value"); + textures.push_back(pair.second.as()); + } + if (textures.size() == 0) textures.push_back("_missing"); + + std::vector lowdef_textures; + if (!ldTexturesOpt) lowdef_textures = textures; + else { + for (auto pair : *ldTexturesOpt) { + if (!pair.second.is()) throw std::runtime_error("lowdef_textures table has non-string value!"); + lowdef_textures.push_back(pair.second.as()); + } + } + if (lowdef_textures.size() == 0) lowdef_textures.push_back("_missing"); + + // Parse through mesh mods and add them + sol::optional meshModTable = modelTable.get>("mesh_mods"); + if (meshModTable) { + for (auto &modEntry : *meshModTable) { + auto modTable = modEntry.second.as(); + std::string meshMod = modTable.get_or("type", "none"); + + if (meshMod == "none") continue; + else if (meshMod == "offset_x") + model.meshMods.emplace_back(MeshMod::OFFSET_X, modTable.get_or("amplitude", 1)); + else if (meshMod == "offset_y") + model.meshMods.emplace_back(MeshMod::OFFSET_Y, modTable.get_or("amplitude", 1)); + else if (meshMod == "offset_z") + model.meshMods.emplace_back(MeshMod::OFFSET_Z, modTable.get_or("amplitude", 1)); + } + } + + // Parse through all of the parts and add them to the model + auto partsOpt = modelTable.get>("parts"); + if (!partsOpt) throw std::runtime_error("blockmodel is missing parts table"); + partsOpt->for_each([&](sol::object key, sol::object value) { + + // Validate that variables are what we expect them to be + if (!value.is()) throw std::runtime_error("meshpart must be a table"); + sol::table meshPartTable = value.as(); + + auto points_optional = meshPartTable.get>("points"); + if (!points_optional) throw std::runtime_error("Meshpart is missing a points table"); + sol::table points = *points_optional; + + if (points.size() % 20 != 0) throw std::runtime_error("Points table must contain a multiple of 20 values"); + + // Populate the Vertices and Indices vectors from the points table + std::vector vertices; + std::vector indices; + + for (int i = 1; i <= points.size() / 5; i++) { + int offset = (i - 1) * 5 + 1; + + glm::vec3 pos(points[offset], points[offset + 1], points[offset + 2]); + glm::vec2 tex(points[offset + 3], points[offset + 4]); + + vertices.push_back(BlockModelVertex{pos, {}, tex, tex, {}, {}}); + } + + int ind = 0; + for (int i = 1; i <= points.size() / 20; i++) { + indices.push_back(ind); + indices.push_back(ind + 1); + indices.push_back(ind + 2); + indices.push_back(ind + 2); + indices.push_back(ind + 3); + indices.push_back(ind); + ind += 4; + } + + // Get the part's texture + int tex = std::max(static_cast(meshPartTable.get_or("tex", 1)), 1); + + auto texture = textures[std::min(tex - 1, (int) textures.size() - 1)]; + unsigned int blendInd = 0; + std::string blendMask = ""; + getMeshPartTexture(texture, blendInd, blendMask); + + // Add texture refs to blockModel if the textures table is provided + std::shared_ptr textureRef = nullptr, blendMaskRef = nullptr; + if (atlas) { + textureRef = (*atlas)[texture]; + model.textureRefs.insert(textureRef); + + if (blendInd && !blendMask.empty()) { + blendMaskRef = (*atlas)[blendMask]; + model.textureRefs.insert(blendMaskRef); + } + } + + // Create the meshpart object + MeshPart meshPart(std::move(vertices), std::move(indices), textureRef, blendInd, blendMaskRef); + + // Add the shader mod, if it exists + sol::optional shaderModTable = meshPartTable.get>("shader_mod"); + if (shaderModTable) { + std::string shaderMod = shaderModTable->get_or("type", "none"); + + if (shaderMod == "none") meshPart.shaderMod = ShaderMod::NONE; + else if (shaderMod == "rotate_x") { + meshPart.shaderMod = ShaderMod::ROTATE_X; + meshPart.modValue = (*shaderModTable).get_or("speed", 1); + } + else if (shaderMod == "rotate_y") { + meshPart.shaderMod = ShaderMod::ROTATE_Y; + meshPart.modValue = (*shaderModTable).get_or("speed", 1); + } + else if (shaderMod == "rotate_z") { + meshPart.shaderMod = ShaderMod::ROTATE_Z; + meshPart.modValue = (*shaderModTable).get_or("speed", 1); + } + else if (shaderMod == "sway_attached") { + meshPart.shaderMod = ShaderMod::SWAY_ATTACHED; + meshPart.modValue = (*shaderModTable).get_or("amplitude", 1); + } + else if (shaderMod == "sway_full_block") { + meshPart.shaderMod = ShaderMod::SWAY_FULL_BLOCK; + meshPart.modValue = (*shaderModTable).get_or("amplitude", 1); + } + } + + //Add the meshpart to the proper face of the model + std::string face = meshPartTable.get_or("face", "nocull"); + + EVec d = + face == "top" ? EVec::TOP : + face == "bottom" ? EVec::BOTTOM : + face == "left" ? EVec::LEFT : + face == "right" ? EVec::RIGHT : + face == "front" ? EVec::FRONT : + face == "back" ? EVec::BACK : + face == "nocull" ? EVec::NO_CULL : + EVec::INVALID; + + if (d == EVec::INVALID) throw std::runtime_error("face value is unrecognized"); + model.parts[static_cast(d)].push_back(meshPart); + }); + + // Create the far model + BlockModel farModel; + auto ldRender = blockTable.get_or("lowdef_render", true); + + if (atlas) { + std::vector> textureRefs; + std::vector blendInds; + std::vector> blendMaskRefs; + + for (auto i = 0; i < lowdef_textures.size(); i++) { + std::string texture = lowdef_textures[i]; + unsigned int blendInd = 0; + std::string blendMask = ""; + getMeshPartTexture(texture, blendInd, blendMask); + + textureRefs.push_back((*atlas)[texture]); + blendInds.push_back(blendInd); + blendMaskRefs.push_back(blendMask != "" ? (*atlas)[blendMask] : nullptr); + } + + farModel = BlockModel::createCube(textureRefs, blendInds, blendMaskRefs); + } + else { + farModel = BlockModel::createCube({}, {}, {}); + } + + farModel.culls = ldRender; + farModel.visible = ldRender; + + return {model, farModel}; + } + + static void addCallback(BlockDef *blockDef, sol::table &blockTable, const std::string &name, Callback enumType) { + auto cb = blockTable.get>(name); + if (cb) blockDef->callbacks.insert({enumType, *cb}); + } + + static void registerBlock(sol::table blocks, sol::table blockModels, + const std::string &identifier, DefinitionAtlas &defs, TextureAtlas *atlas) { + + sol::table blockTable = blocks[identifier]; + + // Basic Block Properties + auto nameOpt = blockTable.get>("name"); + if (!nameOpt) throw std::runtime_error(identifier + " is missing name property!"); + + bool culls = blockTable.get_or("culls", true); + bool solid = blockTable.get_or("solid", true); + bool lightPropagates = blockTable.get_or("light_propagates", false); + auto maxStack = blockTable.get_or("stack", 64); + + unsigned int health = INT32_MAX, defense = 0; + auto toolOpt = blockTable.get>("tool_props"); + if (toolOpt) { + health = toolOpt->get_or("health", INT32_MAX); + defense = toolOpt->get_or("defense", 0); + } + + glm::vec3 lightSource{}; + if (blockTable.get>("light_source")) { + auto light = blockTable.get("light_source"); + lightSource = {light[1], light[2], light[3]}; + } else if (blockTable.get_or("light_source", -1) != -1) { + auto light = blockTable.get("light_source"); + lightSource = {light, light, light}; + } + + // Parse through selection boxes and collision boxes + auto selectionOpt = blockTable.get>("selection_box"); + auto collisionOpt = blockTable.get>("collision_box"); + + std::vector selectionBoxes{}; + try { if (selectionOpt) selectionBoxes = parseBoxes(*selectionOpt); } + catch (const char *error) { throw std::string("selection boxes " + std::string(error)).c_str(); } + if (selectionBoxes.size() == 0) selectionBoxes.emplace_back(glm::vec3{0, 0, 0}, glm::vec3{1, 1, 1}); + + std::vector collisionBoxes{}; + try { if (collisionOpt) collisionBoxes = parseBoxes(*collisionOpt); } + catch (const char *error) { throw std::string("collision boxes " + std::string(error)).c_str(); } + if (collisionBoxes.size() == 0) collisionBoxes.emplace_back(glm::vec3{0, 0, 0}, glm::vec3{1, 1, 1}); + + // Create the block model + std::pair models = createBlockModel(blockTable, blockModels, atlas); + + BlockDef *def = new BlockDef(); + def->identifier = identifier; + def->name = *nameOpt; + def->index = defs.size(); + + def->culls = culls; + def->solid = solid; + def->lightSource = lightSource; + def->lightPropagates = lightPropagates; + + def->health = health; + def->defense = defense; + + def->maxStackSize = maxStack; + + def->model = models.first; + def->farModel = models.second; + + def->sBoxes = std::move(selectionBoxes); + def->cBoxes = std::move(collisionBoxes); + + // Create entity model + if (atlas) def->createModel(); + + // Bind Callbacks + addCallback(def, blockTable, "on_construct", Callback::CONSTRUCT); + addCallback(def, blockTable, "after_construct", Callback::AFTER_CONSTRUCT); + + addCallback(def, blockTable, "on_destruct", Callback::DESTRUCT); + addCallback(def, blockTable, "after_destruct", Callback::AFTER_DESTRUCT); + + addCallback(def, blockTable, "on_place", Callback::PLACE); + addCallback(def, blockTable, "on_place_client", Callback::PLACE_CLIENT); + + addCallback(def, blockTable, "after_place", Callback::AFTER_PLACE); + addCallback(def, blockTable, "after_place_client", Callback::AFTER_PLACE_CLIENT); + + addCallback(def, blockTable, "on_break", Callback::BREAK); + addCallback(def, blockTable, "on_break_client", Callback::BREAK_CLIENT); + + addCallback(def, blockTable, "after_break", Callback::AFTER_BREAK); + addCallback(def, blockTable, "after_break_client", Callback::AFTER_BREAK_CLIENT); + + addCallback(def, blockTable, "on_interact", Callback::INTERACT); + addCallback(def, blockTable, "on_interact_client", Callback::INTERACT_CLIENT); + + // Add Block Definition to the Atlas + defs.registerDef(def); + } + + static void server(sol::table &core, ServerSubgame &game, const std::string &identifier) { + registerBlock(core["registered_blocks"], core["registered_blockmodels"], + identifier, game.getDefs(), nullptr); + } + + static void client(sol::table &core, LocalSubgame &game, const std::string &identifier) { + registerBlock(core["registered_blocks"], core["registered_blockmodels"], + identifier, game.getDefs(), &game.textures); + } +}; diff --git a/src/lua/register/RegisterBlocks.h b/src/lua/register/RegisterBlocks.h deleted file mode 100644 index c5506ec4..00000000 --- a/src/lua/register/RegisterBlocks.h +++ /dev/null @@ -1,373 +0,0 @@ -// -// Created by aurailus on 2020-01-10. -// - -#pragma once - -#include "lua/Lua.h" -#include "lua/Callback.h" -#include "game/def/ItemDef.h" -#include "game/LocalSubgame.h" -#include "game/def/BiomeDef.h" -#include "game/def/BlockDef.h" -#include "game/ServerSubgame.h" -#include "game/def/CraftItemDef.h" -#include "game/def/mesh/BlockModel.h" -#include "game/def/mesh/SelectionBox.h" -#include "game/atlas/LocalDefinitionAtlas.h" -#include "game/atlas/ServerDefinitionAtlas.h" - -namespace RegisterBlocks { - - static std::vector parseBoxes(sol::table boxesTable) { - std::vector boxes {}; - - for (auto pair : boxesTable) { - if (!pair.second.is()) throw std::runtime_error("must be a table"); - sol::table table = pair.second; - - if (table.size() != 6) throw std::runtime_error("must contain exactly 6 elements"); - boxes.emplace_back(glm::vec3 {table[1], table[2], table[3]}, glm::vec3 {table[4], table[5], table[6]}); - } - - return boxes; - } - - static inline void getMeshPartTexture(std::string& texture, unsigned int& blendInd, std::string& blendMask) { - if (strncmp(texture.data(), "tint(", 5) == 0 && texture.find_last_of(')') != std::string::npos) { - // Biome tinting time - texture.erase(std::remove(texture.begin(), texture.end(), ' '), texture.end()); - - std::string::size_type paramsBegin = texture.find_first_of('('); - std::string::size_type paramsEnd = texture.find_last_of(')'); - - std::string paramsString = texture.substr(paramsBegin + 1, paramsEnd - paramsBegin - 1); - - std::vector params; - std::string::size_type pos = 0; - while ((pos = paramsString.find(',')) != std::string::npos) { - params.push_back(paramsString.substr(0, pos)); - paramsString.erase(0, pos + 1); - } - params.push_back(paramsString); - - if (params.size() < 2) throw std::runtime_error("Invalid biome tint values. Must have at least 2 params."); - - texture = params[1]; - blendInd = atoi(params[0].data()) + 1; //TODO: support multiple blend colors - blendMask = (params.size() >= 3 ? params[2] : ""); - } - } - - static std::pair createBlockModel(sol::table blockTable, sol::table blockModels, TextureAtlas* atlas) { - // Get the specified block model - auto modelStr = blockTable.get_or("model", "default:cube"); - auto modelOpt = blockModels.get>(modelStr); - if (!modelOpt) throw std::runtime_error("Non-existent model \"" + modelStr + "\" specified"); - - sol::table modelTable = *modelOpt; - BlockModel model; - - // Apply basic properties - model.culls = blockTable.get_or("culls", true); - model.visible = blockTable.get_or("visible", true); - - // Convert textures and low-def textures into vectors - auto texturesOpt = blockTable.get>("textures"); - auto ldTexturesOpt = blockTable.get>("lowdef_textures"); - - if (!texturesOpt) throw std::runtime_error("Missing textures property"); - - std::vector textures; - for (auto pair : *texturesOpt) { - if (!pair.second.is()) throw std::runtime_error("textures table contains non-string value"); - textures.push_back(pair.second.as()); - } - if (textures.size() == 0) textures.push_back("_missing"); - - std::vector lowdef_textures; - if (!ldTexturesOpt) lowdef_textures = textures; - else { - for (auto pair : *ldTexturesOpt) { - if (!pair.second.is()) throw std::runtime_error("lowdef_textures table has non-string value!"); - lowdef_textures.push_back(pair.second.as()); - } - } - if (lowdef_textures.size() == 0) lowdef_textures.push_back("_missing"); - - // Parse through mesh mods and add them - sol::optional meshModTable = modelTable.get> ("mesh_mods"); - if (meshModTable) { - for (auto& modEntry : *meshModTable) { - auto modTable = modEntry.second.as(); - std::string meshMod = modTable.get_or("type", "none"); - - if (meshMod == "none") continue; - else if (meshMod == "offset_x") - model.meshMods.emplace_back(MeshMod::OFFSET_X, modTable.get_or("amplitude", 1)); - else if (meshMod == "offset_y") - model.meshMods.emplace_back(MeshMod::OFFSET_Y, modTable.get_or("amplitude", 1)); - else if (meshMod == "offset_z") - model.meshMods.emplace_back(MeshMod::OFFSET_Z, modTable.get_or("amplitude", 1)); - } - } - - // Parse through all of the parts and add them to the model - auto partsOpt = modelTable.get>("parts"); - if (!partsOpt) throw std::runtime_error("blockmodel is missing parts table"); - partsOpt->for_each([&](sol::object key, sol::object value) { - - // Validate that variables are what we expect them to be - if (!value.is()) throw std::runtime_error("meshpart must be a table"); - sol::table meshPartTable = value.as(); - - auto points_optional = meshPartTable.get>("points"); - if (!points_optional) throw std::runtime_error("Meshpart is missing a points table"); - sol::table points = *points_optional; - - if (points.size() % 20 != 0) throw std::runtime_error("Points table must contain a multiple of 20 values"); - - // Populate the Vertices and Indices vectors from the points table - std::vector vertices; - std::vector indices; - - for (int i = 1; i <= points.size() / 5; i++) { - int offset = (i - 1) * 5 + 1; - - glm::vec3 pos(points[offset], points[offset + 1], points[offset + 2]); - glm::vec2 tex(points[offset + 3], points[offset + 4]); - - vertices.push_back(BlockModelVertex {pos, {}, tex, tex, {}, {}}); - } - - int ind = 0; - for (int i = 1; i <= points.size() / 20; i++) { - indices.push_back(ind); - indices.push_back(ind + 1); - indices.push_back(ind + 2); - indices.push_back(ind + 2); - indices.push_back(ind + 3); - indices.push_back(ind); - ind += 4; - } - - // Get the part's texture - int tex = std::max(static_cast(meshPartTable.get_or("tex", 1)), 1); - - auto texture = textures[std::min(tex - 1, (int) textures.size() - 1)]; - unsigned int blendInd = 0; - std::string blendMask = ""; - getMeshPartTexture(texture, blendInd, blendMask); - - // Add texture refs to blockModel if the textures table is provided - std::shared_ptr textureRef = nullptr, blendMaskRef = nullptr; - if (atlas) { - textureRef = (*atlas)[texture]; - model.textureRefs.insert(textureRef); - - if (blendInd && !blendMask.empty()) { - blendMaskRef = (*atlas)[blendMask]; - model.textureRefs.insert(blendMaskRef); - } - } - - // Create the meshpart object - MeshPart meshPart(std::move(vertices), std::move(indices), textureRef, blendInd, blendMaskRef); - - // Add the shader mod, if it exists - sol::optional shaderModTable = meshPartTable.get>("shader_mod"); - if (shaderModTable) { - std::string shaderMod = shaderModTable->get_or("type", "none"); - - if (shaderMod == "none") meshPart.shaderMod = ShaderMod::NONE; - else if (shaderMod == "rotate_x") { - meshPart.shaderMod = ShaderMod::ROTATE_X; - meshPart.modValue = (*shaderModTable).get_or("speed", 1); - } - else if (shaderMod == "rotate_y") { - meshPart.shaderMod = ShaderMod::ROTATE_Y; - meshPart.modValue = (*shaderModTable).get_or("speed", 1); - } - else if (shaderMod == "rotate_z") { - meshPart.shaderMod = ShaderMod::ROTATE_Z; - meshPart.modValue = (*shaderModTable).get_or("speed", 1); - } - else if (shaderMod == "sway_attached") { - meshPart.shaderMod = ShaderMod::SWAY_ATTACHED; - meshPart.modValue = (*shaderModTable).get_or("amplitude", 1); - } - else if (shaderMod == "sway_full_block") { - meshPart.shaderMod = ShaderMod::SWAY_FULL_BLOCK; - meshPart.modValue = (*shaderModTable).get_or("amplitude", 1); - } - } - - //Add the meshpart to the proper face of the model - std::string face = meshPartTable.get_or("face", "nocull"); - - EVec d = - face == "top" ? EVec::TOP : - face == "bottom" ? EVec::BOTTOM : - face == "left" ? EVec::LEFT : - face == "right" ? EVec::RIGHT : - face == "front" ? EVec::FRONT : - face == "back" ? EVec::BACK : - face == "nocull" ? EVec::NO_CULL : - EVec::INVALID ; - - if (d == EVec::INVALID) throw std::runtime_error("face value is unrecognized"); - model.parts[static_cast(d)].push_back(meshPart); - }); - - // Create the far model - BlockModel farModel; - auto ldRender = blockTable.get_or("lowdef_render", true); - - if (atlas) { - std::vector> textureRefs; - std::vector blendInds; - std::vector> blendMaskRefs; - - for (auto i = 0; i < lowdef_textures.size(); i++) { - std::string texture = lowdef_textures[i]; - unsigned int blendInd = 0; - std::string blendMask = ""; - getMeshPartTexture(texture, blendInd, blendMask); - - textureRefs.push_back((*atlas)[texture]); - blendInds.push_back(blendInd); - blendMaskRefs.push_back(blendMask != "" ? (*atlas)[blendMask] : nullptr); - } - - farModel = BlockModel::createCube(textureRefs, blendInds, blendMaskRefs); - } - else { - farModel = BlockModel::createCube({}, {}, {}); - } - - farModel.culls = ldRender; - farModel.visible = ldRender; - - return {model, farModel}; - } - - static void addCallback(BlockDef* blockDef, sol::table& blockTable, const std::string& name, Callback enumType) { - auto cb = blockTable.get>(name); - if (cb) blockDef->callbacks.insert({enumType, *cb}); - } - - static void registerBlocks(sol::table source, sol::table blockModels, DefinitionAtlas& defs, TextureAtlas* atlas) { - // Parses through all of the zepha.registered_blocks and makes BlockDefs. - for (auto blockRef : source) { - - // Validate that the identifier and definition table exist - std::string identifier = blockRef.first.as(); - - if (!blockRef.second || !blockRef.second.is()) - throw std::runtime_error("register_block expects a table as the second parameter"); - sol::table blockTable = blockRef.second.as(); - - // Basic Block Properties - auto nameOpt = blockTable.get>("name"); - if (!nameOpt) throw std::runtime_error(identifier + " is missing name property!"); - - bool culls = blockTable.get_or("culls", true); - bool solid = blockTable.get_or("solid", true); - bool lightPropagates = blockTable.get_or("light_propagates", false); - auto maxStack = blockTable.get_or("stack", 64); - - unsigned int health = INT32_MAX, defense = 0; - auto toolOpt = blockTable.get>("tool_props"); - if (toolOpt) { - health = toolOpt->get_or("health", INT32_MAX); - defense = toolOpt->get_or("defense", 0); - } - - glm::vec3 lightSource {}; - if (blockTable.get>("light_source")) { - auto light = blockTable.get("light_source"); - lightSource = { light[1], light[2], light[3] }; - } - else if (blockTable.get_or("light_source", -1) != -1) { - auto light = blockTable.get("light_source"); - lightSource = { light, light, light }; - } - - // Parse through selection boxes and collision boxes - auto selectionOpt = blockTable.get>("selection_box"); - auto collisionOpt = blockTable.get>("collision_box"); - - std::vector selectionBoxes {}; - try { if (selectionOpt) selectionBoxes = parseBoxes(*selectionOpt); } - catch (const char* error) { throw std::string("selection boxes " + std::string(error)).c_str(); } - if (selectionBoxes.size() == 0) selectionBoxes.emplace_back(glm::vec3 {0, 0, 0}, glm::vec3 {1, 1, 1}); - - std::vector collisionBoxes {}; - try { if (collisionOpt) collisionBoxes = parseBoxes(*collisionOpt); } - catch (const char* error) { throw std::string("collision boxes " + std::string(error)).c_str(); } - if (collisionBoxes.size() == 0) collisionBoxes.emplace_back(glm::vec3 {0, 0, 0}, glm::vec3 {1, 1, 1}); - - // Create the block model - std::pair models = createBlockModel(blockTable, blockModels, atlas); - - BlockDef* def = new BlockDef(); - def->identifier = identifier; - def->name = *nameOpt; - def->index = defs.size(); - - def->culls = culls; - def->solid = solid; - def->lightSource = lightSource; - def->lightPropagates = lightPropagates; - - def->health = health; - def->defense = defense; - - def->maxStackSize = maxStack; - - def->model = models.first; - def->farModel = models.second; - - def->sBoxes = std::move(selectionBoxes); - def->cBoxes = std::move(collisionBoxes); - - // Create entity model - if (atlas) def->createModel(); - - // Bind Callbacks - addCallback(def, blockTable, "on_construct", Callback::CONSTRUCT); - addCallback(def, blockTable, "after_construct", Callback::AFTER_CONSTRUCT); - - addCallback(def, blockTable, "on_destruct", Callback::DESTRUCT); - addCallback(def, blockTable, "after_destruct", Callback::AFTER_DESTRUCT); - - addCallback(def, blockTable, "on_place", Callback::PLACE); - addCallback(def, blockTable, "on_place_client", Callback::PLACE_CLIENT); - - addCallback(def, blockTable, "after_place", Callback::AFTER_PLACE); - addCallback(def, blockTable, "after_place_client", Callback::AFTER_PLACE_CLIENT); - - addCallback(def, blockTable, "on_break", Callback::BREAK); - addCallback(def, blockTable, "on_break_client", Callback::BREAK_CLIENT); - - addCallback(def, blockTable, "after_break", Callback::AFTER_BREAK); - addCallback(def, blockTable, "after_break_client", Callback::AFTER_BREAK_CLIENT); - - addCallback(def, blockTable, "on_interact", Callback::INTERACT); - addCallback(def, blockTable, "on_interact_client", Callback::INTERACT_CLIENT); - - // Add Block Definition to the AtlasK - defs.registerDef(def); - } - } - - static void server(sol::table& core, ServerSubgame& game) { - registerBlocks(core.get("registered_blocks"), - core.get("registered_blockmodels"), game.getDefs(), nullptr); - } - - static void client(sol::table& core, LocalSubgame& game) { - registerBlocks(core.get("registered_blocks"), - core.get("registered_blockmodels"), game.getDefs(), &game.textures); - } -}; diff --git a/src/lua/usertype/Dimension.cpp b/src/lua/usertype/Dimension.cpp index c8709d3d..59047416 100644 --- a/src/lua/usertype/Dimension.cpp +++ b/src/lua/usertype/Dimension.cpp @@ -55,6 +55,7 @@ sol::table Api::Usertype::Dimension::add_entity_c(sol::this_state s, glm::vec3 p if (core["registered_entities"][identifier] == sol::nil) throw std::runtime_error(identifier + " is not a valid entity identifier."); sol::table def = core["registered_entities"][identifier]; + def["__index"] = def; sol::table luaEntity = lua.create_table(); luaEntity[sol::metatable_key] = def; @@ -92,6 +93,7 @@ sol::table Api::Usertype::Dimension::add_entity_s(sol::this_state s, glm::vec3 p if (core["registered_entities"][identifier] == sol::nil) throw std::runtime_error(identifier + " is not a valid entity identifier."); sol::table def = core["registered_entities"][identifier]; + def["__index"] = def; sol::table luaEntity = lua.create_table(); luaEntity[sol::metatable_key] = def;