mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-26 12:16:20 +00:00
pull in project-slippi/Ishiiruka/commit/923195dd5f662a5e65f85b10162fe14b1f0cd57a
This commit is contained in:
parent
00a48b7233
commit
d5723669de
4 changed files with 186 additions and 80 deletions
|
@ -1532,7 +1532,8 @@ void CEXISlippi::handleOnlineInputs(u8* payload)
|
||||||
{
|
{
|
||||||
m_read_queue.clear();
|
m_read_queue.clear();
|
||||||
|
|
||||||
int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3];
|
s32 frame = Common::swap32(&payload[0]);
|
||||||
|
s32 finalizedFrame = Common::swap32(&payload[4]);
|
||||||
|
|
||||||
if (frame == 1)
|
if (frame == 1)
|
||||||
{
|
{
|
||||||
|
@ -1571,19 +1572,25 @@ void CEXISlippi::handleOnlineInputs(u8* payload)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shouldSkipOnlineFrame(frame))
|
// Drop inputs that we no longer need (inputs older than the finalized frame passed in)
|
||||||
|
slippi_netplay->DropOldRemoteInputs(finalizedFrame);
|
||||||
|
|
||||||
|
bool shouldSkip = shouldSkipOnlineFrame(frame, finalizedFrame);
|
||||||
|
if (shouldSkip)
|
||||||
{
|
{
|
||||||
// Send inputs that have not yet been acked
|
// Send inputs that have not yet been acked
|
||||||
slippi_netplay->SendSlippiPad(nullptr);
|
slippi_netplay->SendSlippiPad(nullptr);
|
||||||
m_read_queue.push_back(2);
|
}
|
||||||
return;
|
else
|
||||||
|
{
|
||||||
|
// Send the input for this frame along with everything that has yet to be acked
|
||||||
|
handleSendInputs(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSendInputs(payload);
|
prepareOpponentInputs(frame, shouldSkip);
|
||||||
prepareOpponentInputs(payload);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CEXISlippi::shouldSkipOnlineFrame(s32 frame)
|
bool CEXISlippi::shouldSkipOnlineFrame(s32 frame, s32 finalizedFrame)
|
||||||
{
|
{
|
||||||
auto status = slippi_netplay->GetSlippiConnectStatus();
|
auto status = slippi_netplay->GetSlippiConnectStatus();
|
||||||
bool connectionFailed =
|
bool connectionFailed =
|
||||||
|
@ -1603,8 +1610,16 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame)
|
||||||
|
|
||||||
// Return true if we are too far ahead for rollback. ROLLBACK_MAX_FRAMES is the number of frames
|
// Return true if we are too far ahead for rollback. ROLLBACK_MAX_FRAMES is the number of frames
|
||||||
// we can receive for the opponent at one time and is our "look-ahead" limit
|
// we can receive for the opponent at one time and is our "look-ahead" limit
|
||||||
int32_t latestRemoteFrame = slippi_netplay->GetSlippiLatestRemoteFrame();
|
// Example: finalizedFrame = 100 means the last savestate we need is 101. We can then store
|
||||||
if (frame - latestRemoteFrame >= ROLLBACK_MAX_FRAMES)
|
// states 101 to 107 before running out of savestates. So 107 - 100 = 7. We need to make sure
|
||||||
|
// we have enough inputs to finalize to not overflow the available states, so if our latest frame
|
||||||
|
// is 101, we can't let frame 109 be created. 101 - 100 >= 109 - 100 - 7 : 1 >= 2 (false).
|
||||||
|
// It has to work this way because we only have room to move our states forward by one for frame
|
||||||
|
// 108
|
||||||
|
s32 latestRemoteFrame = slippi_netplay->GetSlippiLatestRemoteFrame(ROLLBACK_MAX_FRAMES);
|
||||||
|
auto hasEnoughNewInputs =
|
||||||
|
latestRemoteFrame - finalizedFrame >= (frame - finalizedFrame - ROLLBACK_MAX_FRAMES);
|
||||||
|
if (!hasEnoughNewInputs)
|
||||||
{
|
{
|
||||||
stallFrameCount++;
|
stallFrameCount++;
|
||||||
if (stallFrameCount > 60 * 7)
|
if (stallFrameCount > 60 * 7)
|
||||||
|
@ -1613,16 +1628,19 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame)
|
||||||
isConnectionStalled = true;
|
isConnectionStalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
WARN_LOG(SLIPPI_ONLINE,
|
WARN_LOG_FMT(
|
||||||
"Halting for one frame due to rollback limit (frame: %d | latest: %d)...", frame,
|
SLIPPI_ONLINE,
|
||||||
latestRemoteFrame);
|
"Halting for one frame due to rollback limit (frame: {} | latest: {} | finalized: {})...",
|
||||||
|
frame, latestRemoteFrame, finalizedFrame);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
stallFrameCount = 0;
|
stallFrameCount = 0;
|
||||||
|
|
||||||
// Return true if we are over 60% of a frame ahead of our opponent. Currently limiting how
|
s32 frameTime = 16683;
|
||||||
// often this happens because I'm worried about jittery data causing a lot of unneccesary delays.
|
s32 t1 = 10000;
|
||||||
|
s32 t2 = (2 * frameTime) + t1;
|
||||||
|
|
||||||
// Only skip once for a given frame because our time detection method doesn't take into
|
// Only skip once for a given frame because our time detection method doesn't take into
|
||||||
// consideration waiting for a frame. Also it's less jarring and it happens often enough that it
|
// consideration waiting for a frame. Also it's less jarring and it happens often enough that it
|
||||||
// will smoothly get to the right place
|
// will smoothly get to the right place
|
||||||
|
@ -1632,10 +1650,14 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame)
|
||||||
auto offsetUs = slippi_netplay->CalcTimeOffsetUs();
|
auto offsetUs = slippi_netplay->CalcTimeOffsetUs();
|
||||||
INFO_LOG_FMT(SLIPPI_ONLINE, "[Frame {}] Offset for skip is: {} us", frame, offsetUs);
|
INFO_LOG_FMT(SLIPPI_ONLINE, "[Frame {}] Offset for skip is: {} us", frame, offsetUs);
|
||||||
|
|
||||||
|
// At the start of the game, let's make sure to sync perfectly, but after that let the slow
|
||||||
|
// instance try to do more work before we stall
|
||||||
|
|
||||||
// The decision to skip a frame only happens when we are already pretty far off ahead. The hope
|
// The decision to skip a frame only happens when we are already pretty far off ahead. The hope
|
||||||
// is that this won't really be used much because the frame advance of the slow client will pick
|
// is that this won't really be used much because the frame advance of the slow client along
|
||||||
// up the difference most of the time. But at some point it's probably better to slow down...
|
// with dynamic emulation speed will pick up the difference most of the time. But at some point
|
||||||
if (offsetUs > 26000)
|
// it's probably better to slow down...
|
||||||
|
if (offsetUs > (frame <= 120 ? t1 : t2))
|
||||||
{
|
{
|
||||||
isCurrentlySkipping = true;
|
isCurrentlySkipping = true;
|
||||||
|
|
||||||
|
@ -1665,6 +1687,17 @@ bool CEXISlippi::shouldSkipOnlineFrame(s32 frame)
|
||||||
|
|
||||||
bool CEXISlippi::shouldAdvanceOnlineFrame(s32 frame)
|
bool CEXISlippi::shouldAdvanceOnlineFrame(s32 frame)
|
||||||
{
|
{
|
||||||
|
// Logic below is used to test frame advance by forcing it more often
|
||||||
|
// SConfig::GetInstance().m_EmulationSpeed = 0.5f;
|
||||||
|
// if (frame > 120 && frame % 10 < 3)
|
||||||
|
//{
|
||||||
|
// Common::SleepCurrentThread(1); // Sleep to try to let inputs come in to make late rollbacks
|
||||||
|
// more likely return true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// return false;
|
||||||
|
// return frame % 2 == 0;
|
||||||
|
|
||||||
// Return true if we are over 60% of a frame behind our opponent. We limit how often this happens
|
// Return true if we are over 60% of a frame behind our opponent. We limit how often this happens
|
||||||
// to get a reliable average to act on. We will allow advancing up to 5 frames (spread out) over
|
// to get a reliable average to act on. We will allow advancing up to 5 frames (spread out) over
|
||||||
// the 30 frame period. This makes the game feel relatively smooth still
|
// the 30 frame period. This makes the game feel relatively smooth still
|
||||||
|
@ -1676,12 +1709,30 @@ bool CEXISlippi::shouldAdvanceOnlineFrame(s32 frame)
|
||||||
|
|
||||||
// Dynamically adjust emulation speed in order to fine-tune time sync to reduce one sided
|
// Dynamically adjust emulation speed in order to fine-tune time sync to reduce one sided
|
||||||
// rollbacks even more
|
// rollbacks even more
|
||||||
auto maxSpeedDeviation = 0.005f;
|
// Modify emulation speed up to a max of 1% at 3 frames offset or more. Don't slow down the
|
||||||
auto deviation = (offsetUs / 33366.0f) * maxSpeedDeviation;
|
// front instance as much because we want to prioritize performance for the fast PC
|
||||||
if (deviation > maxSpeedDeviation)
|
float deviation = 0;
|
||||||
deviation = maxSpeedDeviation;
|
float maxSlowDownAmount = 0.005f;
|
||||||
else if (deviation < -maxSpeedDeviation)
|
float maxSpeedUpAmount = 0.01f;
|
||||||
deviation = -maxSpeedDeviation;
|
int frameWindow = 3;
|
||||||
|
if (offsetUs > -250 && offsetUs < 8000)
|
||||||
|
{
|
||||||
|
// Do nothing, leave deviation at 0 for 100% emulation speed when ahead by 8 ms or less
|
||||||
|
}
|
||||||
|
else if (offsetUs < 0)
|
||||||
|
{
|
||||||
|
// Here we are behind, so let's speed up our instance
|
||||||
|
float frameWindowMultiplier = std::min(-offsetUs / (frameWindow * 16683.0f), 1.0f);
|
||||||
|
deviation = frameWindowMultiplier * maxSpeedUpAmount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Here we are ahead, so let's slow down our instance
|
||||||
|
float frameWindowMultiplier = std::min(offsetUs / (frameWindow * 16683.0f), 1.0f);
|
||||||
|
deviation = frameWindowMultiplier * -maxSlowDownAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dynamicEmulationSpeed = 1.0f + deviation;
|
||||||
|
|
||||||
// If we are behind (negative offset) we want to go above 100% run speed, so we need to subtract
|
// If we are behind (negative offset) we want to go above 100% run speed, so we need to subtract
|
||||||
// the deviation value
|
// the deviation value
|
||||||
|
@ -1692,13 +1743,17 @@ bool CEXISlippi::shouldAdvanceOnlineFrame(s32 frame)
|
||||||
INFO_LOG(SLIPPI_ONLINE, "[Frame %d] Offset for advance is: %d us. New speed: %.2f%%", frame,
|
INFO_LOG(SLIPPI_ONLINE, "[Frame %d] Offset for advance is: %d us. New speed: %.2f%%", frame,
|
||||||
offsetUs, dynamicEmulationSpeed * 100.0f);
|
offsetUs, dynamicEmulationSpeed * 100.0f);
|
||||||
|
|
||||||
|
s32 frameTime = 16683;
|
||||||
|
s32 t1 = 10000;
|
||||||
|
s32 t2 = frameTime + t1;
|
||||||
|
|
||||||
// Count the number of times we're below a threshold we should easily be able to clear. This is
|
// Count the number of times we're below a threshold we should easily be able to clear. This is
|
||||||
// checked twice per second.
|
// checked twice per second.
|
||||||
fallBehindCounter += offsetUs < -10000 ? 1 : 0;
|
fallBehindCounter += offsetUs < -t1 ? 1 : 0;
|
||||||
fallFarBehindCounter += offsetUs < -25000 ? 1 : 0;
|
fallFarBehindCounter += offsetUs < -t2 ? 1 : 0;
|
||||||
|
|
||||||
bool isSlow = (offsetUs < -10000 && fallBehindCounter > 50) ||
|
bool isSlow =
|
||||||
(offsetUs < -25000 && fallFarBehindCounter > 15);
|
(offsetUs < -t1 && fallBehindCounter > 50) || (offsetUs < -t2 && fallFarBehindCounter > 15);
|
||||||
if (isSlow && lastSearch.mode != SlippiMatchmaking::OnlinePlayMode::TEAMS)
|
if (isSlow && lastSearch.mode != SlippiMatchmaking::OnlinePlayMode::TEAMS)
|
||||||
{
|
{
|
||||||
// We don't show this message for teams because it seems to false positive a lot there, maybe
|
// We don't show this message for teams because it seems to false positive a lot there, maybe
|
||||||
|
@ -1710,13 +1765,13 @@ bool CEXISlippi::shouldAdvanceOnlineFrame(s32 frame)
|
||||||
OSD::Color::RED);
|
OSD::Color::RED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offsetUs < -10000 && !isCurrentlyAdvancing)
|
if (offsetUs < -t2 && !isCurrentlyAdvancing)
|
||||||
{
|
{
|
||||||
isCurrentlyAdvancing = true;
|
isCurrentlyAdvancing = true;
|
||||||
|
|
||||||
// On early frames, don't advance any frames. Let the stalling logic handle the initial sync
|
// On early frames, don't advance any frames. Let the stalling logic handle the initial sync
|
||||||
int maxAdvFrames = frame > 120 ? 5 : 0;
|
int maxAdvFrames = frame > 120 ? 3 : 0;
|
||||||
framesToAdvance = ((-offsetUs - 10000) / 16683) + 1;
|
framesToAdvance = ((-offsetUs - t1) / frameTime) + 1;
|
||||||
framesToAdvance = framesToAdvance > maxAdvFrames ? maxAdvFrames : framesToAdvance;
|
framesToAdvance = framesToAdvance > maxAdvFrames ? maxAdvFrames : framesToAdvance;
|
||||||
|
|
||||||
WARN_LOG(SLIPPI_ONLINE,
|
WARN_LOG(SLIPPI_ONLINE,
|
||||||
|
@ -1747,8 +1802,8 @@ void CEXISlippi::handleSendInputs(u8* payload)
|
||||||
if (isConnectionStalled)
|
if (isConnectionStalled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3];
|
s32 frame = Common::swap32(&payload[0]);
|
||||||
u8 delay = payload[4];
|
u8 delay = payload[8];
|
||||||
|
|
||||||
// On the first frame sent, we need to queue up empty dummy pads for as many
|
// On the first frame sent, we need to queue up empty dummy pads for as many
|
||||||
// frames as we have delay
|
// frames as we have delay
|
||||||
|
@ -1761,22 +1816,27 @@ void CEXISlippi::handleSendInputs(u8* payload)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pad = std::make_unique<SlippiPad>(frame + delay, &payload[5]);
|
auto pad = std::make_unique<SlippiPad>(frame + delay, &payload[9]);
|
||||||
|
|
||||||
slippi_netplay->SendSlippiPad(std::move(pad));
|
slippi_netplay->SendSlippiPad(std::move(pad));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXISlippi::prepareOpponentInputs(u8* payload)
|
void CEXISlippi::prepareOpponentInputs(s32 frame, bool shouldSkip)
|
||||||
{
|
{
|
||||||
m_read_queue.clear();
|
m_read_queue.clear();
|
||||||
|
|
||||||
int32_t frame = payload[0] << 24 | payload[1] << 16 | payload[2] << 8 | payload[3];
|
|
||||||
|
|
||||||
u8 frameResult = 1; // Indicates to continue frame
|
u8 frameResult = 1; // Indicates to continue frame
|
||||||
|
|
||||||
auto state = slippi_netplay->GetSlippiConnectStatus();
|
auto state = slippi_netplay->GetSlippiConnectStatus();
|
||||||
if (state != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED ||
|
if (shouldSkip)
|
||||||
isConnectionStalled)
|
{
|
||||||
|
// Event though we are skipping an input, we still want to prepare the opponent inputs because
|
||||||
|
// in the case where we get a stall on an advance frame, we need to keep the RXB inputs
|
||||||
|
// populated for when the frame inputs are requested on a rollback
|
||||||
|
frameResult = 2;
|
||||||
|
}
|
||||||
|
else if (state != SlippiNetplayClient::SlippiConnectStatus::NET_CONNECT_STATUS_CONNECTED ||
|
||||||
|
isConnectionStalled)
|
||||||
{
|
{
|
||||||
frameResult = 3; // Indicates we have disconnected
|
frameResult = 3; // Indicates we have disconnected
|
||||||
}
|
}
|
||||||
|
@ -1785,19 +1845,21 @@ void CEXISlippi::prepareOpponentInputs(u8* payload)
|
||||||
frameResult = 4;
|
frameResult = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_read_queue.push_back(frameResult); // Indicate a continue frame
|
m_read_queue.push_back(frameResult); // Write out the control message value
|
||||||
|
|
||||||
u8 remotePlayerCount = matchmaking->RemotePlayerCount();
|
u8 remotePlayerCount = matchmaking->RemotePlayerCount();
|
||||||
m_read_queue.push_back(remotePlayerCount); // Indicate the number of remote players
|
m_read_queue.push_back(remotePlayerCount); // Indicate the number of remote players
|
||||||
|
|
||||||
std::unique_ptr<SlippiRemotePadOutput> results[SLIPPI_REMOTE_PLAYER_MAX];
|
std::unique_ptr<SlippiRemotePadOutput> results[SLIPPI_REMOTE_PLAYER_MAX];
|
||||||
int offset[SLIPPI_REMOTE_PLAYER_MAX];
|
int offset[SLIPPI_REMOTE_PLAYER_MAX];
|
||||||
INFO_LOG(SLIPPI_ONLINE, "Preparing pad data for frame %d", frame);
|
|
||||||
|
int32_t latestFrameRead[SLIPPI_REMOTE_PLAYER_MAX]{};
|
||||||
|
|
||||||
// Get pad data for each remote player and write each of their latest frame nums to the buf
|
// 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++)
|
for (int i = 0; i < remotePlayerCount; i++)
|
||||||
{
|
{
|
||||||
results[i] = slippi_netplay->GetSlippiRemotePad(frame, i);
|
results[i] = slippi_netplay->GetSlippiRemotePad(i, ROLLBACK_MAX_FRAMES);
|
||||||
|
// results[i] = slippi_netplay->GetFakePadOutput(frame);
|
||||||
|
|
||||||
// determine offset from which to copy data
|
// determine offset from which to copy data
|
||||||
offset[i] = (results[i]->latestFrame - frame) * SLIPPI_PAD_FULL_SIZE;
|
offset[i] = (results[i]->latestFrame - frame) * SLIPPI_PAD_FULL_SIZE;
|
||||||
|
@ -1807,16 +1869,19 @@ void CEXISlippi::prepareOpponentInputs(u8* payload)
|
||||||
int32_t latestFrame = results[i]->latestFrame;
|
int32_t latestFrame = results[i]->latestFrame;
|
||||||
if (latestFrame > frame)
|
if (latestFrame > frame)
|
||||||
latestFrame = frame;
|
latestFrame = frame;
|
||||||
appendWordToBuffer(&m_read_queue, *(u32*)&latestFrame);
|
appendWordToBuffer(&m_read_queue, static_cast<u32>(latestFrame));
|
||||||
// INFO_LOG(SLIPPI_ONLINE, "Sending frame num %d for pIdx %d (offset: %d)", latestFrame, i,
|
// INFO_LOG(SLIPPI_ONLINE, "Sending frame num %d for pIdx %d (offset: %d)", latestFrame, i,
|
||||||
// offset[i]);
|
// offset[i]);
|
||||||
}
|
}
|
||||||
// Send the current frame for any unused player slots.
|
// Send the current frame for any unused player slots.
|
||||||
for (int i = remotePlayerCount; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
|
for (int i = remotePlayerCount; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
|
||||||
{
|
{
|
||||||
appendWordToBuffer(&m_read_queue, *(u32*)&frame);
|
appendWordToBuffer(&m_read_queue, static_cast<u32>(frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s32* val = std::min_element(std::begin(latestFrameRead), std::end(latestFrameRead));
|
||||||
|
appendWordToBuffer(&m_read_queue, static_cast<u32>(*val));
|
||||||
|
|
||||||
// copy pad data over
|
// copy pad data over
|
||||||
for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
|
for (int i = 0; i < SLIPPI_REMOTE_PLAYER_MAX; i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -112,7 +112,7 @@ private:
|
||||||
{CMD_GET_GECKO_CODES, 0},
|
{CMD_GET_GECKO_CODES, 0},
|
||||||
|
|
||||||
// The following are used for Slippi online and also have fixed sizes
|
// The following are used for Slippi online and also have fixed sizes
|
||||||
{CMD_ONLINE_INPUTS, 17},
|
{CMD_ONLINE_INPUTS, 21},
|
||||||
{CMD_CAPTURE_SAVESTATE, 32},
|
{CMD_CAPTURE_SAVESTATE, 32},
|
||||||
{CMD_LOAD_SAVESTATE, 32},
|
{CMD_LOAD_SAVESTATE, 32},
|
||||||
{CMD_GET_MATCH_STATE, 0},
|
{CMD_GET_MATCH_STATE, 0},
|
||||||
|
@ -173,7 +173,7 @@ private:
|
||||||
u16 getRandomStage();
|
u16 getRandomStage();
|
||||||
bool isDisconnected();
|
bool isDisconnected();
|
||||||
void handleOnlineInputs(u8* payload);
|
void handleOnlineInputs(u8* payload);
|
||||||
void prepareOpponentInputs(u8* payload);
|
void prepareOpponentInputs(s32 frame, bool shouldSkip);
|
||||||
void handleSendInputs(u8* payload);
|
void handleSendInputs(u8* payload);
|
||||||
void handleCaptureSavestate(u8* payload);
|
void handleCaptureSavestate(u8* payload);
|
||||||
void handleLoadSavestate(u8* payload);
|
void handleLoadSavestate(u8* payload);
|
||||||
|
@ -181,7 +181,7 @@ private:
|
||||||
void startFindMatch(u8* payload);
|
void startFindMatch(u8* payload);
|
||||||
void prepareOnlineMatchState();
|
void prepareOnlineMatchState();
|
||||||
void setMatchSelections(u8* payload);
|
void setMatchSelections(u8* payload);
|
||||||
bool shouldSkipOnlineFrame(s32 frame);
|
bool shouldSkipOnlineFrame(s32 frame, s32 finalizedFrame);
|
||||||
bool shouldAdvanceOnlineFrame(s32 frame);
|
bool shouldAdvanceOnlineFrame(s32 frame);
|
||||||
void handleLogInRequest();
|
void handleLogInRequest();
|
||||||
void handleLogOutRequest();
|
void handleLogOutRequest();
|
||||||
|
|
|
@ -289,8 +289,8 @@ unsigned int SlippiNetplayClient::OnData(sf::Packet& packet, ENetPeer* peer)
|
||||||
int32_t headFrame = remotePadQueue[pIdx].empty() ? 0 : remotePadQueue[pIdx].front()->frame;
|
int32_t headFrame = remotePadQueue[pIdx].empty() ? 0 : remotePadQueue[pIdx].front()->frame;
|
||||||
int inputsToCopy = frame - headFrame;
|
int inputsToCopy = frame - headFrame;
|
||||||
|
|
||||||
// Check that the packet actually contains the data it claims to
|
// Not sure what the max is here. If we never ack frames it could get big...
|
||||||
if ((5 + inputsToCopy * SLIPPI_PAD_DATA_SIZE) > (int)packet.getDataSize())
|
if (inputsToCopy > 128)
|
||||||
{
|
{
|
||||||
ERROR_LOG_FMT(
|
ERROR_LOG_FMT(
|
||||||
SLIPPI_ONLINE,
|
SLIPPI_ONLINE,
|
||||||
|
@ -723,9 +723,9 @@ void SlippiNetplayClient::ThreadFunc()
|
||||||
|
|
||||||
if (isAlreadyConnected)
|
if (isAlreadyConnected)
|
||||||
{
|
{
|
||||||
// Don't add this person again if they are already connected. Not doing this can cause one
|
// Don't add this person again if they are already connected. Not doing this can cause
|
||||||
// person to take up 2 or more spots, denying one or more players from connecting and thus
|
// one person to take up 2 or more spots, denying one or more players from connecting
|
||||||
// getting stuck on the "Waiting" step
|
// and thus getting stuck on the "Waiting" step
|
||||||
INFO_LOG(SLIPPI_ONLINE, "Already connected!");
|
INFO_LOG(SLIPPI_ONLINE, "Already connected!");
|
||||||
break; // Breaks out of case
|
break; // Breaks out of case
|
||||||
}
|
}
|
||||||
|
@ -735,12 +735,12 @@ void SlippiNetplayClient::ThreadFunc()
|
||||||
// This check used to check for port as well as host. The problem was that for some
|
// This check used to check for port as well as host. The problem was that for some
|
||||||
// people, their internet will switch the port they're sending from. This means these
|
// people, their internet will switch the port they're sending from. This means these
|
||||||
// people struggle to connect to others but they sometimes do succeed. When we were
|
// people struggle to connect to others but they sometimes do succeed. When we were
|
||||||
// checking for port here though we would get into a state where the person they succeeded
|
// checking for port here though we would get into a state where the person they
|
||||||
// to connect to would not accept the connection with them, this would lead the player
|
// succeeded to connect to would not accept the connection with them, this would lead
|
||||||
// with this internet issue to get stuck waiting for the other player. The only downside
|
// the player with this internet issue to get stuck waiting for the other player. The
|
||||||
// to this that I can guess is that if you fail to connect to one person out of two that
|
// only downside to this that I can guess is that if you fail to connect to one person
|
||||||
// are on your LAN, it might report that you failed to connect to the wrong person. There
|
// out of two that are on your LAN, it might report that you failed to connect to the
|
||||||
// might be more problems tho, not sure
|
// wrong person. There might be more problems tho, not sure
|
||||||
INFO_LOG_FMT(SLIPPI_ONLINE, "[Netplay] Comparing connection address: {} - {}",
|
INFO_LOG_FMT(SLIPPI_ONLINE, "[Netplay] Comparing connection address: {} - {}",
|
||||||
remoteAddrs[i].host, netEvent.peer->address.host);
|
remoteAddrs[i].host, netEvent.peer->address.host);
|
||||||
if (remoteAddrs[i].host == netEvent.peer->address.host && !connections[i])
|
if (remoteAddrs[i].host == netEvent.peer->address.host && !connections[i])
|
||||||
|
@ -1106,8 +1106,39 @@ u8 SlippiNetplayClient::GetSlippiRemoteSentChatMessage()
|
||||||
return copiedMessageId;
|
return copiedMessageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<SlippiRemotePadOutput> SlippiNetplayClient::GetSlippiRemotePad(int32_t curFrame,
|
std::unique_ptr<SlippiRemotePadOutput> SlippiNetplayClient::GetFakePadOutput(int frame)
|
||||||
int index)
|
{
|
||||||
|
// Used for testing purposes, will ignore the opponent's actual inputs and provide fake
|
||||||
|
// ones to trigger rollback scenarios
|
||||||
|
std::unique_ptr<SlippiRemotePadOutput> padOutput = std::make_unique<SlippiRemotePadOutput>();
|
||||||
|
|
||||||
|
// Triggers rollback where the first few inputs were correctly predicted
|
||||||
|
if (frame % 60 < 5)
|
||||||
|
{
|
||||||
|
// Return old inputs for a bit
|
||||||
|
padOutput->latestFrame = frame - (frame % 60);
|
||||||
|
padOutput->data.insert(padOutput->data.begin(), SLIPPI_PAD_FULL_SIZE, 0);
|
||||||
|
}
|
||||||
|
else if (frame % 60 == 5)
|
||||||
|
{
|
||||||
|
padOutput->latestFrame = frame;
|
||||||
|
// Add 5 frames of 0'd inputs
|
||||||
|
padOutput->data.insert(padOutput->data.begin(), 5 * SLIPPI_PAD_FULL_SIZE, 0);
|
||||||
|
|
||||||
|
// Press A button for 2 inputs prior to this frame causing a rollback
|
||||||
|
padOutput->data[2 * SLIPPI_PAD_FULL_SIZE] = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
padOutput->latestFrame = frame;
|
||||||
|
padOutput->data.insert(padOutput->data.begin(), SLIPPI_PAD_FULL_SIZE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(padOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SlippiRemotePadOutput> SlippiNetplayClient::GetSlippiRemotePad(int index,
|
||||||
|
int maxFrameCount)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(pad_mutex); // TODO: Is this the correct lock?
|
std::lock_guard<std::mutex> lk(pad_mutex); // TODO: Is this the correct lock?
|
||||||
|
|
||||||
|
@ -1125,21 +1156,37 @@ std::unique_ptr<SlippiRemotePadOutput> SlippiNetplayClient::GetSlippiRemotePad(i
|
||||||
return std::move(padOutput);
|
return std::move(padOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int inputCount = 0;
|
||||||
|
|
||||||
padOutput->latestFrame = 0;
|
padOutput->latestFrame = 0;
|
||||||
// Copy the entire remaining remote buffer
|
|
||||||
for (auto it = remotePadQueue[index].begin(); it != remotePadQueue[index].end(); ++it)
|
// Copy inputs from the remote pad queue to the output. We iterate backwards because
|
||||||
|
// we want to get the oldest frames possible (will have been cleared to contain the last
|
||||||
|
// finalized frame at the back). I think it's very unlikely but I think before we
|
||||||
|
// iterated from the front and it's possible the 7 frame limit left out an input the
|
||||||
|
// game actually needed.
|
||||||
|
for (auto it = remotePadQueue[index].rbegin(); it != remotePadQueue[index].rend(); ++it)
|
||||||
{
|
{
|
||||||
if ((*it)->frame > padOutput->latestFrame)
|
if ((*it)->frame > padOutput->latestFrame)
|
||||||
padOutput->latestFrame = (*it)->frame;
|
padOutput->latestFrame = (*it)->frame;
|
||||||
|
|
||||||
|
// NOTICE_LOG(SLIPPI_ONLINE, "[%d] (Remote) P%d %08X %08X %08X", (*it)->frame,
|
||||||
|
// index >= playerIdx ? index + 1 : index, Common::swap32(&(*it)->padBuf[0]),
|
||||||
|
// Common::swap32(&(*it)->padBuf[4]), Common::swap32(&(*it)->padBuf[8]));
|
||||||
|
|
||||||
auto padIt = std::begin((*it)->padBuf);
|
auto padIt = std::begin((*it)->padBuf);
|
||||||
padOutput->data.insert(padOutput->data.end(), padIt, padIt + SLIPPI_PAD_FULL_SIZE);
|
padOutput->data.insert(padOutput->data.begin(), padIt, padIt + SLIPPI_PAD_FULL_SIZE);
|
||||||
|
|
||||||
|
// Limit max amount of inputs to send
|
||||||
|
inputCount++;
|
||||||
|
if (inputCount >= maxFrameCount)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::move(padOutput);
|
return std::move(padOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SlippiNetplayClient::DropOldRemoteInputs(int32_t curFrame)
|
void SlippiNetplayClient::DropOldRemoteInputs(int32_t finalizedFrame)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(pad_mutex);
|
std::lock_guard<std::mutex> lk(pad_mutex);
|
||||||
|
|
||||||
|
@ -1162,13 +1209,10 @@ void SlippiNetplayClient::DropOldRemoteInputs(int32_t curFrame)
|
||||||
for (int i = 0; i < m_remotePlayerCount; i++)
|
for (int i = 0; i < m_remotePlayerCount; i++)
|
||||||
{
|
{
|
||||||
// INFO_LOG_FMT(SLIPPI_ONLINE, "remotePadQueue[{}] size: {}", i, remotePadQueue[i].size());
|
// INFO_LOG_FMT(SLIPPI_ONLINE, "remotePadQueue[{}] size: {}", i, remotePadQueue[i].size());
|
||||||
while (remotePadQueue[i].size() > 1 && remotePadQueue[i].back()->frame < lowestCommonFrame &&
|
while (remotePadQueue[i].size() > 1 && remotePadQueue[i].back()->frame < finalizedFrame)
|
||||||
remotePadQueue[i].back()->frame < curFrame)
|
|
||||||
{
|
|
||||||
/*INFO_LOG_FMT(SLIPPI_ONLINE, "Popping inputs for frame {} from back of player {} queue",
|
/*INFO_LOG_FMT(SLIPPI_ONLINE, "Popping inputs for frame {} from back of player {} queue",
|
||||||
remotePadQueue[i].back()->frame, i);*/
|
remotePadQueue[i].back()->frame, i);*/
|
||||||
remotePadQueue[i].pop_back();
|
remotePadQueue[i].pop_back();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1177,23 +1221,19 @@ SlippiMatchInfo* SlippiNetplayClient::GetMatchInfo()
|
||||||
return &matchInfo;
|
return &matchInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t SlippiNetplayClient::GetSlippiLatestRemoteFrame()
|
int32_t SlippiNetplayClient::GetSlippiLatestRemoteFrame(int maxFrameCount)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(pad_mutex); // TODO: Is this the correct lock?
|
|
||||||
|
|
||||||
// Return the lowest frame among remote queues
|
// Return the lowest frame among remote queues
|
||||||
int lowestFrame = 0;
|
int lowestFrame = 0;
|
||||||
|
bool isFrameSet = false;
|
||||||
for (int i = 0; i < m_remotePlayerCount; i++)
|
for (int i = 0; i < m_remotePlayerCount; i++)
|
||||||
{
|
{
|
||||||
if (remotePadQueue[i].empty())
|
auto rp = GetSlippiRemotePad(i, maxFrameCount);
|
||||||
{
|
int f = rp->latestFrame;
|
||||||
return 0;
|
if (f < lowestFrame || !isFrameSet)
|
||||||
}
|
|
||||||
|
|
||||||
int f = remotePadQueue[i].front()->frame;
|
|
||||||
if (f < lowestFrame || lowestFrame == 0)
|
|
||||||
{
|
{
|
||||||
lowestFrame = f;
|
lowestFrame = f;
|
||||||
|
isFrameSet = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,10 +137,11 @@ public:
|
||||||
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, int index);
|
std::unique_ptr<SlippiRemotePadOutput> GetFakePadOutput(int frame);
|
||||||
void DropOldRemoteInputs(int32_t curFrame);
|
std::unique_ptr<SlippiRemotePadOutput> GetSlippiRemotePad(int index, int maxFrameCount);
|
||||||
|
void DropOldRemoteInputs(int32_t finalizedFrame);
|
||||||
SlippiMatchInfo* GetMatchInfo();
|
SlippiMatchInfo* GetMatchInfo();
|
||||||
int32_t GetSlippiLatestRemoteFrame();
|
int32_t GetSlippiLatestRemoteFrame(int maxFrameCount);
|
||||||
SlippiPlayerSelections GetSlippiRemoteChatMessage();
|
SlippiPlayerSelections GetSlippiRemoteChatMessage();
|
||||||
u8 GetSlippiRemoteSentChatMessage();
|
u8 GetSlippiRemoteSentChatMessage();
|
||||||
s32 CalcTimeOffsetUs();
|
s32 CalcTimeOffsetUs();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue