-
Notifications
You must be signed in to change notification settings - Fork 177
fix persistent variables and containers #7192
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
base: master
Are you sure you want to change the base?
Conversation
0f0de2a to
ef67aec
Compare
ef67aec to
adbfb8a
Compare
Storage of persistent variables had been broken since scp-fs2open#1631 -- the player-persistent variable code would inadvertently not run if there were no campaign-persistent variables. Additionally, there were several places where the persistent variable logic was hard to follow, which could have hidden other related bugs. This fixes the check and cleans up and consolidates the relevant functions.
adbfb8a to
98ecb98
Compare
jg18
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still reading, but adding a few thoughts.
code/mission/missioncampaign.cpp
Outdated
| } | ||
| if (store_red_alert) { | ||
| for (auto& current_rav : Campaign.red_alert_variables) { | ||
| Campaign.persistent_variables.push_back(current_rav); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should probably check for naming collisions between existing persistent variables and the RA variables we're adding. Ditto for containers.
code/mission/missioncampaign.cpp
Outdated
| } else if (any(persistence_type & ContainerType::SAVE_ON_MISSION_PROGRESS) && | ||
| any(container.type & persistence_type) && container.is_eternal()) { | ||
| // we might need to save some eternal player-persistent containers | ||
| if (!any(container.type & persistence_type)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use none() instead of !any().
jg18
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still studying, but one concern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tons and tons of good stuff here, some of which I wanted to call out explicitly, but some concerns and suggestions.
| if (add_it) { | ||
| Player->variables.push_back(Sexp_variables[i]); | ||
| } | ||
| if (store_red_alert) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering why we now save red alert variables at the end, instead of at the start?
| } else if (any(persistence_type & ContainerType::SAVE_ON_MISSION_PROGRESS) && | ||
| any(container.type & persistence_type) && container.is_eternal()) { | ||
| // we might need to save some eternal player-persistent containers | ||
| // player-persistent (aka "eternal") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional but strongly encouraged: I think it's worth documenting in code somewhere the 5 types of persistence and what "campaign-persistent"/"player-persistent" originally meant, to avoid potential future confusion, but up to you.
| auto ppc_it = std::find_if(Player->containers.begin(), | ||
| Player->containers.end(), | ||
| [container](const sexp_container &ppc) { | ||
| [&container](const sexp_container& ppc) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch about the &container. I wasn't aware of that syntax.
| void mission_campaign_end_do() | ||
| { | ||
| mission_campaign_store_variables(SEXP_VARIABLE_SAVE_ON_MISSION_PROGRESS, false); | ||
| mission_campaign_store_containers(ContainerType::SAVE_ON_MISSION_PROGRESS, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused on the reason for this change. Were we failing to save "save on mission progress" persistent data?
| break; | ||
| else if (Sexp_variables[j].type & SEXP_VARIABLE_IS_PERSISTENT) { | ||
| Sexp_variables[j].type = campaign_var.type; | ||
| strcpy_s(Sexp_variables[j].text, campaign_var.text); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of scope, but we should probably create a helper function for assigning variables. Too easy to get it wrong or forget a line.
| } | ||
|
|
||
| Assert(container.is_eternal()); | ||
| if (!container.is_eternal()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same concern as above that we're asserting that a condition is true (i.e., something's wrong if it's false) and taking action if the condition is false.
| gameseq_post_event(GS_EVENT_DEBRIEF); | ||
| } else { | ||
| mission_campaign_store_goals_and_events_and_variables(); | ||
| mission_campaign_store_goals_and_events_and_variables(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch here.
|
|
||
| // eternal variables belong in the player file | ||
| Assert(!(temp_var.type & SEXP_VARIABLE_SAVE_TO_PLAYER_FILE)); | ||
| if (temp_var.type & SEXP_VARIABLE_SAVE_TO_PLAYER_FILE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same confusion about asserting that X had better be false but then handling the case where X is true, anyway.
This pattern has a very odd smell and is likely to confuse future readers.
If the code needs to handle the case where X is true, even though it shouldn't happen, but the situation doesn't actually indicate a code bug, then an assertion may be the wrong check to use.
|
|
||
| // stores containers which will be saved only on mission progression | ||
| void mission_campaign_store_containers(ContainerType persistence_type, bool store_red_alert = true); | ||
| void mission_campaign_store_containers(ContainerType persistence_type, bool store_red_alert); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call on removing default values. Too much potential for hiding bugs, as we've learned here.
| mission_campaign_store_variables(SEXP_VARIABLE_SAVE_ON_MISSION_PROGRESS); | ||
| mission_campaign_store_containers(ContainerType::SAVE_ON_MISSION_PROGRESS); | ||
| mission_campaign_store_variables(SEXP_VARIABLE_SAVE_ON_MISSION_PROGRESS, false); | ||
| mission_campaign_store_containers(ContainerType::SAVE_ON_MISSION_PROGRESS, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Optional but strongly encouraged: given that
- this pair of function calls occurs frequently
- there's no scenario where we'd want to call one function but not the other
I suggest exporting a function like
void mission_campaign_store_persistent_data(int persistence_type, bool store_red_alert);and not exporting these two functions anymore.
The new function can internally do the int-to-ContainerType conversion for storing containers.
jg18
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed a concern about the save_on_close_*() functions getting dropped, even though I think that merging this functionality with the other "store data" function is a good idea overall.
|
|
||
| // make sure we are actually playing a single-player campaign | ||
| if (!(Game_mode & GM_CAMPAIGN_MODE) || (Campaign.type != CAMPAIGN_TYPE_SINGLE) || (Campaign.current_mission < 0)) | ||
| return; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This "save on close"-specific check is now getting lost, which makes me nervous about potentially introducing regressions.
From a quick look, seems like the difference is that "save on close" applies to single-player campaigns only.
Should "save on close" variables even be supported for multi campaigns?
Storage of persistent variables had been broken since #1631 -- the player-persistent variable code would inadvertently not run if there were no campaign-persistent variables. Additionally, there were several places where the persistent variable logic was hard to follow, which could have hidden other related bugs. This fixes the check and cleans up and consolidates the relevant functions.