Skip to content

Conversation

@Goober5000
Copy link
Contributor

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.

@Goober5000 Goober5000 added this to the Release 25.0 milestone Jan 18, 2026
@Goober5000 Goober5000 added cleanup A modification or rewrite of code to make it more understandable or easier to maintain. fix A fix for bugs, not-a-bugs, and/or regressions. labels Jan 18, 2026
@Goober5000 Goober5000 force-pushed the variable_fix branch 4 times, most recently from 0f0de2a to ef67aec Compare January 19, 2026 01:43
} else {
// new player-persistent container
Player->containers.emplace_back(container);
Player->containers.push_back(container);
Copy link
Member

Choose a reason for hiding this comment

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

Why push_back()?

Campaign.persistent_containers below uses emplace_back() to add a new container.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well they should indeed be consistent; thanks for catching that.

I tend to use push_back unless I specifically need emplace_back, e.g. if I'm constructing a new element in-place. I've seen push_back recommended as a rule-of-thumb, such as here:
https://quuxplusone.github.io/blog/2021/03/03/push-back-emplace-back/

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.
@Goober5000 Goober5000 marked this pull request as ready for review January 19, 2026 21:37
Copy link
Member

@jg18 jg18 left a 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.

// player-persistent (aka "eternal")
if (Sexp_variables[i].type & SEXP_VARIABLE_SAVE_TO_PLAYER_FILE) {
// see if we already have a variable with this name
int j = find_item_with_string(Player->variables, &sexp_variable::variable_name, Sexp_variables[i].variable_name);
Copy link
Member

Choose a reason for hiding this comment

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

Personal opinion is that find_item_with_string() is hard to understand, both how it's called and how it's implemented.

Given that player::variables is just an SCP_vector, I'd think that std::find_if() with a lambda as predicate would be both easier to understand and more idiomatic to C++.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It may be more idiomatic, but it's also more verbose. The new find_item_with_string and related functions are intended to be shorthand versions of handling the common cases. The main confusing part is the pointer-to-member-field; if you ignore that, it works just like any standard index function.

Campaign.persistent_variables.push_back(Sexp_variables[i]);
}
}
// player-persistent (aka "eternal")
Copy link
Member

Choose a reason for hiding this comment

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

Wait, I thought "player-persistent" != "eternal"?

IIRC:

  • player-persistent: "save on mission close"
  • campaign-persistent: "save on mission progress"
  • eternal: "save to player file"

Copy link
Member

@jg18 jg18 Jan 21, 2026

Choose a reason for hiding this comment

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

sexp.cpp:5059 and also on lines 5048-5056 back up my recollection.

Copy link
Contributor Author

@Goober5000 Goober5000 Jan 21, 2026

Choose a reason for hiding this comment

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

"player-persistent" has always meant "save to player file", and "campaign-persistent" has always meant "save to campaign file". A lot of the current code uses "eternal" and "non-eternal" to mean the same things, respectively. But originally "player-persistent" also carried the implicit additional meaning of "save-on-mission-close" whereas "campaign-persistent" also carried the implicit additional meaning of "save-on-mission-progress". This was the main motivation for the upgrade in 2018.

cur = Campaign.current_mission;

mission_obj = &Campaign.missions[cur];
int cur = Campaign.current_mission;
Copy link
Member

@jg18 jg18 Jan 21, 2026

Choose a reason for hiding this comment

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

Since this functionality occurs three times in this file, consider adding a helper function

cmission* get_current_mission();

and calling that.

}
if (store_red_alert) {
for (auto& current_rav : Campaign.red_alert_variables) {
Campaign.persistent_variables.push_back(current_rav);
Copy link
Member

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.

} 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)) {
Copy link
Member

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().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup A modification or rewrite of code to make it more understandable or easier to maintain. fix A fix for bugs, not-a-bugs, and/or regressions.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants