Skip to content

Commit af5661e

Browse files
authored
Merge pull request #26 from Gtt1229/Dev
Add MMR Data
2 parents 56f2380 + 53d8f9f commit af5661e

6 files changed

Lines changed: 328 additions & 3 deletions

File tree

RocketLeagueAssistant/RocketLeagueAssistant.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ void RocketLeagueAssistant::onLoad()
3535
cvarManager->registerCvar("matchCountdown_enabled", "1", "Enable Match Countdown Webhook", true, true, 0, true, 1);
3636
cvarManager->registerCvar("endGameCountdown_enabled", "1", "Enable Match End Webhook", true, true, 0, true, 1);
3737
cvarManager->registerCvar("matchEnd_enabled", "1", "Enable Match End Webhook", true, true, 0, true, 1);
38+
cvarManager->registerCvar("mmr_enabled", "1", "Enable MMR/Rank Data in JSON", true, true, 0, true, 1);
3839
cvarManager->registerCvar("exit_enabled", "1", "Enable Exit Webhook", true, true, 0, true, 1);
3940
cvarManager->registerCvar("isReplay", "0", "Replay boolean", true, true, 0, true, 1);
4041
cvarManager->registerCvar("hideURL", "false", "HideURL boolean", true, true, 0, true, 1);
@@ -129,6 +130,8 @@ void RocketLeagueAssistant::LoadHooks()
129130
//Not sure which of these is best, the EventMatchEnded is only called if the podium is shown, so added another for good measure.
130131
gameWrapper->HookEvent("Function TAGame.GameEvent_Soccar_TA.OnMatchEnded", std::bind(&RocketLeagueAssistant::MatchEndHook, this, std::placeholders::_1));
131132
gameWrapper->HookEvent("Function TAGame.PRI_TA.ServerVoteToForfeit", std::bind(&RocketLeagueAssistant::MatchEndHook, this, std::placeholders::_1));
133+
// Hook for updated MMR data
134+
gameWrapper->HookEvent("Function TAGame.GameEvent_Soccar_TA.OnMatchWinnerSet", std::bind(&RocketLeagueAssistant::OnMatchWinnerSet, this, std::placeholders::_1));
132135
//
133136

134137
//Main Menu
@@ -707,7 +710,15 @@ void RocketLeagueAssistant::MatchEndHook(std::string name)
707710
//Get match end automation url, transform, and convert to string
708711
std::string event = "matchEnded";
709712
LOG("Using matchend Hook");
710-
SendCommands(event);
713+
714+
// Check if MMR is enabled
715+
CVarWrapper mmrEnabledCvar = cvarManager->getCvar("mmr_enabled");
716+
bool mmrEnabled = mmrEnabledCvar && mmrEnabledCvar.getBoolValue();
717+
718+
// Only send immediately if MMR is disabled
719+
if (!mmrEnabled) {
720+
SendCommands(event);
721+
}
711722
if ( endGameCountdown == true) {
712723
endGameCountdown = false;
713724
return;

RocketLeagueAssistant/RocketLeagueAssistant.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ class RocketLeagueAssistant: public BakkesMod::Plugin::BakkesModPlugin, public B
4040
void Replay(std::string name);
4141
void NotReplay(std::string name);
4242
void SendCommands(std::string event);
43+
std::string GetMMRData();
44+
void OnMatchWinnerSet(std::string eventName);
45+
void CheckMMRAndSend(int retryCount);
4346
void PodiumMode(std::string name);
4447
std::string GenWebHook();
4548
std::string GenAutomationID();

RocketLeagueAssistant/RocketLeagueAssistantGUI.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ void RocketLeagueAssistant::RenderSettings() {
4444

4545
CVarWrapper exitEnableCvar = cvarManager->getCvar("exit_enabled");
4646

47+
CVarWrapper mmrEnabledCvar = cvarManager->getCvar("mmr_enabled");
48+
4749
CVarWrapper haJsonURLCvargui = cvarManager->getCvar("ha_jsonURL");
4850

4951
CVarWrapper haHaBaseURLCvargui = cvarManager->getCvar("ha_haBaseURL");
@@ -65,6 +67,7 @@ void RocketLeagueAssistant::RenderSettings() {
6567
if (!mainmenuEnableCvar) { return; }
6668
if (!matchCountdownEnableCvar) { return; }
6769
if (!exitEnableCvar) { return; }
70+
if (!mmrEnabledCvar) { return; }
6871

6972

7073

@@ -212,6 +215,17 @@ void RocketLeagueAssistant::RenderSettings() {
212215
ImGui::SetTooltip("Toggle Webhook");
213216
}
214217

218+
//MMR Data hook Gui
219+
220+
bool mmrEnabled = mmrEnabledCvar.getBoolValue();
221+
222+
if (ImGui::Checkbox("Enable MMR/Rank Data in Match End Webhook", &mmrEnabled)) {
223+
mmrEnabledCvar.setValue(mmrEnabled);
224+
}
225+
if (ImGui::IsItemHovered()) {
226+
ImGui::SetTooltip("Include comprehensive MMR and rank data for all playlists when match ends");
227+
}
228+
215229

216230

217231
//Exit hook Gui

RocketLeagueAssistant/SendCommands.cpp

Lines changed: 298 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ void RocketLeagueAssistant::SendCommands(std::string event)
5656
otherScoreString = std::to_string(otherScore);
5757
}
5858

59+
// Get MMR data if enabled
60+
std::string mmrDataString = "";
61+
CVarWrapper mmrEnabledCvar = cvarManager->getCvar("mmr_enabled");
62+
if (mmrEnabledCvar && mmrEnabledCvar.getBoolValue() && (event == "matchEnded")) {
63+
mmrDataString = ", " + GetMMRData();
64+
}
65+
5966
//JSON formatting
6067
CurlRequest req;
6168

@@ -77,7 +84,7 @@ void RocketLeagueAssistant::SendCommands(std::string event)
7784
},
7885
"score": )T" + otherScoreString + R"T(
7986
}
80-
}
87+
})T" + mmrDataString + R"T(
8188
}
8289
8390
)T";
@@ -104,4 +111,294 @@ void RocketLeagueAssistant::SendCommands(std::string event)
104111
{
105112
LOG("Body result(Generally empty): {}", result);
106113
});
114+
}
115+
116+
std::string RocketLeagueAssistant::GetMMRData()
117+
{
118+
try {
119+
// Only get MMR data for online games (not freeplay or replays)
120+
if (!gameWrapper->IsInOnlineGame()) {
121+
LOG("Not in online game, skipping MMR data");
122+
return "";
123+
}
124+
125+
MMRWrapper mmrWrapper = gameWrapper->GetMMRWrapper();
126+
if (mmrWrapper.memory_address == 0) {
127+
LOG("MMRWrapper is null");
128+
return "";
129+
}
130+
131+
ServerWrapper server = gameWrapper->GetCurrentGameState();
132+
if (!server) {
133+
LOG("Server is null");
134+
return "";
135+
}
136+
137+
auto primaryPlayer = server.GetLocalPrimaryPlayer();
138+
if (!primaryPlayer) {
139+
LOG("Primary player is null");
140+
return "";
141+
}
142+
143+
auto playerPRI = primaryPlayer.GetPRI();
144+
if (!playerPRI) {
145+
LOG("Player PRI is null");
146+
return "";
147+
}
148+
149+
UniqueIDWrapper playerUID = playerPRI.GetUniqueIdWrapper();
150+
151+
int currentPlaylist = mmrWrapper.GetCurrentPlaylist();
152+
153+
// Check if MMR is synced and not currently syncing
154+
bool isSynced = mmrWrapper.IsSynced(playerUID, currentPlaylist);
155+
bool isSyncing = mmrWrapper.IsSyncing(playerUID);
156+
157+
if (!isSynced || isSyncing) {
158+
LOG("MMR not synced or currently syncing, skipping MMR data");
159+
return "";
160+
}
161+
162+
// Get current MMR and rank for the current playlist
163+
float currentMMRFloat = mmrWrapper.GetPlayerMMR(playerUID, currentPlaylist);
164+
int currentMMR = static_cast<int>(std::round(currentMMRFloat));
165+
SkillRank currentRank = mmrWrapper.GetPlayerRank(playerUID, currentPlaylist);
166+
167+
// Map playlist IDs to names
168+
std::string playlistName = "Unknown";
169+
switch (currentPlaylist) {
170+
case 10: playlistName = "Solo Duel"; break;
171+
case 11: playlistName = "Doubles"; break;
172+
case 13: playlistName = "Standard"; break;
173+
case 27: playlistName = "Hoops"; break;
174+
case 28: playlistName = "Rumble"; break;
175+
case 29: playlistName = "Dropshot"; break;
176+
case 30: playlistName = "Snow Day"; break;
177+
case 34: playlistName = "Tournaments"; break;
178+
default: playlistName = "Playlist_" + std::to_string(currentPlaylist); break;
179+
}
180+
181+
// Map tier to rank name
182+
std::string rankName = "Unranked";
183+
if (currentRank.Tier > 0 && currentRank.Tier <= 22) {
184+
const std::vector<std::string> rankNames = {
185+
"Unranked", "Bronze I", "Bronze II", "Bronze III",
186+
"Silver I", "Silver II", "Silver III",
187+
"Gold I", "Gold II", "Gold III",
188+
"Platinum I", "Platinum II", "Platinum III",
189+
"Diamond I", "Diamond II", "Diamond III",
190+
"Champion I", "Champion II", "Champion III",
191+
"Grand Champion I", "Grand Champion II", "Grand Champion III",
192+
"Supersonic Legend"
193+
};
194+
rankName = rankNames[currentRank.Tier];
195+
196+
// Add division info for ranks
197+
if (currentRank.Tier < 22 && currentRank.Division >= 0 && currentRank.Division < 4) {
198+
rankName += " Div " + std::to_string(currentRank.Division + 1);
199+
}
200+
}
201+
202+
// Get player name
203+
std::string playerName = playerPRI.GetPlayerName().ToString();
204+
205+
// Get playlist name
206+
auto getPlaylistName = [](int playlistId) -> std::string {
207+
switch (playlistId) {
208+
case 10: return "Solo_Duel";
209+
case 11: return "Doubles";
210+
case 13: return "Standard";
211+
case 27: return "Hoops";
212+
case 28: return "Rumble";
213+
case 29: return "Dropshot";
214+
case 30: return "Snow_Day";
215+
case 34: return "Tournaments";
216+
default: return "Playlist_" + std::to_string(playlistId);
217+
}
218+
};
219+
220+
// Get rank name
221+
auto getRankName = [](int tier, int division) -> std::string {
222+
if (tier <= 0 || tier > 22) return "Unranked";
223+
224+
const std::vector<std::string> rankNames = {
225+
"Unranked", "Bronze I", "Bronze II", "Bronze III",
226+
"Silver I", "Silver II", "Silver III",
227+
"Gold I", "Gold II", "Gold III",
228+
"Platinum I", "Platinum II", "Platinum III",
229+
"Diamond I", "Diamond II", "Diamond III",
230+
"Champion I", "Champion II", "Champion III",
231+
"Grand Champion I", "Grand Champion II", "Grand Champion III",
232+
"Supersonic Legend"
233+
};
234+
235+
std::string rankName = rankNames[tier];
236+
237+
// Add division info for ranks
238+
if (tier < 22 && division >= 0 && division < 4) {
239+
rankName += " Div " + std::to_string(division + 1);
240+
}
241+
242+
return rankName;
243+
};
244+
245+
// Get current playlist info
246+
std::string currentPlaylistName = getPlaylistName(currentPlaylist);
247+
std::string currentRankName = getRankName(currentRank.Tier, currentRank.Division);
248+
249+
// MMR data JSON with all playlists
250+
std::string mmrDataJson = R"T("MMRData": {
251+
"player_data": {
252+
"name": ")T" + playerName + R"T(",
253+
"uid": ")T" + playerUID.GetIdString() + R"T("
254+
},
255+
"current_playlist": {
256+
"id": )T" + std::to_string(currentPlaylist) + R"T(,
257+
"name": ")T" + currentPlaylistName + R"T(",
258+
"mmr": )T" + std::to_string(currentMMR) + R"T(,
259+
"tier": )T" + std::to_string(currentRank.Tier) + R"T(,
260+
"division": )T" + std::to_string(currentRank.Division) + R"T(,
261+
"matches_played": )T" + std::to_string(currentRank.MatchesPlayed) + R"T(,
262+
"rank_name": ")T" + currentRankName + R"T(",
263+
"is_synced": )T" + (isSynced ? "true" : "false") + R"T(
264+
},
265+
"ranks": {)T";
266+
267+
// Get all playlist data
268+
std::vector<int> allPlaylists = {10, 11, 13, 27, 28, 29, 30, 34}; // All ranked playlists
269+
bool firstPlaylist = true;
270+
271+
for (int playlistId : allPlaylists) {
272+
try {
273+
// Skip if this is a playlist that doesn't exist for this player
274+
if (!mmrWrapper.IsRanked(playlistId)) {
275+
continue;
276+
}
277+
278+
float playlistMMRFloat = mmrWrapper.GetPlayerMMR(playerUID, playlistId);
279+
SkillRank playlistRank = mmrWrapper.GetPlayerRank(playerUID, playlistId);
280+
281+
// Skip if no data available
282+
if (playlistMMRFloat <= 0 && playlistRank.Tier <= 0) {
283+
continue;
284+
}
285+
286+
int playlistMMR = static_cast<int>(std::round(playlistMMRFloat));
287+
std::string playlistNameClean = getPlaylistName(playlistId);
288+
std::string playlistRankName = getRankName(playlistRank.Tier, playlistRank.Division);
289+
290+
if (!firstPlaylist) {
291+
mmrDataJson += ",";
292+
}
293+
firstPlaylist = false;
294+
295+
mmrDataJson += R"T(
296+
")T" + playlistNameClean + R"T(": {
297+
"mmr": )T" + std::to_string(playlistMMR) + R"T(,
298+
"tier": )T" + std::to_string(playlistRank.Tier) + R"T(,
299+
"division": )T" + std::to_string(playlistRank.Division) + R"T(,
300+
"matches_played": )T" + std::to_string(playlistRank.MatchesPlayed) + R"T(,
301+
"rank_name": ")T" + playlistRankName + R"T("
302+
})T";
303+
304+
} catch (...) {
305+
// Skip this playlist if there's an error getting data
306+
continue;
307+
}
308+
}
309+
310+
mmrDataJson += R"T(
311+
}
312+
})T";
313+
314+
LOG("MMR Data Generated: {}", mmrDataJson);
315+
return mmrDataJson;
316+
317+
} catch (const std::exception& e) {
318+
LOG("Exception in GetMMRData: {}", e.what());
319+
return "";
320+
} catch (...) {
321+
LOG("Unknown exception in GetMMRData");
322+
return "";
323+
}
324+
}
325+
326+
void RocketLeagueAssistant::OnMatchWinnerSet(std::string eventName)
327+
{
328+
// Check if MMR is enabled
329+
CVarWrapper mmrEnabledCvar = cvarManager->getCvar("mmr_enabled");
330+
if (!mmrEnabledCvar || !mmrEnabledCvar.getBoolValue()) {
331+
return;
332+
}
333+
334+
// Only for online ranked games
335+
ServerWrapper sw = gameWrapper->GetOnlineGame();
336+
if (sw.IsNull() || !sw.IsOnlineMultiplayer() || gameWrapper->IsInReplay()) {
337+
return;
338+
}
339+
340+
// Get the current playlist to check if it's ranked
341+
MMRWrapper mw = gameWrapper->GetMMRWrapper();
342+
if (mw.memory_address == 0) {
343+
return;
344+
}
345+
346+
int currentPlaylist = mw.GetCurrentPlaylist();
347+
if (mw.IsRanked(currentPlaylist)) {
348+
// retry
349+
CheckMMRAndSend(5);
350+
}
351+
}
352+
353+
void RocketLeagueAssistant::CheckMMRAndSend(int retryCount)
354+
{
355+
// Check if MMR is still enabled
356+
CVarWrapper mmrEnabledCvar = cvarManager->getCvar("mmr_enabled");
357+
if (!mmrEnabledCvar || !mmrEnabledCvar.getBoolValue()) {
358+
return;
359+
}
360+
361+
// Null checks
362+
ServerWrapper sw = gameWrapper->GetOnlineGame();
363+
if (sw.IsNull() || !sw.IsOnlineMultiplayer() || gameWrapper->IsInReplay()) {
364+
return;
365+
}
366+
367+
if (retryCount > 20 || retryCount < 0) {
368+
LOG("MMR retry count exceeded, giving up");
369+
return;
370+
}
371+
372+
MMRWrapper mw = gameWrapper->GetMMRWrapper();
373+
if (mw.memory_address == 0) {
374+
return;
375+
}
376+
377+
UniqueIDWrapper uniqueID = gameWrapper->GetUniqueID();
378+
int userPlaylist = mw.GetCurrentPlaylist();
379+
380+
if (userPlaylist != 0) {
381+
gameWrapper->SetTimeout([retryCount, this, uniqueID, userPlaylist](GameWrapper* gameWrapper) {
382+
MMRWrapper mmrWrapper = gameWrapper->GetMMRWrapper();
383+
384+
// Check if MMR is synced
385+
if (mmrWrapper.IsSynced(uniqueID, userPlaylist) && !mmrWrapper.IsSyncing(uniqueID)) {
386+
LOG("MMR is synced, sending updated match data");
387+
// Send the match ended command with MMR data
388+
SendCommands("matchEnded");
389+
} else {
390+
// Retry if not synced yet
391+
if (retryCount > 0) {
392+
LOG("MMR not synced yet, retrying... (attempts left: {})", retryCount);
393+
gameWrapper->SetTimeout([retryCount, this](GameWrapper* gameWrapper) {
394+
this->CheckMMRAndSend(retryCount - 1);
395+
}, 0.5f);
396+
} else {
397+
LOG("MMR sync timeout, sending without MMR data");
398+
// Send without MMR data as fallback
399+
SendCommands("matchEnded");
400+
}
401+
}
402+
}, 3.0f); // Wait if not synced
403+
}
107404
}

0 commit comments

Comments
 (0)