mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-26 20:25:58 +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("OnlineDelay", m_slippiOnlineDelay);
|
||||||
slippi->Set("SaveReplays", m_slippiSaveReplays);
|
slippi->Set("SaveReplays", m_slippiSaveReplays);
|
||||||
|
slippi->Set("EnableQuickChat", m_slippiEnableQuickChat);
|
||||||
slippi->Set("ReplayMonthFolders", m_slippiReplayMonthFolders);
|
slippi->Set("ReplayMonthFolders", m_slippiReplayMonthFolders);
|
||||||
slippi->Set("ReplayDir", m_strSlippiReplayDir);
|
slippi->Set("ReplayDir", m_strSlippiReplayDir);
|
||||||
slippi->Set("PlaybackControls", m_slippiEnableSeek);
|
slippi->Set("PlaybackControls", m_slippiEnableSeek);
|
||||||
|
@ -543,6 +544,7 @@ void SConfig::LoadSlippiSettings(IniFile& ini)
|
||||||
slippi->Get("PlaybackControls", &m_slippiEnableSeek, true);
|
slippi->Get("PlaybackControls", &m_slippiEnableSeek, true);
|
||||||
slippi->Get("OnlineDelay", &m_slippiOnlineDelay, 2);
|
slippi->Get("OnlineDelay", &m_slippiOnlineDelay, 2);
|
||||||
slippi->Get("SaveReplays", &m_slippiSaveReplays, true);
|
slippi->Get("SaveReplays", &m_slippiSaveReplays, true);
|
||||||
|
slippi->Get("EnableQuickChat", &m_slippiEnableQuickChat, true);
|
||||||
slippi->Get("ReplayMonthFolders", &m_slippiReplayMonthFolders, false);
|
slippi->Get("ReplayMonthFolders", &m_slippiReplayMonthFolders, false);
|
||||||
std::string default_replay_dir = File::GetHomeDirectory() + DIR_SEP + "Slippi";
|
std::string default_replay_dir = File::GetHomeDirectory() + DIR_SEP + "Slippi";
|
||||||
slippi->Get("ReplayDir", &m_strSlippiReplayDir, default_replay_dir);
|
slippi->Get("ReplayDir", &m_strSlippiReplayDir, default_replay_dir);
|
||||||
|
|
|
@ -154,6 +154,7 @@ struct SConfig
|
||||||
int m_slippiOnlineDelay = 2;
|
int m_slippiOnlineDelay = 2;
|
||||||
bool m_slippiEnableSeek = true;
|
bool m_slippiEnableSeek = true;
|
||||||
bool m_slippiSaveReplays = true;
|
bool m_slippiSaveReplays = true;
|
||||||
|
bool m_slippiEnableQuickChat = true;
|
||||||
bool m_slippiReplayMonthFolders = false;
|
bool m_slippiReplayMonthFolders = false;
|
||||||
std::string m_strSlippiReplayDir;
|
std::string m_strSlippiReplayDir;
|
||||||
bool m_blockingPipes = false;
|
bool m_blockingPipes = false;
|
||||||
|
|
|
@ -448,19 +448,15 @@ void CEXISlippi::writeToFile(std::unique_ptr<WriteMessage> msg)
|
||||||
|
|
||||||
// Get display names and connection codes from slippi netplay client
|
// Get display names and connection codes from slippi netplay client
|
||||||
if (slippi_netplay)
|
if (slippi_netplay)
|
||||||
{
|
{
|
||||||
auto userInfo = user->GetUserInfo();
|
auto playerInfo = matchmaking->GetPlayerInfo();
|
||||||
auto oppInfo = matchmaking->GetOpponent();
|
|
||||||
|
|
||||||
auto isDecider = slippi_netplay->IsDecider();
|
for (int i = 0; i < playerInfo.size(); i++)
|
||||||
int local_port = isDecider ? 0 : 1;
|
{
|
||||||
int remote_port = isDecider ? 1 : 0;
|
slippi_names[i] = playerInfo[i].display_name;
|
||||||
|
slippi_connect_codes[i] = playerInfo[i].connect_code;
|
||||||
slippi_names[local_port] = userInfo.display_name;
|
}
|
||||||
slippi_connect_codes[local_port] = userInfo.connect_code;
|
}
|
||||||
slippi_names[remote_port] = oppInfo.display_name;
|
|
||||||
slippi_connect_codes[remote_port] = oppInfo.connect_code;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no file, do nothing
|
// If no file, do nothing
|
||||||
|
@ -1499,12 +1495,6 @@ void CEXISlippi::handleOnlineInputs(u8* payload)
|
||||||
|
|
||||||
int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3];
|
int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3];
|
||||||
|
|
||||||
if (isDisconnected())
|
|
||||||
{
|
|
||||||
m_read_queue.push_back(3); // Indicate we disconnected
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame == 1)
|
if (frame == 1)
|
||||||
{
|
{
|
||||||
availableSavestates.clear();
|
availableSavestates.clear();
|
||||||
|
@ -1525,6 +1515,13 @@ void CEXISlippi::handleOnlineInputs(u8* payload)
|
||||||
slippi_netplay->StartSlippiGame();
|
slippi_netplay->StartSlippiGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDisconnected())
|
||||||
|
{
|
||||||
|
auto status = slippi_netplay->GetSlippiConnectStatus();
|
||||||
|
m_read_queue.push_back(3); // Indicate we disconnected
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldSkipOnlineFrame(frame))
|
if (shouldSkipOnlineFrame(frame))
|
||||||
{
|
{
|
||||||
// Send inputs that have not yet been acked
|
// Send inputs that have not yet been acked
|
||||||
|
@ -1586,6 +1583,7 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame)
|
||||||
auto offsetUs = slippi_netplay->CalcTimeOffsetUs();
|
auto offsetUs = slippi_netplay->CalcTimeOffsetUs();
|
||||||
INFO_LOG(SLIPPI_ONLINE, "[Frame %d] Offset is: %d us", frame, offsetUs);
|
INFO_LOG(SLIPPI_ONLINE, "[Frame %d] Offset is: %d us", frame, offsetUs);
|
||||||
|
|
||||||
|
// TODO: figure out a better solution here for doubles?
|
||||||
if (offsetUs > 10000)
|
if (offsetUs > 10000)
|
||||||
{
|
{
|
||||||
isCurrentlySkipping = true;
|
isCurrentlySkipping = true;
|
||||||
|
@ -1642,27 +1640,56 @@ void CEXISlippi::prepareOpponentInputs(u8* payload)
|
||||||
|
|
||||||
m_read_queue.push_back(frameResult); // Indicate a continue frame
|
m_read_queue.push_back(frameResult); // Indicate a continue frame
|
||||||
|
|
||||||
int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3];
|
u8 remotePlayerCount = matchmaking->RemotePlayerCount();
|
||||||
|
m_read_queue.push_back(remotePlayerCount); // Indicate the number of remote players
|
||||||
|
|
||||||
auto result = slippi_netplay->GetSlippiRemotePad(frame);
|
int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3];
|
||||||
|
|
||||||
// determine offset from which to copy data
|
std::unique_ptr<SlippiRemotePadOutput> results[SLIPPI_REMOTE_PLAYER_MAX];
|
||||||
int offset = (result->latestFrame - frame) * SLIPPI_PAD_FULL_SIZE;
|
int offset[SLIPPI_REMOTE_PLAYER_MAX];
|
||||||
offset = offset < 0 ? 0 : offset;
|
INFO_LOG(SLIPPI_ONLINE, "Preparing pad data for frame %d", frame);
|
||||||
|
|
||||||
// add latest frame we are transfering to begining of return buf
|
// Get pad data for each remote player and write each of their latest frame nums to the buf
|
||||||
int32_t latestFrame = offset > 0 ? frame : result->latestFrame;
|
for (int i = 0; i < remotePlayerCount; i++)
|
||||||
appendWordToBuffer(&m_read_queue, *(u32*)&latestFrame);
|
{
|
||||||
|
results[i] = slippi_netplay->GetSlippiRemotePad(frame, i);
|
||||||
|
|
||||||
// copy pad data over
|
// determine offset from which to copy data
|
||||||
auto txStart = result->data.begin() + offset;
|
offset[i] = (results[i]->latestFrame - frame) * SLIPPI_PAD_FULL_SIZE;
|
||||||
auto txEnd = result->data.end();
|
offset[i] = offset[i] < 0 ? 0 : offset[i];
|
||||||
|
|
||||||
std::vector<u8> tx;
|
// add latest frame we are transfering to begining of return buf
|
||||||
tx.insert(tx.end(), txStart, txEnd);
|
int32_t latestFrame = results[i]->latestFrame;
|
||||||
tx.resize(SLIPPI_PAD_FULL_SIZE * ROLLBACK_MAX_FRAMES, 0);
|
if (latestFrame > frame)
|
||||||
|
latestFrame = frame;
|
||||||
|
appendWordToBuffer(&m_read_queue, *(u32 *)&latestFrame);
|
||||||
|
// INFO_LOG(SLIPPI_ONLINE, "Sending frame num %d for pIdx %d (offset: %d)", latestFrame, i, offset[i]);
|
||||||
|
}
|
||||||
|
// Send the current frame for any unused player slots.
|
||||||
|
for (int i = remotePlayerCount; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
|
||||||
|
{
|
||||||
|
appendWordToBuffer(&m_read_queue, *(u32 *)&frame);
|
||||||
|
}
|
||||||
|
|
||||||
m_read_queue.insert(m_read_queue.end(), tx.begin(), tx.end());
|
// copy pad data over
|
||||||
|
for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
|
||||||
|
{
|
||||||
|
std::vector<u8> tx;
|
||||||
|
|
||||||
|
// Get pad data if this remote player exists
|
||||||
|
if (i < remotePlayerCount)
|
||||||
|
{
|
||||||
|
auto txStart = results[i]->data.begin() + offset[i];
|
||||||
|
auto txEnd = results[i]->data.end();
|
||||||
|
tx.insert(tx.end(), txStart, txEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.resize(SLIPPI_PAD_FULL_SIZE * ROLLBACK_MAX_FRAMES, 0);
|
||||||
|
|
||||||
|
m_read_queue.insert(m_read_queue.end(), tx.begin(), tx.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
slippi_netplay->DropOldRemoteInputs(frame);
|
||||||
|
|
||||||
// ERROR_LOG(SLIPPI_ONLINE, "EXI: [%d] %X %X %X %X %X %X %X %X", latestFrame, m_read_queue[5],
|
// ERROR_LOG(SLIPPI_ONLINE, "EXI: [%d] %X %X %X %X %X %X %X %X", latestFrame, m_read_queue[5],
|
||||||
// m_read_queue[6], m_read_queue[7], m_read_queue[8], m_read_queue[9], m_read_queue[10],
|
// m_read_queue[6], m_read_queue[7], m_read_queue[8], m_read_queue[9], m_read_queue[10],
|
||||||
|
@ -1771,7 +1798,7 @@ void CEXISlippi::startFindMatch(u8* payload)
|
||||||
// give someone an early error before they even queue so that they wont enter the queue and make
|
// give someone an early error before they even queue so that they wont enter the queue and make
|
||||||
// someone else get force removed from queue and have to requeue
|
// someone else get force removed from queue and have to requeue
|
||||||
auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT;
|
auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT;
|
||||||
if (search.mode != directMode && localSelections.characterId >= 26)
|
if (search.mode < directMode && localSelections.characterId >= 26)
|
||||||
{
|
{
|
||||||
forcedError = "The character you selected is not allowed in this mode";
|
forcedError = "The character you selected is not allowed in this mode";
|
||||||
return;
|
return;
|
||||||
|
@ -1794,31 +1821,28 @@ void CEXISlippi::startFindMatch(u8* payload)
|
||||||
|
|
||||||
void CEXISlippi::prepareOnlineMatchState()
|
void CEXISlippi::prepareOnlineMatchState()
|
||||||
{
|
{
|
||||||
// This match block is a VS match with P1 Red Falco vs P2 Red Bowser on Battlefield. The proper
|
// This match block is a VS match with P1 Red Falco vs P2 Red Bowser vs P3 Young Link vs P4 Young Link
|
||||||
// values will be overwritten
|
// on Battlefield. The proper values will be overwritten
|
||||||
static std::vector<u8> onlineMatchBlock = {
|
static std::vector<u8> onlineMatchBlock = {
|
||||||
0x32, 0x01, 0x86, 0x4C, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x6E, 0x00,
|
0x32, 0x01, 0x86, 0x4C, 0xC3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x6E, 0x00, 0x1F, 0x00, 0x00,
|
||||||
0x1F, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x01, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F,
|
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
|
||||||
0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
|
0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
|
||||||
0x00, 0x78, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x05, 0x00, 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
|
||||||
0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x05, 0x00, 0x04,
|
0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
|
||||||
0x01, 0x00, 0x01, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0xC0, 0x00, 0x04, 0x01, 0x00, 0x00,
|
0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x15, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F,
|
0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
|
||||||
0x80, 0x00, 0x00, 0x1A, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
|
0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x15, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
|
||||||
0x40, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00,
|
0xC0, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
|
||||||
0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x1A, 0x03, 0x04, 0x00, 0x00, 0xFF,
|
0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
|
||||||
0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0x40, 0x00, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
|
||||||
0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00,
|
0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00,
|
||||||
0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09, 0x00, 0x78, 0x00, 0x40, 0x00, 0x04,
|
0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80,
|
0x00, 0x00, 0x3F, 0x80, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x21, 0x03, 0x04, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x09,
|
};
|
||||||
0x00, 0x78, 0x00, 0x40, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00,
|
|
||||||
};
|
|
||||||
|
|
||||||
m_read_queue.clear();
|
m_read_queue.clear();
|
||||||
|
|
||||||
|
@ -1838,12 +1862,10 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
m_read_queue.push_back(mmState); // Matchmaking State
|
m_read_queue.push_back(mmState); // Matchmaking State
|
||||||
|
|
||||||
u8 localPlayerReady = localSelections.isCharacterSelected;
|
u8 localPlayerReady = localSelections.isCharacterSelected;
|
||||||
u8 remotePlayerReady = 0;
|
u8 remotePlayersReady = 0;
|
||||||
u8 localPlayerIndex = 0;
|
u8 localPlayerIndex = matchmaking->LocalPlayerIndex();
|
||||||
u8 remotePlayerIndex = 1;
|
u8 remotePlayerIndex = 1;
|
||||||
|
|
||||||
auto opponent = matchmaking->GetOpponent();
|
|
||||||
std::string oppName = opponent.display_name;
|
|
||||||
auto userInfo = user->GetUserInfo();
|
auto userInfo = user->GetUserInfo();
|
||||||
|
|
||||||
if (mmState == SlippiMatchmaking::ProcessState::CONNECTION_SUCCESS)
|
if (mmState == SlippiMatchmaking::ProcessState::CONNECTION_SUCCESS)
|
||||||
|
@ -1871,9 +1893,24 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
{
|
{
|
||||||
auto matchInfo = slippi_netplay->GetMatchInfo();
|
auto matchInfo = slippi_netplay->GetMatchInfo();
|
||||||
#ifdef LOCAL_TESTING
|
#ifdef LOCAL_TESTING
|
||||||
remotePlayerReady = true;
|
remotePlayersReady = true;
|
||||||
#else
|
#else
|
||||||
remotePlayerReady = matchInfo->remotePlayerSelections.isCharacterSelected;
|
remotePlayersReady = 1;
|
||||||
|
u8 remotePlayerCount = matchmaking->RemotePlayerCount();
|
||||||
|
for (int i = 0; i < remotePlayerCount; i++)
|
||||||
|
{
|
||||||
|
if (!matchInfo->remotePlayerSelections[i].isCharacterSelected)
|
||||||
|
{
|
||||||
|
remotePlayersReady = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remotePlayerCount == 1)
|
||||||
|
{
|
||||||
|
auto isDecider = slippi_netplay->IsDecider();
|
||||||
|
localPlayerIndex = isDecider ? 0 : 1;
|
||||||
|
remotePlayerIndex = isDecider ? 1 : 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
auto isDecider = slippi_netplay->IsDecider();
|
auto isDecider = slippi_netplay->IsDecider();
|
||||||
|
@ -1892,9 +1929,13 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
// Here we are connected, check to see if we should init play session
|
// Here we are connected, check to see if we should init play session
|
||||||
if (!is_play_session_active)
|
if (!is_play_session_active)
|
||||||
{
|
{
|
||||||
std::vector<std::string> uids{"", ""};
|
std::vector<std::string> uids;
|
||||||
uids[localPlayerIndex] = userInfo.uid;
|
|
||||||
uids[remotePlayerIndex] = opponent.uid;
|
auto mmPlayers = matchmaking->GetPlayerInfo();
|
||||||
|
for (auto mmp : mmPlayers)
|
||||||
|
{
|
||||||
|
uids.push_back(mmp.uid);
|
||||||
|
}
|
||||||
|
|
||||||
game_reporter->StartNewSession(uids);
|
game_reporter->StartNewSession(uids);
|
||||||
|
|
||||||
|
@ -1906,55 +1947,93 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
slippi_netplay = nullptr;
|
slippi_netplay = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_read_queue.push_back(localPlayerReady); // Local player ready
|
|
||||||
m_read_queue.push_back(remotePlayerReady); // Remote player ready
|
|
||||||
m_read_queue.push_back(localPlayerIndex); // Local player index
|
|
||||||
m_read_queue.push_back(remotePlayerIndex); // Remote player index
|
|
||||||
|
|
||||||
u32 rngOffset = 0;
|
u32 rngOffset = 0;
|
||||||
|
std::string localPlayerName = "";
|
||||||
|
std::string oppName = "";
|
||||||
std::string p1Name = "";
|
std::string p1Name = "";
|
||||||
std::string p2Name = "";
|
std::string p2Name = "";
|
||||||
u8 chatMessageId = 0;
|
u8 chatMessageId = 0;
|
||||||
|
u8 chatMessagePlayerIdx = 0;
|
||||||
u8 sentChatMessageId = 0;
|
u8 sentChatMessageId = 0;
|
||||||
|
|
||||||
#ifdef LOCAL_TESTING
|
#ifdef LOCAL_TESTING
|
||||||
chatMessageId = localChatMessageId;
|
localPlayerIndex = 0;
|
||||||
localChatMessageId = 0;
|
chatMessageId = localChatMessageId;
|
||||||
// in CSS p1 is always current player and p2 is opponent
|
chatMessagePlayerIdx = 0;
|
||||||
p1Name = "Player 1";
|
localChatMessageId = 0;
|
||||||
p2Name = "Player 2";
|
// in CSS p1 is always current player and p2 is opponent
|
||||||
|
localPlayerName = p1Name = "Player 1";
|
||||||
|
oppName = p2Name = "Player 2";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Set chat message if any
|
m_read_queue.push_back(localPlayerReady); // Local player ready
|
||||||
if (slippi_netplay)
|
m_read_queue.push_back(remotePlayersReady); // Remote players ready
|
||||||
{
|
m_read_queue.push_back(localPlayerIndex); // Local player index
|
||||||
chatMessageId = slippi_netplay->GetSlippiRemoteChatMessage();
|
m_read_queue.push_back(remotePlayerIndex); // Remote player index
|
||||||
sentChatMessageId = slippi_netplay->GetSlippiRemoteSentChatMessage();
|
|
||||||
// in CSS p1 is always current player and p2 is opponent
|
|
||||||
p1Name = userInfo.display_name;
|
|
||||||
p2Name = oppName;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT;
|
// Set chat message if any
|
||||||
|
if (slippi_netplay)
|
||||||
|
{
|
||||||
|
auto remoteMessageSelection = slippi_netplay->GetSlippiRemoteChatMessage();
|
||||||
|
chatMessageId = remoteMessageSelection.messageId;
|
||||||
|
chatMessagePlayerIdx = remoteMessageSelection.playerIdx;
|
||||||
|
sentChatMessageId = slippi_netplay->GetSlippiRemoteSentChatMessage();
|
||||||
|
// in CSS p1 is always current player and p2 is opponent
|
||||||
|
localPlayerName = p1Name = userInfo.display_name;
|
||||||
|
}
|
||||||
|
|
||||||
if (localPlayerReady && remotePlayerReady)
|
auto directMode = SlippiMatchmaking::OnlinePlayMode::DIRECT;
|
||||||
{
|
|
||||||
auto isDecider = slippi_netplay->IsDecider();
|
|
||||||
|
|
||||||
auto matchInfo = slippi_netplay->GetMatchInfo();
|
std::vector<u8> leftTeamPlayers = {};
|
||||||
SlippiPlayerSelections lps = matchInfo->localPlayerSelections;
|
std::vector<u8> rightTeamPlayers = {};
|
||||||
SlippiPlayerSelections rps = matchInfo->remotePlayerSelections;
|
|
||||||
|
if (localPlayerReady && remotePlayersReady)
|
||||||
|
{
|
||||||
|
auto isDecider = slippi_netplay->IsDecider();
|
||||||
|
u8 remotePlayerCount = matchmaking->RemotePlayerCount();
|
||||||
|
auto matchInfo = slippi_netplay->GetMatchInfo();
|
||||||
|
SlippiPlayerSelections lps = matchInfo->localPlayerSelections;
|
||||||
|
auto rps = matchInfo->remotePlayerSelections;
|
||||||
|
|
||||||
#ifdef LOCAL_TESTING
|
#ifdef LOCAL_TESTING
|
||||||
rps.characterId = 0x2;
|
lps.playerIdx = 0;
|
||||||
rps.characterColor = 2;
|
|
||||||
oppName = std::string("Player");
|
for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
|
||||||
|
{
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
rps[i].characterColor = 1;
|
||||||
|
rps[i].teamId = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rps[i].characterColor = 2;
|
||||||
|
rps[i].teamId = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
rps[i].characterId = 0x2 + i;
|
||||||
|
rps[i].playerIdx = i + 1;
|
||||||
|
rps[i].isCharacterSelected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastSearch.mode == SlippiMatchmaking::OnlinePlayMode::TEAMS)
|
||||||
|
{
|
||||||
|
remotePlayerCount = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
oppName = std::string("Player");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Check if someone is picking dumb characters in non-direct
|
// Check if someone is picking dumb characters in non-direct
|
||||||
auto localCharOk = lps.characterId < 26;
|
auto localCharOk = lps.characterId < 26;
|
||||||
auto remoteCharOk = rps.characterId < 26;
|
auto remoteCharOk = true;
|
||||||
if (lastSearch.mode != directMode && (!localCharOk || !remoteCharOk))
|
INFO_LOG(SLIPPI_ONLINE, "remotePlayerCount: %d", remotePlayerCount);
|
||||||
|
for (int i = 0; i < remotePlayerCount; i++)
|
||||||
|
{
|
||||||
|
if (rps[i].characterId >= 26)
|
||||||
|
remoteCharOk = false;
|
||||||
|
}
|
||||||
|
if (lastSearch.mode < directMode && (!localCharOk || !remoteCharOk))
|
||||||
{
|
{
|
||||||
// If we get here, someone is doing something bad, clear the lobby
|
// If we get here, someone is doing something bad, clear the lobby
|
||||||
handleConnectionCleanup();
|
handleConnectionCleanup();
|
||||||
|
@ -1965,52 +2044,99 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite local player character
|
// Overwrite local player character
|
||||||
onlineMatchBlock[0x60 + localPlayerIndex * 0x24] = lps.characterId;
|
onlineMatchBlock[0x60 + (lps.playerIdx) * 0x24] = lps.characterId;
|
||||||
onlineMatchBlock[0x63 + localPlayerIndex * 0x24] = lps.characterColor;
|
onlineMatchBlock[0x63 + (lps.playerIdx) * 0x24] = lps.characterColor;
|
||||||
|
onlineMatchBlock[0x67 + (lps.playerIdx) * 0x24] = 0;
|
||||||
|
onlineMatchBlock[0x69 + (lps.playerIdx) * 0x24] = lps.teamId;
|
||||||
|
|
||||||
// Overwrite remote player character
|
// Overwrite remote player character
|
||||||
onlineMatchBlock[0x60 + remotePlayerIndex * 0x24] = rps.characterId;
|
for (int i = 0; i < remotePlayerCount; i++)
|
||||||
onlineMatchBlock[0x63 + remotePlayerIndex * 0x24] = rps.characterColor;
|
{
|
||||||
|
u8 idx = matchInfo->remotePlayerSelections[i].playerIdx;
|
||||||
|
onlineMatchBlock[0x60 + idx * 0x24] = matchInfo->remotePlayerSelections[i].characterId;
|
||||||
|
|
||||||
// Make one character lighter if same character, same color
|
// Set Char Colors
|
||||||
bool isSheikVsZelda = lps.characterId == 0x12 && rps.characterId == 0x13 ||
|
onlineMatchBlock[0x63 + idx * 0x24] = matchInfo->remotePlayerSelections[i].characterColor;
|
||||||
lps.characterId == 0x13 && rps.characterId == 0x12;
|
|
||||||
bool charMatch = lps.characterId == rps.characterId || isSheikVsZelda;
|
|
||||||
bool colMatch = lps.characterColor == rps.characterColor;
|
|
||||||
|
|
||||||
onlineMatchBlock[0x67 + 0x24] = charMatch && colMatch ? 1 : 0;
|
// Set Team Ids
|
||||||
|
onlineMatchBlock[0x69 + idx * 0x24] = matchInfo->remotePlayerSelections[i].teamId;
|
||||||
|
}
|
||||||
|
|
||||||
// Overwrite stage
|
// Handle Singles/Teams specific logic
|
||||||
u16 stageId;
|
if (remotePlayerCount < 3)
|
||||||
if (isDecider)
|
{
|
||||||
{
|
onlineMatchBlock[0x8] = 0; // is Teams = false
|
||||||
stageId = lps.isStageSelected ? lps.stageId : rps.stageId;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stageId = rps.isStageSelected ? rps.stageId : lps.stageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// int seconds = 0;
|
// Set p3/p4 player type to none
|
||||||
// u32 *timer = (u32 *)&onlineMatchBlock[0x10];
|
onlineMatchBlock[0x61 + 2 * 0x24] = 3;
|
||||||
//*timer = Common::swap32(seconds * 60);
|
onlineMatchBlock[0x61 + 3 * 0x24] = 3;
|
||||||
|
|
||||||
u16* stage = (u16*)&onlineMatchBlock[0xE];
|
// Make one character lighter if same character, same color
|
||||||
*stage = Common::swap16(stageId);
|
bool isSheikVsZelda = lps.characterId == 0x12 && rps[0].characterId == 0x13 ||
|
||||||
|
lps.characterId == 0x13 && rps[0].characterId == 0x12;
|
||||||
|
bool charMatch = lps.characterId == rps[0].characterId || isSheikVsZelda;
|
||||||
|
bool colMatch = lps.characterColor == rps[0].characterColor;
|
||||||
|
|
||||||
// Set rng offset
|
onlineMatchBlock[0x67 + 0x24] = charMatch && colMatch ? 1 : 0;
|
||||||
rngOffset = isDecider ? lps.rngOffset : rps.rngOffset;
|
}
|
||||||
WARN_LOG(SLIPPI_ONLINE, "Rng Offset: 0x%x", rngOffset);
|
else
|
||||||
WARN_LOG(SLIPPI_ONLINE, "P1 Char: 0x%X, P2 Char: 0x%X", onlineMatchBlock[0x60],
|
{
|
||||||
onlineMatchBlock[0x84]);
|
onlineMatchBlock[0x8] = 1; // is Teams = true
|
||||||
|
|
||||||
// Set player names
|
// Set p3/p4 player type to human
|
||||||
p1Name = isDecider ? userInfo.display_name : oppName;
|
onlineMatchBlock[0x61 + 2 * 0x24] = 0;
|
||||||
p2Name = isDecider ? oppName : userInfo.display_name;
|
onlineMatchBlock[0x61 + 3 * 0x24] = 0;
|
||||||
|
|
||||||
// Turn pause on in direct, off in everything else
|
// Set alt color to light/dark costume for multiples of the same character on a team
|
||||||
u8* gameBitField3 = (u8*)&onlineMatchBlock[2];
|
int characterCount[26][3] = {0};
|
||||||
*gameBitField3 = lastSearch.mode == directMode ? *gameBitField3 & 0xF7 : *gameBitField3 | 0x8;
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
int charId = onlineMatchBlock[0x60 + i * 0x24];
|
||||||
|
int teamId = onlineMatchBlock[0x69 + i * 0x24];
|
||||||
|
onlineMatchBlock[0x67 + i * 0x24] = characterCount[charId][teamId];
|
||||||
|
characterCount[charId][teamId]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite stage
|
||||||
|
u16 stageId;
|
||||||
|
if (isDecider)
|
||||||
|
{
|
||||||
|
stageId = lps.isStageSelected ? lps.stageId : rps[0].stageId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stageId = rps[0].isStageSelected ? rps[0].stageId : lps.stageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 *stage = (u16 *)&onlineMatchBlock[0xE];
|
||||||
|
*stage = Common::swap16(stageId);
|
||||||
|
|
||||||
|
// Set rng offset
|
||||||
|
rngOffset = isDecider ? lps.rngOffset : rps[0].rngOffset;
|
||||||
|
WARN_LOG(SLIPPI_ONLINE, "Rng Offset: 0x%x", rngOffset);
|
||||||
|
WARN_LOG(SLIPPI_ONLINE, "P1 Char: 0x%X, P2 Char: 0x%X", onlineMatchBlock[0x60], onlineMatchBlock[0x84]);
|
||||||
|
|
||||||
|
// Turn pause on in direct, off in everything else
|
||||||
|
u8 *gameBitField3 = (u8 *)&onlineMatchBlock[2];
|
||||||
|
*gameBitField3 = lastSearch.mode >= directMode ? *gameBitField3 & 0xF7 : *gameBitField3 | 0x8;
|
||||||
|
//*gameBitField3 = *gameBitField3 | 0x8;
|
||||||
|
|
||||||
|
// Group players into left/right side for team splash screen display
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
int teamId = onlineMatchBlock[0x69 + i * 0x24];
|
||||||
|
if (teamId == lps.teamId)
|
||||||
|
leftTeamPlayers.push_back(i);
|
||||||
|
else
|
||||||
|
rightTeamPlayers.push_back(i);
|
||||||
|
}
|
||||||
|
int leftTeamSize = leftTeamPlayers.size();
|
||||||
|
int rightTeamSize = rightTeamPlayers.size();
|
||||||
|
leftTeamPlayers.resize(4, 0);
|
||||||
|
rightTeamPlayers.resize(4, 0);
|
||||||
|
leftTeamPlayers[3] = leftTeamSize;
|
||||||
|
rightTeamPlayers[3] = rightTeamSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add rng offset to output
|
// Add rng offset to output
|
||||||
|
@ -2022,13 +2148,52 @@ void CEXISlippi::prepareOnlineMatchState()
|
||||||
// Add chat messages id
|
// Add chat messages id
|
||||||
m_read_queue.push_back((u8)sentChatMessageId);
|
m_read_queue.push_back((u8)sentChatMessageId);
|
||||||
m_read_queue.push_back((u8)chatMessageId);
|
m_read_queue.push_back((u8)chatMessageId);
|
||||||
|
m_read_queue.push_back((u8)chatMessagePlayerIdx);
|
||||||
|
|
||||||
// Add names to output
|
// Add player groupings for VS splash screen
|
||||||
p1Name = ConvertStringForGame(p1Name, MAX_NAME_LENGTH);
|
leftTeamPlayers.resize(4, 0);
|
||||||
m_read_queue.insert(m_read_queue.end(), p1Name.begin(), p1Name.end());
|
rightTeamPlayers.resize(4, 0);
|
||||||
p2Name = ConvertStringForGame(p2Name, MAX_NAME_LENGTH);
|
m_read_queue.insert(m_read_queue.end(), leftTeamPlayers.begin(), leftTeamPlayers.end());
|
||||||
m_read_queue.insert(m_read_queue.end(), p2Name.begin(), p2Name.end());
|
m_read_queue.insert(m_read_queue.end(), rightTeamPlayers.begin(), rightTeamPlayers.end());
|
||||||
oppName = ConvertStringForGame(oppName, MAX_NAME_LENGTH);
|
|
||||||
|
// Add names to output
|
||||||
|
// Always send static local player name
|
||||||
|
localPlayerName = ConvertStringForGame(localPlayerName, MAX_NAME_LENGTH);
|
||||||
|
m_read_queue.insert(m_read_queue.end(), localPlayerName.begin(), localPlayerName.end());
|
||||||
|
|
||||||
|
#ifdef LOCAL_TESTING
|
||||||
|
std::string defaultNames[] = {"Player 1", "Player 2", "Player 3", "Player 4"};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
std::string name = matchmaking->GetPlayerName(i);
|
||||||
|
#ifdef LOCAL_TESTING
|
||||||
|
name = defaultNames[i];
|
||||||
|
#endif
|
||||||
|
name = ConvertStringForGame(name, MAX_NAME_LENGTH);
|
||||||
|
m_read_queue.insert(m_read_queue.end(), name.begin(), name.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the opponent string using the names of all players on opposing teams
|
||||||
|
int teamIdx = onlineMatchBlock[0x69 + localPlayerIndex * 0x24];
|
||||||
|
std::string oppText = "";
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
if (i == localPlayerIndex)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (onlineMatchBlock[0x69 + i * 0x24] != teamIdx)
|
||||||
|
{
|
||||||
|
if (oppText != "")
|
||||||
|
oppText += "/";
|
||||||
|
|
||||||
|
oppText += matchmaking->GetPlayerName(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchmaking->RemotePlayerCount() == 1)
|
||||||
|
oppText = matchmaking->GetPlayerName(remotePlayerIndex);
|
||||||
|
oppName = ConvertStringForGame(oppText, MAX_NAME_LENGTH * 2 + 1);
|
||||||
m_read_queue.insert(m_read_queue.end(), oppName.begin(), oppName.end());
|
m_read_queue.insert(m_read_queue.end(), oppName.begin(), oppName.end());
|
||||||
|
|
||||||
// Add error message if there is one
|
// Add error message if there is one
|
||||||
|
@ -2071,12 +2236,13 @@ void CEXISlippi::setMatchSelections(u8* payload)
|
||||||
{
|
{
|
||||||
SlippiPlayerSelections s;
|
SlippiPlayerSelections s;
|
||||||
|
|
||||||
s.characterId = payload[0];
|
s.teamId = payload[0];
|
||||||
s.characterColor = payload[1];
|
s.characterId = payload[1];
|
||||||
s.isCharacterSelected = payload[2];
|
s.characterColor = payload[2];
|
||||||
|
s.isCharacterSelected = payload[3];
|
||||||
|
|
||||||
s.stageId = Common::swap16(&payload[3]);
|
s.stageId = Common::swap16(&payload[4]);
|
||||||
u8 stageSelectOption = payload[5];
|
u8 stageSelectOption = payload[6];
|
||||||
|
|
||||||
s.isStageSelected = stageSelectOption == 1 || stageSelectOption == 3;
|
s.isStageSelected = stageSelectOption == 1 || stageSelectOption == 3;
|
||||||
if (stageSelectOption == 3)
|
if (stageSelectOption == 3)
|
||||||
|
@ -2085,7 +2251,16 @@ void CEXISlippi::setMatchSelections(u8* payload)
|
||||||
s.stageId = getRandomStage();
|
s.stageId = getRandomStage();
|
||||||
}
|
}
|
||||||
|
|
||||||
s.rngOffset = generator() % 0xFFFF;
|
INFO_LOG(SLIPPI, "LPS set char: %d, iSS: %d, %d, stage: %d, team: %d", s.isCharacterSelected, stageSelectOption,
|
||||||
|
s.isStageSelected, s.stageId, s.teamId);
|
||||||
|
|
||||||
|
s.rngOffset = generator() % 0xFFFF;
|
||||||
|
|
||||||
|
if (matchmaking->LocalPlayerIndex() == 1 && firstMatch)
|
||||||
|
{
|
||||||
|
firstMatch = false;
|
||||||
|
s.stageId = getRandomStage();
|
||||||
|
}
|
||||||
|
|
||||||
// Merge these selections
|
// Merge these selections
|
||||||
localSelections.Merge(s);
|
localSelections.Merge(s);
|
||||||
|
@ -2142,7 +2317,7 @@ void CEXISlippi::handleChatMessage(u8* payload)
|
||||||
auto packet = std::make_unique<sf::Packet>();
|
auto packet = std::make_unique<sf::Packet>();
|
||||||
// OSD::AddMessage("[Me]: "+ msg, OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
|
// OSD::AddMessage("[Me]: "+ msg, OSD::Duration::VERY_LONG, OSD::Color::YELLOW);
|
||||||
slippi_netplay->remoteSentChatMessageId = messageId;
|
slippi_netplay->remoteSentChatMessageId = messageId;
|
||||||
slippi_netplay->WriteChatMessageToPacket(*packet, messageId);
|
slippi_netplay->WriteChatMessageToPacket(*packet, messageId, slippi_netplay->LocalPlayerPort());
|
||||||
slippi_netplay->SendAsync(std::move(packet));
|
slippi_netplay->SendAsync(std::move(packet));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2258,6 +2433,7 @@ void CEXISlippi::handleConnectionCleanup()
|
||||||
|
|
||||||
// Reset play session
|
// Reset play session
|
||||||
is_play_session_active = false;
|
is_play_session_active = false;
|
||||||
|
firstMatch = true;
|
||||||
|
|
||||||
#ifdef LOCAL_TESTING
|
#ifdef LOCAL_TESTING
|
||||||
isLocalConnected = false;
|
isLocalConnected = false;
|
||||||
|
|
|
@ -189,6 +189,7 @@ private:
|
||||||
void logMessageFromGame(u8* payload);
|
void logMessageFromGame(u8* payload);
|
||||||
void prepareFileLength(u8* payload);
|
void prepareFileLength(u8* payload);
|
||||||
void prepareFileLoad(u8* payload);
|
void prepareFileLoad(u8* payload);
|
||||||
|
int getCharColor(u8 charId, u8 teamId);
|
||||||
|
|
||||||
void FileWriteThread(void);
|
void FileWriteThread(void);
|
||||||
|
|
||||||
|
@ -214,6 +215,7 @@ private:
|
||||||
u32 frameSeqIdx = 0;
|
u32 frameSeqIdx = 0;
|
||||||
|
|
||||||
bool isEnetInitialized = false;
|
bool isEnetInitialized = false;
|
||||||
|
bool firstMatch = true;
|
||||||
|
|
||||||
std::default_random_engine generator;
|
std::default_random_engine generator;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,13 @@
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
|
|
||||||
|
#if defined __linux__ && HAVE_ALSA
|
||||||
|
#elif defined __APPLE__
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#elif defined _WIN32
|
||||||
|
#endif
|
||||||
|
|
||||||
class MmMessageType
|
class MmMessageType
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -68,11 +75,6 @@ std::string SlippiMatchmaking::GetErrorMessage()
|
||||||
return m_errorMsg;
|
return m_errorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
SlippiUser::UserInfo SlippiMatchmaking::GetOpponent()
|
|
||||||
{
|
|
||||||
return m_oppUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SlippiMatchmaking::IsSearching()
|
bool SlippiMatchmaking::IsSearching()
|
||||||
{
|
{
|
||||||
return searchingStates.count(m_state) != 0;
|
return searchingStates.count(m_state) != 0;
|
||||||
|
@ -208,9 +210,13 @@ void SlippiMatchmaking::startMatchmaking()
|
||||||
m_client = nullptr;
|
m_client = nullptr;
|
||||||
|
|
||||||
int retryCount = 0;
|
int retryCount = 0;
|
||||||
|
auto userInfo = m_user->GetUserInfo();
|
||||||
while (m_client == nullptr && retryCount < 15)
|
while (m_client == nullptr && retryCount < 15)
|
||||||
{
|
{
|
||||||
m_hostPort = 49000 + (generator() % 2000);
|
if (userInfo.port > 0)
|
||||||
|
m_hostPort = userInfo.port;
|
||||||
|
else
|
||||||
|
m_hostPort = 49000 + (generator() % 2000);
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Port to use: %d...", m_hostPort);
|
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Port to use: %d...", m_hostPort);
|
||||||
|
|
||||||
// We are explicitly setting the client address because we are trying to utilize our connection
|
// We are explicitly setting the client address because we are trying to utilize our connection
|
||||||
|
@ -269,6 +275,7 @@ void SlippiMatchmaking::startMatchmaking()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
netEvent.peer->data = &userInfo.display_name;
|
||||||
m_client->intercept = ENetUtil::InterceptCallback;
|
m_client->intercept = ENetUtil::InterceptCallback;
|
||||||
isMmConnected = true;
|
isMmConnected = true;
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Connected to mm server...");
|
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Connected to mm server...");
|
||||||
|
@ -276,15 +283,40 @@ void SlippiMatchmaking::startMatchmaking()
|
||||||
|
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Trying to find match...");
|
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Trying to find match...");
|
||||||
|
|
||||||
if (!m_user->IsLoggedIn())
|
// if (!m_user->IsLoggedIn())
|
||||||
{
|
// {
|
||||||
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Must be logged in to queue");
|
// ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Must be logged in to queue");
|
||||||
m_state = ProcessState::ERROR_ENCOUNTERED;
|
// m_state = ProcessState::ERROR_ENCOUNTERED;
|
||||||
m_errorMsg = "Must be logged in to queue. Go back to menu";
|
// m_errorMsg = "Must be logged in to queue. Go back to menu";
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
auto userInfo = m_user->GetUserInfo();
|
// Compute LAN IP, in case 2 people are connecting from one IP we can send them each other's local
|
||||||
|
// IP instead of public. Experimental to allow people from behind one router to connect.
|
||||||
|
char host[256];
|
||||||
|
char lan_addr[30];
|
||||||
|
char* ip;
|
||||||
|
struct hostent* host_entry;
|
||||||
|
int hostname;
|
||||||
|
hostname = gethostname(host, sizeof(host)); // find the host name
|
||||||
|
if (hostname == -1)
|
||||||
|
{
|
||||||
|
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Error finding LAN address");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
host_entry = gethostbyname(host); // find host information
|
||||||
|
if (host_entry == NULL)
|
||||||
|
{
|
||||||
|
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Error finding LAN host");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ip = inet_ntoa(*((struct in_addr*)host_entry->h_addr_list[0])); // Convert into IP string
|
||||||
|
INFO_LOG(SLIPPI_ONLINE, "[Matchmaking] LAN IP: %s", ip);
|
||||||
|
sprintf(lan_addr, "%s:%d", ip, m_hostPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<u8> connectCodeBuf;
|
std::vector<u8> connectCodeBuf;
|
||||||
connectCodeBuf.insert(connectCodeBuf.end(), m_searchSettings.connectCode.begin(),
|
connectCodeBuf.insert(connectCodeBuf.end(), m_searchSettings.connectCode.begin(),
|
||||||
|
@ -296,6 +328,7 @@ void SlippiMatchmaking::startMatchmaking()
|
||||||
request["user"] = {{"uid", userInfo.uid}, {"playKey", userInfo.play_key}};
|
request["user"] = {{"uid", userInfo.uid}, {"playKey", userInfo.play_key}};
|
||||||
request["search"] = {{"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf}};
|
request["search"] = {{"mode", m_searchSettings.mode}, {"connectCode", connectCodeBuf}};
|
||||||
request["appVersion"] = Common::scm_slippi_semver_str;
|
request["appVersion"] = Common::scm_slippi_semver_str;
|
||||||
|
request["ipAddressLan"] = lan_addr;
|
||||||
sendMessage(request);
|
sendMessage(request);
|
||||||
|
|
||||||
// Get response from server
|
// Get response from server
|
||||||
|
@ -385,37 +418,64 @@ void SlippiMatchmaking::handleMatchmaking()
|
||||||
m_isSwapAttempt = false;
|
m_isSwapAttempt = false;
|
||||||
m_netplayClient = nullptr;
|
m_netplayClient = nullptr;
|
||||||
|
|
||||||
// Clear old user
|
// Clear old users
|
||||||
SlippiUser::UserInfo emptyInfo;
|
m_remoteIps.clear();
|
||||||
m_oppUser = emptyInfo;
|
m_playerInfo.clear();
|
||||||
|
|
||||||
auto queue = getResp["players"];
|
auto queue = getResp["players"];
|
||||||
if (queue.is_array())
|
if (queue.is_array())
|
||||||
{
|
{
|
||||||
for (json::iterator it = queue.begin(); it != queue.end(); ++it)
|
std::string localExternalIp = "";
|
||||||
{
|
|
||||||
json el = *it;
|
|
||||||
//SlippiUser::UserInfo playerInfo;
|
|
||||||
|
|
||||||
bool isLocal = el.value("isLocalPlayer", false);
|
for (json::iterator it = queue.begin(); it != queue.end(); ++it)
|
||||||
//playerInfo.uid = el.value("uid", "");
|
{
|
||||||
//playerInfo.displayName = el.value("displayName", "");
|
json el = *it;
|
||||||
//playerInfo.connectCode = el.value("connectCode", "");
|
SlippiUser::UserInfo playerInfo;
|
||||||
//playerInfo.port = el.value("port", 0);
|
|
||||||
|
|
||||||
if (!isLocal)
|
bool isLocal = el.value("isLocalPlayer", false);
|
||||||
{
|
playerInfo.uid = el.value("uid", "");
|
||||||
m_oppIp = el.value("ipAddress", "1.1.1.1:123");
|
playerInfo.display_name = el.value("displayName", "");
|
||||||
m_oppUser.uid = el.value("uid", "");
|
playerInfo.connect_code = el.value("connectCode", "");
|
||||||
m_oppUser.display_name = el.value("displayName", "");
|
playerInfo.port = el.value("port", 0);
|
||||||
m_oppUser.connect_code = el.value("connectCode", "");
|
m_playerInfo.push_back(playerInfo);
|
||||||
}
|
|
||||||
|
|
||||||
//else
|
if (isLocal)
|
||||||
// m_localPlayerPort = playerInfo.port - 1;
|
{
|
||||||
};
|
std::vector<std::string> localIpParts;
|
||||||
}
|
localIpParts = SplitString(el.value("ipAddress", "1.1.1.1:123"), ':');
|
||||||
m_isHost = getResp.value("isHost", false);
|
localExternalIp = localIpParts[0];
|
||||||
|
m_localPlayerIndex = playerInfo.port - 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loop a second time to get the correct remote IPs
|
||||||
|
for (json::iterator it = queue.begin(); it != queue.end(); ++it)
|
||||||
|
{
|
||||||
|
json el = *it;
|
||||||
|
|
||||||
|
if (el.value("port", 0) - 1 == m_localPlayerIndex)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto extIp = el.value("ipAddress", "1.1.1.1:123");
|
||||||
|
std::vector<std::string> exIpParts;
|
||||||
|
exIpParts = SplitString(extIp, ':');
|
||||||
|
|
||||||
|
auto lanIp = el.value("ipAddressLan", "1.1.1.1:123");
|
||||||
|
|
||||||
|
if (exIpParts[0] != localExternalIp || lanIp.empty())
|
||||||
|
{
|
||||||
|
// If external IPs are different, just use that address
|
||||||
|
m_remoteIps.push_back(extIp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Instead of using one or the other, it might be better to try both
|
||||||
|
|
||||||
|
// If external IPs are the same, try using LAN IPs
|
||||||
|
m_remoteIps.push_back(lanIp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_isHost = getResp.value("isHost", false);
|
||||||
|
|
||||||
// Disconnect and destroy enet client to mm server
|
// Disconnect and destroy enet client to mm server
|
||||||
terminateMmConnection();
|
terminateMmConnection();
|
||||||
|
@ -425,13 +485,62 @@ void SlippiMatchmaking::handleMatchmaking()
|
||||||
m_isHost ? "true" : "false");
|
m_isHost ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SlippiMatchmaking::LocalPlayerIndex()
|
||||||
|
{
|
||||||
|
return m_localPlayerIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<SlippiUser::UserInfo> SlippiMatchmaking::GetPlayerInfo()
|
||||||
|
{
|
||||||
|
return m_playerInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SlippiMatchmaking::GetPlayerName(u8 port)
|
||||||
|
{
|
||||||
|
if (port >= m_playerInfo.size())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return m_playerInfo[port].display_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 SlippiMatchmaking::RemotePlayerCount()
|
||||||
|
{
|
||||||
|
if (m_playerInfo.size() == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return (u8)m_playerInfo.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
void SlippiMatchmaking::handleConnecting()
|
void SlippiMatchmaking::handleConnecting()
|
||||||
{
|
{
|
||||||
std::vector<std::string> ipParts = SplitString(m_oppIp, ':');
|
auto userInfo = m_user->GetUserInfo();
|
||||||
|
|
||||||
|
m_isSwapAttempt = false;
|
||||||
|
m_netplayClient = nullptr;
|
||||||
|
|
||||||
|
u8 remotePlayerCount = (u8)m_remoteIps.size();
|
||||||
|
std::vector<std::string> remoteParts;
|
||||||
|
std::vector<std::string> addrs;
|
||||||
|
std::vector<u16> ports;
|
||||||
|
for (int i = 0; i < m_remoteIps.size(); i++)
|
||||||
|
{
|
||||||
|
remoteParts.clear();
|
||||||
|
remoteParts = SplitString(m_remoteIps[i], ':');
|
||||||
|
addrs.push_back(remoteParts[0]);
|
||||||
|
ports.push_back(std::stoi(remoteParts[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ipLog;
|
||||||
|
ipLog << "Remote player IPs: ";
|
||||||
|
for (int i = 0; i < m_remoteIps.size(); i++)
|
||||||
|
{
|
||||||
|
ipLog << m_remoteIps[i] << ", ";
|
||||||
|
}
|
||||||
|
|
||||||
// Is host is now used to specify who the decider is
|
// Is host is now used to specify who the decider is
|
||||||
auto client = std::make_unique<SlippiNetplayClient>(ipParts[0], std::stoi(ipParts[1]), m_hostPort,
|
auto client = std::make_unique<SlippiNetplayClient>(addrs, ports, remotePlayerCount, m_hostPort,
|
||||||
m_isHost);
|
m_isHost, m_localPlayerIndex);
|
||||||
|
|
||||||
while (!m_netplayClient)
|
while (!m_netplayClient)
|
||||||
{
|
{
|
||||||
|
@ -447,6 +556,32 @@ void SlippiMatchmaking::handleConnecting()
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else if (status == SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_FAILED &&
|
||||||
|
m_searchSettings.mode == SlippiMatchmaking::OnlinePlayMode::TEAMS)
|
||||||
|
{
|
||||||
|
// If we failed setting up a connection in teams mode, show a detailed error about who we had
|
||||||
|
// issues connecting to.
|
||||||
|
ERROR_LOG(SLIPPI_ONLINE, "[Matchmaking] Failed to connect to players");
|
||||||
|
m_state = ProcessState::ERROR_ENCOUNTERED;
|
||||||
|
m_errorMsg = "Timed out waiting for other players to connect";
|
||||||
|
auto failedConns = client->GetFailedConnections();
|
||||||
|
if (!failedConns.empty())
|
||||||
|
{
|
||||||
|
std::stringstream err;
|
||||||
|
err << "Could not connect to players: ";
|
||||||
|
for (int i = 0; i < failedConns.size(); i++)
|
||||||
|
{
|
||||||
|
int p = failedConns[i];
|
||||||
|
if (p >= m_localPlayerIndex)
|
||||||
|
p++;
|
||||||
|
|
||||||
|
err << m_playerInfo[p].display_name << " ";
|
||||||
|
}
|
||||||
|
m_errorMsg = err.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
else if (status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED)
|
else if (status != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED)
|
||||||
{
|
{
|
||||||
ERROR_LOG(SLIPPI_ONLINE,
|
ERROR_LOG(SLIPPI_ONLINE,
|
||||||
|
|
|
@ -5,11 +5,15 @@
|
||||||
#include "Core/Slippi/SlippiNetplay.h"
|
#include "Core/Slippi/SlippiNetplay.h"
|
||||||
#include "Core/Slippi/SlippiUser.h"
|
#include "Core/Slippi/SlippiUser.h"
|
||||||
|
|
||||||
#include <enet/enet.h>
|
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
@ -24,6 +28,7 @@ public:
|
||||||
RANKED = 0,
|
RANKED = 0,
|
||||||
UNRANKED = 1,
|
UNRANKED = 1,
|
||||||
DIRECT = 2,
|
DIRECT = 2,
|
||||||
|
TEAMS = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ProcessState
|
enum ProcessState
|
||||||
|
@ -48,7 +53,10 @@ public:
|
||||||
bool IsSearching();
|
bool IsSearching();
|
||||||
std::unique_ptr<SlippiNetplayClient> GetNetplayClient();
|
std::unique_ptr<SlippiNetplayClient> GetNetplayClient();
|
||||||
std::string GetErrorMessage();
|
std::string GetErrorMessage();
|
||||||
SlippiUser::UserInfo GetOpponent();
|
int LocalPlayerIndex();
|
||||||
|
std::vector<SlippiUser::UserInfo> GetPlayerInfo();
|
||||||
|
std::string GetPlayerName(u8 port);
|
||||||
|
u8 RemotePlayerCount();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const std::string MM_HOST_DEV = "35.197.121.196"; // Dev host
|
const std::string MM_HOST_DEV = "35.197.121.196"; // Dev host
|
||||||
|
@ -76,9 +84,11 @@ protected:
|
||||||
int m_isSwapAttempt = false;
|
int m_isSwapAttempt = false;
|
||||||
|
|
||||||
int m_hostPort;
|
int m_hostPort;
|
||||||
std::string m_oppIp;
|
int m_localPlayerIndex;
|
||||||
|
std::vector<std::string> m_remoteIps;
|
||||||
|
std::vector<SlippiUser::UserInfo> m_playerInfo;
|
||||||
|
bool m_joinedLobby;
|
||||||
bool m_isHost;
|
bool m_isHost;
|
||||||
SlippiUser::UserInfo m_oppUser;
|
|
||||||
|
|
||||||
std::unique_ptr<SlippiNetplayClient> m_netplayClient;
|
std::unique_ptr<SlippiNetplayClient> m_netplayClient;
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,6 +13,7 @@
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/Event.h"
|
#include "Common/Event.h"
|
||||||
|
@ -21,7 +22,6 @@
|
||||||
#include "Core/NetPlayProto.h"
|
#include "Core/NetPlayProto.h"
|
||||||
#include "Core/Slippi/SlippiPad.h"
|
#include "Core/Slippi/SlippiPad.h"
|
||||||
#include "InputCommon/GCPadStatus.h"
|
#include "InputCommon/GCPadStatus.h"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Qos2.h>
|
#include <Qos2.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,18 +29,24 @@
|
||||||
#define SLIPPI_ONLINE_LOCKSTEP_INTERVAL \
|
#define SLIPPI_ONLINE_LOCKSTEP_INTERVAL \
|
||||||
30 // Number of frames to wait before attempting to time-sync
|
30 // Number of frames to wait before attempting to time-sync
|
||||||
#define SLIPPI_PING_DISPLAY_INTERVAL 60
|
#define SLIPPI_PING_DISPLAY_INTERVAL 60
|
||||||
|
#define SLIPPI_REMOTE_PLAYER_MAX 3
|
||||||
|
#define SLIPPI_REMOTE_PLAYER_COUNT 3
|
||||||
|
|
||||||
struct SlippiRemotePadOutput
|
struct SlippiRemotePadOutput
|
||||||
{
|
{
|
||||||
int32_t latestFrame;
|
int32_t latestFrame;
|
||||||
|
u8 playerIdx;
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SlippiPlayerSelections
|
class SlippiPlayerSelections
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
u8 playerIdx = 0;
|
||||||
u8 characterId = 0;
|
u8 characterId = 0;
|
||||||
u8 characterColor = 0;
|
u8 characterColor = 0;
|
||||||
|
u8 teamId = 0;
|
||||||
|
|
||||||
bool isCharacterSelected = false;
|
bool isCharacterSelected = false;
|
||||||
|
|
||||||
u16 stageId = 0;
|
u16 stageId = 0;
|
||||||
|
@ -64,6 +70,7 @@ public:
|
||||||
{
|
{
|
||||||
this->characterId = s.characterId;
|
this->characterId = s.characterId;
|
||||||
this->characterColor = s.characterColor;
|
this->characterColor = s.characterColor;
|
||||||
|
this->teamId = s.teamId;
|
||||||
this->isCharacterSelected = true;
|
this->isCharacterSelected = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,6 +80,7 @@ public:
|
||||||
characterId = 0;
|
characterId = 0;
|
||||||
characterColor = 0;
|
characterColor = 0;
|
||||||
isCharacterSelected = false;
|
isCharacterSelected = false;
|
||||||
|
teamId = 0;
|
||||||
|
|
||||||
stageId = 0;
|
stageId = 0;
|
||||||
isStageSelected = false;
|
isStageSelected = false;
|
||||||
|
@ -85,12 +93,15 @@ class SlippiMatchInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SlippiPlayerSelections localPlayerSelections;
|
SlippiPlayerSelections localPlayerSelections;
|
||||||
SlippiPlayerSelections remotePlayerSelections;
|
SlippiPlayerSelections remotePlayerSelections[SLIPPI_REMOTE_PLAYER_MAX];
|
||||||
|
|
||||||
void Reset()
|
void Reset()
|
||||||
{
|
{
|
||||||
localPlayerSelections.Reset();
|
localPlayerSelections.Reset();
|
||||||
remotePlayerSelections.Reset();
|
for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
|
||||||
|
{
|
||||||
|
remotePlayerSelections[i].Reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,8 +112,9 @@ public:
|
||||||
void SendAsync(std::unique_ptr<sf::Packet> packet);
|
void SendAsync(std::unique_ptr<sf::Packet> packet);
|
||||||
|
|
||||||
SlippiNetplayClient(bool isDecider); // Make a dummy client
|
SlippiNetplayClient(bool isDecider); // Make a dummy client
|
||||||
SlippiNetplayClient(const std::string& address, const u16 remotePort, const u16 localPort,
|
SlippiNetplayClient(std::vector<std::string> addrs, std::vector<u16> ports,
|
||||||
bool isDecider);
|
const u8 remotePlayerCount, const u16 localPort, bool isDecider,
|
||||||
|
u8 playerIdx);
|
||||||
~SlippiNetplayClient();
|
~SlippiNetplayClient();
|
||||||
|
|
||||||
// Slippi Online
|
// Slippi Online
|
||||||
|
@ -117,23 +129,26 @@ public:
|
||||||
|
|
||||||
bool IsDecider();
|
bool IsDecider();
|
||||||
bool IsConnectionSelected();
|
bool IsConnectionSelected();
|
||||||
|
u8 LocalPlayerPort();
|
||||||
SlippiConnectStatus GetSlippiConnectStatus();
|
SlippiConnectStatus GetSlippiConnectStatus();
|
||||||
|
std::vector<int> GetFailedConnections();
|
||||||
void StartSlippiGame();
|
void StartSlippiGame();
|
||||||
void SendConnectionSelected();
|
void SendConnectionSelected();
|
||||||
void SendSlippiPad(std::unique_ptr<SlippiPad> pad);
|
void SendSlippiPad(std::unique_ptr<SlippiPad> pad);
|
||||||
void SetMatchSelections(SlippiPlayerSelections& s);
|
void SetMatchSelections(SlippiPlayerSelections& s);
|
||||||
std::unique_ptr<SlippiRemotePadOutput> GetSlippiRemotePad(int32_t curFrame);
|
std::unique_ptr<SlippiRemotePadOutput> GetSlippiRemotePad(int32_t curFrame, int index);
|
||||||
|
void DropOldRemoteInputs(int32_t curFrame);
|
||||||
SlippiMatchInfo* GetMatchInfo();
|
SlippiMatchInfo* GetMatchInfo();
|
||||||
u64 GetSlippiPing();
|
int32_t GetSlippiLatestRemoteFrame();
|
||||||
s32 GetSlippiLatestRemoteFrame();
|
SlippiPlayerSelections GetSlippiRemoteChatMessage();
|
||||||
u8 GetSlippiRemoteChatMessage();
|
|
||||||
u8 GetSlippiRemoteSentChatMessage();
|
u8 GetSlippiRemoteSentChatMessage();
|
||||||
s32 CalcTimeOffsetUs();
|
s32 CalcTimeOffsetUs();
|
||||||
|
|
||||||
void WriteChatMessageToPacket(sf::Packet& packet, int messageId);
|
void WriteChatMessageToPacket(sf::Packet& packet, int messageId, u8 playerIdx);
|
||||||
std::unique_ptr<SlippiPlayerSelections> ReadChatMessageFromPacket(sf::Packet& packet);
|
std::unique_ptr<SlippiPlayerSelections> ReadChatMessageFromPacket(sf::Packet& packet);
|
||||||
|
|
||||||
u8 remoteChatMessageId = 0; // most recent chat message id from opponent
|
std::unique_ptr<SlippiPlayerSelections> remoteChatMessageSelection =
|
||||||
|
nullptr; // most recent chat message player selection (message + player index)
|
||||||
u8 remoteSentChatMessageId = 0; // most recent chat message id that current player sent
|
u8 remoteSentChatMessageId = 0; // most recent chat message id that current player sent
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -148,8 +163,9 @@ protected:
|
||||||
std::queue<std::unique_ptr<sf::Packet>> m_async_queue;
|
std::queue<std::unique_ptr<sf::Packet>> m_async_queue;
|
||||||
|
|
||||||
ENetHost* m_client = nullptr;
|
ENetHost* m_client = nullptr;
|
||||||
ENetPeer* m_server = nullptr;
|
std::vector<ENetPeer*> m_server;
|
||||||
std::thread m_thread;
|
std::thread m_thread;
|
||||||
|
u8 m_remotePlayerCount = 0;
|
||||||
|
|
||||||
std::string m_selected_game;
|
std::string m_selected_game;
|
||||||
Common::Flag m_is_running{false};
|
Common::Flag m_is_running{false};
|
||||||
|
@ -166,23 +182,30 @@ protected:
|
||||||
u64 timeUs;
|
u64 timeUs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct
|
struct FrameOffsetData
|
||||||
{
|
{
|
||||||
// TODO: Should the buffer size be dynamic based on time sync interval or not?
|
// TODO: Should the buffer size be dynamic based on time sync interval or not?
|
||||||
int idx;
|
int idx;
|
||||||
std::vector<s32> buf;
|
std::vector<s32> buf;
|
||||||
} frameOffsetData;
|
};
|
||||||
|
|
||||||
bool isConnectionSelected = false;
|
bool isConnectionSelected = false;
|
||||||
bool isDecider = false;
|
bool isDecider = false;
|
||||||
int32_t lastFrameAcked;
|
|
||||||
bool hasGameStarted = false;
|
bool hasGameStarted = false;
|
||||||
FrameTiming lastFrameTiming;
|
u8 playerIdx = 0;
|
||||||
u64 pingUs;
|
|
||||||
std::deque<std::unique_ptr<SlippiPad>> localPadQueue; // most recent inputs at start of deque
|
std::deque<std::unique_ptr<SlippiPad>> localPadQueue; // most recent inputs at start of deque
|
||||||
std::deque<std::unique_ptr<SlippiPad>> remotePadQueue; // most recent inputs at start of deque
|
std::deque<std::unique_ptr<SlippiPad>>
|
||||||
std::queue<FrameTiming> ackTimers;
|
remotePadQueue[SLIPPI_REMOTE_PLAYER_MAX]; // most recent inputs at start of deque
|
||||||
|
|
||||||
|
u64 pingUs[SLIPPI_REMOTE_PLAYER_MAX];
|
||||||
|
int32_t lastFrameAcked[SLIPPI_REMOTE_PLAYER_MAX];
|
||||||
|
FrameOffsetData frameOffsetData[SLIPPI_REMOTE_PLAYER_MAX];
|
||||||
|
FrameTiming lastFrameTiming[SLIPPI_REMOTE_PLAYER_MAX];
|
||||||
|
std::array<std::queue<FrameTiming>, SLIPPI_REMOTE_PLAYER_MAX> ackTimers;
|
||||||
|
|
||||||
SlippiConnectStatus slippiConnectStatus = SlippiConnectStatus::NET_CONNECT_STATUS_UNSET;
|
SlippiConnectStatus slippiConnectStatus = SlippiConnectStatus::NET_CONNECT_STATUS_UNSET;
|
||||||
|
std::vector<int> failedConnections;
|
||||||
SlippiMatchInfo matchInfo;
|
SlippiMatchInfo matchInfo;
|
||||||
|
|
||||||
bool m_is_recording = false;
|
bool m_is_recording = false;
|
||||||
|
@ -191,7 +214,8 @@ protected:
|
||||||
std::unique_ptr<SlippiPlayerSelections> readSelectionsFromPacket(sf::Packet& packet);
|
std::unique_ptr<SlippiPlayerSelections> readSelectionsFromPacket(sf::Packet& packet);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int OnData(sf::Packet& packet);
|
u8 PlayerIdxFromPort(u8 port);
|
||||||
|
unsigned int OnData(sf::Packet& packet, ENetPeer* peer);
|
||||||
void Send(sf::Packet& packet);
|
void Send(sf::Packet& packet);
|
||||||
void Disconnect();
|
void Disconnect();
|
||||||
|
|
||||||
|
@ -207,7 +231,7 @@ private:
|
||||||
|
|
||||||
extern SlippiNetplayClient* SLIPPI_NETPLAY; // singleton static pointer
|
extern SlippiNetplayClient* SLIPPI_NETPLAY; // singleton static pointer
|
||||||
|
|
||||||
inline bool IsOnline()
|
static bool IsOnline()
|
||||||
{
|
{
|
||||||
return SLIPPI_NETPLAY != nullptr;
|
return SLIPPI_NETPLAY != nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,14 @@ SlippiPad::SlippiPad(int32_t frame, u8* padBuf) : SlippiPad(frame)
|
||||||
memcpy(this->padBuf, padBuf, SLIPPI_PAD_DATA_SIZE);
|
memcpy(this->padBuf, padBuf, SLIPPI_PAD_DATA_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SlippiPad::SlippiPad(int32_t frame, u8 playerIdx, u8 *padBuf) : SlippiPad(frame)
|
||||||
|
{
|
||||||
|
this->frame = frame;
|
||||||
|
this->playerIdx = playerIdx;
|
||||||
|
// Overwrite the data portion of the pad
|
||||||
|
memcpy(this->padBuf, padBuf, SLIPPI_PAD_DATA_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
SlippiPad::~SlippiPad()
|
SlippiPad::~SlippiPad()
|
||||||
{
|
{
|
||||||
// Do nothing?
|
// Do nothing?
|
||||||
|
|
|
@ -10,8 +10,10 @@ class SlippiPad
|
||||||
public:
|
public:
|
||||||
SlippiPad(int32_t frame);
|
SlippiPad(int32_t frame);
|
||||||
SlippiPad(int32_t frame, u8* padBuf);
|
SlippiPad(int32_t frame, u8* padBuf);
|
||||||
|
SlippiPad(int32_t frame, u8 playerIdx, u8 *padBuf);
|
||||||
~SlippiPad();
|
~SlippiPad();
|
||||||
|
|
||||||
int32_t frame;
|
int32_t frame;
|
||||||
|
u8 playerIdx;
|
||||||
u8 padBuf[SLIPPI_PAD_FULL_SIZE];
|
u8 padBuf[SLIPPI_PAD_FULL_SIZE];
|
||||||
};
|
};
|
||||||
|
|
|
@ -106,7 +106,7 @@ bool SlippiUser::AttemptLogin()
|
||||||
{
|
{
|
||||||
std::string user_file_path = getUserFilePath();
|
std::string user_file_path = getUserFilePath();
|
||||||
|
|
||||||
INFO_LOG(SLIPPI_ONLINE, "Looking for file at: %s", user_file_path.c_str());
|
// INFO_LOG(SLIPPI_ONLINE, "Looking for file at: %s", user_file_path.c_str());
|
||||||
|
|
||||||
{
|
{
|
||||||
// Put the filename here in its own scope because we don't really need it elsewhere
|
// Put the filename here in its own scope because we don't really need it elsewhere
|
||||||
|
@ -312,6 +312,7 @@ SlippiUser::UserInfo SlippiUser::parseFile(std::string file_contents)
|
||||||
info.play_key = readString(res, "playKey");
|
info.play_key = readString(res, "playKey");
|
||||||
info.connect_code = readString(res, "connectCode");
|
info.connect_code = readString(res, "connectCode");
|
||||||
info.latest_version = readString(res, "latestVersion");
|
info.latest_version = readString(res, "latestVersion");
|
||||||
|
info.port = res.value("port", -1);
|
||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ public:
|
||||||
std::string connect_code = "";
|
std::string connect_code = "";
|
||||||
std::string latest_version = "";
|
std::string latest_version = "";
|
||||||
std::string file_contents = "";
|
std::string file_contents = "";
|
||||||
|
|
||||||
|
int port;
|
||||||
};
|
};
|
||||||
|
|
||||||
SlippiUser();
|
SlippiUser();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue