8c2943b854f742d9b27bdbf5ffe8fc278a3fe318
c891220772e1258e6e9192ef8ec195c227e8f10b
This commit is contained in:
Nikhil Narayana 2021-11-22 22:00:13 -08:00
commit 246ba8397e
20 changed files with 1065 additions and 402 deletions

Binary file not shown.

Binary file not shown.

View file

@ -258,6 +258,7 @@ void SConfig::SaveSlippiSettings(IniFile& ini)
slippi->Set("OnlineDelay", m_slippiOnlineDelay); slippi->Set("OnlineDelay", m_slippiOnlineDelay);
slippi->Set("SaveReplays", m_slippiSaveReplays); slippi->Set("SaveReplays", m_slippiSaveReplays);
slippi->Set("EnableQuickChat", m_slippiEnableQuickChat);
slippi->Set("ReplayMonthFolders", m_slippiReplayMonthFolders); slippi->Set("ReplayMonthFolders", m_slippiReplayMonthFolders);
slippi->Set("ReplayDir", m_strSlippiReplayDir); slippi->Set("ReplayDir", m_strSlippiReplayDir);
slippi->Set("PlaybackControls", m_slippiEnableSeek); slippi->Set("PlaybackControls", m_slippiEnableSeek);
@ -543,6 +544,7 @@ void SConfig::LoadSlippiSettings(IniFile& ini)
slippi->Get("PlaybackControls", &m_slippiEnableSeek, true); slippi->Get("PlaybackControls", &m_slippiEnableSeek, true);
slippi->Get("OnlineDelay", &m_slippiOnlineDelay, 2); slippi->Get("OnlineDelay", &m_slippiOnlineDelay, 2);
slippi->Get("SaveReplays", &m_slippiSaveReplays, true); slippi->Get("SaveReplays", &m_slippiSaveReplays, true);
slippi->Get("EnableQuickChat", &m_slippiEnableQuickChat, true);
slippi->Get("ReplayMonthFolders", &m_slippiReplayMonthFolders, false); slippi->Get("ReplayMonthFolders", &m_slippiReplayMonthFolders, false);
std::string default_replay_dir = File::GetHomeDirectory() + DIR_SEP + "Slippi"; std::string default_replay_dir = File::GetHomeDirectory() + DIR_SEP + "Slippi";
slippi->Get("ReplayDir", &m_strSlippiReplayDir, default_replay_dir); slippi->Get("ReplayDir", &m_strSlippiReplayDir, default_replay_dir);

View file

@ -154,6 +154,7 @@ struct SConfig
int m_slippiOnlineDelay = 2; int m_slippiOnlineDelay = 2;
bool m_slippiEnableSeek = true; bool m_slippiEnableSeek = true;
bool m_slippiSaveReplays = true; bool m_slippiSaveReplays = true;
bool m_slippiEnableQuickChat = true;
bool m_slippiReplayMonthFolders = false; bool m_slippiReplayMonthFolders = false;
std::string m_strSlippiReplayDir; std::string m_strSlippiReplayDir;
bool m_blockingPipes = false; bool m_blockingPipes = false;

View file

@ -448,19 +448,15 @@ void CEXISlippi::writeToFile(std::unique_ptr<WriteMessage> msg)
// Get display names and connection codes from slippi netplay client // Get display names and connection codes from slippi netplay client
if (slippi_netplay) if (slippi_netplay)
{ {
auto userInfo = user->GetUserInfo(); auto playerInfo = matchmaking->GetPlayerInfo();
auto oppInfo = matchmaking->GetOpponent();
auto isDecider = slippi_netplay->IsDecider(); for (int i = 0; i < playerInfo.size(); i++)
int local_port = isDecider ? 0 : 1; {
int remote_port = isDecider ? 1 : 0; slippi_names[i] = playerInfo[i].display_name;
slippi_connect_codes[i] = playerInfo[i].connect_code;
slippi_names[local_port] = userInfo.display_name; }
slippi_connect_codes[local_port] = userInfo.connect_code; }
slippi_names[remote_port] = oppInfo.display_name;
slippi_connect_codes[remote_port] = oppInfo.connect_code;
}
} }
// If no file, do nothing // If no file, do nothing
@ -1499,12 +1495,6 @@ void CEXISlippi::handleOnlineInputs(u8* payload)
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];
if (isDisconnected())
{
m_read_queue.push_back(3); // Indicate we disconnected
return;
}
if (frame == 1) if (frame == 1)
{ {
availableSavestates.clear(); availableSavestates.clear();
@ -1525,6 +1515,13 @@ void CEXISlippi::handleOnlineInputs(u8* payload)
slippi_netplay->StartSlippiGame(); slippi_netplay->StartSlippiGame();
} }
if (isDisconnected())
{
auto status = slippi_netplay->GetSlippiConnectStatus();
m_read_queue.push_back(3); // Indicate we disconnected
return;
}
if (shouldSkipOnlineFrame(frame)) if (shouldSkipOnlineFrame(frame))
{ {
// Send inputs that have not yet been acked // Send inputs that have not yet been acked
@ -1586,6 +1583,7 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame)
auto offsetUs = slippi_netplay->CalcTimeOffsetUs(); auto offsetUs = slippi_netplay->CalcTimeOffsetUs();
INFO_LOG(SLIPPI_ONLINE, "[Frame %d] Offset is: %d us", frame, offsetUs); INFO_LOG(SLIPPI_ONLINE, "[Frame %d] Offset is: %d us", frame, offsetUs);
// TODO: figure out a better solution here for doubles?
if (offsetUs > 10000) if (offsetUs > 10000)
{ {
isCurrentlySkipping = true; isCurrentlySkipping = true;
@ -1642,27 +1640,56 @@ void CEXISlippi::prepareOpponentInputs(u8* payload)
m_read_queue.push_back(frameResult); // Indicate a continue frame m_read_queue.push_back(frameResult); // Indicate a continue frame
int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3]; u8 remotePlayerCount = matchmaking->RemotePlayerCount();
m_read_queue.push_back(remotePlayerCount); // Indicate the number of remote players
auto result = slippi_netplay->GetSlippiRemotePad(frame); int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3];
// determine offset from which to copy data std::unique_ptr<SlippiRemotePadOutput> results[SLIPPI_REMOTE_PLAYER_MAX];
int offset = (result->latestFrame - frame) * SLIPPI_PAD_FULL_SIZE; int offset[SLIPPI_REMOTE_PLAYER_MAX];
offset = offset < 0 ? 0 : offset; INFO_LOG(SLIPPI_ONLINE, "Preparing pad data for frame %d", frame);
// add latest frame we are transfering to begining of return buf // Get pad data for each remote player and write each of their latest frame nums to the buf
int32_t latestFrame = offset > 0 ? frame : result->latestFrame; for (int i = 0; i < remotePlayerCount; i++)
appendWordToBuffer(&m_read_queue, *(u32*)&latestFrame); {
results[i] = slippi_netplay->GetSlippiRemotePad(frame, i);
// copy pad data over // determine offset from which to copy data
auto txStart = result->data.begin() + offset; offset[i] = (results[i]->latestFrame - frame) * SLIPPI_PAD_FULL_SIZE;
auto txEnd = result->data.end(); offset[i] = offset[i] < 0 ? 0 : offset[i];
std::vector<u8> tx; // add latest frame we are transfering to begining of return buf
tx.insert(tx.end(), txStart, txEnd); int32_t latestFrame = results[i]->latestFrame;
tx.resize(SLIPPI_PAD_FULL_SIZE * ROLLBACK_MAX_FRAMES, 0); 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);
}
m_read_queue.insert(m_read_queue.end(), tx.begin(), tx.end()); // copy pad data over
for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
{
std::vector<u8> 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);
}
tx.resize(SLIPPI_PAD_FULL_SIZE * ROLLBACK_MAX_FRAMES, 0);
m_read_queue.insert(m_read_queue.end(), tx.begin(), tx.end());
}
slippi_netplay->DropOldRemoteInputs(frame);
// ERROR_LOG(SLIPPI_ONLINE, "EXI: [%d] %X %X %X %X %X %X %X %X", latestFrame, m_read_queue[5], // 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], // m_read_queue[6], m_read_queue[7], m_read_queue[8], m_read_queue[9], m_read_queue[10],
@ -1771,7 +1798,7 @@ void CEXISlippi::startFindMatch(u8* payload)
// give someone an early error before they even queue so that they wont enter the queue and make // give someone an early error before they even queue so that they wont enter the queue and make
// someone else get force removed from queue and have to requeue // someone else get force removed from queue and have to requeue
auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT; auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT;
if (search.mode != directMode && localSelections.characterId >= 26) if (search.mode < directMode && localSelections.characterId >= 26)
{ {
forcedError = "The character you selected is not allowed in this mode"; forcedError = "The character you selected is not allowed in this mode";
return; return;
@ -1794,31 +1821,28 @@ void CEXISlippi::startFindMatch(u8* payload)
void CEXISlippi::prepareOnlineMatchState() void CEXISlippi::prepareOnlineMatchState()
{ {
// This match block is a VS match with P1 Red Falco vs P2 Red Bowser on Battlefield. The proper // This match block is a VS match with P1 Red Falco vs P2 Red Bowser vs P3 Young Link vs P4 Young Link
// values will be overwritten // on Battlefield. The proper values will be overwritten
static std::vector<u8> onlineMatchBlock = { static std::vector<u8> onlineMatchBlock = {
0x32, 0x01, 0x86, 0x4C, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x6E, 0x00, 0x32, 0x01, 0x86, 0x4C, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x6E, 0x00, 0x1F, 0x00, 0x00,
0x1F, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
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, 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,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
0x00, 0x78, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x05, 0x00, 0x04, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
0x01, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x15, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
0x80, 0x00, 0x00, 0x1A, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x15, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
0x40, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x1A, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0x40, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0x40, 0x00, 0x04, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00,
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(); m_read_queue.clear();
@ -1838,12 +1862,10 @@ void CEXISlippi::prepareOnlineMatchState()
m_read_queue.push_back(mmState); // Matchmaking State m_read_queue.push_back(mmState); // Matchmaking State
u8 localPlayerReady = localSelections.isCharacterSelected; u8 localPlayerReady = localSelections.isCharacterSelected;
u8 remotePlayerReady = 0; u8 remotePlayersReady = 0;
u8 localPlayerIndex = 0; u8 localPlayerIndex = matchmaking->LocalPlayerIndex();
u8 remotePlayerIndex = 1; u8 remotePlayerIndex = 1;
auto opponent = matchmaking->GetOpponent();
std::string oppName = opponent.display_name;
auto userInfo = user->GetUserInfo(); auto userInfo = user->GetUserInfo();
if (mmState == SlippiMatchmaking::ProcessState::CONNECTION_SUCCESS) if (mmState == SlippiMatchmaking::ProcessState::CONNECTION_SUCCESS)
@ -1871,9 +1893,24 @@ void CEXISlippi::prepareOnlineMatchState()
{ {
auto matchInfo = slippi_netplay->GetMatchInfo(); auto matchInfo = slippi_netplay->GetMatchInfo();
#ifdef LOCAL_TESTING #ifdef LOCAL_TESTING
remotePlayerReady = true; remotePlayersReady = true;
#else #else
remotePlayerReady = matchInfo->remotePlayerSelections.isCharacterSelected; remotePlayersReady = 1;
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;
}
#endif #endif
auto isDecider = slippi_netplay->IsDecider(); auto isDecider = slippi_netplay->IsDecider();
@ -1892,9 +1929,13 @@ void CEXISlippi::prepareOnlineMatchState()
// Here we are connected, check to see if we should init play session // Here we are connected, check to see if we should init play session
if (!is_play_session_active) if (!is_play_session_active)
{ {
std::vector<std::string> uids{"", ""}; std::vector<std::string> uids;
uids[localPlayerIndex] = userInfo.uid;
uids[remotePlayerIndex] = opponent.uid; auto mmPlayers = matchmaking->GetPlayerInfo();
for (auto mmp : mmPlayers)
{
uids.push_back(mmp.uid);
}
game_reporter->StartNewSession(uids); game_reporter->StartNewSession(uids);
@ -1906,55 +1947,93 @@ void CEXISlippi::prepareOnlineMatchState()
slippi_netplay = nullptr; slippi_netplay = nullptr;
} }
m_read_queue.push_back(localPlayerReady); // Local player ready
m_read_queue.push_back(remotePlayerReady); // Remote player ready
m_read_queue.push_back(localPlayerIndex); // Local player index
m_read_queue.push_back(remotePlayerIndex); // Remote player index
u32 rngOffset = 0; u32 rngOffset = 0;
std::string localPlayerName = "";
std::string oppName = "";
std::string p1Name = ""; std::string p1Name = "";
std::string p2Name = ""; std::string p2Name = "";
u8 chatMessageId = 0; u8 chatMessageId = 0;
u8 chatMessagePlayerIdx = 0;
u8 sentChatMessageId = 0; u8 sentChatMessageId = 0;
#ifdef LOCAL_TESTING #ifdef LOCAL_TESTING
chatMessageId = localChatMessageId; localPlayerIndex = 0;
localChatMessageId = 0; chatMessageId = localChatMessageId;
// in CSS p1 is always current player and p2 is opponent chatMessagePlayerIdx = 0;
p1Name = "Player 1"; localChatMessageId = 0;
p2Name = "Player 2"; // in CSS p1 is always current player and p2 is opponent
localPlayerName = p1Name = "Player 1";
oppName = p2Name = "Player 2";
#endif #endif
// Set chat message if any m_read_queue.push_back(localPlayerReady); // Local player ready
if (slippi_netplay) m_read_queue.push_back(remotePlayersReady); // Remote players ready
{ m_read_queue.push_back(localPlayerIndex); // Local player index
chatMessageId = slippi_netplay->GetSlippiRemoteChatMessage(); m_read_queue.push_back(remotePlayerIndex); // Remote player index
sentChatMessageId = slippi_netplay->GetSlippiRemoteSentChatMessage();
// in CSS p1 is always current player and p2 is opponent
p1Name = userInfo.display_name;
p2Name = oppName;
}
auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT; // 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;
}
if (localPlayerReady && remotePlayerReady) auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT;
{
auto isDecider = slippi_netplay->IsDecider();
auto matchInfo = slippi_netplay->GetMatchInfo(); std::vector<u8> leftTeamPlayers = {};
SlippiPlayerSelections lps = matchInfo->localPlayerSelections; std::vector<u8> rightTeamPlayers = {};
SlippiPlayerSelections 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 #ifdef LOCAL_TESTING
rps.characterId = 0x2; lps.playerIdx = 0;
rps.characterColor = 2;
oppName = std::string("Player"); 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;
}
if (lastSearch.mode == SlippiMatchmaking::OnlinePlayMode::TEAMS)
{
remotePlayerCount = 4;
}
oppName = std::string("Player");
#endif #endif
// Check if someone is picking dumb characters in non-direct // Check if someone is picking dumb characters in non-direct
auto localCharOk = lps.characterId < 26; auto localCharOk = lps.characterId < 26;
auto remoteCharOk = rps.characterId < 26; auto remoteCharOk = true;
if (lastSearch.mode != directMode && (!localCharOk || !remoteCharOk)) 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 // If we get here, someone is doing something bad, clear the lobby
handleConnectionCleanup(); handleConnectionCleanup();
@ -1965,52 +2044,99 @@ void CEXISlippi::prepareOnlineMatchState()
} }
// Overwrite local player character // Overwrite local player character
onlineMatchBlock[0x60 + localPlayerIndex * 0x24] = lps.characterId; onlineMatchBlock[0x60 + (lps.playerIdx) * 0x24] = lps.characterId;
onlineMatchBlock[0x63 + localPlayerIndex * 0x24] = lps.characterColor; onlineMatchBlock[0x63 + (lps.playerIdx) * 0x24] = lps.characterColor;
onlineMatchBlock[0x67 + (lps.playerIdx) * 0x24] = 0;
onlineMatchBlock[0x69 + (lps.playerIdx) * 0x24] = lps.teamId;
// Overwrite remote player character // Overwrite remote player character
onlineMatchBlock[0x60 + remotePlayerIndex * 0x24] = rps.characterId; for (int i = 0; i < remotePlayerCount; i++)
onlineMatchBlock[0x63 + remotePlayerIndex * 0x24] = rps.characterColor; {
u8 idx = matchInfo->remotePlayerSelections[i].playerIdx;
onlineMatchBlock[0x60 + idx * 0x24] = matchInfo->remotePlayerSelections[i].characterId;
// Make one character lighter if same character, same color // Set Char Colors
bool isSheikVsZelda = lps.characterId == 0x12 && rps.characterId == 0x13 || onlineMatchBlock[0x63 + idx * 0x24] = matchInfo->remotePlayerSelections[i].characterColor;
lps.characterId == 0x13 && rps.characterId == 0x12;
bool charMatch = lps.characterId == rps.characterId || isSheikVsZelda;
bool colMatch = lps.characterColor == rps.characterColor;
onlineMatchBlock[0x67 + 0x24] = charMatch && colMatch ? 1 : 0; // Set Team Ids
onlineMatchBlock[0x69 + idx * 0x24] = matchInfo->remotePlayerSelections[i].teamId;
}
// Overwrite stage // Handle Singles/Teams specific logic
u16 stageId; if (remotePlayerCount < 3)
if (isDecider) {
{ onlineMatchBlock[0x8] = 0; // is Teams = false
stageId = lps.isStageSelected ? lps.stageId : rps.stageId;
}
else
{
stageId = rps.isStageSelected ? rps.stageId : lps.stageId;
}
// int seconds = 0; // Set p3/p4 player type to none
// u32 *timer = (u32 *)&onlineMatchBlock[0x10]; onlineMatchBlock[0x61 + 2 * 0x24] = 3;
//*timer = Common::swap32(seconds * 60); onlineMatchBlock[0x61 + 3 * 0x24] = 3;
u16* stage = (u16*)&onlineMatchBlock[0xE]; // Make one character lighter if same character, same color
*stage = Common::swap16(stageId); 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;
// Set rng offset onlineMatchBlock[0x67 + 0x24] = charMatch && colMatch ? 1 : 0;
rngOffset = isDecider ? lps.rngOffset : rps.rngOffset; }
WARN_LOG(SLIPPI_ONLINE, "Rng Offset: 0x%x", rngOffset); else
WARN_LOG(SLIPPI_ONLINE, "P1 Char: 0x%X, P2 Char: 0x%X", onlineMatchBlock[0x60], {
onlineMatchBlock[0x84]); onlineMatchBlock[0x8] = 1; // is Teams = true
// Set player names // Set p3/p4 player type to human
p1Name = isDecider ? userInfo.display_name : oppName; onlineMatchBlock[0x61 + 2 * 0x24] = 0;
p2Name = isDecider ? oppName : userInfo.display_name; onlineMatchBlock[0x61 + 3 * 0x24] = 0;
// Turn pause on in direct, off in everything else // Set alt color to light/dark costume for multiples of the same character on a team
u8* gameBitField3 = (u8*)&onlineMatchBlock[2]; int characterCount[26][3] = {0};
*gameBitField3 = lastSearch.mode == directMode ? *gameBitField3 & 0xF7 : *gameBitField3 | 0x8; 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;
}
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]);
// 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;
} }
// Add rng offset to output // Add rng offset to output
@ -2022,13 +2148,52 @@ void CEXISlippi::prepareOnlineMatchState()
// Add chat messages id // Add chat messages id
m_read_queue.push_back((u8)sentChatMessageId); m_read_queue.push_back((u8)sentChatMessageId);
m_read_queue.push_back((u8)chatMessageId); m_read_queue.push_back((u8)chatMessageId);
m_read_queue.push_back((u8)chatMessagePlayerIdx);
// Add names to output // Add player groupings for VS splash screen
p1Name = ConvertStringForGame(p1Name, MAX_NAME_LENGTH); leftTeamPlayers.resize(4, 0);
m_read_queue.insert(m_read_queue.end(), p1Name.begin(), p1Name.end()); rightTeamPlayers.resize(4, 0);
p2Name = ConvertStringForGame(p2Name, MAX_NAME_LENGTH); m_read_queue.insert(m_read_queue.end(), leftTeamPlayers.begin(), leftTeamPlayers.end());
m_read_queue.insert(m_read_queue.end(), p2Name.begin(), p2Name.end()); m_read_queue.insert(m_read_queue.end(), rightTeamPlayers.begin(), rightTeamPlayers.end());
oppName = ConvertStringForGame(oppName, MAX_NAME_LENGTH);
// 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"};
#endif
for (int i = 0; i < 4; i++)
{
std::string name = matchmaking->GetPlayerName(i);
#ifdef LOCAL_TESTING
name = defaultNames[i];
#endif
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;
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);
m_read_queue.insert(m_read_queue.end(), oppName.begin(), oppName.end()); m_read_queue.insert(m_read_queue.end(), oppName.begin(), oppName.end());
// Add error message if there is one // Add error message if there is one
@ -2071,12 +2236,13 @@ void CEXISlippi::setMatchSelections(u8* payload)
{ {
SlippiPlayerSelections s; SlippiPlayerSelections s;
s.characterId = payload[0]; s.teamId = payload[0];
s.characterColor = payload[1]; s.characterId = payload[1];
s.isCharacterSelected = payload[2]; s.characterColor = payload[2];
s.isCharacterSelected = payload[3];
s.stageId = Common::swap16(&payload[3]); s.stageId = Common::swap16(&payload[4]);
u8 stageSelectOption = payload[5]; u8 stageSelectOption = payload[6];
s.isStageSelected = stageSelectOption == 1 || stageSelectOption == 3; s.isStageSelected = stageSelectOption == 1 || stageSelectOption == 3;
if (stageSelectOption == 3) if (stageSelectOption == 3)
@ -2085,7 +2251,16 @@ void CEXISlippi::setMatchSelections(u8* payload)
s.stageId = getRandomStage(); s.stageId = getRandomStage();
} }
s.rngOffset = generator() % 0xFFFF; 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;
if (matchmaking->LocalPlayerIndex() == 1 && firstMatch)
{
firstMatch = false;
s.stageId = getRandomStage();
}
// Merge these selections // Merge these selections
localSelections.Merge(s); localSelections.Merge(s);
@ -2142,7 +2317,7 @@ void CEXISlippi::handleChatMessage(u8* payload)
auto packet = std::make_unique<sf::Packet>(); auto packet = std::make_unique<sf::Packet>();
// OSD::AddMessage("[Me]: "+ msg, OSD::Duration::VERY_LONG, OSD::Color::YELLOW); // OSD::AddMessage("[Me]: "+ msg, OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
slippi_netplay->remoteSentChatMessageId = messageId; slippi_netplay->remoteSentChatMessageId = messageId;
slippi_netplay->WriteChatMessageToPacket(*packet, messageId); slippi_netplay->WriteChatMessageToPacket(*packet, messageId, slippi_netplay->LocalPlayerPort());
slippi_netplay->SendAsync(std::move(packet)); slippi_netplay->SendAsync(std::move(packet));
} }
} }
@ -2258,6 +2433,7 @@ void CEXISlippi::handleConnectionCleanup()
// Reset play session // Reset play session
is_play_session_active = false; is_play_session_active = false;
firstMatch = true;
#ifdef LOCAL_TESTING #ifdef LOCAL_TESTING
isLocalConnected = false; isLocalConnected = false;

View file

@ -189,6 +189,7 @@ private:
void logMessageFromGame(u8* payload); void logMessageFromGame(u8* payload);
void prepareFileLength(u8* payload); void prepareFileLength(u8* payload);
void prepareFileLoad(u8* payload); void prepareFileLoad(u8* payload);
int getCharColor(u8 charId, u8 teamId);
void FileWriteThread(void); void FileWriteThread(void);
@ -214,6 +215,7 @@ private:
u32 frameSeqIdx = 0; u32 frameSeqIdx = 0;
bool isEnetInitialized = false; bool isEnetInitialized = false;
bool firstMatch = true;
std::default_random_engine generator; std::default_random_engine generator;

View file

@ -7,6 +7,13 @@
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Common/Version.h" #include "Common/Version.h"
#if defined __linux__ && HAVE_ALSA
#elif defined __APPLE__
#include <arpa/inet.h>
#include <netdb.h>
#elif defined _WIN32
#endif
class MmMessageType class MmMessageType
{ {
public: public:
@ -68,11 +75,6 @@ std::string SlippiMatchmaking::GetErrorMessage()
return m_errorMsg; return m_errorMsg;
} }
SlippiUser::UserInfo SlippiMatchmaking::GetOpponent()
{
return m_oppUser;
}
bool SlippiMatchmaking::IsSearching() bool SlippiMatchmaking::IsSearching()
{ {
return searchingStates.count(m_state) != 0; return searchingStates.count(m_state) != 0;
@ -208,9 +210,13 @@ void SlippiMatchmaking::startMatchmaking()
m_client = nullptr; m_client = nullptr;
int retryCount = 0; int retryCount = 0;
auto userInfo = m_user->GetUserInfo();
while (m_client == nullptr && retryCount < 15) while (m_client == nullptr && retryCount < 15)
{ {
m_hostPort = 49000 + (generator() % 2000); if (userInfo.port > 0)
m_hostPort = userInfo.port;
else
m_hostPort = 49000 + (generator() % 2000);
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Port to use: %d...", m_hostPort); ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Port to use: %d...", m_hostPort);
// We are explicitly setting the client address because we are trying to utilize our connection // We are explicitly setting the client address because we are trying to utilize our connection
@ -269,6 +275,7 @@ void SlippiMatchmaking::startMatchmaking()
continue; continue;
} }
netEvent.peer->data = &userInfo.display_name;
m_client->intercept = ENetUtil::InterceptCallback; m_client->intercept = ENetUtil::InterceptCallback;
isMmConnected = true; isMmConnected = true;
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Connected to mm server..."); ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Connected to mm server...");
@ -276,15 +283,40 @@ void SlippiMatchmaking::startMatchmaking()
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Trying to find match..."); ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Trying to find match...");
if (!m_user->IsLoggedIn()) // if (!m_user->IsLoggedIn())
{ // {
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Must be logged in to queue"); // ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Must be logged in to queue");
m_state = ProcessState::ERROR_ENCOUNTERED; // m_state = ProcessState::ERROR_ENCOUNTERED;
m_errorMsg = "Must be logged in to queue. Go back to menu"; // m_errorMsg = "Must be logged in to queue. Go back to menu";
return; // return;
} // }
auto userInfo = m_user->GetUserInfo(); // Compute LAN IP, in case 2 people are connecting from one IP we can send them each other's local
// IP instead of public. Experimental to allow people from behind one router to connect.
char host[256];
char lan_addr[30];
char* ip;
struct hostent* host_entry;
int hostname;
hostname = gethostname(host, sizeof(host)); // find the host name
if (hostname == -1)
{
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Error finding LAN address");
}
else
{
host_entry = gethostbyname(host); // find host information
if (host_entry == NULL)
{
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Error finding LAN host");
}
else
{
ip = inet_ntoa(*((struct in_addr*)host_entry->h_addr_list[0])); // Convert into IP string
INFO_LOG(SLIPPI_ONLINE, "[Matchmaking] LAN IP: %s", ip);
sprintf(lan_addr, "%s:%d", ip, m_hostPort);
}
}
std::vector<u8> connectCodeBuf; std::vector<u8> connectCodeBuf;
connectCodeBuf.insert(connectCodeBuf.end(), m_searchSettings.connectCode.begin(), connectCodeBuf.insert(connectCodeBuf.end(), m_searchSettings.connectCode.begin(),
@ -296,6 +328,7 @@ void SlippiMatchmaking::startMatchmaking()
request["user"] = {{"uid", userInfo.uid}, {"playKey", userInfo.play_key}}; request["user"] = {{"uid", userInfo.uid}, {"playKey", userInfo.play_key}};
request["search"] = {{"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf}}; request["search"] = {{"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf}};
request["appVersion"] = Common::scm_slippi_semver_str; request["appVersion"] = Common::scm_slippi_semver_str;
request["ipAddressLan"] = lan_addr;
sendMessage(request); sendMessage(request);
// Get response from server // Get response from server
@ -385,37 +418,64 @@ void SlippiMatchmaking::handleMatchmaking()
m_isSwapAttempt = false; m_isSwapAttempt = false;
m_netplayClient = nullptr; m_netplayClient = nullptr;
// Clear old user // Clear old users
SlippiUser::UserInfo emptyInfo; m_remoteIps.clear();
m_oppUser = emptyInfo; m_playerInfo.clear();
auto queue = getResp["players"]; auto queue = getResp["players"];
if (queue.is_array()) if (queue.is_array())
{ {
for (json::iterator it = queue.begin(); it != queue.end(); ++it) std::string localExternalIp = "";
{
json el = *it;
//SlippiUser::UserInfo playerInfo;
bool isLocal = el.value("isLocalPlayer", false); for (json::iterator it = queue.begin(); it != queue.end(); ++it)
//playerInfo.uid = el.value("uid", ""); {
//playerInfo.displayName = el.value("displayName", ""); json el = *it;
//playerInfo.connectCode = el.value("connectCode", ""); SlippiUser::UserInfo playerInfo;
//playerInfo.port = el.value("port", 0);
if (!isLocal) bool isLocal = el.value("isLocalPlayer", false);
{ playerInfo.uid = el.value("uid", "");
m_oppIp = el.value("ipAddress", "1.1.1.1:123"); playerInfo.display_name = el.value("displayName", "");
m_oppUser.uid = el.value("uid", ""); playerInfo.connect_code = el.value("connectCode", "");
m_oppUser.display_name = el.value("displayName", ""); playerInfo.port = el.value("port", 0);
m_oppUser.connect_code = el.value("connectCode", ""); m_playerInfo.push_back(playerInfo);
}
//else if (isLocal)
// m_localPlayerPort = playerInfo.port - 1; {
}; std::vector<std::string> localIpParts;
} localIpParts = SplitString(el.value("ipAddress", "1.1.1.1:123"), ':');
m_isHost = getResp.value("isHost", false); localExternalIp = localIpParts[0];
m_localPlayerIndex = playerInfo.port - 1;
}
};
// Loop a second time to get the correct remote IPs
for (json::iterator it = queue.begin(); it != queue.end(); ++it)
{
json el = *it;
if (el.value("port", 0) - 1 == m_localPlayerIndex)
continue;
auto extIp = el.value("ipAddress", "1.1.1.1:123");
std::vector<std::string> exIpParts;
exIpParts = SplitString(extIp, ':');
auto lanIp = el.value("ipAddressLan", "1.1.1.1:123");
if (exIpParts[0] != localExternalIp || lanIp.empty())
{
// If external IPs are different, just use that address
m_remoteIps.push_back(extIp);
continue;
}
// TODO: Instead of using one or the other, it might be better to try both
// If external IPs are the same, try using LAN IPs
m_remoteIps.push_back(lanIp);
}
}
m_isHost = getResp.value("isHost", false);
// Disconnect and destroy enet client to mm server // Disconnect and destroy enet client to mm server
terminateMmConnection(); terminateMmConnection();
@ -425,13 +485,62 @@ void SlippiMatchmaking::handleMatchmaking()
m_isHost ? "true" : "false"); m_isHost ? "true" : "false");
} }
int SlippiMatchmaking::LocalPlayerIndex()
{
return m_localPlayerIndex;
}
std::vector<SlippiUser::UserInfo> SlippiMatchmaking::GetPlayerInfo()
{
return m_playerInfo;
}
std::string SlippiMatchmaking::GetPlayerName(u8 port)
{
if (port >= m_playerInfo.size())
{
return "";
}
return m_playerInfo[port].display_name;
}
u8 SlippiMatchmaking::RemotePlayerCount()
{
if (m_playerInfo.size() == 0)
return 0;
return (u8)m_playerInfo.size() - 1;
}
void SlippiMatchmaking::handleConnecting() void SlippiMatchmaking::handleConnecting()
{ {
std::vector<std::string> ipParts = SplitString(m_oppIp, ':'); auto userInfo = m_user->GetUserInfo();
m_isSwapAttempt = false;
m_netplayClient = nullptr;
u8 remotePlayerCount = (u8)m_remoteIps.size();
std::vector<std::string> remoteParts;
std::vector<std::string> addrs;
std::vector<u16> ports;
for (int i = 0; i < m_remoteIps.size(); i++)
{
remoteParts.clear();
remoteParts = SplitString(m_remoteIps[i], ':');
addrs.push_back(remoteParts[0]);
ports.push_back(std::stoi(remoteParts[1]));
}
std::stringstream ipLog;
ipLog << "Remote player IPs: ";
for (int i = 0; i < m_remoteIps.size(); i++)
{
ipLog << m_remoteIps[i] << ", ";
}
// Is host is now used to specify who the decider is // Is host is now used to specify who the decider is
auto client = std::make_unique<SlippiNetplayClient>(ipParts[0], std::stoi(ipParts[1]), m_hostPort, auto client = std::make_unique<SlippiNetplayClient>(addrs, ports, remotePlayerCount, m_hostPort,
m_isHost); m_isHost, m_localPlayerIndex);
while (!m_netplayClient) while (!m_netplayClient)
{ {
@ -447,6 +556,32 @@ void SlippiMatchmaking::handleConnecting()
continue; continue;
} }
else if (status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED &&
m_searchSettings.mode == SlippiMatchmaking::OnlinePlayMode::TEAMS)
{
// If we failed setting up a connection in teams mode, show a detailed error about who we had
// issues connecting to.
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Failed to connect to players");
m_state = ProcessState::ERROR_ENCOUNTERED;
m_errorMsg = "Timed out waiting for other players to connect";
auto failedConns = client->GetFailedConnections();
if (!failedConns.empty())
{
std::stringstream err;
err << "Could not connect to players: ";
for (int i = 0; i < failedConns.size(); i++)
{
int p = failedConns[i];
if (p >= m_localPlayerIndex)
p++;
err << m_playerInfo[p].display_name << " ";
}
m_errorMsg = err.str();
}
return;
}
else if (status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED) else if (status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED)
{ {
ERROR_LOG(SLIPPI_ONLINE, ERROR_LOG(SLIPPI_ONLINE,

View file

@ -5,11 +5,15 @@
#include "Core/Slippi/SlippiNetplay.h" #include "Core/Slippi/SlippiNetplay.h"
#include "Core/Slippi/SlippiUser.h" #include "Core/Slippi/SlippiUser.h"
#include <enet/enet.h>
#include <random> #include <random>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#ifndef _WIN32
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
using json = nlohmann::json; using json = nlohmann::json;
@ -24,6 +28,7 @@ public:
RANKED = 0, RANKED = 0,
UNRANKED = 1, UNRANKED = 1,
DIRECT = 2, DIRECT = 2,
TEAMS = 3,
}; };
enum ProcessState enum ProcessState
@ -48,7 +53,10 @@ public:
bool IsSearching(); bool IsSearching();
std::unique_ptr<SlippiNetplayClient> GetNetplayClient(); std::unique_ptr<SlippiNetplayClient> GetNetplayClient();
std::string GetErrorMessage(); std::string GetErrorMessage();
SlippiUser::UserInfo GetOpponent(); int LocalPlayerIndex();
std::vector<SlippiUser::UserInfo> GetPlayerInfo();
std::string GetPlayerName(u8 port);
u8 RemotePlayerCount();
protected: protected:
const std::string MM_HOST_DEV = "35.197.121.196"; // Dev host const std::string MM_HOST_DEV = "35.197.121.196"; // Dev host
@ -76,9 +84,11 @@ protected:
int m_isSwapAttempt = false; int m_isSwapAttempt = false;
int m_hostPort; int m_hostPort;
std::string m_oppIp; int m_localPlayerIndex;
std::vector<std::string> m_remoteIps;
std::vector<SlippiUser::UserInfo> m_playerInfo;
bool m_joinedLobby;
bool m_isHost; bool m_isHost;
SlippiUser::UserInfo m_oppUser;
std::unique_ptr<SlippiNetplayClient> m_netplayClient; std::unique_ptr<SlippiNetplayClient> m_netplayClient;

File diff suppressed because it is too large Load diff

View file

@ -13,6 +13,7 @@
#include <queue> #include <queue>
#include <string> #include <string>
#include <thread> #include <thread>
#include <unordered_map>
#include <vector> #include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Event.h" #include "Common/Event.h"
@ -21,7 +22,6 @@
#include "Core/NetPlayProto.h" #include "Core/NetPlayProto.h"
#include "Core/Slippi/SlippiPad.h" #include "Core/Slippi/SlippiPad.h"
#include "InputCommon/GCPadStatus.h" #include "InputCommon/GCPadStatus.h"
#ifdef _WIN32 #ifdef _WIN32
#include <Qos2.h> #include <Qos2.h>
#endif #endif
@ -29,18 +29,24 @@
#define SLIPPI_ONLINE_LOCKSTEP_INTERVAL \ #define SLIPPI_ONLINE_LOCKSTEP_INTERVAL \
30 // Number of frames to wait before attempting to time-sync 30 // Number of frames to wait before attempting to time-sync
#define SLIPPI_PING_DISPLAY_INTERVAL 60 #define SLIPPI_PING_DISPLAY_INTERVAL 60
#define SLIPPI_REMOTE_PLAYER_MAX 3
#define SLIPPI_REMOTE_PLAYER_COUNT 3
struct SlippiRemotePadOutput struct SlippiRemotePadOutput
{ {
int32_t latestFrame; int32_t latestFrame;
u8 playerIdx;
std::vector<u8> data; std::vector<u8> data;
}; };
class SlippiPlayerSelections class SlippiPlayerSelections
{ {
public: public:
u8 playerIdx = 0;
u8 characterId = 0; u8 characterId = 0;
u8 characterColor = 0; u8 characterColor = 0;
u8 teamId = 0;
bool isCharacterSelected = false; bool isCharacterSelected = false;
u16 stageId = 0; u16 stageId = 0;
@ -64,6 +70,7 @@ public:
{ {
this->characterId = s.characterId; this->characterId = s.characterId;
this->characterColor = s.characterColor; this->characterColor = s.characterColor;
this->teamId = s.teamId;
this->isCharacterSelected = true; this->isCharacterSelected = true;
} }
} }
@ -73,6 +80,7 @@ public:
characterId = 0; characterId = 0;
characterColor = 0; characterColor = 0;
isCharacterSelected = false; isCharacterSelected = false;
teamId = 0;
stageId = 0; stageId = 0;
isStageSelected = false; isStageSelected = false;
@ -85,12 +93,15 @@ class SlippiMatchInfo
{ {
public: public:
SlippiPlayerSelections localPlayerSelections; SlippiPlayerSelections localPlayerSelections;
SlippiPlayerSelections remotePlayerSelections; SlippiPlayerSelections remotePlayerSelections[SLIPPI_REMOTE_PLAYER_MAX];
void Reset() void Reset()
{ {
localPlayerSelections.Reset(); localPlayerSelections.Reset();
remotePlayerSelections.Reset(); for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
{
remotePlayerSelections[i].Reset();
}
} }
}; };
@ -101,8 +112,9 @@ public:
void SendAsync(std::unique_ptr<sf::Packet> packet); void SendAsync(std::unique_ptr<sf::Packet> packet);
SlippiNetplayClient(bool isDecider); // Make a dummy client SlippiNetplayClient(bool isDecider); // Make a dummy client
SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort, SlippiNetplayClient(std::vector<std::string> addrs, std::vector<u16> ports,
bool isDecider); const u8 remotePlayerCount, const u16 localPort, bool isDecider,
u8 playerIdx);
~SlippiNetplayClient(); ~SlippiNetplayClient();
// Slippi Online // Slippi Online
@ -117,23 +129,26 @@ public:
bool IsDecider(); bool IsDecider();
bool IsConnectionSelected(); bool IsConnectionSelected();
u8 LocalPlayerPort();
SlippiConnectStatus GetSlippiConnectStatus(); SlippiConnectStatus GetSlippiConnectStatus();
std::vector<int> GetFailedConnections();
void StartSlippiGame(); void StartSlippiGame();
void SendConnectionSelected(); void SendConnectionSelected();
void SendSlippiPad(std::unique_ptr<SlippiPad> pad); void SendSlippiPad(std::unique_ptr<SlippiPad> pad);
void SetMatchSelections(SlippiPlayerSelections& s); void SetMatchSelections(SlippiPlayerSelections& s);
std::unique_ptr<SlippiRemotePadOutput> GetSlippiRemotePad(int32_t curFrame); std::unique_ptr<SlippiRemotePadOutput> GetSlippiRemotePad(int32_t curFrame, int index);
void DropOldRemoteInputs(int32_t curFrame);
SlippiMatchInfo* GetMatchInfo(); SlippiMatchInfo* GetMatchInfo();
u64 GetSlippiPing(); int32_t GetSlippiLatestRemoteFrame();
s32 GetSlippiLatestRemoteFrame(); SlippiPlayerSelections GetSlippiRemoteChatMessage();
u8 GetSlippiRemoteChatMessage();
u8 GetSlippiRemoteSentChatMessage(); u8 GetSlippiRemoteSentChatMessage();
s32 CalcTimeOffsetUs(); s32 CalcTimeOffsetUs();
void WriteChatMessageToPacket(sf::Packet& packet, int messageId); void WriteChatMessageToPacket(sf::Packet& packet, int messageId, u8 playerIdx);
std::unique_ptr<SlippiPlayerSelections> ReadChatMessageFromPacket(sf::Packet& packet); std::unique_ptr<SlippiPlayerSelections> ReadChatMessageFromPacket(sf::Packet& packet);
u8 remoteChatMessageId = 0; // most recent chat message id from opponent std::unique_ptr<SlippiPlayerSelections> remoteChatMessageSelection =
nullptr; // most recent chat message player selection (message + player index)
u8 remoteSentChatMessageId = 0; // most recent chat message id that current player sent u8 remoteSentChatMessageId = 0; // most recent chat message id that current player sent
protected: protected:
@ -148,8 +163,9 @@ protected:
std::queue<std::unique_ptr<sf::Packet>> m_async_queue; std::queue<std::unique_ptr<sf::Packet>> m_async_queue;
ENetHost* m_client = nullptr; ENetHost* m_client = nullptr;
ENetPeer* m_server = nullptr; std::vector<ENetPeer*> m_server;
std::thread m_thread; std::thread m_thread;
u8 m_remotePlayerCount = 0;
std::string m_selected_game; std::string m_selected_game;
Common::Flag m_is_running{false}; Common::Flag m_is_running{false};
@ -166,23 +182,30 @@ protected:
u64 timeUs; u64 timeUs;
}; };
struct struct FrameOffsetData
{ {
// TODO: Should the buffer size be dynamic based on time sync interval or not? // TODO: Should the buffer size be dynamic based on time sync interval or not?
int idx; int idx;
std::vector<s32> buf; std::vector<s32> buf;
} frameOffsetData; };
bool isConnectionSelected = false; bool isConnectionSelected = false;
bool isDecider = false; bool isDecider = false;
int32_t lastFrameAcked;
bool hasGameStarted = false; bool hasGameStarted = false;
FrameTiming lastFrameTiming; u8 playerIdx = 0;
u64 pingUs;
std::deque<std::unique_ptr<SlippiPad>> localPadQueue; // most recent inputs at start of deque std::deque<std::unique_ptr<SlippiPad>> localPadQueue; // most recent inputs at start of deque
std::deque<std::unique_ptr<SlippiPad>> remotePadQueue; // most recent inputs at start of deque std::deque<std::unique_ptr<SlippiPad>>
std::queue<FrameTiming> ackTimers; remotePadQueue[SLIPPI_REMOTE_PLAYER_MAX]; // most recent inputs at start of deque
u64 pingUs[SLIPPI_REMOTE_PLAYER_MAX];
int32_t lastFrameAcked[SLIPPI_REMOTE_PLAYER_MAX];
FrameOffsetData frameOffsetData[SLIPPI_REMOTE_PLAYER_MAX];
FrameTiming lastFrameTiming[SLIPPI_REMOTE_PLAYER_MAX];
std::array<std::queue<FrameTiming>, SLIPPI_REMOTE_PLAYER_MAX> ackTimers;
SlippiConnectStatus slippiConnectStatus = SlippiConnectStatus::NET_CONNECT_STATUS_UNSET; SlippiConnectStatus slippiConnectStatus = SlippiConnectStatus::NET_CONNECT_STATUS_UNSET;
std::vector<int> failedConnections;
SlippiMatchInfo matchInfo; SlippiMatchInfo matchInfo;
bool m_is_recording = false; bool m_is_recording = false;
@ -191,7 +214,8 @@ protected:
std::unique_ptr<SlippiPlayerSelections> readSelectionsFromPacket(sf::Packet& packet); std::unique_ptr<SlippiPlayerSelections> readSelectionsFromPacket(sf::Packet& packet);
private: private:
unsigned int OnData(sf::Packet& packet); u8 PlayerIdxFromPort(u8 port);
unsigned int OnData(sf::Packet& packet, ENetPeer* peer);
void Send(sf::Packet& packet); void Send(sf::Packet& packet);
void Disconnect(); void Disconnect();
@ -207,7 +231,7 @@ private:
extern SlippiNetplayClient* SLIPPI_NETPLAY; // singleton static pointer extern SlippiNetplayClient* SLIPPI_NETPLAY; // singleton static pointer
inline bool IsOnline() static bool IsOnline()
{ {
return SLIPPI_NETPLAY != nullptr; return SLIPPI_NETPLAY != nullptr;
} }

View file

@ -19,6 +19,14 @@ SlippiPad::SlippiPad(int32_t frame, u8* padBuf) : SlippiPad(frame)
memcpy(this->padBuf, padBuf, SLIPPI_PAD_DATA_SIZE); memcpy(this->padBuf, padBuf, SLIPPI_PAD_DATA_SIZE);
} }
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);
}
SlippiPad::~SlippiPad() SlippiPad::~SlippiPad()
{ {
// Do nothing? // Do nothing?

View file

@ -10,8 +10,10 @@ class SlippiPad
public: public:
SlippiPad(int32_t frame); SlippiPad(int32_t frame);
SlippiPad(int32_t frame, u8* padBuf); SlippiPad(int32_t frame, u8* padBuf);
SlippiPad(int32_t frame, u8 playerIdx, u8 *padBuf);
~SlippiPad(); ~SlippiPad();
int32_t frame; int32_t frame;
u8 playerIdx;
u8 padBuf[SLIPPI_PAD_FULL_SIZE]; u8 padBuf[SLIPPI_PAD_FULL_SIZE];
}; };

View file

@ -106,7 +106,7 @@ bool SlippiUser::AttemptLogin()
{ {
std::string user_file_path = getUserFilePath(); std::string user_file_path = getUserFilePath();
INFO_LOG(SLIPPI_ONLINE, "Looking for file at: %s", user_file_path.c_str()); // INFO_LOG(SLIPPI_ONLINE, "Looking for file at: %s", user_file_path.c_str());
{ {
// Put the filename here in its own scope because we don't really need it elsewhere // Put the filename here in its own scope because we don't really need it elsewhere
@ -312,6 +312,7 @@ SlippiUser::UserInfo SlippiUser::parseFile(std::string file_contents)
info.play_key = readString(res, "playKey"); info.play_key = readString(res, "playKey");
info.connect_code = readString(res, "connectCode"); info.connect_code = readString(res, "connectCode");
info.latest_version = readString(res, "latestVersion"); info.latest_version = readString(res, "latestVersion");
info.port = res.value("port", -1);
return info; return info;
} }

View file

@ -19,6 +19,8 @@ public:
std::string connect_code = ""; std::string connect_code = "";
std::string latest_version = ""; std::string latest_version = "";
std::string file_contents = ""; std::string file_contents = "";
int port;
}; };
SlippiUser(); SlippiUser();