Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wzapi: change removeObject API function to defer object removal #3736

Conversation

ManManson
Copy link
Member

Introduce scriptQueuedObjectRemovals() vector to hold deferred object removal requests from wzapi::removeObject API.

wzapi::removeObject API call adds the game objects to this list. They will eventually be released during calls to processScriptQueuedObjectRemovals() during the following stages of the main game loop:

  1. recvMessage() - there are a few functions which distribute resources of the defeated players among others and may trigger script events.
  2. updateScripts() - processes queued timer functions.
  3. droidUpdate(), missionDroidUpdate(), structureUpdate() - main routines for updating the state of in-game objects. These would potentially trigger the majority of script events.

Each pair represents <GameObject, NeedToApplyEffectsOnDestruction> tuple.

The sole reason for this breaking API change is to guard against cases like this one:

  1. gameStateUpdate() processes droid state updates inside a mutating_list_iterate.
  2. A droid update triggers some user script, which calls wzapi::removeObject several times in a row.
  3. This may potentially destroy itNext iterator saved inside the mutating_list_iterate from of the upper stack frames, which would lead to invalid memory access.

So, instead of removing multiple objects inside of mutating_list_iterate, which is unsafe, we just queue removal requests and execute actual destruction routines once we are out of the mutating_list_iterate function.

Signed-off-by: Pavel Solodovnikov [email protected]

@ManManson ManManson added the Bug label Apr 15, 2024
@ManManson ManManson requested review from KJeff01 and past-due April 15, 2024 14:19
@ManManson ManManson added this to the 4.5.0-beta1 milestone Apr 15, 2024
src/keybind.cpp Outdated Show resolved Hide resolved
@ManManson ManManson force-pushed the fix_alpha11_crash_remove_droid_from_script branch from 06cb72e to 41ec6c6 Compare April 15, 2024 15:38
@past-due
Copy link
Member

past-due commented Apr 15, 2024

Found a few other places we should probably call wzapi::processScriptQueuedObjectRemovals(); immediately after:

  • after the call to triggerEvent(TRIGGER_START_LEVEL); in startGameLoop()
  • after the call to triggerEvent(TRIGGER_GAME_LOADED); in stageThreeInitialise()
  • after the call to triggerEvent(TRIGGER_GAME_INIT); in stageThreeInitialise()
  • after the call to triggerEvent(TRIGGER_GAME_SAVING); in saveGame()
  • after the call to triggerEvent(TRIGGER_TRANSPORTER_LAUNCH, psCurrTransporter); in processLaunchTransporter()
  • after the call to triggerEvent(TRIGGER_MISSION_TIMEOUT); in missionTimerUpdate()
  • after the call to triggerEvent(TRIGGER_TRANSPORTER_ARRIVED, psTransporter); in intUpdateTransporterTimer
  • after the call to renderLoop() (specifically, after we get the after ticks value) in gameLoop(), to cover a bunch of events triggered by UI stuff
  • after the call to triggerEvent(TRIGGER_VIDEO_QUIT); in videoLoop()

@ManManson ManManson force-pushed the fix_alpha11_crash_remove_droid_from_script branch 2 times, most recently from 2283c39 to 256a3d4 Compare April 18, 2024 10:57
@ManManson ManManson force-pushed the fix_alpha11_crash_remove_droid_from_script branch from c0b5107 to 3c5000d Compare April 18, 2024 14:54
Introduce `scriptQueuedObjectRemovals()` vector to hold
deferred object removal requests from `wzapi::removeObject` API.

`wzapi::removeObject` API call adds the game objects to this list.
They will eventually be released during calls to `processScriptQueuedObjectRemovals()`
during the following stages of the main game loop:
1. `recvMessage()` - there are a few functions which distribute
    resources of the defeated players among others and may trigger script events.
2. `updateScripts()` - processes queued timer functions.
3. `droidUpdate()`, `missionDroidUpdate()`, `structureUpdate()` - main
   routines for updating the state of in-game objects. These would potentially
   trigger the majority of script events.

Each pair represents `<GameObject, NeedToApplyEffectsOnDestruction>` tuple.

The sole reason for this breaking API change is to guard against
cases like this one:
1. `gameStateUpdate()` processes droid state updates inside
    a `mutating_list_iterate`.
2. A droid update triggers some user script, which calls
   `wzapi::removeObject` several times in a row.
3. This may potentially destroy `itNext` iterator saved inside the
   `mutating_list_iterate` from of the upper stack frames, which
   would lead to invalid memory access.

So, instead of removing multiple objects inside of
`mutating_list_iterate`, which is unsafe, we just queue removal
requests and execute actual destruction routines once we are out of
the `mutating_list_iterate` function.

Signed-off-by: Pavel Solodovnikov <[email protected]>
These places include not only those which involve triggers upon
`droidUpdate()` inside the main execution sequence of the game
loop, but also those events that execute prior to and
after the main things in the game loop, e.g.:

* Level start.
* Load/Save game.
* Start playing videos.
* Render loop.

Some of these places may be just extra work and probably
won't have even a slight possibility of calling
`wzapi::removeObject` from inside them, but it's always
better to be safe than sorry.

Also, introduce a check for already-dead entries in
`processScriptQueuedObjectRemovals()` and skip them.

This is a way to deal with duplicate entries to ensure
we don't destroy a single object more than once.

These things can occur when, e.g., several units happen to
move near enough to an artifact lying on the ground, so that
they would pick up the artifact simultaneously within the same
game loop iteration (in the `checkLocalFeatures()` function).

In such case, each of these droids will trigger the
"artifact picked up" event, so JS handler (`cam_eventPickup`)
will be called multiple times for the same object, each trying
to place it into `scriptQueuedObjectRemovals()` list.

Signed-off-by: Pavel Solodovnikov <[email protected]>
…`__camPickupArtifact`

Signed-off-by: Pavel Solodovnikov <[email protected]>
@ManManson ManManson force-pushed the fix_alpha11_crash_remove_droid_from_script branch from 3c5000d to e4927fb Compare April 21, 2024 17:32
@past-due past-due merged commit e35f411 into Warzone2100:master Apr 22, 2024
37 checks passed
@ManManson ManManson deleted the fix_alpha11_crash_remove_droid_from_script branch April 22, 2024 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants