diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 1b2f7bc61..73b2e3477 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -7112,6 +7112,24 @@ ADD COLUMN `first_login` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `xtargets`; )", .content_schema_update = false }, + ManifestEntry{ + .version = 9324, + .description = "2025_06_01_script_constants.sql", + .check = "SHOW TABLES LIKE 'script_constants'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `script_constants` ( + `zone` VARCHAR(32) DEFAULT '', + `version` SMALLINT(5) DEFAULT -1, + `lua_namespace` VARCHAR(100), + `name` VARCHAR(100), + `value` TEXT, + `valuetype` SMALLINT(5), + PRIMARY KEY (`zone`, `version`, `lua_namespace`, `name`) +); +)", + }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ // .version = 9228, diff --git a/common/database_schema.h b/common/database_schema.h index de6f8472f..1003b1297 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -239,6 +239,7 @@ namespace DatabaseSchema { "pets_beastlord_data", "pets_equipmentset", "pets_equipmentset_entries", + "script_constants", "skill_caps", "spawn2", "spawn_conditions", diff --git a/common/repositories/base/base_script_constants_repository.h b/common/repositories/base/base_script_constants_repository.h new file mode 100644 index 000000000..ea5377146 --- /dev/null +++ b/common/repositories/base/base_script_constants_repository.h @@ -0,0 +1,440 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://docs.eqemu.io/developer/repositories + */ + +#ifndef EQEMU_BASE_SCRIPT_CONSTANTS_REPOSITORY_H +#define EQEMU_BASE_SCRIPT_CONSTANTS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseScriptConstantsRepository { +public: + struct ScriptConstants { + std::string zone; + int16_t version; + std::string lua_namespace; + std::string name; + std::string value; + int16_t valuetype; + }; + + static std::string PrimaryKey() + { + return std::string("version"); + } + + static std::vector Columns() + { + return { + "zone", + "version", + "lua_namespace", + "name", + "value", + "valuetype", + }; + } + + static std::vector SelectColumns() + { + return { + "zone", + "version", + "lua_namespace", + "name", + "value", + "valuetype", + }; + } + + static std::string ColumnsRaw() + { + return std::string(Strings::Implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(Strings::Implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("script_constants"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static ScriptConstants NewEntity() + { + ScriptConstants e{}; + + e.zone = ""; + e.version = -1; + e.lua_namespace = ""; + e.name = ""; + e.value = ""; + e.valuetype = 0; + + return e; + } + + static ScriptConstants GetScriptConstants( + const std::vector &script_constantss, + int script_constants_id + ) + { + for (auto &script_constants : script_constantss) { + if (script_constants.version == script_constants_id) { + return script_constants; + } + } + + return NewEntity(); + } + + static ScriptConstants FindOne( + Database& db, + int script_constants_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + script_constants_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + ScriptConstants e{}; + + e.zone = row[0] ? row[0] : ""; + e.version = row[1] ? static_cast(atoi(row[1])) : -1; + e.lua_namespace = row[2] ? row[2] : ""; + e.name = row[3] ? row[3] : ""; + e.value = row[4] ? row[4] : ""; + e.valuetype = row[5] ? static_cast(atoi(row[5])) : 0; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int script_constants_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + script_constants_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const ScriptConstants &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[0] + " = '" + Strings::Escape(e.zone) + "'"); + v.push_back(columns[1] + " = " + std::to_string(e.version)); + v.push_back(columns[2] + " = '" + Strings::Escape(e.lua_namespace) + "'"); + v.push_back(columns[3] + " = '" + Strings::Escape(e.name) + "'"); + v.push_back(columns[4] + " = '" + Strings::Escape(e.value) + "'"); + v.push_back(columns[5] + " = " + std::to_string(e.valuetype)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.version + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static ScriptConstants InsertOne( + Database& db, + ScriptConstants e + ) + { + std::vector v; + + v.push_back("'" + Strings::Escape(e.zone) + "'"); + v.push_back(std::to_string(e.version)); + v.push_back("'" + Strings::Escape(e.lua_namespace) + "'"); + v.push_back("'" + Strings::Escape(e.name) + "'"); + v.push_back("'" + Strings::Escape(e.value) + "'"); + v.push_back(std::to_string(e.valuetype)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + Strings::Implode(",", v) + ) + ); + + if (results.Success()) { + e.version = results.LastInsertedID(); + return e; + } + + e = NewEntity(); + + return e; + } + + static int InsertMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back("'" + Strings::Escape(e.zone) + "'"); + v.push_back(std::to_string(e.version)); + v.push_back("'" + Strings::Escape(e.lua_namespace) + "'"); + v.push_back("'" + Strings::Escape(e.name) + "'"); + v.push_back("'" + Strings::Escape(e.value) + "'"); + v.push_back(std::to_string(e.valuetype)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + ScriptConstants e{}; + + e.zone = row[0] ? row[0] : ""; + e.version = row[1] ? static_cast(atoi(row[1])) : -1; + e.lua_namespace = row[2] ? row[2] : ""; + e.name = row[3] ? row[3] : ""; + e.value = row[4] ? row[4] : ""; + e.valuetype = row[5] ? static_cast(atoi(row[5])) : 0; + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + ScriptConstants e{}; + + e.zone = row[0] ? row[0] : ""; + e.version = row[1] ? static_cast(atoi(row[1])) : -1; + e.lua_namespace = row[2] ? row[2] : ""; + e.name = row[3] ? row[3] : ""; + e.value = row[4] ? row[4] : ""; + e.valuetype = row[5] ? static_cast(atoi(row[5])) : 0; + + all_entries.push_back(e); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, const std::string &where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int64 GetMaxId(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COALESCE(MAX({}), 0) FROM {}", + PrimaryKey(), + TableName() + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static int64 Count(Database& db, const std::string &where_filter = "") + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COUNT(*) FROM {} {}", + TableName(), + (where_filter.empty() ? "" : "WHERE " + where_filter) + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static std::string BaseReplace() + { + return fmt::format( + "REPLACE INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static int ReplaceOne( + Database& db, + const ScriptConstants &e + ) + { + std::vector v; + + v.push_back("'" + Strings::Escape(e.zone) + "'"); + v.push_back(std::to_string(e.version)); + v.push_back("'" + Strings::Escape(e.lua_namespace) + "'"); + v.push_back("'" + Strings::Escape(e.name) + "'"); + v.push_back("'" + Strings::Escape(e.value) + "'"); + v.push_back(std::to_string(e.valuetype)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseReplace(), + Strings::Implode(",", v) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int ReplaceMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back("'" + Strings::Escape(e.zone) + "'"); + v.push_back(std::to_string(e.version)); + v.push_back("'" + Strings::Escape(e.lua_namespace) + "'"); + v.push_back("'" + Strings::Escape(e.name) + "'"); + v.push_back("'" + Strings::Escape(e.value) + "'"); + v.push_back(std::to_string(e.valuetype)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_BASE_SCRIPT_CONSTANTS_REPOSITORY_H diff --git a/common/repositories/script_constants_repository.h b/common/repositories/script_constants_repository.h new file mode 100644 index 000000000..383bc1001 --- /dev/null +++ b/common/repositories/script_constants_repository.h @@ -0,0 +1,52 @@ +#ifndef EQEMU_SCRIPT_CONSTANTS_REPOSITORY_H +#define EQEMU_SCRIPT_CONSTANTS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_script_constants_repository.h" + +class ScriptConstantsRepository: public BaseScriptConstantsRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * ScriptConstantsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * ScriptConstantsRepository::GetWhereNeverExpires() + * ScriptConstantsRepository::GetWhereXAndY() + * ScriptConstantsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + enum ValueTypes { + Number = 1, + String = 2 + }; +}; + +#endif //EQEMU_SCRIPT_CONSTANTS_REPOSITORY_H diff --git a/common/version.h b/common/version.h index 5a2c1bdff..9e0614f12 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9323 +#define CURRENT_BINARY_DATABASE_VERSION 9324 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 #define CUSTOM_BINARY_DATABASE_VERSION 0 diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 8a2e2f21d..142aff8ef 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -30,6 +30,7 @@ #include "../common/events/player_event_logs.h" #include "worldserver.h" #include "zone.h" +#include "../common/repositories/script_constants_repository.h" struct Events { }; struct Factions { }; @@ -8056,4 +8057,68 @@ luabind::scope lua_register_exp_source() { )]; } +// Pulls the data out of the 'script_constants' table and places them in a global 'DBConstants' table. +// +// DBConstants { +// lua_namespace { +// name = value +// } +// } +void lua_register_db_consts(lua_State *L) { + if (!zone) { + return; + } + + const auto &consts = ScriptConstantsRepository::GetWhere( + content_db, + fmt::format( + "(zone = '{}' or zone = '') and (version = {} or version = -1) order by lua_namespace", + zone->GetShortName(), + zone->GetInstanceVersion() + ) + ); + + std::string current_namespace = ""; + + LUABIND_CHECK_STACK(L); //Asserts that we haven't left anything on the stack. + + lua_newtable(L); //Toplevel db constants table + + for (const auto &sc : consts) { + if (sc.lua_namespace != current_namespace) { + if (current_namespace != "") { + // Top of stack (-1) = namespace table + // -2 = db constants table + // Pops our namespace table from the stack + lua_setfield(L, -2, current_namespace.c_str()); //DBConstants[current_namespace] = namespacetable; + } + + lua_newtable(L); + current_namespace = sc.lua_namespace; + } + + switch (sc.valuetype) { + case ScriptConstantsRepository::ValueTypes::Number: + lua_pushnumber(L, Strings::ToInt(sc.value)); + break; + default: + lua_pushstring(L, sc.value.c_str()); + break; + } + + // Top of stack (-1) = value + // -2 = namespace table + // -3 = db constants table + // Pops our value off the stack + lua_setfield(L, -2, sc.name.c_str()); //namespacetable[name] = value; + } + + if (current_namespace != "") { + lua_setfield(L, -2, current_namespace.c_str()); + } + + // Top of stack (-1) = db constants table + lua_setglobal(L, "DBConstants"); +} + #endif diff --git a/zone/lua_general.h b/zone/lua_general.h index 6c195d817..cfcfefc75 100644 --- a/zone/lua_general.h +++ b/zone/lua_general.h @@ -25,6 +25,7 @@ luabind::scope lua_register_rules(); luabind::scope lua_register_journal_speakmode(); luabind::scope lua_register_journal_mode(); luabind::scope lua_register_exp_source(); +void lua_register_db_consts(lua_State *L); #endif #endif diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index fa684a490..5024e7b73 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -1326,6 +1326,8 @@ void LuaParser::MapFunctions(lua_State *L) { lua_register_zone() )]; + lua_register_db_consts(L); + } catch(std::exception &ex) { std::string error = ex.what(); AddError(error);