Skip to content

feat(ui): add missing items report dialog - #1006

Merged
karolak6612 merged 8 commits into
masterfrom
Missing-Items-Handler
Apr 6, 2026
Merged

feat(ui): add missing items report dialog#1006
karolak6612 merged 8 commits into
masterfrom
Missing-Items-Handler

Conversation

@karolak6612

@karolak6612 karolak6612 commented Apr 6, 2026

Copy link
Copy Markdown
Collaborator
  • Added MissingItemReport struct and MissingItemsDialog for tracking and displaying item definition mismatches between DAT/OTB/XML files
  • Refactored ItemDefinitionResolver to collect missing items instead of failing on missing DAT entries, added isDatItemEmptyOrInvalid filter
  • Integrated missing items collection into AssetBundle, ItemDefinitionsLoader, and VersionManager load pipelines
  • Added SHOW_MISSING_ITEMS_WARNING config setting (default false) to control detailed warning output during data load
  • Added "Missing Items Report..." menu item under File -> Reload with handler in FileMenuHandler to display dialog
  • Added preferences checkbox in EditorPage to toggle missing items warning display
  • Registered MISSING_ITEMS_REPORT action in menubar_action_manager and added enum/handler to main_menubar

Summary by CodeRabbit

  • New Features

    • Added a "Missing Items Report..." File menu action and a dialog showing categorized discrepancies (Missing in DAT, Missing in OTB, XML-only, OTB-only). Dialog can copy the full report to clipboard, save it as a text file, or dismiss.
  • Preferences

    • New "Show missing items warning on load" option to display a summarized warning after loading data, with truncated sample entries when enabled.
  • UI Changes

    • Renamed "Reload" to "Reload Data Files" (hotkey unchanged); report menu is enabled only when a data set is loaded.

- Added MissingItemReport struct and MissingItemsDialog for tracking and displaying item definition mismatches between DAT/OTB/XML files
- Refactored ItemDefinitionResolver to collect missing items instead of failing on missing DAT entries, added isDatItemEmptyOrInvalid filter
- Integrated missing items collection into AssetBundle, ItemDefinitionsLoader, and VersionManager load pipelines
- Added SHOW_MISSING_ITEMS_WARNING config setting (default false) to control detailed warning output during data load
- Added "Missing Items Report..." menu item under File -> Reload with handler in FileMenuHandler to display dialog
- Added preferences checkbox in EditorPage to toggle missing items warning display
- Registered MISSING_ITEMS_REPORT action in menubar_action_manager and added enum/handler to main_menubar
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request improves the robustness of the map editor's data loading process by introducing a comprehensive system for detecting and reporting item definition mismatches. Instead of failing when inconsistencies are found between DAT, OTB, and XML files, the editor now collects these issues and provides a user-friendly dialog to review them. This allows users to identify and address data integrity problems without interrupting the loading process, while also providing configurable options to control the visibility of these warnings.

Highlights

  • Missing Items Reporting: Introduced a new MissingItemReport structure and MissingItemsDialog to track and visualize item definition mismatches between DAT, OTB, and XML files.
  • Data Loading Refactoring: Refactored ItemDefinitionResolver to collect missing items instead of failing immediately, and updated load pipelines to support this collection.
  • User Configuration: Added a new preference setting 'SHOW_MISSING_ITEMS_WARNING' to toggle detailed warnings during data loading.
  • UI Integration: Added a 'Missing Items Report...' menu item under File and a corresponding checkbox in the Editor preferences page.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Collects and stores categorized missing-item discrepancies during item resolution, exposes them via VersionManager and AssetBundle, adds a "Missing Items Report..." menu item/action that opens a dialog displaying categorized lists, and adds a preference to optionally show missing-item warnings on load.

Changes

Cohort / File(s) Summary
Menu integration
data/menubar.xml, source/ui/main_menubar.h, source/ui/main_menubar.cpp, source/ui/menubar/file_menu_handler.h, source/ui/menubar/file_menu_handler.cpp, source/ui/menubar/menubar_action_manager.cpp
Added Missing Items Report... menu item and MISSING_ITEMS_REPORT action; wired handlers, registered action, enabled it when data is loaded, and renamed ReloadReload Data Files.
Missing-items data model
source/item_definitions/core/missing_item_report.h
New MissingItemEntry and MissingItemReport structs with four categorized vectors for missing-item records.
Resolver & loader API changes
source/item_definitions/core/item_definition_resolver.h, source/item_definitions/core/item_definition_resolver.cpp, source/item_definitions/core/item_definitions_loader.h, source/item_definitions/core/item_definitions_loader.cpp
Threaded optional MissingItemReport* through resolver/loader APIs (default nullptr); added DAT-empty detection and logic to populate categorized missing lists instead of failing when a report is supplied.
Asset bundle plumbing
source/item_definitions/core/asset_bundle.h, source/item_definitions/core/asset_bundle_loader.cpp
AssetBundle now stores MissingItemReport missing_items; loader forwards report pointer to assembly so bundle gets populated during load.
Version manager state & warnings
source/app/managers/version_manager.h, source/app/managers/version_manager.cpp
VersionManager stores last missing-items report and last_load_has_otb; saves bundle.missing_items after install; conditionally appends formatted missing-item warnings to load warnings when preference enabled and clears stale state on failure.
Preferences & settings
source/app/settings.h, source/app/settings.cpp, source/app/preferences/editor_page.h, source/app/preferences/editor_page.cpp
Added SHOW_MISSING_ITEMS_WARNING Config key (default false) and a "Show missing items warning on load" checkbox in Editor preferences; persisted via g_settings.
Missing Items dialog UI
source/ui/dialogs/missing_items_dialog.h, source/ui/dialogs/missing_items_dialog.cpp
New MissingItemsDialog presenting tabbed lists and counts for each category with Copy-to-Clipboard, Save-to-File, and Ignore actions; static Show helper and report text generation implemented.
Build updates
source/CMakeLists.txt
Added new dialog source/header to CMake lists so dialog is compiled into the project.

Sequence Diagram

sequenceDiagram
    participant User as User
    participant Menu as Menu System
    participant FileHandler as FileMenuHandler
    participant VersionMgr as VersionManager
    participant Dialog as MissingItemsDialog

    User->>Menu: Click "Missing Items Report..."
    Menu->>FileHandler: OnMissingItemsReport()
    FileHandler->>VersionMgr: getLastMissingItems()
    VersionMgr-->>FileHandler: MissingItemReport
    FileHandler->>VersionMgr: lastLoadHasOtb()
    VersionMgr-->>FileHandler: bool
    FileHandler->>Dialog: Show(parent, report, hasOtb)
    Dialog->>Dialog: Build UI (tabs, lists, counts)
    Dialog-->>User: Display modal dialog
    User->>Dialog: Copy / Save / Close
    Dialog-->>User: End modal
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐇 I hopped through rows both big and small,
Finding gaps and names upon each wall,
Tabs stack the missing, tidy and bright,
Copy, save, ignore — then off to the night,
A carrot for fixes, and moonbeams alight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ui): add missing items report dialog' clearly and concisely describes the primary change: a new UI feature adding a missing items report dialog, which aligns with the main additions across all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch Missing-Items-Handler

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Apr 6, 2026

Copy link
Copy Markdown

🤖 Hi @karolak6612, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a Missing Items Report system to identify and display discrepancies between items.otb, tibia.dat, and items.xml. It includes a new UI dialog, a preference setting for load-time warnings, and logic to track missing definitions during item resolution. Feedback focuses on strict adherence to the project's C++20/23 style guide, specifically requiring the use of .contains() for container checks, std::format for string formatting, and FromDIP() for High DPI compatibility. Additionally, a large UI initialization function needs to be refactored to comply with function length limits.

CenterOnParent();
}

void MissingItemsDialog::BuildUI() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The BuildUI function is approximately 120 lines long, which exceeds the 50-line limit defined in the style guide. Please split this function into smaller, logical components (e.g., CreateDatPage, CreateOtbPage, etc.) to improve maintainability and adhere to SRP.

References
  1. SRP LIMITS — Function > 50 lines → split. (link)

Comment on lines +154 to +155
text += wxString::Format("Server ID: %u, Client ID: %u, Name: %s, Description: %s\n",
entry.server_id, entry.client_id, entry.name, entry.description);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using wxString::Format with std::string objects (like entry.name) using the %s specifier is unsafe and can lead to undefined behavior or garbage output if .c_str() is not called. Switching to std::format is safer and required by the style guide.

		text += std::format("Server ID: {}, Client ID: {}, Name: {}, Description: {}\n",
			entry.server_id, entry.client_id, entry.name, entry.description);
References
  1. std::format over sprintf/wxString::Format (link)

if (isDatItemEmptyOrInvalid(dat, input.dat_catalog, client_id)) {
continue;
}
if (dat_ids_referenced_by_otb.find(client_id) == dat_ids_referenced_by_otb.end()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Since the project mandates C++20/23, use .contains() for checking existence in associative containers instead of find() == end(). This is more idiomatic and readable.

Suggested change
if (dat_ids_referenced_by_otb.find(client_id) == dat_ids_referenced_by_otb.end()) {
if (!dat_ids_referenced_by_otb.contains(client_id)) {
References
  1. Every line of code you write or modify MUST use C++20/23 features. Pre-C++17 patterns are technical debt — eliminate on contact. (link)

if (server_id < 100) {
continue;
}
if (fragments.otb.find(server_id) == fragments.otb.end()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Use .contains() for existence checks in maps to adhere to C++20 standards as required by the style guide.

Suggested change
if (fragments.otb.find(server_id) == fragments.otb.end()) {
if (!fragments.otb.contains(server_id)) {
References
  1. C++20/23 is MANDATORY. Every new and modified file MUST use modern C++20/23 features. (link)

if (isDatItemEmptyOrInvalid(dat, input.dat_catalog, client_id)) {
continue;
}
if (dat_ids_used_by_xml.find(client_id) == dat_ids_used_by_xml.end()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Use .contains() for existence checks in sets to adhere to C++20 standards.

Suggested change
if (dat_ids_used_by_xml.find(client_id) == dat_ids_used_by_xml.end()) {
if (!dat_ids_used_by_xml.contains(client_id)) {
References
  1. C++20/23 is MANDATORY. Every new and modified file MUST use modern C++20/23 features. (link)

}
auto* headerText = newd wxStaticText(this, wxID_ANY, headerTextContent,
wxDefaultPosition, wxDefaultSize, wxALIGN_LEFT);
mainSizer->Add(headerText, 0, wxALL | wxEXPAND, 10);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

All hardcoded pixel values, including sizer borders, must be wrapped in FromDIP() to ensure proper scaling on High DPI displays.

	mainSizer->Add(headerText, 0, wxALL | wxEXPAND, FromDIP(10));
References
  1. USE: FromDIP() for any pixel values. BANNED: Hardcoded wxPoint/wxSize pixels. (link)

Comment on lines +57 to +58
wxString::Format("%u", entry.server_id),
wxString::Format("%u", entry.client_id),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The style guide explicitly bans wxString::Format in favor of std::format. Please update all instances in this file.

			std::format("{}", entry.server_id),
			std::format("{}", entry.client_id),
References
  1. std::format over sprintf/wxString::Format (link)

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

## 📋 Review Summary

This PR adds a valuable missing items report feature to help identify discrepancies between DAT, OTB, and XML data files. The implementation correctly integrates into the loading pipeline and provides a modern user interface for viewing and exporting reports.

🔍 General Feedback

  • The use of std::format and other C++23 features is appropriate given the project's configuration.
  • The filtering heuristic in isDatItemEmptyOrInvalid is a clever way to reduce noise from placeholder entries in data files.
  • Critical Fix Needed: In the report generation logic, wxString::Format is used with std::string and %s, which will lead to crashes. This must be corrected to use std::format or .c_str().

Comment on lines +172 to +174
text += wxString::Format("Server ID: %u, Client ID: %u, Name: %s, Description: %s\n",
entry.server_id, entry.client_id, entry.name, entry.description);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Same issue here: wxString::Format with %s will fail with std::string. Use std::format instead.

Suggested change
text += wxString::Format("Server ID: %u, Client ID: %u, Name: %s, Description: %s\n",
entry.server_id, entry.client_id, entry.name, entry.description);
}
for (const auto& entry : report.xml_no_otb) {
text += wxString::FromUTF8(std::format("Server ID: {}, Client ID: {}, Name: {}, Description: {}\n",
entry.server_id, entry.client_id, entry.name, entry.description));
}

}
}

if (rows.empty() && missingReport && missingReport->missing_in_dat.empty()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 This check is inconsistent with resolveDatOnly. In resolveDatOnly, the function returns false if rows is empty, regardless of whether missingReport is provided. If no items are resolved, the editor will likely be in an unusable state, so it should probably always return an error if rows.empty().

Suggested change
if (rows.empty() && missingReport && missingReport->missing_in_dat.empty()) {
if (rows.empty()) {

Comment on lines +154 to +156
text += wxString::Format("Server ID: %u, Client ID: %u, Name: %s, Description: %s\n",
entry.server_id, entry.client_id, entry.name, entry.description);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 wxString::Format with the %s format specifier does not correctly handle std::string objects and will likely cause a crash or undefined behavior. Since the project uses C++23, it is safer and more idiomatic to use std::format and wxString::FromUTF8.

Suggested change
text += wxString::Format("Server ID: %u, Client ID: %u, Name: %s, Description: %s\n",
entry.server_id, entry.client_id, entry.name, entry.description);
}
for (const auto& entry : report.missing_in_dat) {
text += wxString::FromUTF8(std::format("Server ID: {}, Client ID: {}, Name: {}, Description: {}\n",
entry.server_id, entry.client_id, entry.name, entry.description));
}

SetMinSize(wxSize(600, 400));
}

wxString MissingItemsDialog::GenerateReportText(bool hasOtb) const {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The hasOtb parameter shadows the class member hasOtb. Since it is always called with the member value, this parameter is redundant and can be removed.

Suggested change
wxString MissingItemsDialog::GenerateReportText(bool hasOtb) const {
wxString MissingItemsDialog::GenerateReportText() const {

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
source/ui/menubar/menubar_action_manager.cpp (1)

173-173: Consider adding enable/disable state management for MISSING_ITEMS_REPORT.

The action is registered but UpdateState doesn't include an EnableItem call for MISSING_ITEMS_REPORT. Other data-dependent actions like DEBUG_VIEW_DAT (line 298) are conditionally enabled with mb->EnableItem(DEBUG_VIEW_DAT, loaded). The Missing Items Report should likely only be accessible when a version is loaded, since getLastMissingItems() data is populated during version loading.

♻️ Proposed fix to add state management

Add this line near line 298 in UpdateState:

mb->EnableItem(MISSING_ITEMS_REPORT, loaded);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/ui/menubar/menubar_action_manager.cpp` at line 173, Add state
management for MISSING_ITEMS_REPORT in the UpdateState method: ensure the menu
item created by MAKE_ACTION_ICON(MISSING_ITEMS_REPORT, ...,
OnMissingItemsReport) is enabled/disabled based on whether a version is loaded
(same condition used for DEBUG_VIEW_DAT). Call
mb->EnableItem(MISSING_ITEMS_REPORT, loaded) in UpdateState so the Missing Items
Report (which reads getLastMissingItems()) is only accessible when data is
present.
source/app/managers/version_manager.h (1)

33-41: Consider clearing last_missing_items on load failure to avoid stale data.

Looking at the context snippet from version_manager.cpp (lines 87-125), if bundle_loader.load() or bundle_loader.install() fails, UnloadVersion() is called but last_missing_items is not cleared. This means getLastMissingItems() could return stale data from a previous successful load after a failed reload attempt.

While the UI check for empty vectors prevents showing an incorrect dialog when no version is loaded, this edge case could still cause confusion if users check the report after a failed reload.

♻️ Option 1: Clear in UnloadVersion

In version_manager.cpp, add to UnloadVersion():

last_missing_items = MissingItemReport{};
last_load_has_otb = true;
♻️ Option 2: Clear at start of LoadDataFiles

At the beginning of LoadDataFiles():

last_missing_items = MissingItemReport{};

Also applies to: 47-48

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/managers/version_manager.h` around lines 33 - 41,
getLastMissingItems can return stale data after a failed reload because
last_missing_items (and last_load_has_otb) are not cleared when
bundle_loader.load() or bundle_loader.install() fails; fix by clearing
last_missing_items = MissingItemReport{} and resetting last_load_has_otb (e.g.
true or default) either at the start of LoadDataFiles() or inside
UnloadVersion() so any failed load leaves no leftover report — update the code
paths around bundle_loader.load()/bundle_loader.install() and
UnloadVersion/LoadDataFiles to ensure last_missing_items and last_load_has_otb
are reset on failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/item_definitions/core/item_definition_resolver.cpp`:
- Around line 105-125: resolveDatOnly() currently treats only a completely
missing fragments.dat entry as "missing", while resolveDatOtb() treats both
absent and unusable/empty DAT entries as missing; update resolveDatOnly() to use
the same "missing or unusable DAT" predicate as resolveDatOtb(): when
fragments.dat contains effective_client_id but the DAT entry is unusable (e.g.,
has no drawable/content), populate MissingItemEntry (server_id, client_id,
otb.name, otb.description) into missingReport->missing_in_dat or set the same
error string and return false when missingReport is null; also ensure
dat_ids_referenced_by_otb and row.flags are only updated for DAT entries
considered usable (match the logic/path used in resolveDatOtb()) so placeholder
client IDs are reported consistently.
- Around line 14-21: The fast-path in isDatItemEmptyOrInvalid incorrectly treats
any DatItemFragment with default metadata as empty before checking whether the
real definition comes from the sprite/catalog; update isDatItemEmptyOrInvalid to
only return true for the "all-default" case when there is no corresponding entry
in the DatCatalog/sprite source for that item (use the DatCatalog pointer and
client_id or the fragment's sprite/index info to look up if a definition
exists), otherwise fall through and consider the item valid; specifically, add a
catalog lookup (e.g., via an existing lookup method on DatCatalog) when dat
fields are defaults and only return true if that lookup fails.

---

Nitpick comments:
In `@source/app/managers/version_manager.h`:
- Around line 33-41: getLastMissingItems can return stale data after a failed
reload because last_missing_items (and last_load_has_otb) are not cleared when
bundle_loader.load() or bundle_loader.install() fails; fix by clearing
last_missing_items = MissingItemReport{} and resetting last_load_has_otb (e.g.
true or default) either at the start of LoadDataFiles() or inside
UnloadVersion() so any failed load leaves no leftover report — update the code
paths around bundle_loader.load()/bundle_loader.install() and
UnloadVersion/LoadDataFiles to ensure last_missing_items and last_load_has_otb
are reset on failure.

In `@source/ui/menubar/menubar_action_manager.cpp`:
- Line 173: Add state management for MISSING_ITEMS_REPORT in the UpdateState
method: ensure the menu item created by MAKE_ACTION_ICON(MISSING_ITEMS_REPORT,
..., OnMissingItemsReport) is enabled/disabled based on whether a version is
loaded (same condition used for DEBUG_VIEW_DAT). Call
mb->EnableItem(MISSING_ITEMS_REPORT, loaded) in UpdateState so the Missing Items
Report (which reads getLastMissingItems()) is only accessible when data is
present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ae4bcedc-1041-44f2-ac8f-cc6a76174a18

📥 Commits

Reviewing files that changed from the base of the PR and between 3602336 and 4bba30a.

📒 Files selected for processing (22)
  • data/menubar.xml
  • source/CMakeLists.txt
  • source/app/managers/version_manager.cpp
  • source/app/managers/version_manager.h
  • source/app/preferences/editor_page.cpp
  • source/app/preferences/editor_page.h
  • source/app/settings.cpp
  • source/app/settings.h
  • source/item_definitions/core/asset_bundle.h
  • source/item_definitions/core/asset_bundle_loader.cpp
  • source/item_definitions/core/item_definition_resolver.cpp
  • source/item_definitions/core/item_definition_resolver.h
  • source/item_definitions/core/item_definitions_loader.cpp
  • source/item_definitions/core/item_definitions_loader.h
  • source/item_definitions/core/missing_item_report.h
  • source/ui/dialogs/missing_items_dialog.cpp
  • source/ui/dialogs/missing_items_dialog.h
  • source/ui/main_menubar.cpp
  • source/ui/main_menubar.h
  • source/ui/menubar/file_menu_handler.cpp
  • source/ui/menubar/file_menu_handler.h
  • source/ui/menubar/menubar_action_manager.cpp

Comment thread source/item_definitions/core/item_definition_resolver.cpp Outdated
Comment on lines 105 to 125
const auto dat_it = fragments.dat.find(effective_client_id);
if (dat_it == fragments.dat.end()) {
error = wxString::FromUTF8(std::format("Missing DAT definition for client id {} (server id {}).", effective_client_id, server_id));
return false;
// DAT item truly doesn't exist - collect as missing
if (missingReport) {
MissingItemEntry entry;
entry.server_id = server_id;
entry.client_id = effective_client_id;
entry.name = otb.name;
entry.description = otb.description;
missingReport->missing_in_dat.push_back(std::move(entry));
} else {
error = wxString::FromUTF8(std::format("Missing DAT definition for client id {} (server id {}).", effective_client_id, server_id));
return false;
}
// Continue processing other items
continue;
}

// DAT item exists (even if empty) - this is a valid OTB mapping
dat_ids_referenced_by_otb.insert(effective_client_id);
row.flags |= dat_it->second.flags & ~flagMask(ItemFlag::Moveable);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use the same “missing or unusable DAT” predicate in both resolve paths.

Right now resolveDatOtb() only treats an absent DAT fragment as missing, while resolveDatOnly() silently skips XML rows whose DAT slot exists but has no drawable content. That leaves placeholder client IDs out of missing_in_dat entirely, even though they are still missing usable DAT definitions from the editor’s point of view.

Also applies to: 220-238

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/item_definitions/core/item_definition_resolver.cpp` around lines 105 -
125, resolveDatOnly() currently treats only a completely missing fragments.dat
entry as "missing", while resolveDatOtb() treats both absent and unusable/empty
DAT entries as missing; update resolveDatOnly() to use the same "missing or
unusable DAT" predicate as resolveDatOtb(): when fragments.dat contains
effective_client_id but the DAT entry is unusable (e.g., has no
drawable/content), populate MissingItemEntry (server_id, client_id, otb.name,
otb.description) into missingReport->missing_in_dat or set the same error string
and return false when missingReport is null; also ensure
dat_ids_referenced_by_otb and row.flags are only updated for DAT entries
considered usable (match the logic/path used in resolveDatOtb()) so placeholder
client IDs are reported consistently.

Added tracking and UI reporting for OTB entries that lack corresponding XML overrides.

- `source/item_definitions/core/missing_item_report.h` — added `otb_no_xml` vector to MissingItemReport struct
- `source/item_definitions/core/item_definition_resolver.cpp` — collect OTB entries missing XML overrides during resolution, skipping entries with no client ID, no DAT definition, or existing XML entry
- `source/app/managers/version_manager.cpp` — include `otb_no_xml` count in missing items total and add warning log section listing each OTB-no-XML entry
- `source/ui/dialogs/missing_items_dialog.h` — added `listOtbNoXml` and `countOtbNoXml` member pointers
- `source/ui/dialogs/missing_items_dialog.cpp` — added fourth notebook tab for OTB-no-XML entries with server ID, client ID, name, description columns; updated dialog show condition to include `otb_no_xml`
- `source/ui/menubar/file_menu_handler.cpp` — updated missing items report menu check to include `otb_no_xml` emptiness test
@github-actions

github-actions Bot commented Apr 6, 2026

Copy link
Copy Markdown

🤖 Hi @karolak6612, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
source/item_definitions/core/item_definition_resolver.cpp (2)

193-197: Consider harmonizing the empty-rows check between resolve methods.

In resolveDatOtb (line 193), failing returns false only when rows is empty AND missingReport->missing_in_dat is also empty (allowing the load to succeed if items were collected for reporting). However, resolveDatOnly (line 296) always fails when rows is empty.

If the intent is graceful degradation when collecting missing items, consider applying the same pattern to resolveDatOnly:

-	if (rows.empty()) {
+	if (rows.empty() && (!missingReport || missingReport->missing_in_dat.empty())) {
		error = "No item definitions were resolved from DAT/XML.";
		return false;
	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/item_definitions/core/item_definition_resolver.cpp` around lines 193 -
197, resolveDatOnly currently fails whenever rows.empty(); harmonize it with
resolveDatOtb by only treating empty rows as an error when there is no
missingReport data: in resolveDatOnly check if rows.empty() && missingReport &&
missingReport->missing_in_dat.empty(), set error = "No item definitions were
resolved from DAT/OTB/XML." and return false, otherwise return true so the
loader can succeed when rows are empty but missingReport has collected items;
reference resolveDatOnly, rows, missingReport, and missing_in_dat when applying
the change.

279-293: Semantic mismatch: reusing missing_in_otb for DAT-only mode.

In DAT-only mode, this populates missing_in_otb with DAT items not referenced by XML. However, MissingItemReport::missing_in_otb is documented as "In DAT but not in OTB" (see missing_item_report.h:21). The UI correctly adjusts labels (e.g., "DAT not in XML" vs "DAT not in OTB"), but the semantic overloading of this field could cause confusion in logging, debugging, or future maintenance.

Consider either:

  1. Adding a separate field (e.g., missing_in_xml) for DAT-only mode, or
  2. Documenting this dual-use behavior clearly in MissingItemReport
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/item_definitions/core/item_definition_resolver.cpp` around lines 279 -
293, The code is populating MissingItemReport::missing_in_otb for DAT-only mode
which semantically means "in DAT but not in OTB"; add a new field
MissingItemReport::missing_in_xml (and corresponding vector of MissingItemEntry)
and change the loop in item_definition_resolver.cpp that iterates fragments.dat
(currently using missingReport->missing_in_otb) to push entries into
missing_in_xml when fragments.xml is non-empty/when operating in DAT-only mode
instead; update any consumers/serializers/logging that read MissingItemReport to
handle the new missing_in_xml field (or alternatively add clear doc comments to
MissingItemReport::missing_in_otb and adjust all readers to interpret it
conditionally if you prefer documentation over schema change).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/ui/dialogs/missing_items_dialog.cpp`:
- Around line 173-208: GenerateReportText currently omits the "otb_no_xml"
section when hasOtb is true, so add a new block inside GenerateReportText
guarded by the same hasOtb branch that appends a header for report.otb_no_xml
(with its size) and iterates report.otb_no_xml, appending each entry's details
(Server ID, Client ID, Name, Description) using wxString::Format—mirror the
style used for report.xml_no_otb and the other sections so the OTB-only entries
are included in the exported report.

---

Nitpick comments:
In `@source/item_definitions/core/item_definition_resolver.cpp`:
- Around line 193-197: resolveDatOnly currently fails whenever rows.empty();
harmonize it with resolveDatOtb by only treating empty rows as an error when
there is no missingReport data: in resolveDatOnly check if rows.empty() &&
missingReport && missingReport->missing_in_dat.empty(), set error = "No item
definitions were resolved from DAT/OTB/XML." and return false, otherwise return
true so the loader can succeed when rows are empty but missingReport has
collected items; reference resolveDatOnly, rows, missingReport, and
missing_in_dat when applying the change.
- Around line 279-293: The code is populating MissingItemReport::missing_in_otb
for DAT-only mode which semantically means "in DAT but not in OTB"; add a new
field MissingItemReport::missing_in_xml (and corresponding vector of
MissingItemEntry) and change the loop in item_definition_resolver.cpp that
iterates fragments.dat (currently using missingReport->missing_in_otb) to push
entries into missing_in_xml when fragments.xml is non-empty/when operating in
DAT-only mode instead; update any consumers/serializers/logging that read
MissingItemReport to handle the new missing_in_xml field (or alternatively add
clear doc comments to MissingItemReport::missing_in_otb and adjust all readers
to interpret it conditionally if you prefer documentation over schema change).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9746107a-d9ba-4197-ab7f-72d8f99d52d3

📥 Commits

Reviewing files that changed from the base of the PR and between 4bba30a and 9a15d54.

📒 Files selected for processing (6)
  • source/app/managers/version_manager.cpp
  • source/item_definitions/core/item_definition_resolver.cpp
  • source/item_definitions/core/missing_item_report.h
  • source/ui/dialogs/missing_items_dialog.cpp
  • source/ui/dialogs/missing_items_dialog.h
  • source/ui/menubar/file_menu_handler.cpp
🚧 Files skipped from review as they are similar to previous changes (2)
  • source/ui/menubar/file_menu_handler.cpp
  • source/item_definitions/core/missing_item_report.h

Comment thread source/ui/dialogs/missing_items_dialog.cpp Outdated
source/app/managers/version_manager.cpp — Clear last_missing_items and reset last_load_has_otb on bundle load/install failure to prevent stale data
source/item_definitions/core/item_definition_resolver.cpp — Prioritize catalog sprite validation over default metadata checks in isDatItemEmptyOrInvalid; replace .find() == .end() with .contains(); remove empty rows check gating missing_in_dat
source/ui/dialogs/missing_items_dialog.cpp — Wrap all hardcoded pixel values with FromDIP() for DPI awareness; replace wxString::Format with wxString::FromUTF8(std::format()) for integer formatting; remove hasOtb parameter from GenerateReportText and always show OTB no XML section
source/ui/dialogs/missing_items_dialog.h — Remove hasOtb parameter from GenerateReportText declaration
source/ui/menubar/menubar_action_manager.cpp — Enable MISSING_ITEMS_REPORT menu item when project is loaded
@github-actions

github-actions Bot commented Apr 6, 2026

Copy link
Copy Markdown

🤖 Hi @karolak6612, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
source/item_definitions/core/item_definition_resolver.cpp (3)

285-300: Semantic mismatch: missing_in_otb used for "DAT not in XML" in DatOnly mode.

In resolveDatOnly(), unreferenced DAT items are added to missing_in_otb, but this mode has no OTB at all. This reuses the bucket in a semantically different way, which could confuse users viewing the report (the UI labels will say "OTB" when OTB isn't used).

The UI does adapt labels based on hasOtb, but the underlying data structure naming is misleading. This is acceptable as-is given the UI handles it, but consider a comment clarifying this dual-purpose usage.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/item_definitions/core/item_definition_resolver.cpp` around lines 285 -
300, The code is populating missingReport->missing_in_otb with DAT entries in
resolveDatOnly(), which semantically misuses the "OTB" bucket when no OTB
exists; update the code around the loop that inspects fragments.dat (the block
that checks isDatItemEmptyOrInvalid, dat_ids_used_by_xml and pushes
MissingItemEntry into missing_in_otb) to include a clear comment that
missing_in_otb is intentionally reused as a generic "not referenced in XML"
bucket in DatOnly mode (or alternatively create and push to a new descriptive
field); reference resolveDatOnly(), missing_in_otb, MissingItemEntry,
fragments.dat and dat_ids_used_by_xml so reviewers can locate and apply the
comment or rename/refactor accordingly.

156-174: Potential false positives in xml_no_otb collection.

The loop collects XML entries that reference non-existent OTB server IDs. However, when an XML entry specifies a client_id override that differs from the server_id, this entry could be legitimately not in OTB while still being valid as a client-id mapping override for an existing OTB entry.

Additionally, the skip logic at lines 159-165 filters server_id <= 0 and server_id < 100, but these checks seem redundant since server_id < 100 already covers server_id <= 0.

Simplify redundant checks
 	for (const auto& [server_id, xml] : fragments.xml) {
-		// Skip XML entries with invalid IDs
-		if (server_id <= 0) {
-			continue;
-		}
 		// Skip server-side fluid types and special items (IDs < 100)
 		if (server_id < 100) {
 			continue;
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/item_definitions/core/item_definition_resolver.cpp` around lines 156 -
174, The xml_no_otb list currently reports XML entries when their server_id
isn't in fragments.otb, but this produces false positives for entries that
specify a client_id override that maps to an existing OTB entry; update the loop
that iterates fragments.xml to first skip server_id < 100 (remove the redundant
server_id <= 0 check) and also skip adding a MissingItemEntry when
xml.client_id.has_value() and fragments.otb.contains(xml.client_id.value())
(i.e., treat client_id overrides that resolve to an existing OTB entry as
valid), while otherwise constructing MissingItemEntry (server_id, client_id via
xml.client_id.value_or(0), name, description) and pushing to
missingReport->xml_no_otb.

111-131: Inconsistent "missing or unusable DAT" handling between resolve paths.

In resolveDatOtb(), when DAT exists but is empty/invalid (according to isDatItemEmptyOrInvalid), the code still processes it and adds to dat_ids_referenced_by_otb (line 130). However, in resolveDatOnly() (lines 248-253), empty/invalid DAT items are silently skipped and not reported.

This creates an asymmetry:

  • resolveDatOtb: Empty DAT items are treated as valid mappings
  • resolveDatOnly: Empty DAT items silently skip XML entries without reporting

Consider whether empty/invalid DAT entries should be reported in missing_in_dat when they exist but are unusable, for consistency between both paths.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/item_definitions/core/item_definition_resolver.cpp` around lines 111 -
131, The two resolver paths are inconsistent: resolveDatOtb currently treats DAT
entries that exist but are empty/invalid as valid (it inserts into
dat_ids_referenced_by_otb and proceeds), while resolveDatOnly skips and reports
them; to fix, update resolveDatOtb to call isDatItemEmptyOrInvalid for the found
DAT entry and handle unusable entries the same way as resolveDatOnly — i.e., if
isDatItemEmptyOrInvalid(effective_client_id) is true, add a MissingItemEntry
(server_id, client_id, otb.name, otb.description) to
missingReport->missing_in_dat or set error and return false when missingReport
is null, and do NOT insert into dat_ids_referenced_by_otb or apply flags; keep
resolveDatOnly behavior unchanged so both paths report unusable DATs
consistently.
source/app/managers/version_manager.cpp (1)

140-185: Consider bounded output for very large missing item lists.

When SHOW_MISSING_ITEMS_WARNING is enabled and there are thousands of missing items, the warnings list could become extremely large, potentially impacting performance or usability of any UI that displays these warnings.

Consider adding a cap (e.g., first N items per category) with a note like "...and X more" to keep the warning output manageable while still providing useful diagnostic information.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/managers/version_manager.cpp` around lines 140 - 185, The warnings
builder in version_manager.cpp (inside the block guarded by
g_settings.getBoolean(Config::SHOW_MISSING_ITEMS_WARNING)) unboundedly pushes
every missing entry from last_missing_items.* into warnings; cap output
per-category (e.g., int MAX_SHOW = 50) by iterating only the first MIN(size,
MAX_SHOW) entries for missing_in_dat, missing_in_otb, xml_no_otb, and
otb_no_xml, and after each category append a summary line like "...and X more"
when size > MAX_SHOW; preserve the original top-level total_missing count and
the existing header lines in warnings, and reference the existing symbols
last_missing_items, warnings, and Config::SHOW_MISSING_ITEMS_WARNING when making
the change.
source/ui/dialogs/missing_items_dialog.h (1)

10-19: Consider forward declarations or moving includes to cpp.

Several <wx/*.h> headers (clipboard, filedlg, textfile, stream, txtstrm, wfstream) are only needed for implementation details in the cpp file, not for the class declaration. Moving these to the cpp file would reduce header dependencies and potentially improve compile times.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/ui/dialogs/missing_items_dialog.h` around lines 10 - 19, The header
includes heavy implementation-only wx headers which should be moved to the .cpp:
remove `#include` lines for <wx/clipbrd.h>, <wx/filedlg.h>, <wx/textfile.h>,
<wx/stream.h>, <wx/txtstrm.h>, and <wx/wfstream.h> from
source/ui/dialogs/missing_items_dialog.h and add minimal forward declarations
(e.g., forward-declare any wx classes referenced in the class declaration of
MissingItemsDialog such as wxClipboard, wxFileDialog, wxTextFile, wxInputStream/
wxOutputStream or other specific types used) so the header only depends on
declarations; then include the removed wx headers in missing_items_dialog.cpp
where the actual clipboard/file/stream/text operations are implemented. Ensure
all symbols used in inline methods or member variables remain declared in the
header via forward declarations or by keeping only the necessary lightweight wx
headers (like <wx/wx.h>) if required.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/ui/dialogs/missing_items_dialog.cpp`:
- Around line 217-223: MissingItemsDialog::OnCopyToClipboard currently silently
skips when wxTheClipboard->Open() returns false; update this method to show an
error message to the user when wxTheClipboard->Open() fails (e.g., call
wxMessageBox with a descriptive error and wxICON_ERROR), then return early; keep
the successful branch behavior (SetData, Close, success message) unchanged and
ensure you only call wxTheClipboard->SetData(...) when Open() succeeded.
- Around line 92-117: The code creates page3, sizer3, countXmlNoOtb and
listXmlNoOtb unconditionally but only adds page3 to notebook when hasOtb,
causing a leak; fix by moving the entire creation/population block for page3
(the newd calls and the for loop that appends items and sets the label) inside
the if (hasOtb) branch so those widgets are only created when
notebook->AddPage(...) will be called, or alternatively delete page3 (and its
owned children) when hasOtb is false; reference symbols: page3, sizer3,
countXmlNoOtb, listXmlNoOtb, notebook, report.xml_no_otb.

---

Nitpick comments:
In `@source/app/managers/version_manager.cpp`:
- Around line 140-185: The warnings builder in version_manager.cpp (inside the
block guarded by g_settings.getBoolean(Config::SHOW_MISSING_ITEMS_WARNING))
unboundedly pushes every missing entry from last_missing_items.* into warnings;
cap output per-category (e.g., int MAX_SHOW = 50) by iterating only the first
MIN(size, MAX_SHOW) entries for missing_in_dat, missing_in_otb, xml_no_otb, and
otb_no_xml, and after each category append a summary line like "...and X more"
when size > MAX_SHOW; preserve the original top-level total_missing count and
the existing header lines in warnings, and reference the existing symbols
last_missing_items, warnings, and Config::SHOW_MISSING_ITEMS_WARNING when making
the change.

In `@source/item_definitions/core/item_definition_resolver.cpp`:
- Around line 285-300: The code is populating missingReport->missing_in_otb with
DAT entries in resolveDatOnly(), which semantically misuses the "OTB" bucket
when no OTB exists; update the code around the loop that inspects fragments.dat
(the block that checks isDatItemEmptyOrInvalid, dat_ids_used_by_xml and pushes
MissingItemEntry into missing_in_otb) to include a clear comment that
missing_in_otb is intentionally reused as a generic "not referenced in XML"
bucket in DatOnly mode (or alternatively create and push to a new descriptive
field); reference resolveDatOnly(), missing_in_otb, MissingItemEntry,
fragments.dat and dat_ids_used_by_xml so reviewers can locate and apply the
comment or rename/refactor accordingly.
- Around line 156-174: The xml_no_otb list currently reports XML entries when
their server_id isn't in fragments.otb, but this produces false positives for
entries that specify a client_id override that maps to an existing OTB entry;
update the loop that iterates fragments.xml to first skip server_id < 100
(remove the redundant server_id <= 0 check) and also skip adding a
MissingItemEntry when xml.client_id.has_value() and
fragments.otb.contains(xml.client_id.value()) (i.e., treat client_id overrides
that resolve to an existing OTB entry as valid), while otherwise constructing
MissingItemEntry (server_id, client_id via xml.client_id.value_or(0), name,
description) and pushing to missingReport->xml_no_otb.
- Around line 111-131: The two resolver paths are inconsistent: resolveDatOtb
currently treats DAT entries that exist but are empty/invalid as valid (it
inserts into dat_ids_referenced_by_otb and proceeds), while resolveDatOnly skips
and reports them; to fix, update resolveDatOtb to call isDatItemEmptyOrInvalid
for the found DAT entry and handle unusable entries the same way as
resolveDatOnly — i.e., if isDatItemEmptyOrInvalid(effective_client_id) is true,
add a MissingItemEntry (server_id, client_id, otb.name, otb.description) to
missingReport->missing_in_dat or set error and return false when missingReport
is null, and do NOT insert into dat_ids_referenced_by_otb or apply flags; keep
resolveDatOnly behavior unchanged so both paths report unusable DATs
consistently.

In `@source/ui/dialogs/missing_items_dialog.h`:
- Around line 10-19: The header includes heavy implementation-only wx headers
which should be moved to the .cpp: remove `#include` lines for <wx/clipbrd.h>,
<wx/filedlg.h>, <wx/textfile.h>, <wx/stream.h>, <wx/txtstrm.h>, and
<wx/wfstream.h> from source/ui/dialogs/missing_items_dialog.h and add minimal
forward declarations (e.g., forward-declare any wx classes referenced in the
class declaration of MissingItemsDialog such as wxClipboard, wxFileDialog,
wxTextFile, wxInputStream/ wxOutputStream or other specific types used) so the
header only depends on declarations; then include the removed wx headers in
missing_items_dialog.cpp where the actual clipboard/file/stream/text operations
are implemented. Ensure all symbols used in inline methods or member variables
remain declared in the header via forward declarations or by keeping only the
necessary lightweight wx headers (like <wx/wx.h>) if required.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7cbf1e16-f26d-4a14-8c8e-d1ba525be0fb

📥 Commits

Reviewing files that changed from the base of the PR and between 9a15d54 and 351bafe.

📒 Files selected for processing (5)
  • source/app/managers/version_manager.cpp
  • source/item_definitions/core/item_definition_resolver.cpp
  • source/ui/dialogs/missing_items_dialog.cpp
  • source/ui/dialogs/missing_items_dialog.h
  • source/ui/menubar/menubar_action_manager.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/ui/menubar/menubar_action_manager.cpp

Comment thread source/ui/dialogs/missing_items_dialog.cpp Outdated
Comment thread source/ui/dialogs/missing_items_dialog.cpp
- Capped missing items warning output to 50 entries per category with "...and N more" suffix to prevent excessive log output
- Fixed resolveDatOtb to accept DAT items with empty mappings and added client_id override validation for XML entries
- Removed empty DAT item filtering in resolveDatOnly mode to load all items unconditionally
- Made "XML no OTB" tab in MissingItemsDialog conditional on hasOtb flag instead of always building it
- Moved wxWidgets includes from missing_items_dialog.h to .cpp, removed unused header includes
- Improved clipboard error handling in OnCopyToClipboard with proper open failure check and error message
@github-actions

github-actions Bot commented Apr 6, 2026

Copy link
Copy Markdown

🤖 Hi @karolak6612, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

## 📋 Review Summary

This pull request introduces a comprehensive "Missing Items Report" feature to help identify and resolve discrepancies between DAT, OTB, and XML data files. The implementation is well-structured, integrating cleanly into the existing asset loading pipeline and providing a user-friendly dialog for detailed inspection.

🔍 General Feedback

  • Well-Integrated: The collection of missing items is handled within the ItemDefinitionResolver, ensuring that the report is always accurate based on the current loading mode.
  • UI Polished: The new dialog provides useful features like clipboard export and saving to a file, which are great for debugging data file issues.
  • Performance Consideration: I identified one nested loop in the OTB resolution logic that could become a bottleneck with large data sets; optimizing this with a temporary lookup set is recommended.
  • Consistency: The changes follow established patterns in the codebase, such as the use of wxNotebook and wxDataViewListCtrl for reports.

AssetBundle bundle;
AssetBundleLoader bundle_loader;
if (!bundle_loader.load(asset_request, bundle, error, warnings)) {
error = "Couldn't load canonical asset bundle: " + error;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Resetting last_load_has_otb to true on failure might be inconsistent if the failed load was in DatOnly mode. It would be better to keep the value determined at the start of LoadDataFiles or set it to a more neutral state.

Suggested change
error = "Couldn't load canonical asset bundle: " + error;
// Clear stale data on failure
last_missing_items = {};
UnloadVersion();

bool foundInOtb = false;
for (const auto& [otbServerId, otb] : fragments.otb) {
if (otb.client_id == xml.client_id.value()) {
foundInOtb = true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The nested loop here results in O(N*M) complexity, where N is the number of XML entries and M is the number of OTB entries. Given that these collections can contain thousands of items, this may cause a noticeable delay during data loading.

Consider creating a temporary std::unordered_set<ClientItemId> of all client_id values present in fragments.otb before this loop to allow for O(1) lookups.

		// Pre-collect all client IDs present in OTB for fast lookup
		std::unordered_set<ClientItemId> otb_client_ids;
		for (const auto& [id, otb] : fragments.otb) {
			otb_client_ids.insert(otb.client_id);
		}

		// Collect XML entries that reference non-existent OTB server IDs
		for (const auto& [server_id, xml] : fragments.xml) {
			// ...
			if (xml.client_id.has_value()) {
				if (otb_client_ids.contains(xml.client_id.value())) {
					continue;
				}
			}
			// ...
		}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
source/item_definitions/core/item_definition_resolver.cpp (1)

227-239: ⚠️ Potential issue | 🟠 Major

Avoid duplicate server_ids in DatOnly mode.

This seeds one row for every DAT id and later rewrites row.server_id from XML. If an XML entry maps server_id to a different client_id and that original server_id also exists in DAT, rows ends up with two definitions for the same server id. Please suppress or collapse the default DAT row before applying that remap so the resolved table stays one-definition-per-server-id.

Also applies to: 286-288

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/item_definitions/core/item_definition_resolver.cpp` around lines 227 -
239, The current loop over fragments.dat creates a default
ResolvedItemDefinitionRow per DAT id (in rows and client_to_row), but later XML
remapping can assign a different client_id to an existing server_id, producing
duplicate server_id entries; fix by ensuring DAT defaults are suppressed/merged
when a later XML remap reassigns server_id: do not blindly push_back a DAT row
for client_id if another entry will claim the same server_id, instead insert
into rows via client_to_row only after checking/creating by server_id (use
server_id as the canonical key), and when applying the XML remap update or move
the existing rows entry (and client_to_row mapping) rather than adding a new one
so rows remains one-definition-per-server-id (update code around the
fragments.dat loop and the XML remap handling to consult and modify
client_to_row and rows by server_id).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/app/managers/version_manager.cpp`:
- Around line 147-148: The warning strings currently use a static header and
menu hint that don't reflect the active mode or actual action location; update
the two places that push warnings (the lines that call warnings.push_back with
std::format("Missing item definitions detected...") and the subsequent hint
line) to: 1) choose the header text based on the missing set identifier (e.g.,
use "Not referenced by items.xml" when missing_in_otb represents dat-only items,
otherwise indicate "missing from items.otb"), using the existing variable
missing_in_otb and total_missing to build the message; and 2) change the hint
line to reference the correct menu path/action ("File -> Reload") or
conditionally show the correct menu path matching where the new action was
added; apply the same conditional wording change to the other occurrence noted
(the second push_back around the same pattern).

---

Outside diff comments:
In `@source/item_definitions/core/item_definition_resolver.cpp`:
- Around line 227-239: The current loop over fragments.dat creates a default
ResolvedItemDefinitionRow per DAT id (in rows and client_to_row), but later XML
remapping can assign a different client_id to an existing server_id, producing
duplicate server_id entries; fix by ensuring DAT defaults are suppressed/merged
when a later XML remap reassigns server_id: do not blindly push_back a DAT row
for client_id if another entry will claim the same server_id, instead insert
into rows via client_to_row only after checking/creating by server_id (use
server_id as the canonical key), and when applying the XML remap update or move
the existing rows entry (and client_to_row mapping) rather than adding a new one
so rows remains one-definition-per-server-id (update code around the
fragments.dat loop and the XML remap handling to consult and modify
client_to_row and rows by server_id).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d88a6f8a-b9e9-4073-9c71-c1643df2caaf

📥 Commits

Reviewing files that changed from the base of the PR and between 351bafe and ecf6a20.

📒 Files selected for processing (4)
  • source/app/managers/version_manager.cpp
  • source/item_definitions/core/item_definition_resolver.cpp
  • source/ui/dialogs/missing_items_dialog.cpp
  • source/ui/dialogs/missing_items_dialog.h
🚧 Files skipped from review as they are similar to previous changes (1)
  • source/ui/dialogs/missing_items_dialog.h

Comment thread source/app/managers/version_manager.cpp Outdated
Comment on lines +147 to +148
warnings.push_back(std::format("Missing item definitions detected ({} entries total).", total_missing));
warnings.push_back("Go to File -> Missing Items Report... for detailed view.");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Keep the warning text aligned with the active mode and actual menu path.

missing_in_otb is reused as “not referenced by items.xml” in DatOnly mode, but this header is always rendered as items.otb. The hint line also points to File -> Missing Items Report..., while this PR adds the action under File -> Reload.

Suggested wording fix
-			warnings.push_back("Go to File -> Missing Items Report... for detailed view.");
+			warnings.push_back("Go to File -> Reload -> Missing Items Report... for detailed view.");
...
-				warnings.push_back(std::format("--- tibia.dat items not in items.otb ({}) ---", last_missing_items.missing_in_otb.size()));
+				warnings.push_back(std::format("--- tibia.dat items not in {} ({}) ---",
+					last_load_has_otb ? "items.otb" : "items.xml",
+					last_missing_items.missing_in_otb.size()));

Also applies to: 166-166

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/managers/version_manager.cpp` around lines 147 - 148, The warning
strings currently use a static header and menu hint that don't reflect the
active mode or actual action location; update the two places that push warnings
(the lines that call warnings.push_back with std::format("Missing item
definitions detected...") and the subsequent hint line) to: 1) choose the header
text based on the missing set identifier (e.g., use "Not referenced by
items.xml" when missing_in_otb represents dat-only items, otherwise indicate
"missing from items.otb"), using the existing variable missing_in_otb and
total_missing to build the message; and 2) change the hint line to reference the
correct menu path/action ("File -> Reload") or conditionally show the correct
menu path matching where the new action was added; apply the same conditional
wording change to the other occurrence noted (the second push_back around the
same pattern).

Reorganized resolveDatOnly into clear 3-step process with improved comments
- Moved dat_ids_used_by_xml insertion to duplicate-handling block to avoid tracking unused entries
- Consolidated empty DAT item checks into the reporting phase instead of the XML override phase
- Removed redundant server_id <= 0 check and invalid ID guard
- Simplified missing item collection by deferring empty/invalid filtering to the final loop
@github-actions

github-actions Bot commented Apr 6, 2026

Copy link
Copy Markdown

🤖 Hi @karolak6612, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

## 📋 Review Summary

The pull request introduces a new "Missing Items Report" dialog to identify discrepancies between Tibia's data files (OTB, DAT, and XML). This feature provides essential diagnostic information for map designers to ensure all items are correctly defined. The implementation follows the project's existing architectural patterns and uses modern C++ features consistently.

🔍 General Feedback

  • Good Categorization: Using wxNotebook to separate discrepancies by their source (DAT missing in OTB/XML vs. OTB/XML missing in DAT) makes the report very easy to navigate.
  • DPI Awareness: Proper use of FromDIP in the new UI components ensures compatibility with high-resolution displays.
  • Configuration Control: Adding a preference to toggle automatic warnings while keeping the detailed report available via the menu is a thoughtful UX choice.
  • Performance Consideration: One nested loop in the loading phase was identified as a potential performance bottleneck ($O(N \cdot M)$ complexity) and has been suggested for optimization using a lookup set.
  • Code Cleanliness: A minor unused include in the new dialog file was noted for removal.

// Collect XML entries that reference non-existent OTB server IDs
for (const auto& [server_id, xml] : fragments.xml) {
// Skip server-side fluid types and special items (IDs < 100)
if (server_id < 100) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 This nested loop results in $O(N \cdot M)$ complexity, where $N$ is the number of XML entries and $M$ is the number of OTB entries. Since fragments.otb can be large, this may cause a noticeable delay during data loading.

Consider pre-building a std::unordered_set<ClientItemId> of all client IDs present in OTB to make this check $O(1)$ inside the loop.

Suggested change
if (server_id < 100) {
// Pre-build a set of client IDs referenced by OTB for fast lookup
std::unordered_set<ClientItemId> otb_client_ids;
otb_client_ids.reserve(fragments.otb.size());
for (const auto& [id, otb] : fragments.otb) {
otb_client_ids.insert(otb.client_id);
}
// Collect XML entries that reference non-existent OTB server IDs
for (const auto& [server_id, xml] : fragments.xml) {
// Skip server-side fluid types and special items (IDs < 100)
if (server_id < 100) {
continue;
}
// If XML entry has a client_id override that maps to an existing OTB entry, it's valid
if (xml.client_id.has_value()) {
if (otb_client_ids.contains(xml.client_id.value())) {
continue;
}
}

#include <format>
#include <wx/clipbrd.h>
#include <wx/filedlg.h>
#include <wx/fdrepdlg.h>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 The wx/fdrepdlg.h include (Find/Replace dialog) appears to be unused in this dialog.

Suggested change
#include <wx/fdrepdlg.h>
#include <wx/filedlg.h>
#include <wx/stream.h>

@github-actions

github-actions Bot commented Apr 6, 2026

Copy link
Copy Markdown

🤖 Hi @karolak6612, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@karolak6612

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

## 📋 Review Summary

This pull request introduces a valuable new feature: a "Missing Items Report" dialog that identifies discrepancies between items.otb, tibia.dat, and items.xml files. The implementation is comprehensive, integrating the report collection into the existing data loading pipeline and providing a user-friendly UI for viewing and exporting the results.

🔍 General Feedback

  • Well-Structured: The addition of the MissingItemReport struct and its integration into ItemDefinitionResolver is clean and follows the project's architecture.
  • User Control: Adding a preference setting to toggle detailed warnings on load is a good decision, as it prevents clutter for users who don't need it while keeping the information accessible.
  • Performance: I've suggested an optimization for a nested loop in the report generation to ensure it scales well with large item definitions.
  • Maintainability: The use of modern C++ features (like std::format) is consistent with recent changes in the codebase.

Comment on lines +145 to +163
// Collect XML entries that reference non-existent OTB server IDs
for (const auto& [server_id, xml] : fragments.xml) {
// Skip server-side fluid types and special items (IDs < 100)
if (server_id < 100) {
continue;
}
// If XML entry has a client_id override that maps to an existing OTB entry, it's valid
if (xml.client_id.has_value()) {
bool foundInOtb = false;
for (const auto& [otbServerId, otb] : fragments.otb) {
if (otb.client_id == xml.client_id.value()) {
foundInOtb = true;
break;
}
}
if (foundInOtb) {
continue;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The nested loop here is O(N*M) where N is the number of XML entries and M is the number of OTB entries. This can be significantly slow when both collections are large.

I suggest pre-calculating a set of all OTB client IDs before starting the XML loop to reduce the complexity to O(N+M).

Suggested change
// Collect XML entries that reference non-existent OTB server IDs
for (const auto& [server_id, xml] : fragments.xml) {
// Skip server-side fluid types and special items (IDs < 100)
if (server_id < 100) {
continue;
}
// If XML entry has a client_id override that maps to an existing OTB entry, it's valid
if (xml.client_id.has_value()) {
bool foundInOtb = false;
for (const auto& [otbServerId, otb] : fragments.otb) {
if (otb.client_id == xml.client_id.value()) {
foundInOtb = true;
break;
}
}
if (foundInOtb) {
continue;
}
}
// Collect XML entries that reference non-existent OTB server IDs
std::unordered_set<ClientItemId> all_otb_client_ids;
for (const auto& [id, otb] : fragments.otb) {
all_otb_client_ids.insert(otb.client_id);
}
for (const auto& [server_id, xml] : fragments.xml) {
// Skip server-side fluid types and special items (IDs < 100)
if (server_id < 100) {
continue;
}
// If XML entry has a client_id override that maps to an existing OTB entry, it's valid
if (xml.client_id.has_value()) {
if (all_otb_client_ids.contains(xml.client_id.value())) {
continue;
}
}

wxString::FromUTF8(std::format("{}", entry.client_id)),
wxString::FromUTF8(entry.name.empty() ? "unknown" : entry.name),
wxString::FromUTF8(entry.description)
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 The button label "Ignore" is a bit unusual for closing a report dialog. "Close" or "OK" would be more idiomatic for a report/view-only dialog.

Suggested change
});
auto* ignoreButton = newd wxButton(this, wxID_ANY, "Close");

@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@karolak6612

Copy link
Copy Markdown
Collaborator Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a comprehensive 'Missing Items Report' feature, allowing users to identify discrepancies between items.otb, tibia.dat, and items.xml. Key changes include the implementation of a new reporting dialog, menu actions, and logic within the ItemDefinitionResolver to track missing definitions. Feedback focuses on adhering to the project's mandatory C++20/23 standards, specifically recommending the use of designated initializers, ranges, and views to modernize the implementation and the removal of unused includes.

#include "item_definitions/core/item_definition_resolver.h"
#include "item_definitions/formats/dat/dat_catalog.h"

#include <spdlog/spdlog.h>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The include <spdlog/spdlog.h> is not used in this file and should be removed to keep the dependencies clean. If you intended to use it for debugging, please ensure it's removed before merging.

Suggested change
#include <spdlog/spdlog.h>
#include <algorithm>

if (catalog) {
const auto* entry = catalog->entry(client_id);
if (entry) {
bool hasValidSprite = (entry->numsprites > 0 && entry->sprite_ids.size() > 0 && entry->sprite_ids[0] != 0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Use !entry->sprite_ids.empty() instead of entry->sprite_ids.size() > 0. This is more idiomatic in C++ and follows the project's goal of using modern C++ patterns.

Suggested change
bool hasValidSprite = (entry->numsprites > 0 && entry->sprite_ids.size() > 0 && entry->sprite_ids[0] != 0);
bool hasValidSprite = (entry->numsprites > 0 && !entry->sprite_ids.empty() && entry->sprite_ids[0] != 0);

Comment on lines +103 to +108
MissingItemEntry entry;
entry.server_id = server_id;
entry.client_id = effective_client_id;
entry.name = otb.name;
entry.description = otb.description;
missingReport->missing_in_dat.push_back(std::move(entry));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Use designated initializers for aggregate initialization. This is a core C++20 feature and is explicitly required by the repository style guide (Rule 94) for better readability and safety.

Suggested change
MissingItemEntry entry;
entry.server_id = server_id;
entry.client_id = effective_client_id;
entry.name = otb.name;
entry.description = otb.description;
missingReport->missing_in_dat.push_back(std::move(entry));
missingReport->missing_in_dat.push_back({
.server_id = server_id,
.client_id = effective_client_id,
.name = otb.name,
.description = otb.description
});
References
  1. Designated initializers are mandatory for new code in this repository. (link)

Comment on lines +153 to +162
bool foundInOtb = false;
for (const auto& [otbServerId, otb] : fragments.otb) {
if (otb.client_id == xml.client_id.value()) {
foundInOtb = true;
break;
}
}
if (foundInOtb) {
continue;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

This manual search loop can be replaced with std::ranges::any_of for a more concise and modern C++20 approach, adhering to the project's mandatory C++20/23 requirement.

if (std::ranges::any_of(fragments.otb, [&](const auto& p) { return p.second.client_id == xml.client_id.value(); })) {
	continue;
}

#include "ui/tool_options_window.h"
#include "item_definitions/core/asset_bundle_loader.h"
#include "item_definitions/core/item_definition_store.h"
#include "app/settings.h"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Include <ranges> to support modern C++20 range views and adaptors used in the loading logic.

Suggested change
#include "app/settings.h"
#include "app/settings.h"
#include <ranges>

Comment thread source/app/managers/version_manager.cpp Outdated
Comment on lines +153 to +158
size_t show_count = std::min(last_missing_items.missing_in_dat.size(), MAX_SHOW);
for (size_t i = 0; i < show_count; ++i) {
const auto& entry = last_missing_items.missing_in_dat[i];
warnings.push_back(std::format(" Server ID: {}, Client ID: {}, Name: '{}'",
entry.server_id, entry.client_id, entry.name.empty() ? "unknown" : entry.name));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

Use std::views::take to simplify the loop and avoid manual index management. This aligns with the repository's mandatory C++20/23 standard (Rule 17).

for (const auto& entry : last_missing_items.missing_in_dat | std::views::take(MAX_SHOW)) {
	warnings.push_back(std::format("  Server ID: {}, Client ID: {}, Name: '{}'",
		entry.server_id, entry.client_id, entry.name.empty() ? "unknown" : entry.name));
}
References
  1. Every line of code modified MUST use C++20/23 features. (link)

#include <format>
#include <wx/clipbrd.h>
#include <wx/filedlg.h>
#include <wx/fdrepdlg.h>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

The include <wx/fdrepdlg.h> (Find/Replace dialog) is not used in this file and should be removed.

#include <wx/filedlg.h>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
source/app/managers/version_manager.cpp (1)

147-148: ⚠️ Potential issue | 🟡 Minor

Fix the warning copy for DatOnly mode and the real menu path.

Line 148 still points users to File -> Missing Items Report..., but this action lives under File -> Reload. Line 166 also hardcodes items.otb, even though missing_in_otb means “not referenced by items.xml” when last_load_has_otb is false.

📝 Proposed fix
-			warnings.push_back("Go to File -> Missing Items Report... for detailed view.");
+			warnings.push_back("Go to File -> Reload -> Missing Items Report... for detailed view.");
...
-				warnings.push_back(std::format("--- tibia.dat items not in items.otb ({}) ---", last_missing_items.missing_in_otb.size()));
+				warnings.push_back(std::format("--- tibia.dat items not referenced by {} ({}) ---",
+					last_load_has_otb ? "items.otb" : "items.xml",
+					last_missing_items.missing_in_otb.size()));

Also applies to: 165-166

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/managers/version_manager.cpp` around lines 147 - 148, The warning
text is wrong for DatOnly loads and the menu path is incorrect; update the two
warnings created via warnings.push_back(...) so the menu path reads "File ->
Reload -> Missing Items Report..." and, when last_load_has_otb is false (DatOnly
mode), change the second message and any mention of "items.otb" to reflect that
the missing entries are not referenced by items.xml (e.g., "Not referenced by
items.xml" or a DatOnly-specific phrase) using the existing missing_in_otb and
last_load_has_otb variables to choose the correct wording instead of hardcoding
"items.otb".
🧹 Nitpick comments (1)
source/app/managers/version_manager.cpp (1)

20-20: Include <format> directly in this translation unit.

The new std::format calls below currently depend on a transitive include. Please make that dependency explicit here instead of relying on another header to pull it in.

🔧 Proposed fix
 `#include` <spdlog/spdlog.h>
+#include <format>
 
 `#include` "ui/gui.h"

Also applies to: 147-203

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/managers/version_manager.cpp` at line 20, Add a direct `#include`
<format> to this translation unit so the std::format calls used in this file are
not relying on transitive includes; locate the top-of-file includes around the
existing `#include` "app/settings.h" and insert the <format> include, and also
ensure the same explicit include is present where the other std::format usages
occur (the section referenced around the code that formats version strings /
messages between lines ~147-203).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@source/app/preferences/editor_page.cpp`:
- Line 74: Update the helper string that currently reads "File -> Missing Items
Report..." to point to the actual menu location under File -> Reload (e.g.,
change to "File -> Reload -> Missing Items Report..." or "File -> Reload" as
appropriate) in source/app/preferences/editor_page.cpp where the summary string
is defined so the UI hint matches the actual action placement.

In `@source/app/settings.h`:
- Line 88: You added SHOW_MISSING_ITEMS_WARNING into the middle of the settings
enum which shifts subsequent enum values and can corrupt persisted settings;
move the SHOW_MISSING_ITEMS_WARNING identifier out of the middle and append it
to the stable end of the enum (or just before the enum sentinel/explicit last
key used for stability) so existing enum ordering/values for other keys remain
unchanged; update only the enum declaration (the symbol
SHOW_MISSING_ITEMS_WARNING) and do not renumber or reorder any other existing
enum members.

In `@source/item_definitions/core/item_definition_resolver.cpp`:
- Around line 151-163: In resolveDatOtb(), the code currently skips creating an
xml_no_otb row whenever xml.client_id matches any fragments.otb[].client_id;
instead, only skip when the OTB entry matches the XML by server_id. Change the
fast-path so it checks fragments.otb entries against xml.server_id (or remove
the continue) rather than xml.client_id: locate the loop over fragments.otb and
the if (otb.client_id == xml.client_id.value()) condition and replace it with a
comparison to otb.server_id == xml.server_id (or simply stop skipping based on
client_id), ensuring XML entries whose server_id is absent from OTB are still
emitted as xml_no_otb rows.

---

Duplicate comments:
In `@source/app/managers/version_manager.cpp`:
- Around line 147-148: The warning text is wrong for DatOnly loads and the menu
path is incorrect; update the two warnings created via warnings.push_back(...)
so the menu path reads "File -> Reload -> Missing Items Report..." and, when
last_load_has_otb is false (DatOnly mode), change the second message and any
mention of "items.otb" to reflect that the missing entries are not referenced by
items.xml (e.g., "Not referenced by items.xml" or a DatOnly-specific phrase)
using the existing missing_in_otb and last_load_has_otb variables to choose the
correct wording instead of hardcoding "items.otb".

---

Nitpick comments:
In `@source/app/managers/version_manager.cpp`:
- Line 20: Add a direct `#include` <format> to this translation unit so the
std::format calls used in this file are not relying on transitive includes;
locate the top-of-file includes around the existing `#include` "app/settings.h"
and insert the <format> include, and also ensure the same explicit include is
present where the other std::format usages occur (the section referenced around
the code that formats version strings / messages between lines ~147-203).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e0699263-7ac3-45b9-8506-1b0f6d127640

📥 Commits

Reviewing files that changed from the base of the PR and between 3602336 and 4f2e264.

📒 Files selected for processing (22)
  • data/menubar.xml
  • source/CMakeLists.txt
  • source/app/managers/version_manager.cpp
  • source/app/managers/version_manager.h
  • source/app/preferences/editor_page.cpp
  • source/app/preferences/editor_page.h
  • source/app/settings.cpp
  • source/app/settings.h
  • source/item_definitions/core/asset_bundle.h
  • source/item_definitions/core/asset_bundle_loader.cpp
  • source/item_definitions/core/item_definition_resolver.cpp
  • source/item_definitions/core/item_definition_resolver.h
  • source/item_definitions/core/item_definitions_loader.cpp
  • source/item_definitions/core/item_definitions_loader.h
  • source/item_definitions/core/missing_item_report.h
  • source/ui/dialogs/missing_items_dialog.cpp
  • source/ui/dialogs/missing_items_dialog.h
  • source/ui/main_menubar.cpp
  • source/ui/main_menubar.h
  • source/ui/menubar/file_menu_handler.cpp
  • source/ui/menubar/file_menu_handler.h
  • source/ui/menubar/menubar_action_manager.cpp

missing_items_warn_chkbox = PreferencesLayout::AddCheckBoxRow(
safety_section,
"Show missing items warning on load",
"Display a summary of missing item definitions after loading a map. Items are always tracked and available via File -> Missing Items Report...",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Update menu path in helper text to match actual UI location.

At Line 74, the text says File -> Missing Items Report..., but this action is added under File -> Reload. This can mislead users when they try to find the dialog.

Suggested text fix
-        "Display a summary of missing item definitions after loading a map. Items are always tracked and available via File -> Missing Items Report...",
+        "Display a summary of missing item definitions after loading a map. Items are always tracked and available via File -> Reload -> Missing Items Report...",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"Display a summary of missing item definitions after loading a map. Items are always tracked and available via File -> Missing Items Report...",
"Display a summary of missing item definitions after loading a map. Items are always tracked and available via File -> Reload -> Missing Items Report...",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/preferences/editor_page.cpp` at line 74, Update the helper string
that currently reads "File -> Missing Items Report..." to point to the actual
menu location under File -> Reload (e.g., change to "File -> Reload -> Missing
Items Report..." or "File -> Reload" as appropriate) in
source/app/preferences/editor_page.cpp where the summary string is defined so
the UI hint matches the actual action placement.

Comment thread source/app/settings.h Outdated
Comment thread source/item_definitions/core/item_definition_resolver.cpp

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (4)
source/app/settings.h (1)

88-88: ⚠️ Potential issue | 🟠 Major

Do not insert this config key in the middle of the enum.

At Line 88, this shifts persisted indices for all following keys and can corrupt settings mapping. Move it to the stable tail section (before LAST).

Suggested fix
 		WARN_FOR_DUPLICATE_ID,
-		SHOW_MISSING_ITEMS_WARNING,
 		USE_UPDATER,
@@
 		SEARCH_RESULTS_LIMIT,
 		SHOW_INVALID_TILES,
 		SHOW_INVALID_ZONES,
+		SHOW_MISSING_ITEMS_WARNING,
 
 		LAST,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/settings.h` at line 88, The new enum entry
SHOW_MISSING_ITEMS_WARNING was inserted in the middle of the settings enum which
shifts persisted indices; move the SHOW_MISSING_ITEMS_WARNING identifier out of
the main body and place it in the stable tail section just before the LAST enum
value (i.e., ensure SHOW_MISSING_ITEMS_WARNING is declared immediately before
LAST) so existing enum ordering and persisted mapping remain unchanged.
source/app/preferences/editor_page.cpp (1)

71-76: ⚠️ Potential issue | 🟡 Minor

Update helper text to the real menu path.

Line 74 still says File -> Missing Items Report..., but the action is under File -> Reload. Please align the hint text to avoid user confusion.

Suggested fix
-		"Display a summary of missing item definitions after loading a map. Items are always tracked and available via File -> Missing Items Report...",
+		"Display a summary of missing item definitions after loading a map. Items are always tracked and available via File -> Reload -> Missing Items Report...",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/preferences/editor_page.cpp` around lines 71 - 76, Update the
helper text passed to PreferencesLayout::AddCheckBoxRow for
missing_items_warn_chkbox to reference the correct menu path; replace the
current hint "Display a summary of missing item definitions after loading a map.
Items are always tracked and available via File -> Missing Items Report..." with
wording that points to "File -> Reload" (or equivalent) so the user sees the
accurate location; ensure the change is applied where the call uses
g_settings.getBoolean(Config::SHOW_MISSING_ITEMS_WARNING) so only the
descriptive string is updated.
source/app/managers/version_manager.cpp (1)

147-148: ⚠️ Potential issue | 🟡 Minor

Keep the warning text aligned with the active mode and actual menu path.

In DatOnly mode this section is about DAT items not referenced by items.xml, but the header still says items.otb. The hint line also points users to a menu path this PR doesn't add.

Suggested wording fix
-			warnings.push_back("Go to File -> Missing Items Report... for detailed view.");
+			warnings.push_back("Go to File -> Reload -> Missing Items Report... for detailed view.");
...
-				warnings.push_back(std::format("--- tibia.dat items not in items.otb ({}) ---", last_missing_items.missing_in_otb.size()));
+				warnings.push_back(std::format("--- tibia.dat items not in {} ({}) ---",
+					last_load_has_otb ? "items.otb" : "items.xml",
+					last_missing_items.missing_in_otb.size()));

Also applies to: 165-166

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/app/managers/version_manager.cpp` around lines 147 - 148, The warning
messages added via warnings.push_back(...) are incorrect for DatOnly mode:
update the header and hint text to reference DAT items and the correct menu path
for this PR (not "items.otb" nor "File -> Missing Items Report..."). Locate the
two occurrences (the warnings.push_back calls around total_missing at the block
that checks for DatOnly mode and the similar block at lines 165-166) and change
the first message to mention "DAT item definitions" (or "DAT items not
referenced by items.xml") and the second message to point to the actual menu
path added by this change; ensure the messages remain formatted with
total_missing where used.
source/item_definitions/core/item_definition_resolver.cpp (1)

151-163: ⚠️ Potential issue | 🟠 Major

Don't treat a reused client id as proof that the XML server id exists in OTB.

This method only emits rows from fragments.otb. If an items.xml entry has no matching OTB server_id but happens to reuse another OTB item's client_id, this shortcut suppresses the xml_no_otb report and the mismatch disappears entirely.

Suggested change
-			// If XML entry has a client_id override that maps to an existing OTB entry, it's valid
-			if (xml.client_id.has_value()) {
-				bool foundInOtb = false;
-				for (const auto& [otbServerId, otb] : fragments.otb) {
-					if (otb.client_id == xml.client_id.value()) {
-						foundInOtb = true;
-						break;
-					}
-				}
-				if (foundInOtb) {
-					continue;
-				}
-			}
 			if (!fragments.otb.contains(server_id)) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/item_definitions/core/item_definition_resolver.cpp` around lines 151 -
163, The current logic treats any match on otb.client_id as proof that the XML
entry exists in OTB, which hides xml_no_otb cases; change the check so a match
only counts if the OTB entry’s server_id corresponds to the XML entry’s
server_id (i.e., when xml.client_id has_value(), search fragments.otb for an
entry where otb.client_id == xml.client_id.value() AND otb.server_id ==
xml.server_id, or simply require an otb entry with otb.server_id ==
xml.server_id regardless of client_id), and only then continue; update the code
that references xml.client_id, fragments.otb and otb.client_id/otb.server_id
accordingly so a reused client_id alone does not suppress the xml_no_otb report.
🧹 Nitpick comments (3)
source/ui/main_menubar.h (1)

164-165: Stabilize ActionID numeric values to avoid future command-id drift.

Line 164 inserts a new enum member in the middle, which renumbers trailing IDs. Since command IDs are derived from these values, prefer explicit fixed values (or append-only IDs) to reduce accidental breakage later.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/ui/main_menubar.h` around lines 164 - 165, The ActionID enum is being
altered by inserting MISSING_ITEMS_REPORT mid-list which shifts all subsequent
numeric IDs (including EXTENSIONS), so stabilize command IDs by giving explicit
fixed values: assign explicit integer values to MISSING_ITEMS_REPORT and any
affected trailing members (or alternatively make the enum append-only by adding
new IDs only at the end), update the ActionID enum definition (referencing the
ActionID enum and members MISSING_ITEMS_REPORT and EXTENSIONS) so existing
command IDs do not change and ensure any new IDs do not collide with existing
values.
source/item_definitions/core/item_definitions_loader.cpp (1)

36-39: Reset caller-provided report before collecting new results.

When missingReport is reused by a caller, old entries may bleed into a new load. Clear it at the start of assemble(...).

💡 Proposed fix
 	fragments = {};
 	rows.clear();
+	if (missingReport) {
+		*missingReport = {};
+	}
 	seedVersionInfoFromClient(input, fragments);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/item_definitions/core/item_definitions_loader.cpp` around lines 36 -
39, In assemble(...), reset the caller-provided report to avoid leaking previous
entries by clearing missingReport at the start of the function (e.g., call
missingReport.clear() alongside fragments = {} and rows.clear()) before you call
seedVersionInfoFromClient(input, fragments) or begin collecting results; this
ensures missingReport is empty when assemble begins.
source/ui/dialogs/missing_items_dialog.h (1)

17-19: Don't expose a borrowed report through a public constructor.

report is stored by reference, so MissingItemsDialog(parent, BuildReport()) becomes dangling as soon as construction finishes. Storing the report by value, or making the constructor private and forcing callers through Show(), would make this API much harder to misuse.

Also applies to: 29-30

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/ui/dialogs/missing_items_dialog.h` around lines 17 - 19, The public
constructor MissingItemsDialog(wxWindow* parent, const MissingItemReport&
report, bool hasOtb = true) is unsafe because the dialog stores the report by
reference; change the API so the dialog owns the report (take MissingItemReport
by value and store it by value) or make the constructor(s) private and force
creation via the static Show(wxWindow*, const MissingItemReport&, bool) method
which should copy/move the report into the dialog; apply the same change to the
other constructor overloads mentioned (lines with the same MissingItemsDialog
signature) so no public constructor accepts a borrowed reference to
MissingItemReport.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@data/menubar.xml`:
- Around line 51-54: The <item name="Missing Items Report..."
action="MISSING_ITEMS_REPORT"/> element is currently outside the <menu
name="Reload"> block; move that <item> so it is nested inside the Reload menu
(i.e., place the Missing Items Report item before the closing tag of the <menu
name="Reload"> element) so the navigation becomes File -> Reload -> Missing
Items Report; ensure attributes (name, action, help) are preserved.

In `@source/ui/dialogs/missing_items_dialog.cpp`:
- Around line 230-232: The code currently calls wxTheClipboard->SetData(new
wxTextDataObject(GenerateReportText())) then always shows wxMessageBox("Report
copied to clipboard."); change this to check the boolean return of
wxTheClipboard->SetData and only show the success message when it returns true;
if SetData returns false, show an error/warning message instead and ensure you
still call wxTheClipboard->Close(); reference the SetData call, the
wxTextDataObject(GenerateReportText()) construction, and the wxMessageBox
invocation when implementing the conditional flow.

In `@source/ui/menubar/file_menu_handler.cpp`:
- Around line 98-105: The code currently binds missing to
g_version.getLastMissingItems() by const reference which may observe invalidated
state if g_version changes while the dialog is open; create a local value-copy
(e.g. auto missingCopy = g_version.getLastMissingItems()) and also snapshot any
other g_version-derived values used by the dialog (e.g. auto hasOtb =
g_version.lastLoadHasOtb()), then call MissingItemsDialog::Show(g_gui.root,
missingCopy, hasOtb) so the dialog operates on a stable snapshot rather than a
reference into mutable global state.

---

Duplicate comments:
In `@source/app/managers/version_manager.cpp`:
- Around line 147-148: The warning messages added via warnings.push_back(...)
are incorrect for DatOnly mode: update the header and hint text to reference DAT
items and the correct menu path for this PR (not "items.otb" nor "File ->
Missing Items Report..."). Locate the two occurrences (the warnings.push_back
calls around total_missing at the block that checks for DatOnly mode and the
similar block at lines 165-166) and change the first message to mention "DAT
item definitions" (or "DAT items not referenced by items.xml") and the second
message to point to the actual menu path added by this change; ensure the
messages remain formatted with total_missing where used.

In `@source/app/preferences/editor_page.cpp`:
- Around line 71-76: Update the helper text passed to
PreferencesLayout::AddCheckBoxRow for missing_items_warn_chkbox to reference the
correct menu path; replace the current hint "Display a summary of missing item
definitions after loading a map. Items are always tracked and available via File
-> Missing Items Report..." with wording that points to "File -> Reload" (or
equivalent) so the user sees the accurate location; ensure the change is applied
where the call uses g_settings.getBoolean(Config::SHOW_MISSING_ITEMS_WARNING) so
only the descriptive string is updated.

In `@source/app/settings.h`:
- Line 88: The new enum entry SHOW_MISSING_ITEMS_WARNING was inserted in the
middle of the settings enum which shifts persisted indices; move the
SHOW_MISSING_ITEMS_WARNING identifier out of the main body and place it in the
stable tail section just before the LAST enum value (i.e., ensure
SHOW_MISSING_ITEMS_WARNING is declared immediately before LAST) so existing enum
ordering and persisted mapping remain unchanged.

In `@source/item_definitions/core/item_definition_resolver.cpp`:
- Around line 151-163: The current logic treats any match on otb.client_id as
proof that the XML entry exists in OTB, which hides xml_no_otb cases; change the
check so a match only counts if the OTB entry’s server_id corresponds to the XML
entry’s server_id (i.e., when xml.client_id has_value(), search fragments.otb
for an entry where otb.client_id == xml.client_id.value() AND otb.server_id ==
xml.server_id, or simply require an otb entry with otb.server_id ==
xml.server_id regardless of client_id), and only then continue; update the code
that references xml.client_id, fragments.otb and otb.client_id/otb.server_id
accordingly so a reused client_id alone does not suppress the xml_no_otb report.

---

Nitpick comments:
In `@source/item_definitions/core/item_definitions_loader.cpp`:
- Around line 36-39: In assemble(...), reset the caller-provided report to avoid
leaking previous entries by clearing missingReport at the start of the function
(e.g., call missingReport.clear() alongside fragments = {} and rows.clear())
before you call seedVersionInfoFromClient(input, fragments) or begin collecting
results; this ensures missingReport is empty when assemble begins.

In `@source/ui/dialogs/missing_items_dialog.h`:
- Around line 17-19: The public constructor MissingItemsDialog(wxWindow* parent,
const MissingItemReport& report, bool hasOtb = true) is unsafe because the
dialog stores the report by reference; change the API so the dialog owns the
report (take MissingItemReport by value and store it by value) or make the
constructor(s) private and force creation via the static Show(wxWindow*, const
MissingItemReport&, bool) method which should copy/move the report into the
dialog; apply the same change to the other constructor overloads mentioned
(lines with the same MissingItemsDialog signature) so no public constructor
accepts a borrowed reference to MissingItemReport.

In `@source/ui/main_menubar.h`:
- Around line 164-165: The ActionID enum is being altered by inserting
MISSING_ITEMS_REPORT mid-list which shifts all subsequent numeric IDs (including
EXTENSIONS), so stabilize command IDs by giving explicit fixed values: assign
explicit integer values to MISSING_ITEMS_REPORT and any affected trailing
members (or alternatively make the enum append-only by adding new IDs only at
the end), update the ActionID enum definition (referencing the ActionID enum and
members MISSING_ITEMS_REPORT and EXTENSIONS) so existing command IDs do not
change and ensure any new IDs do not collide with existing values.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: df52f8e1-3054-4b3b-bd2c-63d94bd0f2d9

📥 Commits

Reviewing files that changed from the base of the PR and between 3602336 and 4f2e264.

📒 Files selected for processing (22)
  • data/menubar.xml
  • source/CMakeLists.txt
  • source/app/managers/version_manager.cpp
  • source/app/managers/version_manager.h
  • source/app/preferences/editor_page.cpp
  • source/app/preferences/editor_page.h
  • source/app/settings.cpp
  • source/app/settings.h
  • source/item_definitions/core/asset_bundle.h
  • source/item_definitions/core/asset_bundle_loader.cpp
  • source/item_definitions/core/item_definition_resolver.cpp
  • source/item_definitions/core/item_definition_resolver.h
  • source/item_definitions/core/item_definitions_loader.cpp
  • source/item_definitions/core/item_definitions_loader.h
  • source/item_definitions/core/missing_item_report.h
  • source/ui/dialogs/missing_items_dialog.cpp
  • source/ui/dialogs/missing_items_dialog.h
  • source/ui/main_menubar.cpp
  • source/ui/main_menubar.h
  • source/ui/menubar/file_menu_handler.cpp
  • source/ui/menubar/file_menu_handler.h
  • source/ui/menubar/menubar_action_manager.cpp

Comment thread data/menubar.xml
Comment on lines 51 to +54
<menu name="Reload">
<item name="Reload" hotkey="F5" action="RELOAD_DATA" help="Reloads all data files."/>
<item name="Reload Data Files" hotkey="F5" action="RELOAD_DATA" help="Reloads all data files."/>
</menu>
<item name="Missing Items Report..." action="MISSING_ITEMS_REPORT" help="View missing item definitions between data files."/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing Items Report... is placed outside the Reload submenu.

If the intended navigation is File -> Reload -> Missing Items Report..., Line 54 should be nested inside the Reload menu block.

💡 Proposed fix
 	<menu name="Reload">
 		<item name="Reload Data Files" hotkey="F5" action="RELOAD_DATA" help="Reloads all data files."/>
+		<item name="Missing Items Report..." action="MISSING_ITEMS_REPORT" help="View missing item definitions between data files."/>
 	</menu>
-	<item name="Missing Items Report..." action="MISSING_ITEMS_REPORT" help="View missing item definitions between data files."/>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<menu name="Reload">
<item name="Reload" hotkey="F5" action="RELOAD_DATA" help="Reloads all data files."/>
<item name="Reload Data Files" hotkey="F5" action="RELOAD_DATA" help="Reloads all data files."/>
</menu>
<item name="Missing Items Report..." action="MISSING_ITEMS_REPORT" help="View missing item definitions between data files."/>
<menu name="Reload">
<item name="Reload Data Files" hotkey="F5" action="RELOAD_DATA" help="Reloads all data files."/>
<item name="Missing Items Report..." action="MISSING_ITEMS_REPORT" help="View missing item definitions between data files."/>
</menu>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@data/menubar.xml` around lines 51 - 54, The <item name="Missing Items
Report..." action="MISSING_ITEMS_REPORT"/> element is currently outside the
<menu name="Reload"> block; move that <item> so it is nested inside the Reload
menu (i.e., place the Missing Items Report item before the closing tag of the
<menu name="Reload"> element) so the navigation becomes File -> Reload ->
Missing Items Report; ensure attributes (name, action, help) are preserved.

Comment on lines +230 to +232
wxTheClipboard->SetData(newd wxTextDataObject(GenerateReportText()));
wxTheClipboard->Close();
wxMessageBox("Report copied to clipboard.", "Clipboard", wxOK | wxICON_INFORMATION, this);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

❓ Verification inconclusive

In wxWidgets, what is the documented failure behavior of wxClipboard::SetData(wxDataObject*), and should callers check its boolean return value before showing a success message?


wxClipboard::SetData(wxDataObject*) is documented to replace (clear) the previous clipboard contents, and—if the call succeeds—the clipboard takes ownership of the wxDataObject* (so the caller must not delete it). (git.amebis.si)

Its failure behavior is the normal wxWidgets one for bool-returning APIs: it returns a “special” value (false) to indicate failure (wxWidgets does not use exceptions for this). (wxwidgets.org) Additionally, clipboard operations can fail depending on platform/selection mode (e.g., using PRIMARY selection on platforms that don’t support it makes “all clipboard operations” fail). (git.amebis.si)

So yes: callers should check the returned bool (and also check Open() first) before showing a “copied to clipboard” success message, otherwise you may report success when nothing was actually placed on the clipboard. (git.amebis.si)

Citations:


Script executed:

# Locate and examine the file
fd "missing_items_dialog.cpp" --type f

Repository: karolak6612/remeres-map-editor-redux


Repository: karolak6612/remeres-map-editor-redux
Exit code: 0

stdout:

source/ui/dialogs/missing_items_dialog.cpp

Script executed:

# Read the relevant section of the file to confirm the code snippet and context
sed -n '225,240p' source/ui/dialogs/missing_items_dialog.cpp

Repository: karolak6612/remeres-map-editor-redux


Repository: karolak6612/remeres-map-editor-redux
Exit code: 0

stdout:

		wxMessageBox("Failed to open clipboard. Another application may be using it.",
		             "Clipboard Error", wxOK | wxICON_ERROR, this);
		return;
	}

	wxTheClipboard->SetData(newd wxTextDataObject(GenerateReportText()));
	wxTheClipboard->Close();
	wxMessageBox("Report copied to clipboard.", "Clipboard", wxOK | wxICON_INFORMATION, this);
}

void MissingItemsDialog::OnSaveReport(wxCommandEvent& WXUNUSED(evt)) {
	wxFileDialog saveDlg(this, "Save Report As", "", "missing_items_report.txt",
		"Text files (*.txt)|*.txt", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);

	if (saveDlg.ShowModal() == wxID_OK) {
		wxString path = saveDlg.GetPath();

Check SetData() return value before showing success message.

Open() only acquires the clipboard. SetData() can still fail even after a successful Open() (e.g., on platforms that don't support the requested selection mode). Show the success message only if SetData() returns true.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/ui/dialogs/missing_items_dialog.cpp` around lines 230 - 232, The code
currently calls wxTheClipboard->SetData(new
wxTextDataObject(GenerateReportText())) then always shows wxMessageBox("Report
copied to clipboard."); change this to check the boolean return of
wxTheClipboard->SetData and only show the success message when it returns true;
if SetData returns false, show an error/warning message instead and ensure you
still call wxTheClipboard->Close(); reference the SetData call, the
wxTextDataObject(GenerateReportText()) construction, and the wxMessageBox
invocation when implementing the conditional flow.

Comment on lines +98 to +105
const auto& missing = g_version.getLastMissingItems();
if (missing.missing_in_dat.empty() && missing.missing_in_otb.empty() && missing.xml_no_otb.empty() && missing.otb_no_xml.empty()) {
wxMessageBox("No missing item definitions were detected for the current client version.",
"Missing Items Report", wxOK | wxICON_INFORMATION, g_gui.root);
return;
}
MissingItemsDialog::Show(g_gui.root, missing, g_version.lastLoadHasOtb());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid passing mutable global report by reference into the dialog.

Line 98 binds to g_version state by reference. If missing-items state is replaced while the modal is open, the dialog can observe invalidated data. Snapshot the report before calling Show(...).

💡 Proposed fix
-	const auto& missing = g_version.getLastMissingItems();
+	const auto missing = g_version.getLastMissingItems();
 	if (missing.missing_in_dat.empty() && missing.missing_in_otb.empty() && missing.xml_no_otb.empty() && missing.otb_no_xml.empty()) {
 		wxMessageBox("No missing item definitions were detected for the current client version.",
 		             "Missing Items Report", wxOK | wxICON_INFORMATION, g_gui.root);
 		return;
 	}
 	MissingItemsDialog::Show(g_gui.root, missing, g_version.lastLoadHasOtb());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@source/ui/menubar/file_menu_handler.cpp` around lines 98 - 105, The code
currently binds missing to g_version.getLastMissingItems() by const reference
which may observe invalidated state if g_version changes while the dialog is
open; create a local value-copy (e.g. auto missingCopy =
g_version.getLastMissingItems()) and also snapshot any other g_version-derived
values used by the dialog (e.g. auto hasOtb = g_version.lastLoadHasOtb()), then
call MissingItemsDialog::Show(g_gui.root, missingCopy, hasOtb) so the dialog
operates on a stable snapshot rather than a reference into mutable global state.

Replaced index-based loops with std::views::take for slicing missing item entries across version_manager.cpp and item_definition_resolver.cpp

source/app/managers/version_manager.cpp — switched to ranges::views::take for truncating displayed warnings; added dynamic label for missing_in_otb based on load mode
source/app/settings.cpp — moved SHOW_MISSING_ITEMS_WARNING to end of config section to preserve enum stability
source/app/settings.h — repositioned SHOW_MISSING_ITEMS_WARNING enum value to end of list
source/item_definitions/core/item_definition_resolver.cpp — replaced std::move with aggregate initialization for MissingItemEntry; used std::ranges::any_of for OTB lookup; added early continue to skip duplicate server_id checks; removed unused spdlog import
source/ui/dialogs/missing_items_dialog.cpp — renamed button from "Ignore" to "Close"; removed unused wx/fdrepdlg.h import
@github-actions

github-actions Bot commented Apr 6, 2026

Copy link
Copy Markdown

🤖 Hi @karolak6612, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

## 📋 Review Summary

This PR introduces a valuable feature for identifying and reporting missing item definitions across different data sources (DAT, OTB, and XML). The implementation is well-integrated into the existing UI and loading pipeline.

🔍 General Feedback

  • Feature Completeness: The inclusion of both an automatic warning system on load and a detailed manual report dialog is excellent for user experience.
  • Performance: Most logic is sound, but some $O(N \times M)$ lookups during item resolution should be optimized to ensure responsiveness with large data sets.
  • Code Quality: The code follows established patterns in the repository. Refactoring the warning generation logic would improve maintainability.

Comment on lines +137 to +146
if (!dat_ids_referenced_by_otb.contains(client_id)) {
missingReport->missing_in_otb.push_back({
.server_id = 0,
.client_id = client_id,
.name = "",
.description = ""
});
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 This nested search has $O(N \times M)$ complexity. Since fragments.otb can be large, it is more efficient to pre-calculate a set of client IDs from OTB and use it for lookups.

Suggested change
if (!dat_ids_referenced_by_otb.contains(client_id)) {
missingReport->missing_in_otb.push_back({
.server_id = 0,
.client_id = client_id,
.name = "",
.description = ""
});
}
}
// Pre-index OTB client IDs for faster lookup
std::unordered_set<ClientItemId> otb_client_ids;
for (const auto& [id, otb] : fragments.otb) {
if (otb.client_id != 0) {
otb_client_ids.insert(otb.client_id);
}
}
// Collect XML entries that reference non-existent OTB server IDs
for (const auto& [server_id, xml] : fragments.xml) {
// Skip server-side fluid types and special items (IDs < 100)
if (server_id < 100) {
continue;
}
// If XML server_id already exists in OTB, it's valid
if (fragments.otb.contains(server_id)) {
continue;
}
// If XML entry has a client_id override that maps to an existing OTB entry, it's valid
if (xml.client_id.has_value() && otb_client_ids.contains(xml.client_id.value())) {
continue;
}
missingReport->xml_no_otb.push_back({
.server_id = server_id,
.client_id = xml.client_id.value_or(0),
.name = xml.name,
.description = xml.description
});
}

auto* copyButton = newd wxButton(this, wxID_ANY, "Copy to Clipboard");
copyButton->Bind(wxEVT_BUTTON, &MissingItemsDialog::OnCopyToClipboard, this);
buttonSizer->Add(copyButton, 0, wxALL, FromDIP(5));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 GenerateReportText is called when copying to clipboard or saving to a file. For very large reports, using a std::string with reserve() or a std::ostringstream would be more efficient than repeated += operations which may cause many reallocations.

Suggested change
wxString MissingItemsDialog::GenerateReportText() const {
std::basic_ostringstream<wxChar> oss;
oss << "=== Missing Item Definitions Report ===\n\n";
if (hasOtb) {
oss << wxString::FromUTF8(std::format("--- Items in items.otb missing from tibia.dat ({}) ---\n", report.missing_in_dat.size()));
} else {
oss << wxString::FromUTF8(std::format("--- Items in items.xml missing from tibia.dat ({}) ---\n", report.missing_in_dat.size()));
}
for (const auto& entry : report.missing_in_dat) {
oss << wxString::FromUTF8(std::format("Server ID: {}, Client ID: {}, Name: {}, Description: {}\\n",
entry.server_id, entry.client_id, entry.name, entry.description));
}
oss << "\n";
// ... apply same pattern to other sections ...
return oss.str();
}

last_missing_items.missing_in_otb.size() +
last_missing_items.xml_no_otb.size() +
last_missing_items.otb_no_xml.size();
if (total_missing > 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 The logic for generating these warnings is quite verbose and repeated across different item categories. Consider extracting this into a helper function to improve maintainability and reduce code duplication.

Suggested change
if (total_missing > 0) {
auto add_missing_warnings = [&](const std::string& title, const std::vector<MissingItemEntry>& entries) {
if (entries.empty()) return;
warnings.push_back(std::format("--- {} ({}) ---", title, entries.size()));
for (const auto& entry : entries | std::views::take(MAX_SHOW)) {
if (entry.server_id > 0) {
warnings.push_back(std::format(" Server ID: {}, Client ID: {}, Name: '{}'",
entry.server_id, entry.client_id, entry.name.empty() ? "unknown" : entry.name));
} else {
warnings.push_back(std::format(" Client ID: {}", entry.client_id));
}
}
if (entries.size() > MAX_SHOW) {
warnings.push_back(std::format(" ...and {} more", entries.size() - MAX_SHOW));
}
warnings.push_back("");
};
add_missing_warnings("Items missing from tibia.dat", last_missing_items.missing_in_dat);
add_missing_warnings(missing_in_otb_label, last_missing_items.missing_in_otb);
add_missing_warnings("items.xml entries missing from items.otb", last_missing_items.xml_no_otb);
add_missing_warnings("items.otb entries missing from items.xml", last_missing_items.otb_no_xml);

@karolak6612
karolak6612 merged commit fd5395a into master Apr 6, 2026
8 checks passed
@karolak6612
karolak6612 deleted the Missing-Items-Handler branch April 6, 2026 15:51
@karolak6612
karolak6612 restored the Missing-Items-Handler branch April 11, 2026 10:53
@karolak6612
karolak6612 deleted the Missing-Items-Handler branch April 12, 2026 21:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant