Skip to content

feat(recorder): Play a replay file from the command line - #3227

Merged
xezon merged 2 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/feature/loadreplay-cli
Sep 15, 2026
Merged

xezon merged 2 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/feature/loadreplay-cli

Conversation

@bobtista

@bobtista bobtista commented Aug 27, 2026

Copy link
Copy Markdown

-replay already plays visually when used without -headless, but it enters the replay-simulation workflow before the normal shell is shown and terminates the process when that workflow finishes. This is appropriate for batch simulation and synchronization checking, but not for an operating-system file handler whose playback should return to the menus.

-loadreplay <file> instead plays one replay through the normal client lifecycle. loadQueuedReplay runs at the point -loadsave already uses, once the client has initialized the shell, so the menus the playback returns to are on the stack.

Absolute paths are opened in place while relative names still resolve from the Replay directory. RecorderClass::getReplayPathForRead does that, mirroring GameState::getSaveGamePathForRead from #3226. Because this resolution is shared, existing -replay also gains support for absolute paths and no longer requires the .rep extension.

A replay that cannot be read, or whose map is unavailable, is rejected up front with the same message boxes the Replay menu shows, and the game stays on the main menu rather than failing deep in map loading.

Verified with failing files as controls so a pass is distinguishable from "the game started anyway":

case result
control: bogus absolute path "REPLAY CANNOT BE LOADED" on the main menu
control: file that is not a replay "REPLAY CANNOT BE LOADED" on the main menu
control: replay whose map is not installed "MAP NOT FOUND" on the main menu
Replay from an absolute path containing spaces (quoted) loads and plays
Relative Replay filename loads from the managed directory
Replay from a UNC path (\\localhost\C$\...) loads
Restarting a Replay loaded from an absolute path restarts and replays from the beginning
Normal Replay synchronization reporting reports and pauses (InGame:D9C721A5 Replay:D8A198C0 Frame:110)

Todo:

  • Both games build (z_generals and g_generals)
  • Replay paths outside the user data directory
  • Paths containing spaces
  • Windows drive paths and UNC paths
  • Relative Replay filenames still resolve from the managed directory
  • Restarting an externally loaded Replay
  • Normal Replay synchronization reporting is unchanged
  • Replicate to Generals

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add visual replay playback and absolute file loading to CLI

✨ Enhancement 🕐 40+ Minutes

Grey Divider

AI Description

• Adds -loadreplay to launch visual replay playback after client shell initialization.
• Supports absolute replay and save paths while preserving managed-directory resolution for relative
 names.
• Rejects unreadable, malformed, or map-missing replays before entering gameplay.
Diagram

graph TD
  CLI["CLI parser"] --> Queue["Startup request"] --> Client["Client update"] --> Loader["Replay loader"] --> Resolver["Path resolver"] --> Check{"Replay valid?"}
  Check -->|Yes| Playback["Visual playback"]
  Check -->|No| Quit["Quit game"]
Loading
High-Level Assessment

The queued startup approach is appropriate because it reuses the established -loadsave lifecycle point, ensuring the client shell exists before playback and remains available afterward. Starting playback directly during command-line parsing was considered but would run before required client and filesystem state is initialized; centralizing absolute-versus-relative path resolution also preserves existing menu behavior.

Files changed (15) +214 / -18

Enhancement (15) +214 / -18
FileSystem.hExpose platform-aware absolute path detection +1/-0

Expose platform-aware absolute path detection

• Declares a shared helper for distinguishing explicit absolute paths from names resolved within managed directories.

Core/GameEngine/Include/Common/FileSystem.h

CommandLine.cppAdd and validate startup file-loading options +34/-2

Add and validate startup file-loading options

• Adds the '-loadreplay' parser and registration, validates replay and save extensions, and queues startup playback while suppressing intro and shell-map startup. Missing arguments now consume only the option itself.

Core/GameEngine/Source/Common/CommandLine.cpp

FileSystem.cppImplement cross-platform absolute path recognition +25/-0

Implement cross-platform absolute path recognition

• Recognizes Windows drive-rooted, current-drive-rooted, and UNC-style paths, plus POSIX root paths.

Core/GameEngine/Source/Common/System/FileSystem.cpp

GameState.hDeclare save read-path resolution helper +1/-0

Declare save read-path resolution helper

• Adds the Generals API for resolving absolute save paths or managed-directory filenames.

Generals/Code/GameEngine/Include/Common/GameState.h

GlobalData.hStore queued replay startup requests +1/-0

Store queued replay startup requests

• Adds global startup state for the replay requested through '-loadreplay'.

Generals/Code/GameEngine/Include/Common/GlobalData.h

Recorder.hExpose queued replay loading +1/-0

Expose queued replay loading

• Declares the recorder entry point that validates and starts a command-line replay request.

Generals/Code/GameEngine/Include/Common/Recorder.h

Recorder.cppResolve and preflight queued replays +49/-2

Resolve and preflight queued replays

• Reads absolute replay paths in place while retaining Replay-directory lookup for relative names. Validates replay headers, game options, and map availability before playback, quitting cleanly on failure.

Generals/Code/GameEngine/Source/Common/Recorder.cpp

GameState.cppSupport absolute save paths in Generals +20/-6

Support absolute save paths in Generals

• Centralizes save read-path resolution so command-line absolute paths open in place and relative menu names remain under the Save directory. Applies the helper to metadata, existence, and full-load paths.

Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp

GameClient.cppStart queued replays after client initialization +5/-0

Start queued replays after client initialization

• Invokes replay loading from the established queued-load lifecycle point after shell setup, while retaining save-load priority.

Generals/Code/GameEngine/Source/GameClient/GameClient.cpp

GameState.hDeclare Zero Hour save path resolution +1/-0

Declare Zero Hour save path resolution

• Adds the Zero Hour API for resolving absolute save paths or managed-directory filenames.

GeneralsMD/Code/GameEngine/Include/Common/GameState.h

GlobalData.hStore Zero Hour replay startup requests +1/-0

Store Zero Hour replay startup requests

• Adds global startup state for the replay requested through '-loadreplay'.

GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h

Recorder.hExpose Zero Hour queued replay loading +1/-0

Expose Zero Hour queued replay loading

• Declares the recorder entry point that validates and starts a command-line replay request.

GeneralsMD/Code/GameEngine/Include/Common/Recorder.h

Recorder.cppResolve and preflight Zero Hour replays +49/-2

Resolve and preflight Zero Hour replays

• Mirrors absolute and relative replay path handling for Zero Hour. Validates replay headers, game options, and map availability before playback, quitting cleanly on failure.

GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp

GameState.cppSupport absolute save paths in Zero Hour +20/-6

Support absolute save paths in Zero Hour

• Mirrors centralized save read-path resolution so absolute command-line paths open in place and relative names remain under the Save directory.

GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp

GameClient.cppStart Zero Hour replays after initialization +5/-0

Start Zero Hour replays after initialization

• Invokes queued replay loading after shell setup in Zero Hour while retaining queued save-load priority.

GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp

@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds normal-lifecycle replay playback through -loadreplay, including absolute-path resolution and early replay/map validation.

  • Stores the requested replay in shared startup state and launches it after the shell initializes.
  • Centralizes relative and absolute replay path resolution for menu, simulation, and playback callers.
  • Applies the replay behavior consistently to Generals and Zero Hour.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
Core/GameEngine/Source/Common/CommandLine.cpp Adds -loadreplay parsing and relaxes the existing simulation option's extension restriction.
Generals/Code/GameEngine/Source/Common/Recorder.cpp Adds replay path resolution, queued-playback validation, and the normal client-lifecycle launch flow for Generals.
GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp Mirrors replay path resolution and queued-playback behavior for Zero Hour.
Generals/Code/GameEngine/Source/GameClient/GameClient.cpp Starts a queued replay after shell initialization, using the same startup point as queued save loading.
GeneralsMD/Code/GameEngine/Source/GameClient/GameClient.cpp Mirrors the post-shell queued replay handoff in the Zero Hour client.
Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp Updates menu header reads to use the centralized filename and playback-mode contract.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp Applies the centralized replay-header API to the Zero Hour replay menu.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    CLI[-loadreplay path] --> Global[Store queued replay]
    Global --> Init[Initialize client and shell]
    Init --> Validate[Read replay header and validate map]
    Validate -->|Invalid replay| ReplayError[Show replay-load error and remain in menus]
    Validate -->|Missing map| MapError[Show map error and remain in menus]
    Validate -->|Valid| Playback[Open replay for playback]
    Playback --> Queue[Queue MSG_NEW_GAME]
    Queue --> Game[Start replay]
    Game --> Menus[Return to menus after playback]
Loading

Reviews (7): Last reviewed commit: "feat(cli): Play a replay file from the c..." | Re-trigger Greptile

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 27, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (1) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. -ignoreReplaySyncErrors is unregistered 📎 Requirement gap ≡ Correctness
Description
The PR registers -loadreplay but does not register the required -ignoreReplaySyncErrors option,
so invoking the mandated suppression flag cannot set TheDebugIgnoreSyncErrors. Only the
differently named legacy -ignoresync option reaches parseSync.
Code

Core/GameEngine/Source/Common/CommandLine.cpp[1208]

+	{ "-loadreplay", parseLoadReplay },
Evidence
PR Compliance ID 6 explicitly requires suppression when -ignoreReplaySyncErrors is supplied. The
PR extends the startup command table with -loadreplay at line 1208, while the branch contains no
registration for -ignoreReplaySyncErrors; parseSync at lines 810-814 provides the required
behavior but is registered only under -ignoresync at line 1322.

Honor explicit replay synchronization error suppression
Core/GameEngine/Source/Common/CommandLine.cpp[810-814]
Core/GameEngine/Source/Common/CommandLine.cpp[1205-1208]
Core/GameEngine/Source/Common/CommandLine.cpp[1322-1322]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`-loadreplay` must support the explicit `-ignoreReplaySyncErrors` command-line option, but that name is not registered.
## Issue Context
The existing `parseSync` handler already enables `TheDebugIgnoreSyncErrors`, and the legacy `-ignoresync` registration should remain compatible. Register the required option name as an alias to the same handler.
## Fix Focus Areas
- Core/GameEngine/Source/Common/CommandLine.cpp[1205-1208]
- Core/GameEngine/Source/Common/CommandLine.cpp[1319-1323]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Playerless replay crashes startup 🐞 Bug ≡ Correctness
Description
loadQueuedReplay() passes headers with localPlayerIndex == -1 into playbackFile(), which
dereferences getSlot(-1) and crashes instead of playing or cleanly rejecting the replay. The
recorder itself can write -1 for non-network single-player recordings, so -loadreplay can hit
this with a generated replay file.
Code

Generals/Code/GameEngine/Source/Common/Recorder.cpp[1117]

+	if (!playbackFile(filename))
Evidence
The replay writer initializes the recorded local index to -1 and leaves it unchanged for
non-network, non-skirmish single-player recording. The reader considers -1 valid, while the newly
invoked playback path passes it to GameInfo::getSlot, which returns null for negative indexes
before the caller dereferences it; GeneralsMD mirrors the same path.

Generals/Code/GameEngine/Source/Common/Recorder.cpp[590-640]
Generals/Code/GameEngine/Source/Common/Recorder.cpp[923-938]
Generals/Code/GameEngine/Source/Common/Recorder.cpp[1117-1120]
Generals/Code/GameEngine/Source/Common/Recorder.cpp[1191-1200]
Core/GameEngine/Source/GameNetwork/GameInfo.cpp[445-452]
GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp[1120-1123]
GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp[1194-1203]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Queued playback can receive a valid replay header with `localPlayerIndex == -1`, but `playbackFile()` unconditionally dereferences that slot and crashes. Handle the no-local-player case without calling `getSlot(-1)`, and apply the equivalent fix to both game variants.
## Issue Context
`readReplayHeader()` explicitly accepts `-1`, and `startRecording()` can serialize `-1` for non-network single-player recordings. The multiplayer flag should only inspect a slot when the index is nonnegative; otherwise use the appropriate non-multiplayer default.
## Fix Focus Areas
- Generals/Code/GameEngine/Source/Common/Recorder.cpp[1117-1120]
- Generals/Code/GameEngine/Source/Common/Recorder.cpp[1191-1200]
- GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp[1120-1123]
- GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp[1194-1203]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread Core/GameEngine/Source/Common/CommandLine.cpp
Comment thread Generals/Code/GameEngine/Source/Common/Recorder.cpp
@tintinhamans

Copy link
Copy Markdown

There is no way to play a replay visually from the command line. -replay simulates headlessly, so an externally supplied .rep cannot be launched from an operating-system file handler and watched.

This is not entirely the case. -replay works fine without -headless.

@bobtista

Copy link
Copy Markdown
Author

There is no way to play a replay visually from the command line. -replay simulates headlessly, so an externally supplied .rep cannot be launched from an operating-system file handler and watched.

This is not entirely the case. -replay works fine without -headless.

Yeah that's true, replay without -headless already plays visually. Fixed the description.
The difference is lifecycle:
-replay enters the replay-simulation workflow before the shell is shown and terminates the process after the replay workflow finishes.
-loadreplay queues a single replay after normal shell initialization, so playback returns to the menus afterward.
The absolute-path handling is shared, so this PR also allows -replay to open absolute paths. The separate option is specifically for the normal client lifecycle.

@bobtista
bobtista force-pushed the bobtista/feature/loadreplay-cli branch from a82128f to cbeeaa5 Compare September 11, 2026 17:32
@bobtista

Copy link
Copy Markdown
Author

Both are split into follow ups: -ignoreReplaySyncErrors is implemented in #3152, and the playerless replay crash is fixed in #3240.

Comment thread Core/GameEngine/Source/Common/CommandLine.cpp Outdated
Comment thread Generals/Code/GameEngine/Source/Common/Recorder.cpp Outdated
Comment thread Generals/Code/GameEngine/Source/Common/Recorder.cpp Outdated
Comment thread Generals/Code/GameEngine/Source/Common/Recorder.cpp
Comment thread Generals/Code/GameEngine/Source/Common/Recorder.cpp Outdated
Comment thread Generals/Code/GameEngine/Source/Common/Recorder.cpp Outdated
@bobtista
bobtista force-pushed the bobtista/feature/loadreplay-cli branch from cbeeaa5 to d35d0ff Compare September 12, 2026 15:38
Comment thread GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp
Comment thread GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp Outdated
@bobtista
bobtista force-pushed the bobtista/feature/loadreplay-cli branch from d35d0ff to 00e5bc8 Compare September 12, 2026 19:07
Comment thread Core/GameEngine/Source/Common/CommandLine.cpp Outdated
@bobtista
bobtista force-pushed the bobtista/feature/loadreplay-cli branch 2 times, most recently from 1d058e2 to dc0d164 Compare September 14, 2026 15:48
@xezon xezon added Enhancement Is new feature or request Minor Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour labels Sep 14, 2026

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

Looks reasonable.

@xezon xezon changed the title feat(cli): Play a replay file from the command line feat(recorder): Play a replay file from the command line Sep 14, 2026
@bobtista
bobtista force-pushed the bobtista/feature/loadreplay-cli branch from dc0d164 to 1b5c5be Compare September 14, 2026 21:08
@xezon
xezon merged commit d28c7a5 into TheSuperHackers:main Sep 15, 2026
23 checks passed
fbraz3 added a commit to fbraz3/GeneralsX that referenced this pull request Sep 16, 2026
* bugfix(gamewindow): Remove destroyed windows from the modal stack and prevent duplicate modals for the same window (TheSuperHackers#3224)

* feat(commandline): Add working directory command line options (TheSuperHackers#3149)

Append -useCwd to apply the startup working directory, -setCwd "path" to apply a custom working directory, otherwise it falls back to the default executable working directory

* bugfix(neutronmissile): Fix and improve Nuke Missile damage for large objects inside the outer blast radius (TheSuperHackers#3161)

* bugfix(dozeraiupdate): Fix issue where builders could resume completed tasks after being disabled (TheSuperHackers#2793)

* refactor(milesaudiomanager): Use consistent variable names for PlayingAudio in MilesAudioManager (TheSuperHackers#3254)

* refactor(milesaudiomanager): Simplify MilesAudioManager::notifyOfAudioCompletion() (TheSuperHackers#3254)

* refactor(milesaudiomanager): Simplify MilesAudioManager::findLowestPrioritySound() (TheSuperHackers#3254)

* bugfix(milesaudiomanager): Fix premature 2d and 3d sound cancellations from MilesAudioManager::stopAudioEvent() (TheSuperHackers#3254)

* bugfix(milesaudiomanager): No longer use stopped audio in queries and updates (TheSuperHackers#3254)

* refactor(bink): Replace the Bink SDK stub with a Bink runtime loader (TheSuperHackers#3272)

The Bink SDK stub was linked as an import library, so binkw32.dll had to be
resolvable while the process image was still loading, long before WinMain and
therefore long before the command line was parsed. That is why -setCwd could not
point a build at a retail installation: the working directory it selects is set
far too late to influence how the library is found.

BinkLoader loads binkw32.dll explicitly once BinkVideoPlayer is initialized, at
which point the working directory is final. The Bink functions declared in bink.h
are now ordinary functions that forward to the matching export of the loaded
module, so no call site changes. An unresolved function returns the same neutral
value the stub library returned, which means a missing binkw32.dll disables video
playback instead of preventing the game from starting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(miles): Replace the Miles SDK stub with a Miles runtime loader (TheSuperHackers#3272)

The Miles SDK stub was linked as an import library, so mss32.dll had to be
resolvable while the process image was still loading, long before WinMain and
therefore long before the command line was parsed. That is why -setCwd could not
point a build at a retail installation: the working directory it selects is set
far too late to influence how the library is found.

MilesLoader loads mss32.dll explicitly once the audio device is opened, at which
point the working directory is final. The Miles functions declared in mss/mss.h
are now ordinary functions that forward to the matching export of the loaded
module, so no call site changes. An unresolved function returns the same neutral
value the stub library returned, which means a missing mss32.dll turns audio off
instead of preventing the game from starting.

Nine declarations were dropped along the way, because the retail mss32.dll does
not export them and nothing has called them since they were replaced by their
volume_pan counterparts: AIL_sample_volume, AIL_set_sample_volume, AIL_sample_pan,
AIL_set_sample_pan and the four stream equivalents, plus AIL_open_stream_by_sample.
The MSS_auto_cleanup hook was dropped as well, because its atexit handler would
have called AIL_shutdown after the module was already freed. All 92 remaining
exports were verified to resolve against the retail mss32.dll.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(miles): Fix the written primitive types in mss.h and all its call sites; no ABI changes (TheSuperHackers#3272)

* perf(productionupdate): Simplify and correct implementations of cancel functions in ProductionUpdate (TheSuperHackers#3270)

* chore(gamememory): Compile out the memory link tester in Release (TheSuperHackers#3266)

* perf(gamememory): Early exit delete and free functions on null pointer (TheSuperHackers#3266)

* perf(gamememory): Inline preMainInitMemoryManager (TheSuperHackers#3266)

* perf(gamememory): Add overloads for the deletes with size_t argument (TheSuperHackers#3266)

* chore(gamememory): Remove superfluous extern keywords from operator overloads (TheSuperHackers#3266)

* perf(gamememory): Remove unnecessary calls to preMainInitMemoryManager from delete and free functions and make freeBytes noexcept to get rid of EH frame (TheSuperHackers#3266)

* fix(gamefont): Ceil font glyph buffer size to the actual glyph size to prevent a buffer write overflow (TheSuperHackers#3268)

* refactor(particlesys): Parse IsGroundAligned as an enum instead of a boolean (TheSuperHackers#3265)

* ci(release): Stop requesting permissions from the reusable workflow (TheSuperHackers#3276)

* refactor(basetype): Add utility functions to Region and Coord types (TheSuperHackers#3271)

New functions are:
intersectWith, uniteWith for IRegion3D, IRegion2D, Region3D, Region2D
updateMin, updateMax for ICoord3D, ICoord2D, Coord3D, Coord2D
asICoord2D, asCoord2D for ICoord3D, Coord3D

* build(cmake): Add retail compatibility option in CMake config (TheSuperHackers#2379)

RTS_BUILD_OPTION_RETAIL_COMPATIBLE_GAME=DEFAULT/ON/OFF

* bugfix(meshmatdesc): Fix mesh material color processing (TheSuperHackers#3246)

* ci: Restore CI workflow permission compatibility (TheSuperHackers#3286)

* chore: Remove trailing commas in braced initializers that break clang-format's compact layout (TheSuperHackers#3274)

Scoped to comment-free array/struct literals (BorderColors, TeamGeneric,
BezierSegment, GameMemoryInitPools, BFISH, Properties, Scripts) where
clang-format explodes each element onto its own line without this.

* chore(license): Add SPDX-License-Identifier to LICENSE.md (TheSuperHackers#3290)

Helps github detect the license version

* bugfix(pathfinder): Restore Generals retail compatibility after crash fix changes to Pathfinder::findAttackPath (TheSuperHackers#3289)

* feat(recorder): Play a replay file from the command line (TheSuperHackers#3227)

Use -loadreplay <file> as a command line argument to load the replay with full game context

* fix(audio): Copy SoundSceneObjClass state safely (TheSuperHackers#3247)

* fix(hash): Fix initialization of HashTableIteratorClass and make it work with an empty HashTableClass (TheSuperHackers#3284)

* chore(pathfinder): Remove superfluous CPOP_STARTS_FROM_PREV_SEG macro (TheSuperHackers#3295)

* fix(milesaudiomanager): Prevent heap-buffer-overflow read in MilesAudioManager::selectProvider() (TheSuperHackers#3281)

* bugfix(filesystem): Preserve write paths with missing directories (TheSuperHackers#3104)

* perf(pathfinder): Optimize appending node to end of the path (TheSuperHackers#3198)

PathNode::appendToList() walks the entire list from the head to find the tail on every call, making repeated appendNode() calls O(n^2) in path length. Path already tracks m_pathTail, so append directly onto it in O(1) instead. Removed PathNode::appendToList() as it is not used anywhere else.

* perf(pathfinder): Take parents cell's position outside of for-loop for optimization (TheSuperHackers#3198)

The parent cell's world position fromPos never changes across the neighbour loop, so compute it once instead.

* perf(pathfinder): Remove redundant isCrusher recomputation for optimization (TheSuperHackers#3198)

* ci(windows): make bink and miles runtime stubs optional in build artifacts

* fix(platform): preserve POSIX startup working directory and set Flatpak asset paths

* docs(worklog): document CI fixes and verification for upstream sync PR 304

---------

Co-authored-by: ArcticDolphin <5984296+tintinhamans@users.noreply.github.com>
Co-authored-by: Jacob Lane Ledbetter <23038070+CryoTheRenegade@users.noreply.github.com>
Co-authored-by: xezon <4720891+xezon@users.noreply.github.com>
Co-authored-by: Stubbjax <11547761+Stubbjax@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: stm <14291421+stephanmeesters@users.noreply.github.com>
Co-authored-by: mirelle7 <115191165+mirelle7@users.noreply.github.com>
Co-authored-by: Caball009 <82909616+Caball009@users.noreply.github.com>
Co-authored-by: Bobby Battista <bobtista@gmail.com>
Co-authored-by: SkyAero <21192585+Skyaero42@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement Is new feature or request Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow -loadsave and -loadreplay to load files from any directory

3 participants