Skip to content

Commit f5d7479

Browse files
Fill unused slots with local replays (v1.2.2.1)
New Options: * When racing ghosts from online leaderboards, if there's not enough ghosts to meet the max auto count, then you can substitute those remaining slots with your own local replays (will avoid loading duplicate replays that are both online and local).
1 parent 9b7b28a commit f5d7479

6 files changed

Lines changed: 162 additions & 2 deletions

File tree

Distance.ReplayIntensifies/ConfigurationLogic.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ public int MaxSelectedReplays
2424
set => Set(MaxSelectedReplays_ID, value);
2525
}
2626

27+
private const string FillWithLocalReplays_ID = "replays.fill_with_local_replays";
28+
public bool FillWithLocalReplays
29+
{
30+
get => Get<bool>(FillWithLocalReplays_ID);
31+
set => Set(FillWithLocalReplays_ID, value);
32+
}
33+
2734

2835
private const string MaxSavedLocalReplays_ID = "leaderboards.max_saved_local_replays";
2936
public int MaxSavedLocalReplays
@@ -320,6 +327,7 @@ public void Awake()
320327
// Assign default settings (if not already assigned).
321328
Get(EnableSeparateMaxForSelectedReplays_ID, false);
322329
Get(MaxSelectedReplays_ID, 20);
330+
Get(FillWithLocalReplays_ID, false);
323331

324332
Get(MaxSavedLocalReplays_ID, 500);
325333
Get(MaxOnlineLeaderboards_ID, 1000);

Distance.ReplayIntensifies/Distance.ReplayIntensifies.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
<Compile Include="Harmony\Assembly-CSharp\PlayerDataReplay\get_CreateCarScreen_.cs" />
9595
<Compile Include="Harmony\Assembly-CSharp\PlayerDataReplay\InitCarVirtual.cs" />
9696
<Compile Include="Harmony\Assembly-CSharp\PlayerDataReplay\OnEventReplayOptionsMenuClosed.cs" />
97+
<Compile Include="Harmony\Assembly-CSharp\ReplayManager\OnLoadedOnlineReplaysDownloadFinished.cs" />
9798
<Compile Include="Helpers\SteamworksHelper.cs" />
9899
<Compile Include="Scripts\PlayerDataReplayCompoundData.cs" />
99100
<Compile Include="Harmony\Assembly-CSharp\CarLevelOfDetail\SetLevelOfDetail.cs" />
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
using HarmonyLib;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Reflection.Emit;
6+
using UnityEngine;
7+
8+
namespace Distance.ReplayIntensifies.Harmony
9+
{
10+
/// <summary>
11+
/// Patch to handle filling unused replay slots where there aren't enough online replays to load.
12+
/// <para/>
13+
/// This function is only called when auto-loading replays (AKA not when picking individual replays from a menu).
14+
/// </summary>
15+
/// <remarks>
16+
/// Required For: Fill With Local Replays.
17+
/// </remarks>
18+
[HarmonyPatch(typeof(ReplayManager), nameof(ReplayManager.OnLoadedOnlineReplaysDownloadFinished))]
19+
internal static class ReplayManager__OnLoadedOnlineReplaysDownloadFinished
20+
{
21+
[HarmonyTranspiler]
22+
internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
23+
{
24+
var codes = new List<CodeInstruction>(instructions);
25+
26+
Mod.Instance.Logger.Info("Transpiling...");
27+
// VISUAL:
28+
// After online replays are populated, handle filling unused slots with local replays.
29+
//this.AddOnlineReplaysToGroup(replays, this.loadedReplays_);
30+
// -to-
31+
//this.AddOnlineReplaysToGroup(replays, this.loadedReplays_);
32+
//FillRemainingWithLocalReplays_(this, this.loadedReplays_, userSkipped);
33+
34+
for (int i = 1; i < codes.Count; i++)
35+
{
36+
if ((codes[i - 1].opcode == OpCodes.Ldfld) && // 'group' (should be loadedReplays_)
37+
(codes[i ].opcode == OpCodes.Call && ((MethodInfo)codes[i ].operand).Name == "AddOnlineReplaysToGroup"))
38+
{
39+
Mod.Instance.Logger.Info($"call AddOnlineReplaysToGroup @ {i}");
40+
41+
// After: call AddOnlineReplaysToGroup
42+
// Insert: ldarg.0
43+
// Insert: ldarg.0
44+
// Insert: ldfld 'group' (should be loadedReplays_)
45+
// Insert: ldarg.2
46+
// Insert: call FillRemainingWithLocalReplays_
47+
codes.InsertRange(i + 1, new CodeInstruction[]
48+
{
49+
new CodeInstruction(OpCodes.Ldarg_0, null),
50+
new CodeInstruction(OpCodes.Ldarg_0, null),
51+
new CodeInstruction(OpCodes.Ldfld, codes[i - 1].operand), // 'group' (should be loadedReplays_)
52+
new CodeInstruction(OpCodes.Ldarg_2, null), // userSkipped
53+
new CodeInstruction(OpCodes.Call, typeof(ReplayManager__OnLoadedOnlineReplaysDownloadFinished).GetMethod(nameof(FillRemainingWithLocalReplays_))),
54+
});
55+
56+
break;
57+
}
58+
}
59+
60+
return codes.AsEnumerable();
61+
}
62+
63+
#region Helpers
64+
65+
public static void FillRemainingWithLocalReplays_(ReplayManager replayManager, GameObject group, bool userSkipped)
66+
{
67+
if (!Mod.Instance.Config.FillWithLocalReplays)
68+
{
69+
return; // Stop now if the setting is disabled.
70+
}
71+
// TODO: Should we stop when the user skips loading the remaining online ghosts?
72+
// This is assuming skip is generally only used when unintenionally started a level.
73+
/*if (userSkipped)
74+
{
75+
return;
76+
}*/
77+
78+
CarReplayData[] onlineReplays = group.GetComponentsInChildren<CarReplayData>();
79+
int missingReplayCount = replayManager.settings_.GhostsInArcadeCount_ - onlineReplays.Length;
80+
if (missingReplayCount <= 0)
81+
{
82+
return; // We already have the maximum number of replays.
83+
}
84+
85+
string levelPath = replayManager.gm_.NextLevelPath_;
86+
GameModeID gameModeID = replayManager.gm_.NextGameModeID_;
87+
LocalLeaderboard localLeaderboard = LocalLeaderboard.Load(levelPath, gameModeID);
88+
if (localLeaderboard != null)
89+
{
90+
// Depending on the player's ghost download type (Friends/Global Near Me/Global Best), it's possible that
91+
// we've also downloaded one of our own ghosts. Because that ghost normally would be the top local replay
92+
// on our leaderboard, we need to take time to filter that out to avoid duplicate overlapping ghosts.
93+
94+
// NOTE: It's not guaranteed the top local replay will be our online ghost, since it's possible it was created
95+
// while there was no online connection. So compare all local replays for `missingReplayCount + 1`.
96+
List<CarReplayData> localReplays = CarReplayData.LoadReplaysFromLeaderboard(localLeaderboard, missingReplayCount + 1).ToList();
97+
98+
bool duplicateFound = false;
99+
for (int i = 0; i < onlineReplays.Length && !duplicateFound; i++)
100+
{
101+
var onlineReplay = onlineReplays[i];
102+
103+
for (int j = 0; j < localReplays.Count; j++)
104+
{
105+
var localReplay = localReplays[j];
106+
// Compare the data that's most likely to differ first.
107+
if ((localReplay.steamID_ == 0 || localReplay.steamID_ == onlineReplay.steamID_) &&
108+
localReplay.FinishValue_ == onlineReplay.FinishValue_ &&
109+
localReplay.ReplayLengthMS_ == onlineReplay.ReplayLengthMS_ &&
110+
localReplay.StateBuffer_.Length == onlineReplay.StateBuffer_.Length &&
111+
localReplay.EventBuffer_.Length == onlineReplay.EventBuffer_.Length &&
112+
localReplay.carData_.name_ == onlineReplay.carData_.name_ &&
113+
localReplay.carData_.colors_.primary_ == onlineReplay.carData_.colors_.primary_ &&
114+
localReplay.carData_.colors_.secondary_ == onlineReplay.carData_.colors_.secondary_ &&
115+
localReplay.carData_.colors_.glow_ == onlineReplay.carData_.colors_.glow_ &&
116+
localReplay.carData_.colors_.sparkle_ == onlineReplay.carData_.colors_.sparkle_ &&
117+
localReplay.StateBuffer_.SequenceEqual(onlineReplay.StateBuffer_) &&
118+
localReplay.EventBuffer_.SequenceEqual(onlineReplay.EventBuffer_))
119+
{
120+
// This replay is the same as the player's online replay, avoid populating with duplicates.
121+
localReplays.RemoveAt(j);
122+
duplicateFound = true;
123+
break;
124+
}
125+
}
126+
}
127+
if (!duplicateFound && localReplays.Count > missingReplayCount)
128+
{
129+
// We obtained more replays than we wanted (to check for dups), so remove the last in the list.
130+
localReplays.RemoveAt(localReplays.Count - 1);
131+
}
132+
133+
if (localReplays.Count > 0)
134+
{
135+
replayManager.AddReplaysToGroup(localReplays.ToArray(), replayManager.loadedReplays_);
136+
}
137+
UnityEngine.Object.Destroy(localLeaderboard.gameObject);
138+
}
139+
}
140+
141+
#endregion
142+
}
143+
}

Distance.ReplayIntensifies/Mod.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,13 @@ private void CreateSettingsMenu()
366366
(value) => Config.ShowDataEffectInGhostMode = value,
367367
"The data materialization spawn effect will be used when racing non-ghost cars.");
368368

369+
settingsMenu.CheckBox(MenuDisplayMode.Both,
370+
"setting:fill_with_local_replays",
371+
"FILL WITH LOCAL REPLAYS",
372+
() => Config.FillWithLocalReplays,
373+
(value) => Config.FillWithLocalReplays = value,
374+
"Fill remaining auto slots with local replays when there aren't enough online replays to load.");
375+
369376

370377

371378
Menus.AddNew(MenuDisplayMode.Both, settingsMenu,

Distance.ReplayIntensifies/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("1.2.1.1")]
36-
[assembly: AssemblyFileVersion("1.2.1.1")]
35+
[assembly: AssemblyVersion("1.2.2.1")]
36+
[assembly: AssemblyFileVersion("1.2.2.1")]
3737

3838

3939

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This started off as a port of Reherc's [Replay Limit Breaker](https://github.com
1818
* Change the minimum and maximum car Level of Detail (LOD), to help improve performance or up the graphics.
1919
* Disable clamping opponent car colors, so that ultrabright color presets can be seen (this affects Online mode cars as well).
2020
* Change whether the data materialization effect for car spawning is used when racing ghosts.
21+
* Fill remaining auto slots with local replays when there aren't enough online replays to load.
2122

2223
### Steam Rivals
2324

0 commit comments

Comments
 (0)