Compare commits

...

3 Commits

Author SHA1 Message Date
Jas Laferriere
4c4f2e0059 chore: update playback codes 2025-08-10 01:21:19 -04:00
Jas Laferriere
67af43efee chore: update rust extensions 2025-08-09 23:38:19 -04:00
Jas Laferriere
ff088d5329 feature: add rank display
this is a port of https://github.com/project-slippi/dolphin/pull/38
2025-08-09 22:47:00 -04:00
13 changed files with 267 additions and 13 deletions

View File

@@ -1147,7 +1147,7 @@ B07E01C9 3B200000
7D8903A6 4E800421
7C791B78 3A800000
3ABF0060 3AFE01CB
3B590034 1C74001F
3B590036 1C74001F
7F03BA14 1ED40024
7ED6AA14 88760001
2C030000 40820028
@@ -1161,7 +1161,7 @@ B07E01C9 3B200000
4E800421 3A940001
2C140004 4180FFA0
3A800000 3ABF0060
3AFE0247 3B5900CF
3AFE0247 3B5900D1
1C74000A 7F03BA14
1ED40024 7ED6AA14
88760001 2C030000
@@ -1176,7 +1176,7 @@ B07E01C9 3B200000
3A940001 2C140004
4180FFA0 3A800000
3ABF0060 3AFE026F
3B5900F7 1C74001D
3B5900F9 1C74001D
7F03BA14 1ED40024
7ED6AA14 88760001
2C030000 40820028
@@ -1196,7 +1196,7 @@ B07E01C9 3B200000
618CADF4 7D8903A6
4E800421 987E02E3
2C190000 41820044
387E02E4 38990394
387E02E4 38990396
38A00033 3D808000
618C31F4 7D8903A6
4E800421 3C60803D

View File

@@ -1146,7 +1146,7 @@ B07E01C9 3B200000
7D8903A6 4E800421
7C791B78 3A800000
3ABF0060 3AFE01CB
3B590034 1C74001F
3B590036 1C74001F
7F03BA14 1ED40024
7ED6AA14 88760001
2C030000 40820028
@@ -1160,7 +1160,7 @@ B07E01C9 3B200000
4E800421 3A940001
2C140004 4180FFA0
3A800000 3ABF0060
3AFE0247 3B5900CF
3AFE0247 3B5900D1
1C74000A 7F03BA14
1ED40024 7ED6AA14
88760001 2C030000
@@ -1175,7 +1175,7 @@ B07E01C9 3B200000
3A940001 2C140004
4180FFA0 3A800000
3ABF0060 3AFE026F
3B5900F7 1C74001D
3B5900F9 1C74001D
7F03BA14 1ED40024
7ED6AA14 88760001
2C030000 40820028
@@ -1195,7 +1195,7 @@ B07E01C9 3B200000
618CADF4 7D8903A6
4E800421 987E02E3
2C190000 41820044
387E02E4 38990394
387E02E4 38990396
38A00033 3D808000
618C31F4 7D8903A6
4E800421 3C60803D

Binary file not shown.

View File

@@ -150,6 +150,8 @@ struct SConfig : NonCopyable
bool bAdapterWarning = true;
bool bReduceTimingDispersion = false;
bool bSlippiPlayerRankDisplay = true;
bool bSlippiOpponentRankDisplay = true;
bool bSlippiJukeboxEnabled = true;
int iSlippiJukeboxVolume = 100;

View File

@@ -2148,6 +2148,8 @@ void CEXISlippi::prepareOnlineMatchState()
std::string oppName = "";
std::string p1Name = "";
std::string p2Name = "";
u8 localRank = 0;
u8 oppRank = 0;
u8 chatMessageId = 0;
u8 chatMessagePlayerIdx = 0;
u8 sentChatMessageId = 0;
@@ -2160,6 +2162,8 @@ void CEXISlippi::prepareOnlineMatchState()
// in CSS p1 is always current player and p2 is opponent
localPlayerName = p1Name = userInfo.displayName;
oppName = p2Name = "Player 2";
localRank = 8;
oppRank = 15;
#endif
SlippiDesyncRecoveryResp desync_recovery;
@@ -2488,6 +2492,16 @@ void CEXISlippi::prepareOnlineMatchState()
m_read_queue.push_back((u8)chatMessageId);
m_read_queue.push_back((u8)chatMessagePlayerIdx);
bool isRanked = lastSearch.mode == SlippiMatchmaking::OnlinePlayMode::RANKED;
if (isRanked)
{
localRank = (u8)matchmaking->GetPlayerRank(localPlayerIndex);
oppRank = (u8)matchmaking->GetPlayerRank(remotePlayerIndex);
}
m_read_queue.push_back(localRank);
m_read_queue.push_back(oppRank);
// Add player groupings for VS splash screen
leftTeamPlayers.resize(4, 0);
rightTeamPlayers.resize(4, 0);
@@ -3199,6 +3213,30 @@ void CEXISlippi::handleGetPlayerSettings()
m_read_queue.insert(m_read_queue.end(), data_ptr, data_ptr + sizeof(SlippiExiTypes::GetPlayerSettingsResponse));
}
void CEXISlippi::handleGetRank()
{
RustRankInfo rank_info = slprs_get_rank_info(slprs_exi_device_ptr);
m_read_queue.clear();
// Determine rank info visibility
u8 local_rank_enabled = static_cast<u8>(SConfig::GetInstance().bSlippiPlayerRankDisplay);
u8 opp_rank_enabled = static_cast<u8>(SConfig::GetInstance().bSlippiOpponentRankDisplay);
u8 rank_visibility = local_rank_enabled | (opp_rank_enabled << 1);
// Push rank data header
m_read_queue.push_back(rank_visibility);
m_read_queue.push_back(static_cast<u8>(rank_info.fetch_status));
// ERROR_LOG_FMT(SLIPPI_ONLINE, "Update count: {}", rank_info.rating_update_count);
// Push rank data
m_read_queue.push_back(static_cast<u8>(rank_info.rank));
appendWordToBuffer(&m_read_queue, *(u32 *)(&rank_info.rating_ordinal));
appendWordToBuffer(&m_read_queue, static_cast<u32>(rank_info.rating_update_count));
appendWordToBuffer(&m_read_queue, *(u32 *)(&rank_info.rating_change));
m_read_queue.push_back(static_cast<u8>(rank_info.rank_change));
}
void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize)
{
u8 *memPtr = Memory::GetPointer(_uAddr);
@@ -3390,6 +3428,16 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize)
slprs_jukebox_set_melee_music_volume(slprs_exi_device_ptr, args.volume);
break;
}
case CMD_GET_RANK:
{
handleGetRank();
break;
}
case CMD_FETCH_RANK:
{
slprs_fetch_match_result(slprs_exi_device_ptr, recentMmResult.id.c_str());
break;
}
default:
writeToFileAsync(&memPtr[bufLoc], payloadLen + 1, "");
m_slippiserver->write(&memPtr[bufLoc], payloadLen + 1);

View File

@@ -106,6 +106,9 @@ class CEXISlippi : public IEXIDevice
CMD_CHANGE_MUSIC_VOLUME = 0xD8,
CMD_PREMADE_TEXT_LENGTH = 0xE1,
CMD_PREMADE_TEXT_LOAD = 0xE2,
CMD_GET_RANK = 0xE3,
CMD_FETCH_RANK = 0xE4,
CMD_GET_RANK_VISIBILITY = 0xE5
};
enum
@@ -189,6 +192,9 @@ class CEXISlippi : public IEXIDevice
{CMD_CHANGE_MUSIC_VOLUME, static_cast<u32>(sizeof(SlippiExiTypes::ChangeMusicVolumeQuery) - 1)},
{CMD_PREMADE_TEXT_LENGTH, 0x2},
{CMD_PREMADE_TEXT_LOAD, 0x2},
{CMD_GET_RANK, 0x0},
{CMD_FETCH_RANK, 0x0},
{CMD_GET_RANK_VISIBILITY, 0x0},
};
struct WriteMessage
@@ -257,6 +263,7 @@ class CEXISlippi : public IEXIDevice
void handleCompleteSet(const SlippiExiTypes::ReportSetCompletionQuery &query);
void handleMatchStatusUpdate(const SlippiExiTypes::ReportMatchStatusUpdateQuery &query);
void handleGetPlayerSettings();
void handleGetRank();
// replay playback stuff
void prepareGameInfo(u8 *payload);

View File

@@ -531,6 +531,15 @@ void SlippiMatchmaking::handleMatchmaking()
playerInfo.chatMessages = m_user->GetDefaultChatMessages();
}
json rankEl = el["rank"];
if (rankEl.is_object())
{
playerInfo.rankedRating = rankEl.value("rating", 0.0f);
playerInfo.rankedUpdateCount = rankEl.value("updateCount", 0);
playerInfo.rankedGlobalPlacement = rankEl.value("globalPlacement", 0);
playerInfo.rankedRegionalPlacement = rankEl.value("regionalPlacement", 0);
}
m_playerInfo.push_back(playerInfo);
if (isLocal)
@@ -609,8 +618,11 @@ void SlippiMatchmaking::handleMatchmaking()
// Disconnect and destroy enet client to mm server
terminateMmConnection();
// Report to backend that we are attempting to connect to this match
slprs_exi_device_report_match_status(slprs_exi_device_ptr, matchId.c_str(), "connecting", true);
// If ranked, report to backend that we are attempting to connect to this match
if (matchId.find("mode.ranked") != std::string::npos)
{
slprs_exi_device_report_match_status(slprs_exi_device_ptr, matchId.c_str(), "connecting", true);
}
m_state = ProcessState::OPPONENT_CONNECTING;
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Opponent found. isDecider: %s", m_isHost ? "true" : "false");
@@ -631,6 +643,125 @@ std::vector<u16> SlippiMatchmaking::GetStages()
return m_allowedStages;
}
// This is kind of duplicate code from what exists in rust. Maybe eventually it should be remove
// and exist only on the rust side
SlippiMatchmaking::SlippiRank SlippiMatchmaking::GetPlayerRank(u8 port)
{
if (port >= m_playerInfo.size())
{
return SlippiRank::Unranked;
}
auto info = m_playerInfo[port];
float rating = info.rankedRating;
int updateCount = info.rankedUpdateCount;
int global = info.rankedGlobalPlacement;
int regional = info.rankedRegionalPlacement;
if (updateCount < 5)
{
return SlippiRank::Unranked;
}
if (rating <= 765.42f)
{
return SlippiRank::Bronze1;
}
if (rating > 765.43f && rating <= 913.71f)
{
return SlippiRank::Bronze2;
}
if (rating > 913.72f && rating <= 1054.86f)
{
return SlippiRank::Bronze3;
}
if (rating > 1054.87f && rating <= 1188.87f)
{
return SlippiRank::Silver1;
}
if (rating > 1188.88f && rating <= 1315.74f)
{
return SlippiRank::Silver2;
}
if (rating > 1315.75f && rating <= 1435.47f)
{
return SlippiRank::Silver3;
}
if (rating > 1435.48f && rating <= 1548.06f)
{
return SlippiRank::Gold1;
}
if (rating > 1548.07f && rating <= 1653.51f)
{
return SlippiRank::Gold2;
}
if (rating > 1653.52f && rating <= 1751.82f)
{
return SlippiRank::Gold3;
}
if (rating > 1751.83f && rating <= 1842.99f)
{
return SlippiRank::Platinum1;
}
if (rating > 1843.0f && rating <= 1927.02f)
{
return SlippiRank::Platinum2;
}
if (rating > 1927.03f && rating <= 2003.91f)
{
return SlippiRank::Platinum3;
}
if (rating > 2003.92f && rating <= 2073.66f)
{
return SlippiRank::Diamond1;
}
if (rating > 2073.67f && rating <= 2136.27f)
{
return SlippiRank::Diamond2;
}
if (rating > 2136.28f && rating <= 2191.74f)
{
return SlippiRank::Diamond3;
}
if (rating >= 2191.75f && global > 0 && regional > 0)
{
return SlippiRank::Grandmaster;
}
if (rating > 2191.75f && rating <= 2274.99f)
{
return SlippiRank::Master1;
}
if (rating > 2275.0f && rating <= 2350.0f)
{
return SlippiRank::Master2;
}
if (rating > 2350.0f)
{
return SlippiRank::Master3;
}
return SlippiRank::Unranked;
}
SlippiMatchmaking::MatchmakeResult SlippiMatchmaking::GetMatchmakeResult()
{
return m_mmResult;

View File

@@ -40,6 +40,30 @@ class SlippiMatchmaking
ERROR_ENCOUNTERED,
};
enum SlippiRank
{
Unranked,
Bronze1,
Bronze2,
Bronze3,
Silver1,
Silver2,
Silver3,
Gold1,
Gold2,
Gold3,
Platinum1,
Platinum2,
Platinum3,
Diamond1,
Diamond2,
Diamond3,
Master1,
Master2,
Master3,
Grandmaster
};
struct MatchSearchSettings
{
OnlinePlayMode mode = OnlinePlayMode::RANKED;
@@ -62,6 +86,7 @@ class SlippiMatchmaking
int LocalPlayerIndex();
std::vector<SlippiUser::UserInfo> GetPlayerInfo();
std::string GetPlayerName(u8 port);
SlippiRank GetPlayerRank(u8 port);
std::vector<u16> GetStages();
u8 RemotePlayerCount();
MatchmakeResult GetMatchmakeResult();

View File

@@ -33,6 +33,11 @@ class SlippiUser
std::vector<std::string> chatMessages;
bool isBot = false;
float rankedRating = 0;
int rankedUpdateCount = 0;
int rankedGlobalPlacement = 0;
int rankedRegionalPlacement = 0;
};
SlippiUser(uintptr_t rs_exi_device_ptr);

View File

@@ -101,6 +101,10 @@ void SlippiNetplayConfigPane::InitializeGUI()
_("Make inputs feel more console-like for overclocked GCC to USB "
"adapters at the cost of 1.6ms of input lag (2ms for single-port official adapter)."));
m_slippi_show_player_rank =
new wxCheckBox(this, wxID_ANY, _("Show your rank (Character Select and Ranked Setup Screen)"));
m_slippi_show_opponent_rank = new wxCheckBox(this, wxID_ANY, _("Show opponent's rank (Ranked Setup Screen"));
m_slippi_jukebox_enabled_checkbox = new wxCheckBox(this, wxID_ANY, _("Enable Music"));
// WASAPI does not work with this and we want a note for the user.
@@ -180,6 +184,17 @@ void SlippiNetplayConfigPane::InitializeGUI()
main_sizer->Add(sbSlippiInputSettings, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
main_sizer->AddSpacer(space5);
wxStaticBoxSizer *const sbRankSettings =
new wxStaticBoxSizer(wxVERTICAL, this, _("Rank Settings"));
sbRankSettings->AddSpacer(space5);
sbRankSettings->Add(m_slippi_show_player_rank, 0, wxLEFT | wxRIGHT, space5);
sbRankSettings->AddSpacer(space5);
sbRankSettings->Add(m_slippi_show_opponent_rank, 0, wxLEFT | wxRIGHT, space5);
sbRankSettings->AddSpacer(space5);
main_sizer->Add(sbRankSettings, 0, wxEXPAND | wxLEFT | wxRIGHT, space5);
main_sizer->AddSpacer(space5);
wxStaticBoxSizer *const sbSlippiJukeboxSettings =
new wxStaticBoxSizer(wxVERTICAL, this, _("Slippi Jukebox Settings (Beta)"));
sbSlippiJukeboxSettings->AddSpacer(space5);
@@ -233,6 +248,9 @@ void SlippiNetplayConfigPane::LoadGUIValues()
m_reduce_timing_dispersion_checkbox->SetValue(startup_params.bReduceTimingDispersion);
m_slippi_show_player_rank->SetValue(startup_params.bSlippiPlayerRankDisplay);
m_slippi_show_opponent_rank->SetValue(startup_params.bSlippiOpponentRankDisplay);
m_slippi_jukebox_enabled_checkbox->SetValue(enableJukebox);
m_slippi_jukebox_volume_slider->SetValue(startup_params.iSlippiJukeboxVolume);
m_jukebox_volume_text->SetLabel(wxString::Format("%d %%", startup_params.iSlippiJukeboxVolume));
@@ -267,6 +285,9 @@ void SlippiNetplayConfigPane::BindEvents()
m_reduce_timing_dispersion_checkbox->Bind(wxEVT_CHECKBOX, &SlippiNetplayConfigPane::OnReduceTimingDispersionToggle,
this);
m_slippi_show_player_rank->Bind(wxEVT_CHECKBOX, &SlippiNetplayConfigPane::OnToggleShowPlayerRank, this);
m_slippi_show_opponent_rank->Bind(wxEVT_CHECKBOX, &SlippiNetplayConfigPane::OnToggleShowOpponentRank, this);
m_slippi_jukebox_enabled_checkbox->Bind(wxEVT_CHECKBOX, &SlippiNetplayConfigPane::OnToggleJukeboxEnabled, this);
m_slippi_jukebox_volume_slider->Bind(wxEVT_SLIDER, &SlippiNetplayConfigPane::OnJukeboxVolumeUpdate, this);
}
@@ -353,6 +374,16 @@ void SlippiNetplayConfigPane::OnReduceTimingDispersionToggle(wxCommandEvent &eve
SConfig::GetInstance().bReduceTimingDispersion = m_reduce_timing_dispersion_checkbox->GetValue();
}
void SlippiNetplayConfigPane::OnToggleShowPlayerRank(wxCommandEvent &event)
{
SConfig::GetInstance().bSlippiPlayerRankDisplay = m_slippi_show_player_rank->GetValue();
}
void SlippiNetplayConfigPane::OnToggleShowOpponentRank(wxCommandEvent &event)
{
SConfig::GetInstance().bSlippiOpponentRankDisplay = m_slippi_show_opponent_rank->GetValue();
}
void SlippiNetplayConfigPane::OnToggleJukeboxEnabled(wxCommandEvent &event)
{
bool isEnabled = m_slippi_jukebox_enabled_checkbox->GetValue();

View File

@@ -42,6 +42,10 @@ class SlippiNetplayConfigPane final : public wxPanel
void OnQuickChatChanged(wxCommandEvent &event);
void OnReduceTimingDispersionToggle(wxCommandEvent &event);
void PopulateEnableChatChoiceBox();
void OnToggleJukeboxEnabled(wxCommandEvent &event);
void OnJukeboxVolumeUpdate(wxCommandEvent &event);
void OnToggleShowPlayerRank(wxCommandEvent &event);
void OnToggleShowOpponentRank(wxCommandEvent &event);
wxArrayString m_slippi_enable_quick_chat_strings;
@@ -59,11 +63,12 @@ class SlippiNetplayConfigPane final : public wxPanel
wxCheckBox *m_reduce_timing_dispersion_checkbox;
void OnToggleJukeboxEnabled(wxCommandEvent &event);
wxCheckBox *m_slippi_show_player_rank;
wxCheckBox *m_slippi_show_opponent_rank;
wxCheckBox *m_slippi_jukebox_enabled_checkbox;
DolphinSlider *m_slippi_jukebox_volume_slider;
wxStaticText *m_jukebox_volume_text;
void OnJukeboxVolumeUpdate(wxCommandEvent &event);
};
class SlippiPlaybackConfigPane final : public wxPanel