From cceff622b43a2d1e9b2cc2f9b69f84b89e555c98 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 16:31:13 -0700 Subject: [PATCH] feat(editor): AzerothCore server module generator One-click generation of a complete AzerothCore/TrinityCore server module from editor zone data. File > Generate Server Module creates: - sql/01_map.sql: map_dbc + area_table_dbc registration - sql/02_spawns.sql: creature_template + creature + waypoint_data + quests - sql/03_teleport.sql: game_tele entry for .tele command - sql/04_zone_flags.sql: sanctuary/PvP area flags - conf/mod_wowee.conf.dist: worldserver.conf snippet with zone settings - README.md: step-by-step server admin installation guide - module.json: machine-readable module manifest Server admins can import the SQL files, add the config snippet, and restart their server to have the custom zone fully operational with NPC spawns, patrol paths, quests, teleport commands, and zone flags. --- CMakeLists.txt | 1 + tools/editor/editor_app.cpp | 1 + tools/editor/editor_ui.cpp | 9 ++ tools/editor/server_module_gen.cpp | 149 +++++++++++++++++++++++++++++ tools/editor/server_module_gen.hpp | 34 +++++++ 5 files changed, 194 insertions(+) create mode 100644 tools/editor/server_module_gen.cpp create mode 100644 tools/editor/server_module_gen.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index acaa5ff6..f7a05edf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1298,6 +1298,7 @@ add_executable(wowee_editor tools/editor/npc_spawner.cpp tools/editor/npc_presets.cpp tools/editor/sql_exporter.cpp + tools/editor/server_module_gen.cpp tools/editor/quest_editor.cpp tools/editor/transform_gizmo.cpp tools/editor/zone_manifest.cpp diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index 27c8dad0..f124e165 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -10,6 +10,7 @@ #include "pipeline/wowee_collision.hpp" #include "pipeline/wmo_loader.hpp" #include "sql_exporter.hpp" +#include "server_module_gen.hpp" #include "core/coordinates.hpp" #include #include "rendering/vk_context.hpp" diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index d8c612b2..196d9fea 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -9,6 +9,7 @@ #include "pipeline/custom_zone_discovery.hpp" #include "content_pack.hpp" #include "sql_exporter.hpp" +#include "server_module_gen.hpp" #include "wowee_terrain.hpp" #include "pipeline/wowee_terrain_loader.hpp" #include @@ -373,6 +374,14 @@ void EditorUI::renderMenuBar(EditorApp& app) { sqlPath, app.getZoneManifest().mapId); app.showToast("SQL exported: " + sqlPath); } + if (ImGui::MenuItem("Generate Server Module", nullptr, false, app.hasTerrainLoaded())) { + editor::ServerModuleGenerator::generate( + app.getZoneManifest(), + app.getNpcSpawner().getSpawns(), + app.getQuestEditor().getQuests(), + "output"); + app.showToast("Server module: output/mod_wowee_" + app.getLoadedMap()); + } if (ImGui::MenuItem("Export Content Pack (.wcp)", "Ctrl+Shift+E", false, app.hasTerrainLoaded())) { std::string wcpPath = "output/" + app.getLoadedMap() + ".wcp"; app.exportContentPack(wcpPath); diff --git a/tools/editor/server_module_gen.cpp b/tools/editor/server_module_gen.cpp new file mode 100644 index 00000000..2c7cc2bc --- /dev/null +++ b/tools/editor/server_module_gen.cpp @@ -0,0 +1,149 @@ +#include "server_module_gen.hpp" +#include "sql_exporter.hpp" +#include "core/logger.hpp" +#include +#include +#include +#include +#include + +namespace wowee { +namespace editor { + +bool ServerModuleGenerator::generate(const ZoneManifest& manifest, + const std::vector& creatures, + const std::vector& quests, + const std::string& outputDir) { + namespace fs = std::filesystem; + std::string dir = outputDir + "/mod_wowee_" + manifest.mapName; + fs::create_directories(dir + "/sql"); + fs::create_directories(dir + "/conf"); + + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + char timeBuf[32]; + std::strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", std::localtime(&time)); + + Config cfg; + cfg.mapId = manifest.mapId; + cfg.mapName = manifest.mapName; + cfg.displayName = manifest.displayName.empty() ? manifest.mapName : manifest.displayName; + + // 1. Generate map registration SQL + { + std::ofstream f(dir + "/sql/01_map.sql"); + f << "-- Wowee Custom Zone: " << cfg.displayName << "\n"; + f << "-- Generated: " << timeBuf << "\n"; + f << "-- Map ID: " << cfg.mapId << "\n\n"; + + f << "-- Register custom map\n"; + f << "INSERT INTO `map_dbc` (`ID`, `MapName`, `MapType`, `MapDescription`) VALUES (" + << cfg.mapId << ", '" << cfg.mapName << "', 0, '" + << cfg.displayName << "') ON DUPLICATE KEY UPDATE `MapName`='" + << cfg.mapName << "';\n\n"; + + f << "-- Register zone area\n"; + f << "INSERT INTO `area_table_dbc` (`ID`, `MapID`, `AreaName`, `ExploreFlag`) VALUES (" + << cfg.zoneId << ", " << cfg.mapId << ", '" + << cfg.displayName << "', 1) ON DUPLICATE KEY UPDATE `AreaName`='" + << cfg.displayName << "';\n"; + } + + // 2. Generate creature + quest SQL + if (!creatures.empty() || !quests.empty()) { + SQLExporter::exportAll(creatures, quests, + dir + "/sql/02_spawns.sql", + cfg.mapId, cfg.startCreatureEntry); + } + + // 3. Generate teleport command SQL + if (!manifest.tiles.empty()) { + std::ofstream f(dir + "/sql/03_teleport.sql"); + f << "-- Teleport location for .tele command\n"; + float tileSize = 533.33333f; + float x = (32.0f - manifest.tiles[0].second) * tileSize; + float y = (32.0f - manifest.tiles[0].first) * tileSize; + f << "INSERT INTO `game_tele` (`name`, `position_x`, `position_y`, " + << "`position_z`, `orientation`, `map`) VALUES ('" + << manifest.mapName << "', " << x << ", " << y << ", " + << manifest.baseHeight + 10.0f << ", 0, " << cfg.mapId + << ") ON DUPLICATE KEY UPDATE `position_x`=" << x << ";\n"; + f << "\n-- Usage: .tele " << manifest.mapName << "\n"; + } + + // 4. Generate zone flags SQL + { + std::ofstream f(dir + "/sql/04_zone_flags.sql"); + f << "-- Zone gameplay flags\n"; + if (manifest.isSanctuary) { + f << "-- Sanctuary zone (no PvP)\n"; + f << "UPDATE `area_table_dbc` SET `Flags` = `Flags` | 0x800 WHERE `ID` = " + << cfg.zoneId << ";\n"; + } + if (manifest.pvpEnabled) { + f << "-- PvP zone\n"; + f << "UPDATE `area_table_dbc` SET `Flags` = `Flags` | 0x40 WHERE `ID` = " + << cfg.zoneId << ";\n"; + } + } + + // 5. Generate worldserver.conf snippet + { + std::ofstream f(dir + "/conf/mod_wowee.conf.dist"); + f << "#\n# Wowee Custom Zone: " << cfg.displayName << "\n"; + f << "# Add this to your worldserver.conf\n#\n\n"; + f << "# Enable custom zone " << cfg.displayName << "\n"; + f << "Wowee." << cfg.mapName << ".Enabled = 1\n"; + f << "Wowee." << cfg.mapName << ".MapId = " << cfg.mapId << "\n"; + f << "Wowee." << cfg.mapName << ".ZoneId = " << cfg.zoneId << "\n\n"; + f << "# Zone settings\n"; + f << "Wowee." << cfg.mapName << ".AllowFlying = " + << (manifest.allowFlying ? 1 : 0) << "\n"; + f << "Wowee." << cfg.mapName << ".PvP = " + << (manifest.pvpEnabled ? 1 : 0) << "\n"; + } + + // 6. Generate server admin README + { + std::ofstream f(dir + "/README.md"); + f << "# " << cfg.displayName << " — Custom Zone for AzerothCore\n\n"; + f << "Generated by Wowee World Editor v1.0.0\n\n"; + f << "## Installation\n\n"; + f << "1. Copy SQL files to your AzerothCore `sql/custom/` directory\n"; + f << "2. Execute in order: `01_map.sql`, `02_spawns.sql`, `03_teleport.sql`, `04_zone_flags.sql`\n"; + f << "3. Copy `conf/mod_wowee.conf.dist` settings to `worldserver.conf`\n"; + f << "4. Restart worldserver\n\n"; + f << "## Details\n\n"; + f << "- **Map ID**: " << cfg.mapId << "\n"; + f << "- **Zone**: " << cfg.displayName << "\n"; + f << "- **Creatures**: " << creatures.size() << "\n"; + f << "- **Quests**: " << quests.size() << "\n"; + f << "- **Tiles**: " << manifest.tiles.size() << "\n"; + if (manifest.allowFlying) f << "- **Flying**: Enabled\n"; + if (manifest.pvpEnabled) f << "- **PvP**: Enabled\n"; + if (manifest.isSanctuary) f << "- **Sanctuary**: Yes\n"; + f << "\n## Teleport\n\n"; + f << "```\n.tele " << manifest.mapName << "\n```\n"; + } + + // 7. Generate manifest JSON + { + nlohmann::json j; + j["format"] = "wowee-server-module-1.0"; + j["zone"] = cfg.displayName; + j["mapId"] = cfg.mapId; + j["creatures"] = creatures.size(); + j["quests"] = quests.size(); + j["tiles"] = manifest.tiles.size(); + j["generated"] = timeBuf; + std::ofstream f(dir + "/module.json"); + f << j.dump(2) << "\n"; + } + + LOG_INFO("Server module generated: ", dir, " (", + creatures.size(), " creatures, ", quests.size(), " quests)"); + return true; +} + +} // namespace editor +} // namespace wowee diff --git a/tools/editor/server_module_gen.hpp b/tools/editor/server_module_gen.hpp new file mode 100644 index 00000000..c32cbc13 --- /dev/null +++ b/tools/editor/server_module_gen.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "zone_manifest.hpp" +#include "npc_spawner.hpp" +#include "quest_editor.hpp" +#include +#include + +namespace wowee { +namespace editor { + +// Generates a complete AzerothCore server module from editor zone data +// Output: SQL + worldserver.conf snippet + README for server admins +class ServerModuleGenerator { +public: + struct Config { + uint32_t mapId = 9000; + uint32_t startCreatureEntry = 100000; + uint32_t startQuestEntry = 100000; + uint32_t zoneId = 10000; + uint32_t areaId = 10001; + std::string mapName; + std::string displayName; + }; + + // Generate complete server module directory + static bool generate(const ZoneManifest& manifest, + const std::vector& creatures, + const std::vector& quests, + const std::string& outputDir); +}; + +} // namespace editor +} // namespace wowee