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

View file

@ -154,6 +154,7 @@ struct SConfig
int m_slippiOnlineDelay = 2;
bool m_slippiEnableSeek = true;
bool m_slippiSaveReplays = true;
bool m_slippiEnableQuickChat = true;
bool m_slippiReplayMonthFolders = false;
std::string m_strSlippiReplayDir;
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
if (slippi_netplay)
{
auto userInfo = user->GetUserInfo();
auto oppInfo = matchmaking->GetOpponent();
{
auto playerInfo = matchmaking->GetPlayerInfo();
auto isDecider = slippi_netplay->IsDecider();
int local_port = isDecider ? 0 : 1;
int remote_port = isDecider ? 1 : 0;
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;
}
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
@ -1499,12 +1495,6 @@ void CEXISlippi::handleOnlineInputs(u8* payload)
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)
{
availableSavestates.clear();
@ -1525,6 +1515,13 @@ void CEXISlippi::handleOnlineInputs(u8* payload)
slippi_netplay->StartSlippiGame();
}
if (isDisconnected())
{
auto status = slippi_netplay->GetSlippiConnectStatus();
m_read_queue.push_back(3); // Indicate we disconnected
return;
}
if (shouldSkipOnlineFrame(frame))
{
// Send inputs that have not yet been acked
@ -1586,6 +1583,7 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame)
auto offsetUs = slippi_netplay->CalcTimeOffsetUs();
INFO_LOG(SLIPPI_ONLINE, "[Frame %d] Offset is: %d us", frame, offsetUs);
// TODO: figure out a better solution here for doubles?
if (offsetUs > 10000)
{
isCurrentlySkipping = true;
@ -1642,27 +1640,56 @@ void CEXISlippi::prepareOpponentInputs(u8* payload)
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
int offset = (result->latestFrame - frame) * SLIPPI_PAD_FULL_SIZE;
offset = offset < 0 ? 0 : offset;
std::unique_ptr<SlippiRemotePadOutput> results[SLIPPI_REMOTE_PLAYER_MAX];
int offset[SLIPPI_REMOTE_PLAYER_MAX];
INFO_LOG(SLIPPI_ONLINE, "Preparing pad data for frame %d", frame);
// add latest frame we are transfering to begining of return buf
int32_t latestFrame = offset > 0 ? frame : result->latestFrame;
appendWordToBuffer(&m_read_queue, *(u32*)&latestFrame);
// 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);
// copy pad data over
auto txStart = result->data.begin() + offset;
auto txEnd = result->data.end();
// 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];
std::vector<u8> tx;
tx.insert(tx.end(), txStart, txEnd);
tx.resize(SLIPPI_PAD_FULL_SIZE * ROLLBACK_MAX_FRAMES, 0);
// 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);
}
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],
// 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
// someone else get force removed from queue and have to requeue
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";
return;
@ -1794,31 +1821,28 @@ void CEXISlippi::startFindMatch(u8* payload)
void CEXISlippi::prepareOnlineMatchState()
{
// This match block is a VS match with P1 Red Falco vs P2 Red Bowser on Battlefield. The proper
// values will be overwritten
static std::vector<u8> 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, 0x1A, 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,
0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x1A, 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, 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<u8> 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();
@ -1838,12 +1862,10 @@ void CEXISlippi::prepareOnlineMatchState()
m_read_queue.push_back(mmState); // Matchmaking State
u8 localPlayerReady = localSelections.isCharacterSelected;
u8 remotePlayerReady = 0;
u8 localPlayerIndex = 0;
u8 remotePlayersReady = 0;
u8 localPlayerIndex = matchmaking->LocalPlayerIndex();
u8 remotePlayerIndex = 1;
auto opponent = matchmaking->GetOpponent();
std::string oppName = opponent.display_name;
auto userInfo = user->GetUserInfo();
if (mmState == SlippiMatchmaking::ProcessState::CONNECTION_SUCCESS)
@ -1871,9 +1893,24 @@ void CEXISlippi::prepareOnlineMatchState()
{
auto matchInfo = slippi_netplay->GetMatchInfo();
#ifdef LOCAL_TESTING
remotePlayerReady = true;
remotePlayersReady = true;
#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
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
if (!is_play_session_active)
{
std::vector<std::string> uids{"", ""};
uids[localPlayerIndex] = userInfo.uid;
uids[remotePlayerIndex] = opponent.uid;
std::vector<std::string> uids;
auto mmPlayers = matchmaking->GetPlayerInfo();
for (auto mmp : mmPlayers)
{
uids.push_back(mmp.uid);
}
game_reporter->StartNewSession(uids);
@ -1906,55 +1947,93 @@ void CEXISlippi::prepareOnlineMatchState()
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;
std::string localPlayerName = "";
std::string oppName = "";
std::string p1Name = "";
std::string p2Name = "";
u8 chatMessageId = 0;
u8 chatMessagePlayerIdx = 0;
u8 sentChatMessageId = 0;
#ifdef LOCAL_TESTING
chatMessageId = localChatMessageId;
localChatMessageId = 0;
// in CSS p1 is always current player and p2 is opponent
p1Name = "Player 1";
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
// Set chat message if any
if (slippi_netplay)
{
chatMessageId = slippi_netplay->GetSlippiRemoteChatMessage();
sentChatMessageId = slippi_netplay->GetSlippiRemoteSentChatMessage();
// in CSS p1 is always current player and p2 is opponent
p1Name = userInfo.display_name;
p2Name = oppName;
}
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
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 isDecider = slippi_netplay->IsDecider();
auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT;
auto matchInfo = slippi_netplay->GetMatchInfo();
SlippiPlayerSelections lps = matchInfo->localPlayerSelections;
SlippiPlayerSelections rps = matchInfo->remotePlayerSelections;
std::vector<u8> leftTeamPlayers = {};
std::vector<u8> 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;
#ifdef LOCAL_TESTING
rps.characterId = 0x2;
rps.characterColor = 2;
oppName = std::string("Player");
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;
}
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
// Check if someone is picking dumb characters in non-direct
auto localCharOk = lps.characterId < 26;
auto remoteCharOk = rps.characterId < 26;
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();
@ -1965,52 +2044,99 @@ void CEXISlippi::prepareOnlineMatchState()
}
// Overwrite local player character
onlineMatchBlock[0x60 + localPlayerIndex * 0x24] = lps.characterId;
onlineMatchBlock[0x63 + localPlayerIndex * 0x24] = lps.characterColor;
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;
// Overwrite remote player character
onlineMatchBlock[0x60 + remotePlayerIndex * 0x24] = rps.characterId;
onlineMatchBlock[0x63 + remotePlayerIndex * 0x24] = rps.characterColor;
// 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;
// Make one character lighter if same character, same color
bool isSheikVsZelda = lps.characterId == 0x12 && rps.characterId == 0x13 ||
lps.characterId == 0x13 && rps.characterId == 0x12;
bool charMatch = lps.characterId == rps.characterId || isSheikVsZelda;
bool colMatch = lps.characterColor == rps.characterColor;
// Set Char Colors
onlineMatchBlock[0x63 + idx * 0x24] = matchInfo->remotePlayerSelections[i].characterColor;
onlineMatchBlock[0x67 + 0x24] = charMatch && colMatch ? 1 : 0;
// Set Team Ids
onlineMatchBlock[0x69 + idx * 0x24] = matchInfo->remotePlayerSelections[i].teamId;
}
// Overwrite stage
u16 stageId;
if (isDecider)
{
stageId = lps.isStageSelected ? lps.stageId : rps.stageId;
}
else
{
stageId = rps.isStageSelected ? rps.stageId : lps.stageId;
}
// Handle Singles/Teams specific logic
if (remotePlayerCount < 3)
{
onlineMatchBlock[0x8] = 0; // is Teams = false
// int seconds = 0;
// u32 *timer = (u32 *)&onlineMatchBlock[0x10];
//*timer = Common::swap32(seconds * 60);
// Set p3/p4 player type to none
onlineMatchBlock[0x61 + 2 * 0x24] = 3;
onlineMatchBlock[0x61 + 3 * 0x24] = 3;
u16* stage = (u16*)&onlineMatchBlock[0xE];
*stage = Common::swap16(stageId);
// 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;
// Set rng offset
rngOffset = isDecider ? lps.rngOffset : rps.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]);
onlineMatchBlock[0x67 + 0x24] = charMatch && colMatch ? 1 : 0;
}
else
{
onlineMatchBlock[0x8] = 1; // is Teams = true
// Set player names
p1Name = isDecider ? userInfo.display_name : oppName;
p2Name = isDecider ? oppName : userInfo.display_name;
// Set p3/p4 player type to human
onlineMatchBlock[0x61 + 2 * 0x24] = 0;
onlineMatchBlock[0x61 + 3 * 0x24] = 0;
// Turn pause on in direct, off in everything else
u8* gameBitField3 = (u8*)&onlineMatchBlock[2];
*gameBitField3 = lastSearch.mode == directMode ? *gameBitField3 & 0xF7 : *gameBitField3 | 0x8;
// 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;
}
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
@ -2022,13 +2148,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);
// Add names to output
p1Name = ConvertStringForGame(p1Name, MAX_NAME_LENGTH);
m_read_queue.insert(m_read_queue.end(), p1Name.begin(), p1Name.end());
p2Name = ConvertStringForGame(p2Name, MAX_NAME_LENGTH);
m_read_queue.insert(m_read_queue.end(), p2Name.begin(), p2Name.end());
oppName = ConvertStringForGame(oppName, MAX_NAME_LENGTH);
// 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());
#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());
// Add error message if there is one
@ -2071,12 +2236,13 @@ void CEXISlippi::setMatchSelections(u8* payload)
{
SlippiPlayerSelections s;
s.characterId = payload[0];
s.characterColor = payload[1];
s.isCharacterSelected = payload[2];
s.teamId = payload[0];
s.characterId = payload[1];
s.characterColor = payload[2];
s.isCharacterSelected = payload[3];
s.stageId = Common::swap16(&payload[3]);
u8 stageSelectOption = payload[5];
s.stageId = Common::swap16(&payload[4]);
u8 stageSelectOption = payload[6];
s.isStageSelected = stageSelectOption == 1 || stageSelectOption == 3;
if (stageSelectOption == 3)
@ -2085,7 +2251,16 @@ void CEXISlippi::setMatchSelections(u8* payload)
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
localSelections.Merge(s);
@ -2142,7 +2317,7 @@ void CEXISlippi::handleChatMessage(u8* payload)
auto packet = std::make_unique<sf::Packet>();
// OSD::AddMessage("[Me]: "+ msg, OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
slippi_netplay->remoteSentChatMessageId = messageId;
slippi_netplay->WriteChatMessageToPacket(*packet, messageId);
slippi_netplay->WriteChatMessageToPacket(*packet, messageId, slippi_netplay->LocalPlayerPort());
slippi_netplay->SendAsync(std::move(packet));
}
}
@ -2258,6 +2433,7 @@ void CEXISlippi::handleConnectionCleanup()
// Reset play session
is_play_session_active = false;
firstMatch = true;
#ifdef LOCAL_TESTING
isLocalConnected = false;

View file

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

View file

@ -7,6 +7,13 @@
#include "Common/StringUtil.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
{
public:
@ -68,11 +75,6 @@ std::string SlippiMatchmaking::GetErrorMessage()
return m_errorMsg;
}
SlippiUser::UserInfo SlippiMatchmaking::GetOpponent()
{
return m_oppUser;
}
bool SlippiMatchmaking::IsSearching()
{
return searchingStates.count(m_state) != 0;
@ -208,9 +210,13 @@ void SlippiMatchmaking::startMatchmaking()
m_client = nullptr;
int retryCount = 0;
auto userInfo = m_user->GetUserInfo();
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);
// We are explicitly setting the client address because we are trying to utilize our connection
@ -269,6 +275,7 @@ void SlippiMatchmaking::startMatchmaking()
continue;
}
netEvent.peer->data = &userInfo.display_name;
m_client->intercept = ENetUtil::InterceptCallback;
isMmConnected = true;
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...");
if (!m_user->IsLoggedIn())
{
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Must be logged in to queue");
m_state = ProcessState::ERROR_ENCOUNTERED;
m_errorMsg = "Must be logged in to queue. Go back to menu";
return;
}
// if (!m_user->IsLoggedIn())
// {
// ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Must be logged in to queue");
// m_state = ProcessState::ERROR_ENCOUNTERED;
// m_errorMsg = "Must be logged in to queue. Go back to menu";
// 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;
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["search"] = {{"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf}};
request["appVersion"] = Common::scm_slippi_semver_str;
request["ipAddressLan"] = lan_addr;
sendMessage(request);
// Get response from server
@ -385,37 +418,64 @@ void SlippiMatchmaking::handleMatchmaking()
m_isSwapAttempt = false;
m_netplayClient = nullptr;
// Clear old user
SlippiUser::UserInfo emptyInfo;
m_oppUser = emptyInfo;
// Clear old users
m_remoteIps.clear();
m_playerInfo.clear();
auto queue = getResp["players"];
if (queue.is_array())
{
for (json::iterator it = queue.begin(); it != queue.end(); ++it)
{
json el = *it;
//SlippiUser::UserInfo playerInfo;
if (queue.is_array())
{
std::string localExternalIp = "";
bool isLocal = el.value("isLocalPlayer", false);
//playerInfo.uid = el.value("uid", "");
//playerInfo.displayName = el.value("displayName", "");
//playerInfo.connectCode = el.value("connectCode", "");
//playerInfo.port = el.value("port", 0);
for (json::iterator it = queue.begin(); it != queue.end(); ++it)
{
json el = *it;
SlippiUser::UserInfo playerInfo;
if (!isLocal)
{
m_oppIp = el.value("ipAddress", "1.1.1.1:123");
m_oppUser.uid = el.value("uid", "");
m_oppUser.display_name = el.value("displayName", "");
m_oppUser.connect_code = el.value("connectCode", "");
}
bool isLocal = el.value("isLocalPlayer", false);
playerInfo.uid = el.value("uid", "");
playerInfo.display_name = el.value("displayName", "");
playerInfo.connect_code = el.value("connectCode", "");
playerInfo.port = el.value("port", 0);
m_playerInfo.push_back(playerInfo);
//else
// m_localPlayerPort = playerInfo.port - 1;
};
}
m_isHost = getResp.value("isHost", false);
if (isLocal)
{
std::vector<std::string> localIpParts;
localIpParts = SplitString(el.value("ipAddress", "1.1.1.1:123"), ':');
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
terminateMmConnection();
@ -425,13 +485,62 @@ void SlippiMatchmaking::handleMatchmaking()
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()
{
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
auto client = std::make_unique<SlippiNetplayClient>(ipParts[0], std::stoi(ipParts[1]), m_hostPort,
m_isHost);
auto client = std::make_unique<SlippiNetplayClient>(addrs, ports, remotePlayerCount, m_hostPort,
m_isHost, m_localPlayerIndex);
while (!m_netplayClient)
{
@ -447,6 +556,32 @@ void SlippiMatchmaking::handleConnecting()
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)
{
ERROR_LOG(SLIPPI_ONLINE,

View file

@ -5,11 +5,15 @@
#include "Core/Slippi/SlippiNetplay.h"
#include "Core/Slippi/SlippiUser.h"
#include <enet/enet.h>
#include <random>
#include <unordered_map>
#include <vector>
#ifndef _WIN32
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include <nlohmann/json.hpp>
using json = nlohmann::json;
@ -24,6 +28,7 @@ public:
RANKED = 0,
UNRANKED = 1,
DIRECT = 2,
TEAMS = 3,
};
enum ProcessState
@ -48,7 +53,10 @@ public:
bool IsSearching();
std::unique_ptr<SlippiNetplayClient> GetNetplayClient();
std::string GetErrorMessage();
SlippiUser::UserInfo GetOpponent();
int LocalPlayerIndex();
std::vector<SlippiUser::UserInfo> GetPlayerInfo();
std::string GetPlayerName(u8 port);
u8 RemotePlayerCount();
protected:
const std::string MM_HOST_DEV = "35.197.121.196"; // Dev host
@ -76,9 +84,11 @@ protected:
int m_isSwapAttempt = false;
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;
SlippiUser::UserInfo m_oppUser;
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 <string>
#include <thread>
#include <unordered_map>
#include <vector>
#include "Common/CommonTypes.h"
#include "Common/Event.h"
@ -21,7 +22,6 @@
#include "Core/NetPlayProto.h"
#include "Core/Slippi/SlippiPad.h"
#include "InputCommon/GCPadStatus.h"
#ifdef _WIN32
#include <Qos2.h>
#endif
@ -29,18 +29,24 @@
#define SLIPPI_ONLINE_LOCKSTEP_INTERVAL \
30 // Number of frames to wait before attempting to time-sync
#define SLIPPI_PING_DISPLAY_INTERVAL 60
#define SLIPPI_REMOTE_PLAYER_MAX 3
#define SLIPPI_REMOTE_PLAYER_COUNT 3
struct SlippiRemotePadOutput
{
int32_t latestFrame;
u8 playerIdx;
std::vector<u8> data;
};
class SlippiPlayerSelections
{
public:
u8 playerIdx = 0;
u8 characterId = 0;
u8 characterColor = 0;
u8 teamId = 0;
bool isCharacterSelected = false;
u16 stageId = 0;
@ -64,6 +70,7 @@ public:
{
this->characterId = s.characterId;
this->characterColor = s.characterColor;
this->teamId = s.teamId;
this->isCharacterSelected = true;
}
}
@ -73,6 +80,7 @@ public:
characterId = 0;
characterColor = 0;
isCharacterSelected = false;
teamId = 0;
stageId = 0;
isStageSelected = false;
@ -85,12 +93,15 @@ class SlippiMatchInfo
{
public:
SlippiPlayerSelections localPlayerSelections;
SlippiPlayerSelections remotePlayerSelections;
SlippiPlayerSelections remotePlayerSelections[SLIPPI_REMOTE_PLAYER_MAX];
void 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);
SlippiNetplayClient(bool isDecider); // Make a dummy client
SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort,
bool isDecider);
SlippiNetplayClient(std::vector<std::string> addrs, std::vector<u16> ports,
const u8 remotePlayerCount, const u16 localPort, bool isDecider,
u8 playerIdx);
~SlippiNetplayClient();
// Slippi Online
@ -117,23 +129,26 @@ public:
bool IsDecider();
bool IsConnectionSelected();
u8 LocalPlayerPort();
SlippiConnectStatus GetSlippiConnectStatus();
std::vector<int> GetFailedConnections();
void StartSlippiGame();
void SendConnectionSelected();
void SendSlippiPad(std::unique_ptr<SlippiPad> pad);
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();
u64 GetSlippiPing();
s32 GetSlippiLatestRemoteFrame();
u8 GetSlippiRemoteChatMessage();
int32_t GetSlippiLatestRemoteFrame();
SlippiPlayerSelections GetSlippiRemoteChatMessage();
u8 GetSlippiRemoteSentChatMessage();
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);
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
protected:
@ -148,8 +163,9 @@ protected:
std::queue<std::unique_ptr<sf::Packet>> m_async_queue;
ENetHost* m_client = nullptr;
ENetPeer* m_server = nullptr;
std::vector<ENetPeer*> m_server;
std::thread m_thread;
u8 m_remotePlayerCount = 0;
std::string m_selected_game;
Common::Flag m_is_running{false};
@ -166,23 +182,30 @@ protected:
u64 timeUs;
};
struct
struct FrameOffsetData
{
// TODO: Should the buffer size be dynamic based on time sync interval or not?
int idx;
std::vector<s32> buf;
} frameOffsetData;
};
bool isConnectionSelected = false;
bool isDecider = false;
int32_t lastFrameAcked;
bool hasGameStarted = false;
FrameTiming lastFrameTiming;
u64 pingUs;
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::queue<FrameTiming> ackTimers;
u8 playerIdx = 0;
std::deque<std::unique_ptr<SlippiPad>> localPadQueue; // most recent inputs at start of deque
std::deque<std::unique_ptr<SlippiPad>>
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;
std::vector<int> failedConnections;
SlippiMatchInfo matchInfo;
bool m_is_recording = false;
@ -191,7 +214,8 @@ protected:
std::unique_ptr<SlippiPlayerSelections> readSelectionsFromPacket(sf::Packet& packet);
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 Disconnect();
@ -207,7 +231,7 @@ private:
extern SlippiNetplayClient* SLIPPI_NETPLAY; // singleton static pointer
inline bool IsOnline()
static bool IsOnline()
{
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);
}
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()
{
// Do nothing?

View file

@ -10,8 +10,10 @@ 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[SLIPPI_PAD_FULL_SIZE];
};

View file

@ -106,7 +106,7 @@ bool SlippiUser::AttemptLogin()
{
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
@ -312,6 +312,7 @@ SlippiUser::UserInfo SlippiUser::parseFile(std::string file_contents)
info.play_key = readString(res, "playKey");
info.connect_code = readString(res, "connectCode");
info.latest_version = readString(res, "latestVersion");
info.port = res.value("port", -1);
return info;
}

View file

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