Skip to content

Protect cached simulation results from caller mutation - #7249

Draft
ChevyLevi wants to merge 3 commits into
masterfrom
immutable_simulation_cache_results
Draft

ChevyLevi wants to merge 3 commits into
masterfrom
immutable_simulation_cache_results

Conversation

@ChevyLevi

@ChevyLevi ChevyLevi commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Brief Description of What This PR Does

Prevent callers from modifying results owned by SimulationCache. The existing public getters keep their signatures and return detached mutable copies, so changing a returned balance, process calculation or process list cannot affect subsequent cache reads.

Internal consumers use readonly value views with private backing, by-value elements and struct enumerators. Cache entries remain unchanged after publication, including while an older view survives Clear(). The calculation paths, cache keys and iteration order are preserved.

The public cache getters currently expose mutable cache entries. A caller can clear a returned collection or change a result field and thereby alter later simulation reads. This change fixes that ownership leak while preserving the public mutable result types.

Note: there seems to be a hell lot of code changes, but most are just method extraction and signature changes. I think what might worth discussed about are whether there are further performance or memory improvement from the current data containers' structures.

Related Issues

No related issue.

Progress Checklist

Note: before starting this checklist the PR should be marked as non-draft.

  • PR author has checked that this PR works as intended and doesn't
    break existing features:
    https://wiki.revolutionarygamesstudio.com/wiki/Testing_Checklist
    (this is important as to not waste the time of Thrive team
    members reviewing this PR). This includes gameplay testing by the PR author.
  • Initial code review passed (this and further items should not be checked by the PR author)
  • Functionality is confirmed working by another person (see above checklist link)
  • Final code review is passed and code conforms to the
    styleguide.

Before merging all CI jobs should finish on this PR without errors, if
there are automatically detected style issues they should be fixed by
the PR author. Merging must follow our
styleguide.

@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

/// </summary>
internal readonly struct EnergyBalanceView
{
private readonly EnergyBalanceInfoSimple balance;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I assume whatever tool you are using it likes to use structs a lot...

Why can't this be made simply as a IReadOnlyEnergyBalanceInfo interface? That would then be returned from the methods. That's the C# standard way to prevent modification of returned data.

@ChevyLevi ChevyLevi Sep 8, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The purpose here is to avoid cast IReadOnlyEnergyBalanceInfo to EnergyBalanceInfoSimple and then execute methods like Clear() to change the readonly data. But I guess we might not need such a barrier here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Well obviously the data source should keep a reference to the writeable object, and only return the readonly interface to the places that are not allowed to modify it. It would be a massive hack to ever cast the read only interface back to the modifiable one, that indicates a major design problem.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I see. So that's an over-protection here. I'll change it in the next workday.

var compoundCreated = 0.0f;

foreach (var process in activeProcessList)
for (var i = 0; i < activeProcessList.Count; ++i)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I've used the pattern where I first assign int count = activeProcessList.Count and then in the loop use i < count. I believe this is more efficient as it reads the count property (which incurs a method call) only once and then looks at a local variable for each loop iteration. So I think this pattern could be applied in a few places that change stuff in this PR.

@hhyyrylainen hhyyrylainen added this to the Release 1.7.0 milestone Sep 11, 2026

This branch has not been deployed

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

Labels

None yet

2 participants