mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-26 12:16:20 +00:00
https://github.com/project-slippi/Ishiiruka/pull/225 (doubles) + extra commits detailed below
8c2943b854f742d9b27bdbf5ffe8fc278a3fe318 c891220772e1258e6e9192ef8ec195c227e8f10b
This commit is contained in:
parent
b5dff938a3
commit
246ba8397e
20 changed files with 1065 additions and 402 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Data/Sys/GameFiles/GALE01/SdSlChr.dat.diff
Normal file
BIN
Data/Sys/GameFiles/GALE01/SdSlChr.dat.diff
Normal file
Binary file not shown.
BIN
Data/Sys/GameFiles/GALE01/SdSlChr.usd.diff
Normal file
BIN
Data/Sys/GameFiles/GALE01/SdSlChr.usd.diff
Normal file
Binary file not shown.
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ public:
|
|||
std::string connect_code = "";
|
||||
std::string latest_version = "";
|
||||
std::string file_contents = "";
|
||||
|
||||
int port;
|
||||
};
|
||||
|
||||
SlippiUser();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue