diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp index fab97d884f..290263f611 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp @@ -115,7 +115,7 @@ CEXISlippi::CEXISlippi() localSelections.Reset(); // Forces savestate to re-init regions when a new ISO is loaded - SlippiSavestate::shouldForceInit = true; + SlippiSavestate::shouldForceInit = true; // Update user file and then listen for User #ifndef IS_PLAYBACK @@ -448,15 +448,15 @@ void CEXISlippi::writeToFile(std::unique_ptr msg) // Get display names and connection codes from slippi netplay client if (slippi_netplay) - { - auto playerInfo = matchmaking->GetPlayerInfo(); + { + auto playerInfo = matchmaking->GetPlayerInfo(); - for (int i = 0; i < playerInfo.size(); i++) - { - slippi_names[i] = playerInfo[i].display_name; - slippi_connect_codes[i] = playerInfo[i].connect_code; - } - } + for (int i = 0; i < playerInfo.size(); i++) + { + slippi_names[i] = playerInfo[i].display_name; + slippi_connect_codes[i] = playerInfo[i].connect_code; + } + } } // If no file, do nothing @@ -1516,11 +1516,11 @@ void CEXISlippi::handleOnlineInputs(u8* payload) } if (isDisconnected()) - { - auto status = slippi_netplay->GetSlippiConnectStatus(); - m_read_queue.push_back(3); // Indicate we disconnected - return; - } + { + auto status = slippi_netplay->GetSlippiConnectStatus(); + m_read_queue.push_back(3); // Indicate we disconnected + return; + } if (shouldSkipOnlineFrame(frame)) { @@ -1620,6 +1620,17 @@ void CEXISlippi::handleSendInputs(u8* payload) int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3]; u8 delay = payload[4]; + // On the first frame sent, we need to queue up empty dummy pads for as many + // frames as we have delay + if (frame == 1) + { + for (int i = 1; i <= delay; i++) + { + auto empty = std::make_unique(i); + slippi_netplay->SendSlippiPad(std::move(empty)); + } + } + auto pad = std::make_unique(frame + delay, &payload[5]); slippi_netplay->SendSlippiPad(std::move(pad)); @@ -1641,55 +1652,56 @@ void CEXISlippi::prepareOpponentInputs(u8* payload) m_read_queue.push_back(frameResult); // Indicate a continue frame u8 remotePlayerCount = matchmaking->RemotePlayerCount(); - m_read_queue.push_back(remotePlayerCount); // Indicate the number of remote players + m_read_queue.push_back(remotePlayerCount); // Indicate the number of remote players - int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3]; + int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3]; - std::unique_ptr results[SLIPPI_REMOTE_PLAYER_MAX]; - int offset[SLIPPI_REMOTE_PLAYER_MAX]; - INFO_LOG(SLIPPI_ONLINE, "Preparing pad data for frame %d", frame); + std::unique_ptr results[SLIPPI_REMOTE_PLAYER_MAX]; + int offset[SLIPPI_REMOTE_PLAYER_MAX]; + INFO_LOG(SLIPPI_ONLINE, "Preparing pad data for frame %d", frame); - // Get pad data for each remote player and write each of their latest frame nums to the buf - for (int i = 0; i < remotePlayerCount; i++) - { - results[i] = slippi_netplay->GetSlippiRemotePad(frame, i); + // Get pad data for each remote player and write each of their latest frame nums to the buf + for (int i = 0; i < remotePlayerCount; i++) + { + results[i] = slippi_netplay->GetSlippiRemotePad(frame, i); - // determine offset from which to copy data - offset[i] = (results[i]->latestFrame - frame) * SLIPPI_PAD_FULL_SIZE; - offset[i] = offset[i] < 0 ? 0 : offset[i]; + // determine offset from which to copy data + offset[i] = (results[i]->latestFrame - frame) * SLIPPI_PAD_FULL_SIZE; + offset[i] = offset[i] < 0 ? 0 : offset[i]; - // add latest frame we are transfering to begining of return buf - int32_t latestFrame = results[i]->latestFrame; - if (latestFrame > frame) - latestFrame = frame; - appendWordToBuffer(&m_read_queue, *(u32 *)&latestFrame); - // INFO_LOG(SLIPPI_ONLINE, "Sending frame num %d for pIdx %d (offset: %d)", latestFrame, i, offset[i]); - } - // Send the current frame for any unused player slots. - for (int i = remotePlayerCount; i < SLIPPI_REMOTE_PLAYER_MAX; i++) - { - appendWordToBuffer(&m_read_queue, *(u32 *)&frame); - } + // add latest frame we are transfering to begining of return buf + int32_t latestFrame = results[i]->latestFrame; + if (latestFrame > frame) + latestFrame = frame; + appendWordToBuffer(&m_read_queue, *(u32*)&latestFrame); + // INFO_LOG(SLIPPI_ONLINE, "Sending frame num %d for pIdx %d (offset: %d)", latestFrame, i, + // offset[i]); + } + // Send the current frame for any unused player slots. + for (int i = remotePlayerCount; i < SLIPPI_REMOTE_PLAYER_MAX; i++) + { + appendWordToBuffer(&m_read_queue, *(u32*)&frame); + } - // copy pad data over - for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++) - { - std::vector tx; + // copy pad data over + for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++) + { + std::vector tx; - // Get pad data if this remote player exists - if (i < remotePlayerCount) - { - auto txStart = results[i]->data.begin() + offset[i]; - auto txEnd = results[i]->data.end(); - tx.insert(tx.end(), txStart, txEnd); - } + // Get pad data if this remote player exists + if (i < remotePlayerCount) + { + auto txStart = results[i]->data.begin() + offset[i]; + auto txEnd = results[i]->data.end(); + tx.insert(tx.end(), txStart, txEnd); + } - tx.resize(SLIPPI_PAD_FULL_SIZE * ROLLBACK_MAX_FRAMES, 0); + tx.resize(SLIPPI_PAD_FULL_SIZE * ROLLBACK_MAX_FRAMES, 0); - m_read_queue.insert(m_read_queue.end(), tx.begin(), tx.end()); - } + m_read_queue.insert(m_read_queue.end(), tx.begin(), tx.end()); + } - slippi_netplay->DropOldRemoteInputs(frame); + slippi_netplay->DropOldRemoteInputs(frame); // ERROR_LOG(SLIPPI_ONLINE, "EXI: [%d] %X %X %X %X %X %X %X %X", latestFrame, m_read_queue[5], // m_read_queue[6], m_read_queue[7], m_read_queue[8], m_read_queue[9], m_read_queue[10], @@ -1821,28 +1833,31 @@ void CEXISlippi::startFindMatch(u8* payload) void CEXISlippi::prepareOnlineMatchState() { - // This match block is a VS match with P1 Red Falco vs P2 Red Bowser vs P3 Young Link vs P4 Young Link - // on Battlefield. The proper values will be overwritten - static std::vector onlineMatchBlock = { - 0x32, 0x01, 0x86, 0x4C, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x6E, 0x00, 0x1F, 0x00, 0x00, - 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x15, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x15, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, - 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, - 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, - }; + // This match block is a VS match with P1 Red Falco vs P2 Red Bowser vs P3 Young Link vs P4 Young + // Link on Battlefield. The proper values will be overwritten + static std::vector onlineMatchBlock = { + 0x32, 0x01, 0x86, 0x4C, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x6E, 0x00, + 0x1F, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x78, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x05, 0x00, 0x04, + 0x01, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0x00, 0x15, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, + 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x15, 0x03, 0x04, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, + 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0x40, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, + 0x00, 0x78, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, + }; m_read_queue.clear(); @@ -1896,21 +1911,21 @@ void CEXISlippi::prepareOnlineMatchState() remotePlayersReady = true; #else remotePlayersReady = 1; - u8 remotePlayerCount = matchmaking->RemotePlayerCount(); - for (int i = 0; i < remotePlayerCount; i++) - { - if (!matchInfo->remotePlayerSelections[i].isCharacterSelected) - { - remotePlayersReady = 0; - } - } + u8 remotePlayerCount = matchmaking->RemotePlayerCount(); + for (int i = 0; i < remotePlayerCount; i++) + { + if (!matchInfo->remotePlayerSelections[i].isCharacterSelected) + { + remotePlayersReady = 0; + } + } - if (remotePlayerCount == 1) - { - auto isDecider = slippi_netplay->IsDecider(); - localPlayerIndex = isDecider ? 0 : 1; - remotePlayerIndex = isDecider ? 1 : 0; - } + if (remotePlayerCount == 1) + { + auto isDecider = slippi_netplay->IsDecider(); + localPlayerIndex = isDecider ? 0 : 1; + remotePlayerIndex = isDecider ? 1 : 0; + } #endif auto isDecider = slippi_netplay->IsDecider(); @@ -1922,25 +1937,25 @@ void CEXISlippi::prepareOnlineMatchState() #ifndef LOCAL_TESTING // If we get here, our opponent likely disconnected. Let's trigger a clean up handleConnectionCleanup(); - prepareOnlineMatchState(); // run again with new state + prepareOnlineMatchState(); // run again with new state return; #endif } // Here we are connected, check to see if we should init play session - if (!is_play_session_active) - { - std::vector uids; + if (!is_play_session_active) + { + std::vector uids; - auto mmPlayers = matchmaking->GetPlayerInfo(); - for (auto mmp : mmPlayers) - { - uids.push_back(mmp.uid); - } + auto mmPlayers = matchmaking->GetPlayerInfo(); + for (auto mmp : mmPlayers) + { + uids.push_back(mmp.uid); + } - game_reporter->StartNewSession(uids); + game_reporter->StartNewSession(uids); - is_play_session_active = true; - } + is_play_session_active = true; + } } else { @@ -1949,7 +1964,7 @@ void CEXISlippi::prepareOnlineMatchState() u32 rngOffset = 0; std::string localPlayerName = ""; - std::string oppName = ""; + std::string oppName = ""; std::string p1Name = ""; std::string p2Name = ""; u8 chatMessageId = 0; @@ -1957,83 +1972,83 @@ void CEXISlippi::prepareOnlineMatchState() u8 sentChatMessageId = 0; #ifdef LOCAL_TESTING - localPlayerIndex = 0; - chatMessageId = localChatMessageId; - chatMessagePlayerIdx = 0; - localChatMessageId = 0; - // in CSS p1 is always current player and p2 is opponent - localPlayerName = p1Name = "Player 1"; - oppName = p2Name = "Player 2"; + localPlayerIndex = 0; + chatMessageId = localChatMessageId; + chatMessagePlayerIdx = 0; + localChatMessageId = 0; + // in CSS p1 is always current player and p2 is opponent + localPlayerName = p1Name = "Player 1"; + oppName = p2Name = "Player 2"; #endif - m_read_queue.push_back(localPlayerReady); // Local player ready - m_read_queue.push_back(remotePlayersReady); // Remote players ready - m_read_queue.push_back(localPlayerIndex); // Local player index - m_read_queue.push_back(remotePlayerIndex); // Remote player index + m_read_queue.push_back(localPlayerReady); // Local player ready + m_read_queue.push_back(remotePlayersReady); // Remote players ready + m_read_queue.push_back(localPlayerIndex); // Local player index + m_read_queue.push_back(remotePlayerIndex); // Remote player index - // Set chat message if any - if (slippi_netplay) - { - auto remoteMessageSelection = slippi_netplay->GetSlippiRemoteChatMessage(); - chatMessageId = remoteMessageSelection.messageId; - chatMessagePlayerIdx = remoteMessageSelection.playerIdx; - sentChatMessageId = slippi_netplay->GetSlippiRemoteSentChatMessage(); - // in CSS p1 is always current player and p2 is opponent - localPlayerName = p1Name = userInfo.display_name; - } + // Set chat message if any + if (slippi_netplay) + { + auto remoteMessageSelection = slippi_netplay->GetSlippiRemoteChatMessage(); + chatMessageId = remoteMessageSelection.messageId; + chatMessagePlayerIdx = remoteMessageSelection.playerIdx; + sentChatMessageId = slippi_netplay->GetSlippiRemoteSentChatMessage(); + // in CSS p1 is always current player and p2 is opponent + localPlayerName = p1Name = userInfo.display_name; + } - auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT; + auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT; - std::vector leftTeamPlayers = {}; - std::vector rightTeamPlayers = {}; + std::vector leftTeamPlayers = {}; + std::vector rightTeamPlayers = {}; - if (localPlayerReady && remotePlayersReady) - { - auto isDecider = slippi_netplay->IsDecider(); - u8 remotePlayerCount = matchmaking->RemotePlayerCount(); - auto matchInfo = slippi_netplay->GetMatchInfo(); - SlippiPlayerSelections lps = matchInfo->localPlayerSelections; - auto rps = matchInfo->remotePlayerSelections; + if (localPlayerReady && remotePlayersReady) + { + auto isDecider = slippi_netplay->IsDecider(); + u8 remotePlayerCount = matchmaking->RemotePlayerCount(); + auto matchInfo = slippi_netplay->GetMatchInfo(); + SlippiPlayerSelections lps = matchInfo->localPlayerSelections; + auto rps = matchInfo->remotePlayerSelections; #ifdef LOCAL_TESTING - lps.playerIdx = 0; + lps.playerIdx = 0; - for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++) - { - if (i == 0) - { - rps[i].characterColor = 1; - rps[i].teamId = 1; - } - else - { - rps[i].characterColor = 2; - rps[i].teamId = 2; - } + for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++) + { + if (i == 0) + { + rps[i].characterColor = 1; + rps[i].teamId = 1; + } + else + { + rps[i].characterColor = 2; + rps[i].teamId = 2; + } - rps[i].characterId = 0x2 + i; - rps[i].playerIdx = i + 1; - rps[i].isCharacterSelected = true; - } + rps[i].characterId = 0x2 + i; + rps[i].playerIdx = i + 1; + rps[i].isCharacterSelected = true; + } - if (lastSearch.mode == SlippiMatchmaking::OnlinePlayMode::TEAMS) - { - remotePlayerCount = 4; - } + if (lastSearch.mode == SlippiMatchmaking::OnlinePlayMode::TEAMS) + { + remotePlayerCount = 4; + } - oppName = std::string("Player"); + oppName = std::string("Player"); #endif - // Check if someone is picking dumb characters in non-direct - auto localCharOk = lps.characterId < 26; - auto remoteCharOk = true; - INFO_LOG(SLIPPI_ONLINE, "remotePlayerCount: %d", remotePlayerCount); - for (int i = 0; i < remotePlayerCount; i++) - { - if (rps[i].characterId >= 26) - remoteCharOk = false; - } - if (lastSearch.mode < directMode && (!localCharOk || !remoteCharOk)) + // Check if someone is picking dumb characters in non-direct + auto localCharOk = lps.characterId < 26; + auto remoteCharOk = true; + INFO_LOG(SLIPPI_ONLINE, "remotePlayerCount: %d", remotePlayerCount); + for (int i = 0; i < remotePlayerCount; i++) + { + if (rps[i].characterId >= 26) + remoteCharOk = false; + } + if (lastSearch.mode < directMode && (!localCharOk || !remoteCharOk)) { // If we get here, someone is doing something bad, clear the lobby handleConnectionCleanup(); @@ -2045,98 +2060,99 @@ void CEXISlippi::prepareOnlineMatchState() // Overwrite local player character onlineMatchBlock[0x60 + (lps.playerIdx) * 0x24] = lps.characterId; - onlineMatchBlock[0x63 + (lps.playerIdx) * 0x24] = lps.characterColor; - onlineMatchBlock[0x67 + (lps.playerIdx) * 0x24] = 0; - onlineMatchBlock[0x69 + (lps.playerIdx) * 0x24] = lps.teamId; + onlineMatchBlock[0x63 + (lps.playerIdx) * 0x24] = lps.characterColor; + onlineMatchBlock[0x67 + (lps.playerIdx) * 0x24] = 0; + onlineMatchBlock[0x69 + (lps.playerIdx) * 0x24] = lps.teamId; - // Overwrite remote player character - for (int i = 0; i < remotePlayerCount; i++) - { - u8 idx = matchInfo->remotePlayerSelections[i].playerIdx; - onlineMatchBlock[0x60 + idx * 0x24] = matchInfo->remotePlayerSelections[i].characterId; + // Overwrite remote player character + for (int i = 0; i < remotePlayerCount; i++) + { + u8 idx = matchInfo->remotePlayerSelections[i].playerIdx; + onlineMatchBlock[0x60 + idx * 0x24] = matchInfo->remotePlayerSelections[i].characterId; - // Set Char Colors - onlineMatchBlock[0x63 + idx * 0x24] = matchInfo->remotePlayerSelections[i].characterColor; + // Set Char Colors + onlineMatchBlock[0x63 + idx * 0x24] = matchInfo->remotePlayerSelections[i].characterColor; - // Set Team Ids - onlineMatchBlock[0x69 + idx * 0x24] = matchInfo->remotePlayerSelections[i].teamId; - } + // Set Team Ids + onlineMatchBlock[0x69 + idx * 0x24] = matchInfo->remotePlayerSelections[i].teamId; + } - // Handle Singles/Teams specific logic - if (remotePlayerCount < 3) - { - onlineMatchBlock[0x8] = 0; // is Teams = false + // Handle Singles/Teams specific logic + if (remotePlayerCount < 3) + { + onlineMatchBlock[0x8] = 0; // is Teams = false - // Set p3/p4 player type to none - onlineMatchBlock[0x61 + 2 * 0x24] = 3; - onlineMatchBlock[0x61 + 3 * 0x24] = 3; + // Set p3/p4 player type to none + onlineMatchBlock[0x61 + 2 * 0x24] = 3; + onlineMatchBlock[0x61 + 3 * 0x24] = 3; - // Make one character lighter if same character, same color - bool isSheikVsZelda = lps.characterId == 0x12 && rps[0].characterId == 0x13 || - lps.characterId == 0x13 && rps[0].characterId == 0x12; - bool charMatch = lps.characterId == rps[0].characterId || isSheikVsZelda; - bool colMatch = lps.characterColor == rps[0].characterColor; + // Make one character lighter if same character, same color + bool isSheikVsZelda = lps.characterId == 0x12 && rps[0].characterId == 0x13 || + lps.characterId == 0x13 && rps[0].characterId == 0x12; + bool charMatch = lps.characterId == rps[0].characterId || isSheikVsZelda; + bool colMatch = lps.characterColor == rps[0].characterColor; - onlineMatchBlock[0x67 + 0x24] = charMatch && colMatch ? 1 : 0; - } - else - { - onlineMatchBlock[0x8] = 1; // is Teams = true + onlineMatchBlock[0x67 + 0x24] = charMatch && colMatch ? 1 : 0; + } + else + { + onlineMatchBlock[0x8] = 1; // is Teams = true - // Set p3/p4 player type to human - onlineMatchBlock[0x61 + 2 * 0x24] = 0; - onlineMatchBlock[0x61 + 3 * 0x24] = 0; + // Set p3/p4 player type to human + onlineMatchBlock[0x61 + 2 * 0x24] = 0; + onlineMatchBlock[0x61 + 3 * 0x24] = 0; - // Set alt color to light/dark costume for multiples of the same character on a team - int characterCount[26][3] = {0}; - for (int i = 0; i < 4; i++) - { - int charId = onlineMatchBlock[0x60 + i * 0x24]; - int teamId = onlineMatchBlock[0x69 + i * 0x24]; - onlineMatchBlock[0x67 + i * 0x24] = characterCount[charId][teamId]; - characterCount[charId][teamId]++; - } - } + // Set alt color to light/dark costume for multiples of the same character on a team + int characterCount[26][3] = {0}; + for (int i = 0; i < 4; i++) + { + int charId = onlineMatchBlock[0x60 + i * 0x24]; + int teamId = onlineMatchBlock[0x69 + i * 0x24]; + onlineMatchBlock[0x67 + i * 0x24] = characterCount[charId][teamId]; + characterCount[charId][teamId]++; + } + } - // Overwrite stage - u16 stageId; - if (isDecider) - { - stageId = lps.isStageSelected ? lps.stageId : rps[0].stageId; - } - else - { - stageId = rps[0].isStageSelected ? rps[0].stageId : lps.stageId; - } + // Overwrite stage + u16 stageId; + if (isDecider) + { + stageId = lps.isStageSelected ? lps.stageId : rps[0].stageId; + } + else + { + stageId = rps[0].isStageSelected ? rps[0].stageId : lps.stageId; + } - u16 *stage = (u16 *)&onlineMatchBlock[0xE]; - *stage = Common::swap16(stageId); + u16* stage = (u16*)&onlineMatchBlock[0xE]; + *stage = Common::swap16(stageId); - // Set rng offset - rngOffset = isDecider ? lps.rngOffset : rps[0].rngOffset; - WARN_LOG(SLIPPI_ONLINE, "Rng Offset: 0x%x", rngOffset); - WARN_LOG(SLIPPI_ONLINE, "P1 Char: 0x%X, P2 Char: 0x%X", onlineMatchBlock[0x60], onlineMatchBlock[0x84]); + // Set rng offset + rngOffset = isDecider ? lps.rngOffset : rps[0].rngOffset; + WARN_LOG(SLIPPI_ONLINE, "Rng Offset: 0x%x", rngOffset); + WARN_LOG(SLIPPI_ONLINE, "P1 Char: 0x%X, P2 Char: 0x%X", onlineMatchBlock[0x60], + onlineMatchBlock[0x84]); - // Turn pause on in direct, off in everything else - u8 *gameBitField3 = (u8 *)&onlineMatchBlock[2]; - *gameBitField3 = lastSearch.mode >= directMode ? *gameBitField3 & 0xF7 : *gameBitField3 | 0x8; - //*gameBitField3 = *gameBitField3 | 0x8; + // Turn pause on in direct, off in everything else + u8* gameBitField3 = (u8*)&onlineMatchBlock[2]; + *gameBitField3 = lastSearch.mode >= directMode ? *gameBitField3 & 0xF7 : *gameBitField3 | 0x8; + //*gameBitField3 = *gameBitField3 | 0x8; - // Group players into left/right side for team splash screen display - for (int i = 0; i < 4; i++) - { - int teamId = onlineMatchBlock[0x69 + i * 0x24]; - if (teamId == lps.teamId) - leftTeamPlayers.push_back(i); - else - rightTeamPlayers.push_back(i); - } - int leftTeamSize = leftTeamPlayers.size(); - int rightTeamSize = rightTeamPlayers.size(); - leftTeamPlayers.resize(4, 0); - rightTeamPlayers.resize(4, 0); - leftTeamPlayers[3] = leftTeamSize; - rightTeamPlayers[3] = rightTeamSize; + // Group players into left/right side for team splash screen display + for (int i = 0; i < 4; i++) + { + int teamId = onlineMatchBlock[0x69 + i * 0x24]; + if (teamId == lps.teamId) + leftTeamPlayers.push_back(i); + else + rightTeamPlayers.push_back(i); + } + int leftTeamSize = leftTeamPlayers.size(); + int rightTeamSize = rightTeamPlayers.size(); + leftTeamPlayers.resize(4, 0); + rightTeamPlayers.resize(4, 0); + leftTeamPlayers[3] = leftTeamSize; + rightTeamPlayers[3] = rightTeamSize; } // Add rng offset to output @@ -2148,52 +2164,52 @@ void CEXISlippi::prepareOnlineMatchState() // Add chat messages id m_read_queue.push_back((u8)sentChatMessageId); m_read_queue.push_back((u8)chatMessageId); -m_read_queue.push_back((u8)chatMessagePlayerIdx); + m_read_queue.push_back((u8)chatMessagePlayerIdx); - // Add player groupings for VS splash screen - leftTeamPlayers.resize(4, 0); - rightTeamPlayers.resize(4, 0); - m_read_queue.insert(m_read_queue.end(), leftTeamPlayers.begin(), leftTeamPlayers.end()); - m_read_queue.insert(m_read_queue.end(), rightTeamPlayers.begin(), rightTeamPlayers.end()); + // Add player groupings for VS splash screen + leftTeamPlayers.resize(4, 0); + rightTeamPlayers.resize(4, 0); + m_read_queue.insert(m_read_queue.end(), leftTeamPlayers.begin(), leftTeamPlayers.end()); + m_read_queue.insert(m_read_queue.end(), rightTeamPlayers.begin(), rightTeamPlayers.end()); - // Add names to output - // Always send static local player name - localPlayerName = ConvertStringForGame(localPlayerName, MAX_NAME_LENGTH); - m_read_queue.insert(m_read_queue.end(), localPlayerName.begin(), localPlayerName.end()); + // Add names to output + // Always send static local player name + localPlayerName = ConvertStringForGame(localPlayerName, MAX_NAME_LENGTH); + m_read_queue.insert(m_read_queue.end(), localPlayerName.begin(), localPlayerName.end()); #ifdef LOCAL_TESTING - std::string defaultNames[] = {"Player 1", "Player 2", "Player 3", "Player 4"}; + std::string defaultNames[] = {"Player 1", "Player 2", "Player 3", "Player 4"}; #endif - for (int i = 0; i < 4; i++) - { - std::string name = matchmaking->GetPlayerName(i); + for (int i = 0; i < 4; i++) + { + std::string name = matchmaking->GetPlayerName(i); #ifdef LOCAL_TESTING - name = defaultNames[i]; + name = defaultNames[i]; #endif - name = ConvertStringForGame(name, MAX_NAME_LENGTH); - m_read_queue.insert(m_read_queue.end(), name.begin(), name.end()); - } + name = ConvertStringForGame(name, MAX_NAME_LENGTH); + m_read_queue.insert(m_read_queue.end(), name.begin(), name.end()); + } - // Create the opponent string using the names of all players on opposing teams - int teamIdx = onlineMatchBlock[0x69 + localPlayerIndex * 0x24]; - std::string oppText = ""; - for (int i = 0; i < 4; i++) - { - if (i == localPlayerIndex) - continue; + // Create the opponent string using the names of all players on opposing teams + int teamIdx = onlineMatchBlock[0x69 + localPlayerIndex * 0x24]; + std::string oppText = ""; + for (int i = 0; i < 4; i++) + { + if (i == localPlayerIndex) + continue; - if (onlineMatchBlock[0x69 + i * 0x24] != teamIdx) - { - if (oppText != "") - oppText += "/"; + if (onlineMatchBlock[0x69 + i * 0x24] != teamIdx) + { + if (oppText != "") + oppText += "/"; - oppText += matchmaking->GetPlayerName(i); - } - } - if (matchmaking->RemotePlayerCount() == 1) - oppText = matchmaking->GetPlayerName(remotePlayerIndex); - oppName = ConvertStringForGame(oppText, MAX_NAME_LENGTH * 2 + 1); + oppText += matchmaking->GetPlayerName(i); + } + } + if (matchmaking->RemotePlayerCount() == 1) + oppText = matchmaking->GetPlayerName(remotePlayerIndex); + oppName = ConvertStringForGame(oppText, MAX_NAME_LENGTH * 2 + 1); m_read_queue.insert(m_read_queue.end(), oppName.begin(), oppName.end()); // Add error message if there is one @@ -2237,12 +2253,12 @@ void CEXISlippi::setMatchSelections(u8* payload) SlippiPlayerSelections s; s.teamId = payload[0]; - s.characterId = payload[1]; - s.characterColor = payload[2]; - s.isCharacterSelected = payload[3]; + s.characterId = payload[1]; + s.characterColor = payload[2]; + s.isCharacterSelected = payload[3]; - s.stageId = Common::swap16(&payload[4]); - u8 stageSelectOption = payload[6]; + s.stageId = Common::swap16(&payload[4]); + u8 stageSelectOption = payload[6]; s.isStageSelected = stageSelectOption == 1 || stageSelectOption == 3; if (stageSelectOption == 3) @@ -2251,16 +2267,16 @@ void CEXISlippi::setMatchSelections(u8* payload) s.stageId = getRandomStage(); } - INFO_LOG(SLIPPI, "LPS set char: %d, iSS: %d, %d, stage: %d, team: %d", s.isCharacterSelected, stageSelectOption, - s.isStageSelected, s.stageId, s.teamId); + INFO_LOG(SLIPPI, "LPS set char: %d, iSS: %d, %d, stage: %d, team: %d", s.isCharacterSelected, + stageSelectOption, s.isStageSelected, s.stageId, s.teamId); - s.rngOffset = generator() % 0xFFFF; + s.rngOffset = generator() % 0xFFFF; - if (matchmaking->LocalPlayerIndex() == 1 && firstMatch) - { - firstMatch = false; - s.stageId = getRandomStage(); - } + if (matchmaking->LocalPlayerIndex() == 1 && firstMatch) + { + firstMatch = false; + s.stageId = getRandomStage(); + } // Merge these selections localSelections.Merge(s); @@ -2432,7 +2448,7 @@ void CEXISlippi::handleConnectionCleanup() forcedError.clear(); // Reset play session - is_play_session_active = false; + is_play_session_active = false; firstMatch = true; #ifdef LOCAL_TESTING @@ -2451,28 +2467,28 @@ void CEXISlippi::prepareNewSeed() appendWordToBuffer(&m_read_queue, newSeed); } -void CEXISlippi::handleReportGame(u8 *payload) +void CEXISlippi::handleReportGame(u8* payload) { - SlippiGameReporter::GameReport r; - r.duration_frames = Common::swap32(&payload[0]); + SlippiGameReporter::GameReport r; + r.duration_frames = Common::swap32(&payload[0]); - //ERROR_LOG(SLIPPI_ONLINE, "Frames: %d", r.duration_frames); + // ERROR_LOG(SLIPPI_ONLINE, "Frames: %d", r.duration_frames); - for (auto i = 0; i < 2; ++i) - { - SlippiGameReporter::PlayerReport p; - auto offset = i * 6; - p.stocks_remaining = payload[5 + offset]; + for (auto i = 0; i < 2; ++i) + { + SlippiGameReporter::PlayerReport p; + auto offset = i * 6; + p.stocks_remaining = payload[5 + offset]; - auto swappedDamageDone = Common::swap32(&payload[6 + offset]); - p.damage_done = *(float *)&swappedDamageDone; + auto swappedDamageDone = Common::swap32(&payload[6 + offset]); + p.damage_done = *(float*)&swappedDamageDone; - //ERROR_LOG(SLIPPI_ONLINE, "Stocks: %d, DamageDone: %f", p.stocks_remaining, p.damage_done); + // ERROR_LOG(SLIPPI_ONLINE, "Stocks: %d, DamageDone: %f", p.stocks_remaining, p.damage_done); - r.players.push_back(p); - } + r.players.push_back(p); + } - game_reporter->StartReport(r); + game_reporter->StartReport(r); } void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize) @@ -2602,8 +2618,8 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize) prepareNewSeed(); break; case CMD_REPORT_GAME: - handleReportGame(&memPtr[bufLoc + 1]); - break; + handleReportGame(&memPtr[bufLoc + 1]); + break; default: writeToFileAsync(&memPtr[bufLoc], payloadLen + 1, ""); SlippiSpectateServer::getInstance().write(&memPtr[bufLoc], payloadLen + 1); diff --git a/Source/Core/Core/Slippi/SlippiGameReporter.cpp b/Source/Core/Core/Slippi/SlippiGameReporter.cpp index c7d3c32034..8fba33b17a 100644 --- a/Source/Core/Core/Slippi/SlippiGameReporter.cpp +++ b/Source/Core/Core/Slippi/SlippiGameReporter.cpp @@ -16,123 +16,123 @@ #include using json = nlohmann::json; -static size_t receive(char *ptr, size_t size, size_t nmemb, void *rcvBuf) +static size_t receive(char* ptr, size_t size, size_t nmemb, void* rcvBuf) { - size_t len = size * nmemb; - INFO_LOG(SLIPPI_ONLINE, "[User] Received data: %d", len); + size_t len = size * nmemb; + INFO_LOG(SLIPPI_ONLINE, "[User] Received data: %d", len); - std::string *buf = (std::string *)rcvBuf; + std::string* buf = (std::string*)rcvBuf; - buf->insert(buf->end(), ptr, ptr + len); + buf->insert(buf->end(), ptr, ptr + len); - return len; + return len; } -SlippiGameReporter::SlippiGameReporter(SlippiUser *user) +SlippiGameReporter::SlippiGameReporter(SlippiUser* user) { - CURL *curl = curl_easy_init(); - if (curl) - { - // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &receive); - curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000); + CURL* curl = curl_easy_init(); + if (curl) + { + // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &receive); + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000); - // Set up HTTP Headers - m_curl_header_list = curl_slist_append(m_curl_header_list, "Content-Type: application/json"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_curl_header_list); + // Set up HTTP Headers + m_curl_header_list = curl_slist_append(m_curl_header_list, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, m_curl_header_list); #ifdef _WIN32 - // ALPN support is enabled by default but requires Windows >= 8.1. - curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, false); + // ALPN support is enabled by default but requires Windows >= 8.1. + curl_easy_setopt(curl, CURLOPT_SSL_ENABLE_ALPN, false); #endif - m_curl = curl; - } + m_curl = curl; + } - m_user = user; + m_user = user; - run_thread = true; - reportingThread = std::thread(&SlippiGameReporter::ReportThreadHandler, this); + run_thread = true; + reportingThread = std::thread(&SlippiGameReporter::ReportThreadHandler, this); } SlippiGameReporter::~SlippiGameReporter() { - run_thread = false; - cv.notify_one(); - if (reportingThread.joinable()) - reportingThread.join(); + run_thread = false; + cv.notify_one(); + if (reportingThread.joinable()) + reportingThread.join(); - if (m_curl) - { - curl_slist_free_all(m_curl_header_list); - curl_easy_cleanup(m_curl); - } + if (m_curl) + { + curl_slist_free_all(m_curl_header_list); + curl_easy_cleanup(m_curl); + } } void SlippiGameReporter::StartReport(GameReport report) { - game_report_queue.emplace(report); - cv.notify_one(); + game_report_queue.emplace(report); + cv.notify_one(); } void SlippiGameReporter::StartNewSession(std::vector player_uids) { - this->player_uids = player_uids; - gameIndex = 1; + this->player_uids = player_uids; + gameIndex = 1; } void SlippiGameReporter::ReportThreadHandler() { - std::unique_lock lck(mtx); + std::unique_lock lck(mtx); - while (run_thread) - { - // Wait for report to come in - cv.wait(lck); + while (run_thread) + { + // Wait for report to come in + cv.wait(lck); - // Process all messages - while (!game_report_queue.empty()) - { - auto report = game_report_queue.front(); - game_report_queue.pop(); + // Process all messages + while (!game_report_queue.empty()) + { + auto report = game_report_queue.front(); + game_report_queue.pop(); - auto userInfo = m_user->GetUserInfo(); + auto userInfo = m_user->GetUserInfo(); - // Prepare report - json request; - request["uid"] = userInfo.uid; - request["playKey"] = userInfo.play_key; - request["gameIndex"] = gameIndex; - request["gameDurationFrames"] = report.duration_frames; + // Prepare report + json request; + request["uid"] = userInfo.uid; + request["playKey"] = userInfo.play_key; + request["gameIndex"] = gameIndex; + request["gameDurationFrames"] = report.duration_frames; - json players = json::array(); - for (int i = 0; i < report.players.size(); i++) - { - json p; - p["uid"] = player_uids[i]; - p["damage_done"] = report.players[i].damage_done; - p["stocks_remaining"] = report.players[i].stocks_remaining; + json players = json::array(); + for (int i = 0; i < report.players.size(); i++) + { + json p; + p["uid"] = player_uids[i]; + p["damage_done"] = report.players[i].damage_done; + p["stocks_remaining"] = report.players[i].stocks_remaining; - players[i] = p; - } + players[i] = p; + } - request["players"] = players; + request["players"] = players; - auto requestString = request.dump(); + auto requestString = request.dump(); - // Send report - curl_easy_setopt(m_curl, CURLOPT_POST, true); - curl_easy_setopt(m_curl, CURLOPT_URL, REPORT_URL.c_str()); - curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, requestString.c_str()); - curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, requestString.length()); - CURLcode res = curl_easy_perform(m_curl); + // Send report + curl_easy_setopt(m_curl, CURLOPT_POST, true); + curl_easy_setopt(m_curl, CURLOPT_URL, REPORT_URL.c_str()); + curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, requestString.c_str()); + curl_easy_setopt(m_curl, CURLOPT_POSTFIELDSIZE, requestString.length()); + CURLcode res = curl_easy_perform(m_curl); - if (res != 0) - { - ERROR_LOG(SLIPPI_ONLINE, "[GameReport] Got error executing request. Err code: %d", res); - } + if (res != 0) + { + ERROR_LOG(SLIPPI_ONLINE, "[GameReport] Got error executing request. Err code: %d", res); + } - gameIndex++; - Common::SleepCurrentThread(0); - } - } + gameIndex++; + Common::SleepCurrentThread(0); + } + } } diff --git a/Source/Core/Core/Slippi/SlippiGameReporter.h b/Source/Core/Core/Slippi/SlippiGameReporter.h index 517741003a..1a0b825700 100644 --- a/Source/Core/Core/Slippi/SlippiGameReporter.h +++ b/Source/Core/Core/Slippi/SlippiGameReporter.h @@ -1,49 +1,49 @@ #pragma once -#include "Common/CommonTypes.h" -#include "Core/Slippi/SlippiUser.h" #include -#include // std::condition_variable +#include // std::condition_variable #include -#include // std::mutex, std::unique_lock +#include // std::mutex, std::unique_lock +#include #include #include #include -#include +#include "Common/CommonTypes.h" +#include "Core/Slippi/SlippiUser.h" class SlippiGameReporter { - public: - struct PlayerReport - { - float damage_done; - u8 stocks_remaining; - }; - struct GameReport - { - u32 duration_frames = 0; - std::vector players; - }; +public: + struct PlayerReport + { + float damage_done; + u8 stocks_remaining; + }; + struct GameReport + { + u32 duration_frames = 0; + std::vector players; + }; - SlippiGameReporter(SlippiUser *user); - ~SlippiGameReporter(); + SlippiGameReporter(SlippiUser* user); + ~SlippiGameReporter(); - void StartReport(GameReport report); - void StartNewSession(std::vector player_uids); - void ReportThreadHandler(); + void StartReport(GameReport report); + void StartNewSession(std::vector player_uids); + void ReportThreadHandler(); - protected: - const std::string REPORT_URL = "https://rankings-dot-slippi.uc.r.appspot.com/report"; - CURL *m_curl = nullptr; - struct curl_slist *m_curl_header_list = nullptr; +protected: + const std::string REPORT_URL = "https://rankings-dot-slippi.uc.r.appspot.com/report"; + CURL* m_curl = nullptr; + struct curl_slist* m_curl_header_list = nullptr; - u32 gameIndex = 1; - std::vector player_uids; + u32 gameIndex = 1; + std::vector player_uids; - SlippiUser *m_user; - std::queue game_report_queue; - std::thread reportingThread; - std::mutex mtx; - std::condition_variable cv; - std::atomic run_thread; + SlippiUser* m_user; + std::queue game_report_queue; + std::thread reportingThread; + std::mutex mtx; + std::condition_variable cv; + std::atomic run_thread; }; diff --git a/Source/Core/Core/Slippi/SlippiNetplay.cpp b/Source/Core/Core/Slippi/SlippiNetplay.cpp index 6caf580120..0db8233311 100644 --- a/Source/Core/Core/Slippi/SlippiNetplay.cpp +++ b/Source/Core/Core/Slippi/SlippiNetplay.cpp @@ -244,9 +244,10 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet, ENetPeer* peer) // Check that the packet actually contains the data it claims to if ((5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE) > (int)packet.getDataSize()) { - ERROR_LOG(SLIPPI_ONLINE, - "Netplay packet too small to read pad buffer. Size: %d, Inputs: %d, MinSize: %d", - (int)packet.getDataSize(), inputsToCopy, 5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE); + ERROR_LOG_FMT( + SLIPPI_ONLINE, + "Netplay packet too small to read pad buffer. Size: {}, Inputs: {}, MinSize: {}", + (int)packet.getDataSize(), inputsToCopy, 5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE); break; } diff --git a/Source/Core/Core/Slippi/SlippiPad.cpp b/Source/Core/Core/Slippi/SlippiPad.cpp index ecf001e30d..b06bc19795 100644 --- a/Source/Core/Core/Slippi/SlippiPad.cpp +++ b/Source/Core/Core/Slippi/SlippiPad.cpp @@ -19,12 +19,12 @@ SlippiPad::SlippiPad(int32_t frame, u8* padBuf) : SlippiPad(frame) memcpy(this->padBuf, padBuf, SLIPPI_PAD_DATA_SIZE); } -SlippiPad::SlippiPad(int32_t frame, u8 playerIdx, u8 *padBuf) : SlippiPad(frame) +SlippiPad::SlippiPad(int32_t frame, u8 playerIdx, u8* padBuf) : SlippiPad(frame) { - this->frame = frame; - this->playerIdx = playerIdx; - // Overwrite the data portion of the pad - memcpy(this->padBuf, padBuf, SLIPPI_PAD_DATA_SIZE); + this->frame = frame; + this->playerIdx = playerIdx; + // Overwrite the data portion of the pad + memcpy(this->padBuf, padBuf, SLIPPI_PAD_DATA_SIZE); } SlippiPad::~SlippiPad() diff --git a/Source/Core/Core/Slippi/SlippiPad.h b/Source/Core/Core/Slippi/SlippiPad.h index b25da60cc4..b8dfe31f35 100644 --- a/Source/Core/Core/Slippi/SlippiPad.h +++ b/Source/Core/Core/Slippi/SlippiPad.h @@ -10,7 +10,7 @@ class SlippiPad public: SlippiPad(int32_t frame); SlippiPad(int32_t frame, u8* padBuf); - SlippiPad(int32_t frame, u8 playerIdx, u8 *padBuf); + SlippiPad(int32_t frame, u8 playerIdx, u8* padBuf); ~SlippiPad(); int32_t frame; diff --git a/Source/Core/Core/Slippi/SlippiSpectate.cpp b/Source/Core/Core/Slippi/SlippiSpectate.cpp index ee70a1cfb3..493bda222f 100644 --- a/Source/Core/Core/Slippi/SlippiSpectate.cpp +++ b/Source/Core/Core/Slippi/SlippiSpectate.cpp @@ -1,9 +1,9 @@ #include "SlippiSpectate.h" +#include #include "Common/Base64.hpp" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" #include "Common/Version.h" -#include // Networking #ifdef _WIN32 @@ -21,12 +21,13 @@ inline bool isSpectatorEnabled() SlippiSpectateServer& SlippiSpectateServer::getInstance() { static SlippiSpectateServer instance; - return instance; + return instance; } -void SlippiSpectateServer::write(u8 *payload, u32 length) +void SlippiSpectateServer::write(u8* payload, u32 length) { - if (isSpectatorEnabled()) { + if (isSpectatorEnabled()) + { std::string str_payload((char*)payload, length); m_event_queue.Push(str_payload); } @@ -35,134 +36,135 @@ void SlippiSpectateServer::write(u8 *payload, u32 length) // CALLED FROM DOLPHIN MAIN THREAD void SlippiSpectateServer::startGame() { - if (isSpectatorEnabled()) - { + if (isSpectatorEnabled()) + { m_event_queue.Push("START_GAME"); - } + } } // CALLED FROM DOLPHIN MAIN THREAD void SlippiSpectateServer::endGame() { - if (isSpectatorEnabled()) - { + if (isSpectatorEnabled()) + { m_event_queue.Push("END_GAME"); - } + } } // CALLED FROM SERVER THREAD void SlippiSpectateServer::writeEvents(u16 peer_id) { - // Send menu events - if (!m_in_game && (m_sockets[peer_id]->m_menu_cursor != m_menu_cursor)) - { - ENetPacket *packet = enet_packet_create(m_menu_event.data(), m_menu_event.length(), ENET_PACKET_FLAG_RELIABLE); - // Batch for sending - enet_peer_send(m_sockets[peer_id]->m_peer, 0, packet); - // Record for the peer that it was sent - m_sockets[peer_id]->m_menu_cursor = m_menu_cursor; - } + // Send menu events + if (!m_in_game && (m_sockets[peer_id]->m_menu_cursor != m_menu_cursor)) + { + ENetPacket* packet = + enet_packet_create(m_menu_event.data(), m_menu_event.length(), ENET_PACKET_FLAG_RELIABLE); + // Batch for sending + enet_peer_send(m_sockets[peer_id]->m_peer, 0, packet); + // Record for the peer that it was sent + m_sockets[peer_id]->m_menu_cursor = m_menu_cursor; + } - // Send game events - // Loop through each event that needs to be sent - // send all the events starting at their cursor - // If the client's cursor is beyond the end of the event buffer, then - // it's probably left over from an old game. (Or is invalid anyway) - // So reset it back to 0 - if (m_sockets[peer_id]->m_cursor > m_event_buffer.size()) - { - m_sockets[peer_id]->m_cursor = 0; - } + // Send game events + // Loop through each event that needs to be sent + // send all the events starting at their cursor + // If the client's cursor is beyond the end of the event buffer, then + // it's probably left over from an old game. (Or is invalid anyway) + // So reset it back to 0 + if (m_sockets[peer_id]->m_cursor > m_event_buffer.size()) + { + m_sockets[peer_id]->m_cursor = 0; + } - for (u64 i = m_sockets[peer_id]->m_cursor; i < m_event_buffer.size(); i++) - { - ENetPacket *packet = - enet_packet_create(m_event_buffer[i].data(), m_event_buffer[i].size(), ENET_PACKET_FLAG_RELIABLE); - // Batch for sending - enet_peer_send(m_sockets[peer_id]->m_peer, 0, packet); - m_sockets[peer_id]->m_cursor++; - } + for (u64 i = m_sockets[peer_id]->m_cursor; i < m_event_buffer.size(); i++) + { + ENetPacket* packet = enet_packet_create(m_event_buffer[i].data(), m_event_buffer[i].size(), + ENET_PACKET_FLAG_RELIABLE); + // Batch for sending + enet_peer_send(m_sockets[peer_id]->m_peer, 0, packet); + m_sockets[peer_id]->m_cursor++; + } } // CALLED FROM SERVER THREAD void SlippiSpectateServer::popEvents() { - // Loop through the event queue and keep popping off events and handling them - while (!m_event_queue.Empty()) - { - std::string event; - m_event_queue.Pop(event); - // These two are meta-events, used to signify the start/end of a game - // They are not sent over the wire - if (event == "END_GAME") - { - m_menu_cursor = 0; - if (m_event_buffer.size() > 0) - { - m_cursor_offset += m_event_buffer.size(); - } - m_menu_event.clear(); - m_in_game = false; - continue; - } - if (event == "START_GAME") - { - m_event_buffer.clear(); - m_in_game = true; - continue; - } + // Loop through the event queue and keep popping off events and handling them + while (!m_event_queue.Empty()) + { + std::string event; + m_event_queue.Pop(event); + // These two are meta-events, used to signify the start/end of a game + // They are not sent over the wire + if (event == "END_GAME") + { + m_menu_cursor = 0; + if (m_event_buffer.size() > 0) + { + m_cursor_offset += m_event_buffer.size(); + } + m_menu_event.clear(); + m_in_game = false; + continue; + } + if (event == "START_GAME") + { + m_event_buffer.clear(); + m_in_game = true; + continue; + } - // Make json wrapper for game event - json game_event; + // Make json wrapper for game event + json game_event; - // An SLP event with an empty payload is a quasi-event that signifies - // the unclean exit of a game. Send this out as its own event - // (Since you can't meaningfully concat it with other events) - if (event.empty()) - { - game_event["payload"] = ""; - game_event["type"] = "game_event"; - m_event_buffer.push_back(game_event.dump()); - continue; - } + // An SLP event with an empty payload is a quasi-event that signifies + // the unclean exit of a game. Send this out as its own event + // (Since you can't meaningfully concat it with other events) + if (event.empty()) + { + game_event["payload"] = ""; + game_event["type"] = "game_event"; + m_event_buffer.push_back(game_event.dump()); + continue; + } - if (!m_in_game) - { - game_event["payload"] = base64::Base64::Encode(event); - m_menu_cursor += 1; - game_event["type"] = "menu_event"; - m_menu_event = game_event.dump(); - continue; - } + if (!m_in_game) + { + game_event["payload"] = base64::Base64::Encode(event); + m_menu_cursor += 1; + game_event["type"] = "menu_event"; + m_menu_event = game_event.dump(); + continue; + } - u8 command = (u8)event[0]; - m_event_concat = m_event_concat + event; + u8 command = (u8)event[0]; + m_event_concat = m_event_concat + event; - static std::unordered_map sendEvents = { - {0x36, true}, // GAME_INIT - {0x3C, true}, // FRAME_END - {0x39, true}, // GAME_END - {0x10, true}, // SPLIT_MESSAGE - }; + static std::unordered_map sendEvents = { + {0x36, true}, // GAME_INIT + {0x3C, true}, // FRAME_END + {0x39, true}, // GAME_END + {0x10, true}, // SPLIT_MESSAGE + }; - if (sendEvents.count(command)) - { - u32 cursor = (u32)(m_event_buffer.size() + m_cursor_offset); - game_event["payload"] = base64::Base64::Encode(m_event_concat); - game_event["type"] = "game_event"; - game_event["cursor"] = cursor; - game_event["next_cursor"] = cursor + 1; - m_event_buffer.push_back(game_event.dump()); + if (sendEvents.count(command)) + { + u32 cursor = (u32)(m_event_buffer.size() + m_cursor_offset); + game_event["payload"] = base64::Base64::Encode(m_event_concat); + game_event["type"] = "game_event"; + game_event["cursor"] = cursor; + game_event["next_cursor"] = cursor + 1; + m_event_buffer.push_back(game_event.dump()); - m_event_concat = ""; - } - } + m_event_concat = ""; + } + } } // CALLED ONCE EVER, DOLPHIN MAIN THREAD SlippiSpectateServer::SlippiSpectateServer() { - if (isSpectatorEnabled()) + if (isSpectatorEnabled()) { m_in_game = false; m_menu_cursor = 0; @@ -170,196 +172,197 @@ SlippiSpectateServer::SlippiSpectateServer() // Spawn thread for socket listener m_stop_socket_thread = false; m_socketThread = std::thread(&SlippiSpectateServer::SlippicommSocketThread, this); - } + } } // CALLED FROM DOLPHIN MAIN THREAD SlippiSpectateServer::~SlippiSpectateServer() { - // The socket thread will be blocked waiting for input - // So to wake it up, let's connect to the socket! - m_stop_socket_thread = true; - if (m_socketThread.joinable()) - { - m_socketThread.join(); - } + // The socket thread will be blocked waiting for input + // So to wake it up, let's connect to the socket! + m_stop_socket_thread = true; + if (m_socketThread.joinable()) + { + m_socketThread.join(); + } } // CALLED FROM SERVER THREAD -void SlippiSpectateServer::handleMessage(u8 *buffer, u32 length, u16 peer_id) +void SlippiSpectateServer::handleMessage(u8* buffer, u32 length, u16 peer_id) { - // Unpack the message - std::string message((char *)buffer, length); - json json_message = json::parse(message); - if (!json_message.is_discarded() && (json_message.find("type") != json_message.end())) - { - // Check what type of message this is - if (!json_message["type"].is_string()) - { - return; - } + // Unpack the message + std::string message((char*)buffer, length); + json json_message = json::parse(message); + if (!json_message.is_discarded() && (json_message.find("type") != json_message.end())) + { + // Check what type of message this is + if (!json_message["type"].is_string()) + { + return; + } - if (json_message["type"] == "connect_request") - { - // Get the requested cursor - if (json_message.find("cursor") == json_message.end()) - { - return; - } - if (!json_message["cursor"].is_number_integer()) - { - return; - } - u32 requested_cursor = json_message["cursor"]; - u32 sent_cursor = 0; - // Set the user's cursor position - if (requested_cursor >= m_cursor_offset) - { - // If the requested cursor is past what events we even have, then just tell them to start over - if (requested_cursor > m_event_buffer.size() + m_cursor_offset) - { - m_sockets[peer_id]->m_cursor = 0; - } - // Requested cursor is in the middle of a live match, events that we have - else - { - m_sockets[peer_id]->m_cursor = requested_cursor - m_cursor_offset; - } - } - else - { - // The client requested a cursor that was too low. Bring them up to the present - m_sockets[peer_id]->m_cursor = 0; - } + if (json_message["type"] == "connect_request") + { + // Get the requested cursor + if (json_message.find("cursor") == json_message.end()) + { + return; + } + if (!json_message["cursor"].is_number_integer()) + { + return; + } + u32 requested_cursor = json_message["cursor"]; + u32 sent_cursor = 0; + // Set the user's cursor position + if (requested_cursor >= m_cursor_offset) + { + // If the requested cursor is past what events we even have, then just tell them to start + // over + if (requested_cursor > m_event_buffer.size() + m_cursor_offset) + { + m_sockets[peer_id]->m_cursor = 0; + } + // Requested cursor is in the middle of a live match, events that we have + else + { + m_sockets[peer_id]->m_cursor = requested_cursor - m_cursor_offset; + } + } + else + { + // The client requested a cursor that was too low. Bring them up to the present + m_sockets[peer_id]->m_cursor = 0; + } - sent_cursor = (u32)m_sockets[peer_id]->m_cursor + (u32)m_cursor_offset; + sent_cursor = (u32)m_sockets[peer_id]->m_cursor + (u32)m_cursor_offset; - // If someone joins while at the menu, don't catch them up - // set their cursor to the end - if (!m_in_game) - { - m_sockets[peer_id]->m_cursor = m_event_buffer.size(); - } + // If someone joins while at the menu, don't catch them up + // set their cursor to the end + if (!m_in_game) + { + m_sockets[peer_id]->m_cursor = m_event_buffer.size(); + } - json reply; - reply["type"] = "connect_reply"; - reply["nick"] = "Slippi Online"; - reply["version"] = Common::scm_slippi_semver_str; - reply["cursor"] = sent_cursor; + json reply; + reply["type"] = "connect_reply"; + reply["nick"] = "Slippi Online"; + reply["version"] = Common::scm_slippi_semver_str; + reply["cursor"] = sent_cursor; - std::string packet_buffer = reply.dump(); + std::string packet_buffer = reply.dump(); - ENetPacket *packet = - enet_packet_create(packet_buffer.data(), (u32)packet_buffer.length(), ENET_PACKET_FLAG_RELIABLE); + ENetPacket* packet = enet_packet_create(packet_buffer.data(), (u32)packet_buffer.length(), + ENET_PACKET_FLAG_RELIABLE); - // Batch for sending - enet_peer_send(m_sockets[peer_id]->m_peer, 0, packet); - // Put the client in the right in_game state - m_sockets[peer_id]->m_shook_hands = true; - } - } + // Batch for sending + enet_peer_send(m_sockets[peer_id]->m_peer, 0, packet); + // Put the client in the right in_game state + m_sockets[peer_id]->m_shook_hands = true; + } + } } void SlippiSpectateServer::SlippicommSocketThread(void) { - if (enet_initialize() != 0) - { - WARN_LOG(SLIPPI, "An error occurred while initializing spectator server."); - return; - } + if (enet_initialize() != 0) + { + WARN_LOG(SLIPPI, "An error occurred while initializing spectator server."); + return; + } - ENetAddress server_address = {0}; - server_address.host = ENET_HOST_ANY; - server_address.port = SConfig::GetInstance().m_spectator_local_port; + ENetAddress server_address = {0}; + server_address.host = ENET_HOST_ANY; + server_address.port = SConfig::GetInstance().m_spectator_local_port; - // Create the spectator server - // This call can fail if the system is already listening on the specified port - // or for some period of time after it closes down. You basically have to just - // retry until the OS lets go of the port and we can claim it again - // This typically only takes a few seconds - ENetHost *server = enet_host_create(&server_address, MAX_CLIENTS, 2, 0, 0); - int tries = 0; - while (server == nullptr && tries < 20) - { - server = enet_host_create(&server_address, MAX_CLIENTS, 2, 0, 0); - tries += 1; - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - } + // Create the spectator server + // This call can fail if the system is already listening on the specified port + // or for some period of time after it closes down. You basically have to just + // retry until the OS lets go of the port and we can claim it again + // This typically only takes a few seconds + ENetHost* server = enet_host_create(&server_address, MAX_CLIENTS, 2, 0, 0); + int tries = 0; + while (server == nullptr && tries < 20) + { + server = enet_host_create(&server_address, MAX_CLIENTS, 2, 0, 0); + tries += 1; + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } - if (server == nullptr) - { - WARN_LOG(SLIPPI, "Could not create spectator server"); - enet_deinitialize(); - return; - } + if (server == nullptr) + { + WARN_LOG(SLIPPI, "Could not create spectator server"); + enet_deinitialize(); + return; + } - // Main slippicomm server loop - while (1) - { - // If we're told to stop, then quit - if (m_stop_socket_thread) - { - enet_host_destroy(server); - enet_deinitialize(); - return; - } + // Main slippicomm server loop + while (1) + { + // If we're told to stop, then quit + if (m_stop_socket_thread) + { + enet_host_destroy(server); + enet_deinitialize(); + return; + } - // Pop off any events in the queue - popEvents(); + // Pop off any events in the queue + popEvents(); - std::map>::iterator it = m_sockets.begin(); - for (; it != m_sockets.end(); it++) - { - if (it->second->m_shook_hands) - { - writeEvents(it->first); - } - } + std::map>::iterator it = m_sockets.begin(); + for (; it != m_sockets.end(); it++) + { + if (it->second->m_shook_hands) + { + writeEvents(it->first); + } + } - ENetEvent event; - while (enet_host_service(server, &event, 1) > 0) - { - switch (event.type) - { - case ENET_EVENT_TYPE_CONNECT: - { + ENetEvent event; + while (enet_host_service(server, &event, 1) > 0) + { + switch (event.type) + { + case ENET_EVENT_TYPE_CONNECT: + { + INFO_LOG(SLIPPI, "A new spectator connected from %x:%u.\n", event.peer->address.host, + event.peer->address.port); - INFO_LOG(SLIPPI, "A new spectator connected from %x:%u.\n", event.peer->address.host, - event.peer->address.port); + std::shared_ptr newSlippiSocket(new SlippiSocket()); + newSlippiSocket->m_peer = event.peer; + m_sockets[event.peer->incomingPeerID] = newSlippiSocket; + break; + } + case ENET_EVENT_TYPE_RECEIVE: + { + handleMessage(event.packet->data, (u32)event.packet->dataLength, + event.peer->incomingPeerID); + /* Clean up the packet now that we're done using it. */ + enet_packet_destroy(event.packet); - std::shared_ptr newSlippiSocket(new SlippiSocket()); - newSlippiSocket->m_peer = event.peer; - m_sockets[event.peer->incomingPeerID] = newSlippiSocket; - break; - } - case ENET_EVENT_TYPE_RECEIVE: - { - handleMessage(event.packet->data, (u32)event.packet->dataLength, event.peer->incomingPeerID); - /* Clean up the packet now that we're done using it. */ - enet_packet_destroy(event.packet); + break; + } + case ENET_EVENT_TYPE_DISCONNECT: + { + INFO_LOG(SLIPPI, "A spectator disconnected from %x:%u.\n", event.peer->address.host, + event.peer->address.port); - break; - } - case ENET_EVENT_TYPE_DISCONNECT: - { - INFO_LOG(SLIPPI, "A spectator disconnected from %x:%u.\n", event.peer->address.host, - event.peer->address.port); + // Delete the item in the m_sockets map + m_sockets.erase(event.peer->incomingPeerID); + /* Reset the peer's client information. */ + event.peer->data = NULL; + break; + } + default: + { + INFO_LOG(SLIPPI, "Spectator sent an unknown ENet event type"); + break; + } + } + } + } - // Delete the item in the m_sockets map - m_sockets.erase(event.peer->incomingPeerID); - /* Reset the peer's client information. */ - event.peer->data = NULL; - break; - } - default: - { - INFO_LOG(SLIPPI, "Spectator sent an unknown ENet event type"); - break; - } - } - } - } - - enet_host_destroy(server); - enet_deinitialize(); + enet_host_destroy(server); + enet_deinitialize(); } diff --git a/Source/Core/Core/Slippi/SlippiSpectate.h b/Source/Core/Core/Slippi/SlippiSpectate.h index 2afd0e5b20..6e36a47f43 100644 --- a/Source/Core/Core/Slippi/SlippiSpectate.h +++ b/Source/Core/Core/Slippi/SlippiSpectate.h @@ -5,9 +5,9 @@ #include #include +#include #include "Common/SPSCQueue.h" #include "nlohmann/json.hpp" -#include using json = nlohmann::json; // Sockets in windows are unsigned @@ -30,10 +30,10 @@ typedef int SOCKET; class SlippiSocket { public: - u64 m_cursor = 0; // Index of the last game event this client sent - u64 m_menu_cursor = 0; // The latest menu event that this socket has sent - bool m_shook_hands = false; // Has this client shaken hands yet? - ENetPeer* m_peer = NULL; // The ENet peer object for the socket + u64 m_cursor = 0; // Index of the last game event this client sent + u64 m_menu_cursor = 0; // The latest menu event that this socket has sent + bool m_shook_hands = false; // Has this client shaken hands yet? + ENetPeer* m_peer = NULL; // The ENet peer object for the socket }; class SlippiSpectateServer