88using osu . Framework . Allocation ;
99using osu . Framework . Graphics ;
1010using osu . Framework . Logging ;
11+ using osu . Game ;
1112using osu . Game . Rulesets ;
1213using osu . Game . Rulesets . Mods ;
1314
@@ -21,7 +22,7 @@ namespace osu.Game.Beatmaps
2122 /// <remarks>
2223 /// <para>
2324 /// The cache stores one <em>canonical</em> (immutable) copy keyed by
24- /// <c>(BeatmapInfo.ID, rulesetShortName, orderedModsKey)</c>.
25+ /// <c>(BeatmapInfo.ID + BeatmapInfo.Hash , rulesetShortName, orderedModsKey, gameVersion )</c>.
2526 /// Callers receive a shallow <see cref="IBeatmap.Clone"/> so that gameplay state does not
2627 /// bleed across sessions while still reusing the pre-built hit-object graph.
2728 /// </para>
@@ -33,20 +34,23 @@ namespace osu.Game.Beatmaps
3334 /// </remarks>
3435 public class PlayableBeatmapCache : Component
3536 {
36- private readonly record struct CacheKey ( Guid BeatmapId , string RulesetShortName , string ModsKey ) ;
37+ private readonly record struct CacheKey ( Guid BeatmapId , string BeatmapHash , string RulesetShortName , string ModsKey , string GameVersion ) ;
3738
3839 private readonly Dictionary < CacheKey , IBeatmap > cache = new Dictionary < CacheKey , IBeatmap > ( ) ;
3940
4041 private WorkingBeatmapCache ? workingBeatmapCache ;
42+ private string gameVersion = string . Empty ;
4143
42- [ BackgroundDependencyLoader ]
43- private void load ( IWorkingBeatmapCache beatmapCache )
44+ [ BackgroundDependencyLoader ( true ) ]
45+ private void load ( IWorkingBeatmapCache beatmapCache , OsuGameBase ? game )
4446 {
4547 if ( beatmapCache is WorkingBeatmapCache concrete )
4648 {
4749 workingBeatmapCache = concrete ;
4850 workingBeatmapCache . OnInvalidated += handleInvalidated ;
4951 }
52+
53+ gameVersion = game ? . VersionHash ?? typeof ( OsuGameBase ) . Assembly . GetName ( ) . Version ? . ToString ( ) ?? "unknown" ;
5054 }
5155
5256 /// <summary>
@@ -110,15 +114,15 @@ private void handleInvalidated(WorkingBeatmap working)
110114 }
111115 }
112116
113- private static CacheKey makeKey ( BeatmapInfo beatmapInfo , IRulesetInfo ruleset , IReadOnlyList < Mod > mods )
117+ private CacheKey makeKey ( BeatmapInfo beatmapInfo , IRulesetInfo ruleset , IReadOnlyList < Mod > mods )
114118 {
115119 // Build a deterministic key from ordered mod acronyms and their settings hash.
116120 // Mod.GetHashCode() accounts for both the type and any user-adjustable settings.
117121 string modsKey = string . Join ( ';' , mods
118122 . OrderBy ( m => m . Acronym )
119123 . Select ( m => $ "{ m . Acronym } :{ m . GetHashCode ( ) } ") ) ;
120124
121- return new CacheKey ( beatmapInfo . ID , ruleset . ShortName , modsKey ) ;
125+ return new CacheKey ( beatmapInfo . ID , beatmapInfo . Hash , ruleset . ShortName , modsKey , gameVersion ) ;
122126 }
123127
124128 protected override void Dispose ( bool isDisposing )
0 commit comments