Skip to content

unify(worldbuilder): Align script import and export - #3409

Open
OmarAglan wants to merge 2 commits into
TheSuperHackers:mainfrom
OmarAglan:unify/worldbuilder-script-import-export
Open

OmarAglan wants to merge 2 commits into
TheSuperHackers:mainfrom
OmarAglan:unify/worldbuilder-script-import-export

Conversation

@OmarAglan

@OmarAglan OmarAglan commented Oct 3, 2026 •

Copy link
Copy Markdown

Depends on #3408. Merge #3408 first; the second commit contains the alignment changes.

Follow up for #3368.
Relates to #555.

Aligns the import/export portions of ScriptDialog and ExportScriptsOptions with Zero Hour.

Generals inherits side-dictionary import, team-owner selection handling and the export-side option. Both editors use the aligned export dialog, sized to contain its controls.

Compatibility:

  • Retail-compatible Generals continues writing ScriptsPlayers v1, with side export disabled.
  • Generals without RETAIL_COMPATIBLE_DATA and Zero Hour write v2.
  • Both readers accept v1 and v2.
  • Script-bundle PolygonTriggers remain v3.

The import fixes originate in #3408. This branch builds on that commit and aligns Generals with the corrected Zero Hour importer. Script-warning changes and Core moves remain outside this PR.

Validation:

  • Both VC6 Release WorldBuilder targets build.

  • All 16 targeted VC6 compilations pass across both games, debug/release and DATA=0/1.

  • 1,904 extracted writer/reader fixture cases pass.

  • Focused team-owner and export-dialog checks pass.

  • 249 additional import-validation, capacity, tree-index and rollback cases pass per game.

  • test how the world builder looks (WIP)

AI assistance was used for implementation and local verification.

@coderabbitai

coderabbitai Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Walkthrough

WorldBuilder adds an option to export side data. Script export writes versioned player data, with Generals retail-compatible builds retaining version 1. Import validates and reads side dictionaries, updates side lists and script trees, and preserves a valid selected team owner.

Changes

Side-data export and import

Layer / File(s) Summary
Side-data export option
Generals/Code/Tools/WorldBuilder/include/ExportScriptsOptions.h, Generals/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp, Generals/Code/Tools/WorldBuilder/res/WorldBuilder.rc, Generals/Code/Tools/WorldBuilder/res/resource.h, GeneralsMD/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp, GeneralsMD/Code/Tools/WorldBuilder/res/WorldBuilder.rc
The dialog adds an “Include sides” checkbox and stores its state. Generals retail-compatible builds clear and disable the option.
Player-data export
Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp, GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
Exports use version 2 and write the side-export flag outside Generals retail-compatible builds. Those builds use version 1 and disable side export.
Player-data import
Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp, GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
Import parsing validates player counts and side dictionaries, avoids duplicate sides, and rejects capacity overflow or missing lookups. Successful imports update dialog entries and script trees. Team imports preserve a valid selected owner or use the neutral player.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant ExportOptions
  participant ScriptDialog
  participant PlayerDataChunk
  ExportOptions->>ScriptDialog: Provide side-export setting
  ScriptDialog->>PlayerDataChunk: Write versioned player data and optional side dictionaries
Loading

Merge Risk: 🔵 Low · up to f16cf

Imports may retain allocated data when a malformed file fails to parse. The previously reported missing tree item appears addressed; confirm error-path cleanup before merging.

Security Architecture Review

Security architecture risk: 🔵 Low · up to f16cf

Compatibility gates, player-identity checks, capacity limits, and parse-failure restoration constrain the change. No introduced security vulnerability was established, but recovery after later import failures and the full downstream effects of imported side fields remain incompletely assessed.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The directly inspected exposure is the importing editor and its active map state. A supplied bundle can introduce new side dictionaries and script content following user-selected import. The side model connects this data to game-player state, but the complete downstream interpretation of dictionary fields was not established.

Trust Boundaries and Controls

  • observed — The relevant trust transition is from script-bundle contents into editor-owned side and script state. Dictionary identity must agree with the serialized player name; existing sides are preserved and new sides are capacity-limited. These controls establish import consistency, not authenticity of the bundle or authorization of every dictionary field.

Resilience and Maintainability Implications

  • observed — The new failure guard restores side and team state before propagating a parse failure. Side copying duplicates script lists, while the inspected editor selection and player-tree paths use indices rather than retaining script-object pointers. These checks support the restoration mechanism but do not establish cleanup of every parser-owned object or recovery after later application failures.

Hardening Proposals

  • proposed — Consider staging the complete import and publishing it as one recoverable transaction, with coordinated cleanup and undo for sides, teams, scripts, objects, triggers, and waypoint state. This would strengthen failure containment beyond the current parse-scoped restoration; it is a hardening proposal, not an established PR vulnerability.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: aligning WorldBuilder script import and export.
Description check ✅ Passed The description explains the import/export alignment, compatibility behavior, scope, and validation results. It is directly related to the changeset.
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@greptile-apps

greptile-apps Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 4/5

[Medium risk] Adds sides export option to script import and export.

The PR is not yet safe to merge because rejecting a script bundle can leave changes in the open map.

Findings

  1. P1 Failed imports leave map changes ▶
Summary

The PR aligns script-bundle import and export across Generals and Zero Hour, adds optional side dictionaries with a retail-compatible Generals export path, and resizes the export dialogs.

  • The latest import changes validate player data and defer tree updates until parsing succeeds.
  • A rejected bundle can still leave waypoint links and pending script lists behind.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  Bundle[Script bundle] --> Parse[Parse chunks]
  Parse --> Waypoints[Add waypoint links to document]
  Parse --> Scripts[Hold parsed script lists]
  Parse --> Players{Player data valid?}
  Players -->|Yes| Apply[Apply sides and scripts]
  Players -->|No| Restore[Restore sides only]
  Restore --> Residue[Document links and pending lists remain]
Loading

Reviews (2) · Last reviewed commit: "unify(worldbuilder): Align script import..."

Comment thread Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp Outdated
Comment thread Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp Outdated

@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


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 01404c5e-443a-4e11-bd76-3f3b6bf198af
📥 Commits

Reviewing files that changed from the base of the PR and between 5ae042c and fc90243.

📒 Files selected for processing (8)
  • Generals/Code/Tools/WorldBuilder/include/ExportScriptsOptions.h
  • Generals/Code/Tools/WorldBuilder/res/WorldBuilder.rc
  • Generals/Code/Tools/WorldBuilder/res/resource.h
  • Generals/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp
  • Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/res/WorldBuilder.rc
  • GeneralsMD/Code/Tools/WorldBuilder/src/ExportScriptsOptions.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

// A script list must be created.
sides->setScriptList(pList);
// Update the dialog.
pThis->addPlayer(i);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

sed -n '1655,1710p' Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
rg -n 'addPlayer' Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 2073


🏁 Script executed:

printf '%s\\n' '--- addPlayer ---'
sed -n '385,435p' Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
printf '%s\\n' '--- side collection definitions ---'
rg -n 'SidesList::addSide|addSide\\s*\\(|class SidesList|SidesList::getNumSides' Generals/Code

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 1946


🏁 Script executed:

printf '%s\n' '--- addPlayer remainder ---'
sed -n '415,455p' Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
printf '%s\n' '--- side collection symbol locations ---'
rg -n 'addSide|class SidesList' Generals/Code | head -80

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 3896


🏁 Script executed:

printf '%s\n' '--- SidesList and addSide definitions/usages ---'
rg -n 'class SidesList|struct SidesList|SidesList::|addSide[[:space:]]*\(' --glob '*.{h,cpp}' .
printf '%s\n' '--- ScriptDialog import/tree flow ---'
rg -n -C 5 'ParsePlayersDataChunk|DeleteAllItems|addPlayer[[:space:]]*\(|OnLoad|LoadScript' Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 14035


🏁 Script executed:

printf '%s\n' '--- SidesList declaration and index lookup ---'
sed -n '132,226p' Core/GameEngine/Include/GameLogic/SidesList.h
sed -n '390,407p' Core/GameEngine/Source/GameLogic/Map/SidesList.cpp
printf '%s\n' '--- SidesList append implementation ---'
sed -n '598,614p' Core/GameEngine/Source/GameLogic/Map/SidesList.cpp
printf '%s\n' '--- OnLoad post-parse flow ---'
sed -n '1359,1495p' Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
printf '%s\n' '--- reviewed diff hunk ---'
git diff --unified=4 5ae042cafb4f08c5ec264acbdfc2db964a41b295 fc90243e3240e59f3849d82e10e1073f2e8fae0c -- Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp | rg -n -C 12 'addPlayer\(i\)|addSide\(&sideDict\)'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 8860


🏁 Script executed:

rg -n -C 20 'ScriptDialog::reloadPlayer|reloadPlayer[[:space:]]*\(' Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp Generals/Code/Tools/WorldBuilder/include/ScriptDialog.h

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 26581


🏁 Script executed:

sed -n '629,700p' Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 2053


Pass the appended side’s index to addPlayer.

If the destination already has sides, or earlier imported records were skipped, i can differ from the new side’s index. For example, with two existing sides, a new first imported record appends at index 2, but addPlayer(0) inserts another tree item for side 0. The later reloadPlayer(2) cannot find the new side’s item and returns.

Suggested fix
 				pThis->m_sides.addSide(&sideDict);
 				ScriptList* pList = newInstance(ScriptList);
-				SidesInfo* sides = pThis->m_sides.findSideInfo(pThis->m_readPlayerNames[i]);
+				Int sideIndex;
+				SidesInfo* sides = pThis->m_sides.findSideInfo(pThis->m_readPlayerNames[i], &sideIndex);
 				// A script list must be created.
 				sides->setScriptList(pList);
 				// Update the dialog.
-				pThis->addPlayer(i);
+				pThis->addPlayer(sideIndex);

@OmarAglan
OmarAglan force-pushed the unify/worldbuilder-script-import-export branch from fc90243 to f16cf9a Compare October 4, 2026 08:21
throw(ERROR_CORRUPT_FILE_FORMAT);
}
} catch(...) {
m_sides = sidesBeforeImport;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High src/ScriptDialog.cpp:1404

A parse failure leaves imported waypoint links in the active document, so a file with valid waypoints followed by invalid player/script data produces a partial import that can be saved. ParseWaypointDataChunk calls pDoc->addWaypointLink(...) during file.parse(this), but this catch block restores only m_sides; roll back those document-side changes as well, or make the import transactional.

🤖 Copy this AI Prompt to have your agent fix this:
In file @Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp around line 1404:

A parse failure leaves imported waypoint links in the active document, so a file with valid waypoints followed by invalid player/script data produces a partial import that can be saved. `ParseWaypointDataChunk` calls `pDoc->addWaypointLink(...)` during `file.parse(this)`, but this catch block restores only `m_sides`; roll back those document-side changes as well, or make the import transactional.

@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.

🧹 Nitpick comments (1)
Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp (1)

1396-1405: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

The restore covers sides only. Imported objects and triggers still leak when parsing fails.

A parse can fail after ParseObjectsDataChunk or ParsePolygonTriggersDataChunk has allocated m_firstReadObject or m_firstTrigger. The catch block restores m_sides and rethrows. It does not free those lists. The objects stay unowned. The next import sets the pointers to nullptr again. The impact is a memory leak in an editor error path.

The comment also says "sides and teams." SidesList holds the team data, so that part is correct.

Free the partially read lists in the catch block before you rethrow:

} catch(...) {
	m_sides = sidesBeforeImport;
	if (m_firstReadObject) { deleteInstance(m_firstReadObject); m_firstReadObject = nullptr; }
	// free the m_firstTrigger chain in the same way
	throw;
}

ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 08fff1ad-a655-4c27-8513-5ecaf7dab8b9
📥 Commits

Reviewing files that changed from the base of the PR and between fc90243 and f16cf9a.

📒 Files selected for processing (5)
  • Generals/Code/Tools/WorldBuilder/include/ExportScriptsOptions.h
  • Generals/Code/Tools/WorldBuilder/res/WorldBuilder.rc
  • Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/res/WorldBuilder.rc
  • GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.

Comment on lines +1403 to +1405
} catch(...) {
m_sides = sidesBeforeImport;
throw;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Failed imports leave map changes

If a bundle contains waypoint links and then fails a player-count or dictionary-name check, parsing has already added those links to the document. This catch restores the sides but leaves links to waypoints that were never imported. Parsed script lists can also remain pending for the next import. The same rollback behavior occurs in the Zero Hour editor.

Knowledge Base Used: Game development tools

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