@@ -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