-
Notifications
You must be signed in to change notification settings - Fork 14
feat(data): load XML data palletes, tilesets, brushes, etc. #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,21 +12,33 @@ ClientVersion::ClientVersion(uint32_t index, uint32_t version, const std::string | |
| } | ||
|
|
||
| std::filesystem::path ClientVersion::getItemMetadataPath() const { | ||
| // 1. User-provided path from preferences (explicit override) | ||
| if (!custom_items_db_path_.empty()) { | ||
| return custom_items_db_path_; | ||
| } | ||
| if (client_path_.empty()) { | ||
| return {}; | ||
| } | ||
| switch (data_source_) { | ||
| case ItemDataSource::SRV: | ||
| return client_path_ / "items.srv"; | ||
| case ItemDataSource::OTB: | ||
| return client_path_ / "items.otb"; | ||
| case ItemDataSource::DAT: | ||
| default: | ||
| return {}; | ||
|
|
||
| // 2. Default: data/[dataDirectory]/items.otb (or items.srv) | ||
| auto data_dir = resolveDataPath(); | ||
| if (!data_dir.empty()) { | ||
| switch (data_source_) { | ||
| case ItemDataSource::SRV: { | ||
| auto srv = data_dir / "items.srv"; | ||
| if (std::filesystem::exists(srv)) return srv; | ||
| break; | ||
| } | ||
| case ItemDataSource::OTB: { | ||
| auto otb = data_dir / "items.otb"; | ||
| if (std::filesystem::exists(otb)) return otb; | ||
| break; | ||
| } | ||
| case ItemDataSource::DAT: | ||
| default: | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| // 3. No fallback — client_path_ is not used for items.otb | ||
| return {}; | ||
|
Comment on lines
+40
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preserve metadata filename on unresolved lookup. Returning an empty path here drops filename context needed by downstream fallback code (e.g., checks that build Suggested fix- // 3. No fallback — client_path_ is not used for items.otb
- return {};
+ // 3. Preserve filename fallback for downstream resolution (./data/<filename>)
+ switch (data_source_) {
+ case ItemDataSource::SRV:
+ return std::filesystem::path("items.srv");
+ case ItemDataSource::OTB:
+ return std::filesystem::path("items.otb");
+ case ItemDataSource::DAT:
+ default:
+ return {};
+ }🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| bool ClientVersion::hasValidPaths() const { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -112,8 +112,45 @@ class ClientVersion { | |
| void setDefault(bool is_default) { is_default_ = is_default; } | ||
|
|
||
| // Data directory and description | ||
| // setDataDirectory normalizes paths like "data/1098" or absolute paths ending | ||
| // in "/data/<version>" to just the final segment (e.g., "1098"). | ||
| // autoDetectDataDirectory() and MapLoadingService both depend on | ||
| // std::filesystem::current_path() to resolve data/[version]/ at runtime. | ||
| const std::string &getDataDirectory() const { return data_directory_; } | ||
| void setDataDirectory(const std::string &dir) { data_directory_ = dir; } | ||
| void setDataDirectory(const std::string &dir) { | ||
| // Normalize: reduce .../data/<version> to just <version> | ||
| std::filesystem::path p(dir); | ||
| if (p.has_parent_path() && p.parent_path().filename() == "data") { | ||
| data_directory_ = p.filename().string(); | ||
| } else { | ||
| data_directory_ = dir; | ||
| } | ||
| } | ||
|
Comment on lines
+120
to
+128
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Description: Check how setDataDirectory is called to understand expected input patterns
rg -nP 'setDataDirectory\(' -A2 -B2Repository: Open-Tibia-Tools/tibia-imgui-map-editor Length of output: 4373 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the relevant implementation area in ClientVersion.h
echo "---- ClientVersion.h (around setDataDirectory) ----"
sed -n '90,170p' ImguiMapEditor/Domain/ClientVersion.h
# Find all mentions of autoDetectDataDirectory
echo "---- autoDetectDataDirectory references ----"
rg -n "autoDetectDataDirectory" ImguiMapEditor/Domain/ClientVersion.h ImguiMapEditor -S --hidden
# Show where getDataDirectory / dataDirectory property is used (brief)
echo "---- getDataDirectory usages ----"
rg -n "getDataDirectory\(" ImguiMapEditor -S --hidden
echo "---- dataDirectory property persistence ----"
sed -n '1,220p' ImguiMapEditor/Services/ClientVersionPersistence.cppRepository: Open-Tibia-Tools/tibia-imgui-map-editor Length of output: 12395 🏁 Script executed: #!/bin/bash
set -euo pipefail
# Show the UI/editor code that feeds into setDataDirectory(...)
echo "---- ClientPropertyEditor.cpp (setDataDirectory call sites) ----"
sed -n '200,520p' ImguiMapEditor/UI/Dialogs/ClientConfiguration/ClientPropertyEditor.cpp
# Find where eb.dataDir is declared/filled (best-effort search)
echo "---- Search for 'dataDir' in ClientPropertyEditor.cpp ----"
rg -n "dataDir" ImguiMapEditor/UI/Dialogs/ClientConfiguration/ClientPropertyEditor.cpp
# Inspect UI controller where data_dir_buf_ is set from cv.getDataDirectory()
echo "---- ClientConfigurationController.cpp (data_dir_buf_ usage) ----"
sed -n '300,430p' ImguiMapEditor/Presentation/Dialogs/ClientConfigurationController.cpp
# Inspect StartupController client_info.data_directory usage
echo "---- StartupController.cpp (client_info.data_directory) ----"
sed -n '260,470p' ImguiMapEditor/Controllers/StartupController.cpp
# Inspect MapLoadingService usage to understand expected format
echo "---- MapLoadingService.cpp (data_dir_for_resources/data_dir) ----"
sed -n '520,680p' ImguiMapEditor/Services/Map/MapLoadingService.cppRepository: Open-Tibia-Tools/tibia-imgui-map-editor Length of output: 37469 🏁 Script executed: #!/bin/bash
set -euo pipefail
sed -n '1,120p' ImguiMapEditor/Services/ClientVersionRegistry.cppRepository: Open-Tibia-Tools/tibia-imgui-map-editor Length of output: 3729 Fix
Also, 🤖 Prompt for AI Agents |
||
|
|
||
| // Resolve the version-specific data directory to an absolute path. | ||
| // Returns empty path if data_directory_ is not set or doesn't exist. | ||
| // Handles both "1098" and "data/1098" formats. | ||
| std::filesystem::path resolveDataPath() const { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Relying on |
||
| if (data_directory_.empty()) return {}; | ||
| auto candidate = std::filesystem::current_path() / "data" / data_directory_; | ||
| if (std::filesystem::exists(candidate)) return candidate; | ||
| // Try stripping "data/" prefix if present | ||
| std::filesystem::path dir_path(data_directory_); | ||
| if (dir_path.has_parent_path() && dir_path.parent_path().filename() == "data") { | ||
| auto stripped = std::filesystem::current_path() / "data" / dir_path.filename(); | ||
| if (std::filesystem::exists(stripped)) return stripped; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 While this check handles the case where |
||
| } | ||
| return {}; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 This secondary stripping logic seems redundant since |
||
| } | ||
|
|
||
| // Auto-detect data directory: checks if data/[version]/ exists | ||
| void autoDetectDataDirectory() { | ||
| auto candidate = std::filesystem::current_path() / "data" / std::to_string(version_); | ||
| if (std::filesystem::exists(candidate)) { | ||
| data_directory_ = std::to_string(version_); | ||
| } | ||
| } | ||
|
Comment on lines
+147
to
+152
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
🔧 Use executable-relative or application base path instead void autoDetectDataDirectory() {
- auto candidate = std::filesystem::current_path() / "data" / std::to_string(version_);
+ // Assuming a getApplicationBasePath() or similar exists that returns executable directory
+ auto candidate = getApplicationBasePath() / "data" / std::to_string(version_);
if (std::filesystem::exists(candidate)) {
data_directory_ = std::to_string(version_);
}
}Alternatively, if the application already stores a base path elsewhere (e.g., in a configuration service), pass it as a parameter rather than relying on 🤖 Prompt for AI Agents |
||
|
|
||
| const std::string &getDescription() const { return description_; } | ||
| void setDescription(const std::string &desc) { description_ = desc; } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,10 +10,12 @@ | |
| #include "UI/Dialogs/Properties/SpawnPropertiesDialog.h" | ||
| #include "UI/Map/MapContextMenu.h" | ||
| #include "UI/Map/MapPanel.h" | ||
| #include "UI/Panels/NewMapPanel.h" | ||
| #include "UI/Windows/IngameBoxWindow.h" | ||
| #include <functional> | ||
| #include <memory> | ||
| #include <optional> | ||
| #include <filesystem> | ||
|
|
||
| namespace MapEditor::Rendering { | ||
| class MapRenderer; | ||
|
|
@@ -153,6 +155,10 @@ class MainWindow { | |
| UI::ItemPropertiesDialog properties_dialog_; | ||
| UI::SpawnPropertiesDialog spawn_properties_dialog_; | ||
| UI::CreaturePropertiesDialog creature_properties_dialog_; | ||
|
|
||
| // Editor-state modal dialog callbacks | ||
| std::function<void(const UI::NewMapPanel::State&)> new_map_callback_; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 These callbacks are added to support the new modal dialogs, but the corresponding methods Additionally, |
||
| std::function<void(const std::filesystem::path&, uint32_t)> open_sec_callback_; | ||
| }; | ||
|
|
||
| } // namespace Presentation | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,7 +15,7 @@ namespace Services { | |
|
|
||
| MapLoadingService::MapLoadingService(ClientVersionRegistry &version_registry, | ||
| ViewSettings &view_settings, | ||
| Brushes::BrushRegistry &brush_registry, | ||
| ::MapEditor::Brushes::BrushRegistry &brush_registry, | ||
| TilesetService &tileset_service, | ||
| const OtbmSettings &otbm_settings) | ||
| : version_registry_(version_registry), view_settings_(view_settings), | ||
|
|
@@ -567,11 +567,14 @@ bool MapLoadingService::loadClientData( | |
| ? pending_path | ||
| : pending_path.parent_path(); | ||
|
|
||
| if (!tryLoadCreatures(map_dir, client_path)) { | ||
| // Resolve version data path once — used for creatures, items, tilesets, palettes | ||
| const std::filesystem::path version_data_path = version_info->resolveDataPath(); | ||
|
|
||
| if (!tryLoadCreatures(map_dir, client_path, version_data_path)) { | ||
| spdlog::warn("No creature data loaded. Spawns may look incorrect."); | ||
| } | ||
|
|
||
| if (!tryLoadItems(map_dir, client_path)) { | ||
| if (!tryLoadItems(map_dir, client_path, version_data_path)) { | ||
| spdlog::warn("No items.xml loaded. Item names may be missing."); | ||
| } | ||
|
|
||
|
|
@@ -595,21 +598,23 @@ bool MapLoadingService::loadClientData( | |
| } | ||
| } | ||
|
|
||
| // Use injected TilesetService instead of creating locally | ||
| // Always use the application's data folder for tilesets and palettes, | ||
| // NOT the map directory - these are app resources, not per-map resources | ||
| std::filesystem::path app_data_path = | ||
| std::filesystem::current_path() / "data"; | ||
|
|
||
| bool tilesets_loaded = tileset_service_.loadMaterials(app_data_path); | ||
| if (!tilesets_loaded) { | ||
| spdlog::warn("materials.xml could not be loaded. Falling back to direct tileset/palette loading."); | ||
| tilesets_loaded = tileset_service_.loadTilesets(app_data_path); | ||
| // Load tilesets, palettes, brushes from version data directory | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟢 Using |
||
| if (version_data_path.empty()) { | ||
| spdlog::warn("Data directory '{}' not found. Skipping materials/tilesets/palettes loading.", | ||
| version_info->getDataDirectory()); | ||
| } else { | ||
| spdlog::info("Loading materials from: {}", version_data_path.string()); | ||
| bool tilesets_loaded = tileset_service_.loadMaterials(version_data_path); | ||
| if (!tilesets_loaded) { | ||
| spdlog::warn("No tilesets found. The palette will be empty."); | ||
| } | ||
| if (!tileset_service_.loadPalettes(app_data_path)) { | ||
| spdlog::warn("No palettes loaded. Ribbon palette buttons will be empty."); | ||
| spdlog::warn("materials.xml not found in '{}'. Falling back to direct loading.", | ||
| version_data_path.string()); | ||
| tilesets_loaded = tileset_service_.loadTilesets(version_data_path); | ||
| if (!tilesets_loaded) { | ||
| spdlog::warn("No tilesets found in '{}'.", version_data_path.string()); | ||
| } | ||
| if (!tileset_service_.loadPalettes(version_data_path)) { | ||
| spdlog::warn("No palettes found in '{}'.", version_data_path.string()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -676,29 +681,53 @@ Domain::Position MapLoadingService::findCameraCenter() const { | |
|
|
||
| bool MapLoadingService::tryLoadCreatures( | ||
| const std::filesystem::path &map_dir, | ||
| const std::filesystem::path &client_path) { | ||
| const std::filesystem::path &client_path, | ||
| const std::filesystem::path &version_data_path) { | ||
| const auto load = [this](const std::filesystem::path &path) { | ||
| return client_data_service_->loadCreatureData(path); | ||
| }; | ||
|
|
||
| if (tryLoadResource("creatures.xml", map_dir, client_path, load)) { | ||
| return true; | ||
| } | ||
| return tryLoadResource(std::filesystem::path("creatures") / "creatures.xml", | ||
| map_dir, client_path, load); | ||
| if (tryLoadResource(std::filesystem::path("creatures") / "creatures.xml", | ||
| map_dir, client_path, load)) { | ||
| return true; | ||
| } | ||
| // Try version-specific data folder | ||
| if (!version_data_path.empty()) { | ||
| auto version_file = version_data_path / "creatures" / "creatures.xml"; | ||
| if (std::filesystem::exists(version_file) && load(version_file)) { | ||
| spdlog::info("Loaded creatures.xml from version data folder"); | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| bool MapLoadingService::tryLoadItems(const std::filesystem::path &map_dir, | ||
| const std::filesystem::path &client_path) { | ||
| const std::filesystem::path &client_path, | ||
| const std::filesystem::path &version_data_path) { | ||
| const auto load = [this](const std::filesystem::path &path) { | ||
| return client_data_service_->loadItemData(path); | ||
| }; | ||
|
|
||
| if (tryLoadResource("items.xml", map_dir, client_path, load)) { | ||
| return true; | ||
| } | ||
| return tryLoadResource(std::filesystem::path("items") / "items.xml", map_dir, | ||
| client_path, load); | ||
| if (tryLoadResource(std::filesystem::path("items") / "items.xml", map_dir, | ||
| client_path, load)) { | ||
| return true; | ||
| } | ||
| // Try version-specific data folder | ||
| if (!version_data_path.empty()) { | ||
| auto version_file = version_data_path / "items" / "items.xml"; | ||
| if (std::filesystem::exists(version_file) && load(version_file)) { | ||
| spdlog::info("Loaded items.xml from version data folder"); | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| void MapLoadingService::loadWaypoints(const std::filesystem::path &otbm_path, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟢 Good change. Removing the fallback to
client_path_foritems.otbensures that only the intended editor-specific metadata is used, preventing potential issues with incompatible OTB files found in client directories.