ggpo: detect input size mismatch and abort gracefully

This commit is contained in:
Flyinghead 2021-09-16 19:24:49 +02:00
parent 3b7a940e9b
commit 4c53fcecfa
6 changed files with 132 additions and 50 deletions

View File

@ -112,7 +112,8 @@ typedef struct GGPOLocalEndpoint {
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INPUT_DROPPED, 8) \
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_PLAYER_DISCONNECTED, 9) \
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_TOO_MANY_SPECTATORS, 10) \
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INVALID_REQUEST, 11)
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INVALID_REQUEST, 11) \
GGPO_ERRORLIST_ENTRY(GGPO_ERRORCODE_INPUT_SIZE_DIFF, 12)
#define GGPO_ERRORLIST_ENTRY(name, value) name = value,
typedef enum {

View File

@ -35,6 +35,17 @@ typedef char int8;
typedef short int16;
typedef int int32;
#include "ggponet.h"
#include <stdexcept>
class GGPOException : public std::runtime_error {
public:
GGPOException(const std::string& what, GGPOErrorCode ggpoError)
: std::runtime_error(what), ggpoError(ggpoError) {}
GGPOErrorCode ggpoError;
};
/*
* Additional headers
*/
@ -48,8 +59,6 @@ typedef int int32;
#include "log.h"
/*
* Macros
*/

View File

@ -218,7 +218,8 @@ InputQueue::AddDelayedInputToQueue(GameInput &input, int frame_number)
{
Log("adding delayed input frame number %d to queue.\n", frame_number);
ASSERT(input.size == _prediction.size);
if (input.size != _prediction.size)
throw GGPOException("Input size differs from peer", GGPO_ERRORCODE_INPUT_SIZE_DIFF);
ASSERT(_last_added_frame == GameInput::NullFrame || frame_number == _last_added_frame + 1);

View File

@ -35,9 +35,8 @@ ggpo_log(GGPOSession *ggpo, const char *fmt, ...)
void
ggpo_logv(GGPOSession *ggpo, const char *fmt, va_list args)
{
if (ggpo) {
if (ggpo)
ggpo->Logv(fmt, args);
}
}
GGPOErrorCode
@ -48,12 +47,17 @@ ggpo_start_session(GGPOSession **session,
int input_size,
unsigned short localport)
{
*session= (GGPOSession *)new Peer2PeerBackend(cb,
game,
localport,
num_players,
input_size);
return GGPO_OK;
try {
*session= (GGPOSession *)new Peer2PeerBackend(cb,
game,
localport,
num_players,
input_size);
return GGPO_OK;
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_start_session: %s", e.what());
return e.ggpoError;
}
}
GGPOErrorCode
@ -61,10 +65,14 @@ ggpo_add_player(GGPOSession *ggpo,
GGPOPlayer *player,
GGPOPlayerHandle *handle)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->AddPlayer(player, handle);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_add_player: %s", e.what());
return e.ggpoError;
}
return ggpo->AddPlayer(player, handle);
}
@ -77,8 +85,13 @@ ggpo_start_synctest(GGPOSession **ggpo,
int input_size,
int frames)
{
*ggpo = (GGPOSession *)new SyncTestBackend(cb, game, frames, num_players);
return GGPO_OK;
try {
*ggpo = (GGPOSession *)new SyncTestBackend(cb, game, frames, num_players);
return GGPO_OK;
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_start_synctest: %s", e.what());
return e.ggpoError;
}
}
GGPOErrorCode
@ -86,19 +99,27 @@ ggpo_set_frame_delay(GGPOSession *ggpo,
GGPOPlayerHandle player,
int frame_delay)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->SetFrameDelay(player, frame_delay);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_set_frame_delay: %s", e.what());
return e.ggpoError;
}
return ggpo->SetFrameDelay(player, frame_delay);
}
GGPOErrorCode
ggpo_idle(GGPOSession *ggpo, int timeout)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->DoPoll(timeout);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_idle: %s", e.what());
return e.ggpoError;
}
return ggpo->DoPoll(timeout);
}
GGPOErrorCode
@ -107,10 +128,14 @@ ggpo_add_local_input(GGPOSession *ggpo,
void *values,
int size)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->AddLocalInput(player, values, size);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_add_local_input: %s", e.what());
return e.ggpoError;
}
return ggpo->AddLocalInput(player, values, size);
}
GGPOErrorCode
@ -119,37 +144,53 @@ ggpo_synchronize_input(GGPOSession *ggpo,
int size,
int *disconnect_flags)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->SyncInput(values, size, disconnect_flags);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_synchronize_input: %s", e.what());
return e.ggpoError;
}
return ggpo->SyncInput(values, size, disconnect_flags);
}
GGPOErrorCode ggpo_disconnect_player(GGPOSession *ggpo,
GGPOPlayerHandle player)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->DisconnectPlayer(player);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_disconnect_player: %s", e.what());
return e.ggpoError;
}
return ggpo->DisconnectPlayer(player);
}
GGPOErrorCode
ggpo_advance_frame(GGPOSession *ggpo)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->IncrementFrame();
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_advance_frame: %s", e.what());
return e.ggpoError;
}
return ggpo->IncrementFrame();
}
GGPOErrorCode
ggpo_client_chat(GGPOSession *ggpo, char *text)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->Chat(text);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_client_chat: %s", e.what());
return e.ggpoError;
}
return ggpo->Chat(text);
}
GGPOErrorCode
@ -157,10 +198,14 @@ ggpo_get_network_stats(GGPOSession *ggpo,
GGPOPlayerHandle player,
GGPONetworkStats *stats)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->GetNetworkStats(stats, player);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_get_network_stats: %s", e.what());
return e.ggpoError;
}
return ggpo->GetNetworkStats(stats, player);
}
@ -177,19 +222,27 @@ ggpo_close_session(GGPOSession *ggpo)
GGPOErrorCode
ggpo_set_disconnect_timeout(GGPOSession *ggpo, int timeout)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->SetDisconnectTimeout(timeout);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_set_disconnect_timeout: %s", e.what());
return e.ggpoError;
}
return ggpo->SetDisconnectTimeout(timeout);
}
GGPOErrorCode
ggpo_set_disconnect_notify_start(GGPOSession *ggpo, int timeout)
{
if (!ggpo) {
if (!ggpo)
return GGPO_ERRORCODE_INVALID_SESSION;
try {
return ggpo->SetDisconnectNotifyStart(timeout);
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_set_disconnect_notify_start: %s", e.what());
return e.ggpoError;
}
return ggpo->SetDisconnectNotifyStart(timeout);
}
GGPOErrorCode ggpo_start_spectating(GGPOSession **session,
@ -201,13 +254,18 @@ GGPOErrorCode ggpo_start_spectating(GGPOSession **session,
char *host_ip,
unsigned short host_port)
{
*session= (GGPOSession *)new SpectatorBackend(cb,
game,
local_port,
num_players,
input_size,
host_ip,
host_port);
return GGPO_OK;
try {
*session= (GGPOSession *)new SpectatorBackend(cb,
game,
local_port,
num_players,
input_size,
host_ip,
host_port);
return GGPO_OK;
} catch (const GGPOException& e) {
Log("GGPOException in ggpo_start_spectating: %s", e.what());
return e.ggpoError;
}
}

View File

@ -478,7 +478,6 @@ UdpProtocol::OnSyncRequest(UdpMsg *msg, int len)
return false;
}
// FIXME
//bool requeueSyncRequest = _last_send_time && _last_send_time + 20 < Platform::GetCurrentTimeMS();
if (_state.sync.roundtrips_remaining == NUM_SYNC_PACKETS && msg->hdr.sequence_number == 0) {
Log("Sync request 0 received... Re-queueing sync packet.\n");
SendSyncRequest();

View File

@ -450,7 +450,12 @@ void getInput(MapleInputState inputState[4])
u32 inputSize = sizeof(u32) + config::GGPOAnalogAxes;
std::vector<u8> inputs(inputSize * MAX_PLAYERS);
// should not call any callback
ggpo_synchronize_input(ggpoSession, (void *)&inputs[0], inputs.size(), nullptr);
GGPOErrorCode error = ggpo_synchronize_input(ggpoSession, (void *)&inputs[0], inputs.size(), nullptr);
if (error != GGPO_OK)
{
stopSession();
throw FlycastException("GGPO error");
}
for (int player = 0; player < MAX_PLAYERS; player++)
{
@ -486,10 +491,20 @@ bool nextFrame()
if (ggpoSession == nullptr)
return false;
// will call save_game_state
ggpo_advance_frame(ggpoSession);
GGPOErrorCode error = ggpo_advance_frame(ggpoSession);
// may rollback
ggpo_idle(ggpoSession, 0);
if (error == GGPO_OK)
error = ggpo_idle(ggpoSession, 0);
if (error != GGPO_OK)
{
stopSession();
if (error == GGPO_ERRORCODE_INPUT_SIZE_DIFF)
throw FlycastException("GGPO analog settings are different from peer");
else if (error != GGPO_OK)
throw FlycastException("GGPO error");
}
// may call save_game_state
do {
u32 input = ~kcode[localPlayerNum];
@ -519,8 +534,7 @@ bool nextFrame()
WARN_LOG(NETWORK, "ggpo_add_local_input failed %d", result);
if (result != GGPO_ERRORCODE_PREDICTION_THRESHOLD)
{
ggpo_close_session(ggpoSession);
ggpoSession = nullptr;
stopSession();
throw FlycastException("GGPO error");
}
std::this_thread::sleep_for(std::chrono::milliseconds(5));