Netplay: Drop-in at any time

This commit is contained in:
Stenzek 2023-05-07 23:22:22 +10:00
parent 68e7fe0209
commit f8a06969a8
12 changed files with 852 additions and 326 deletions

View File

@ -201,12 +201,6 @@ typedef struct {
* functions during the game. All callback functions must be implemented.
*/
typedef struct {
/*
* begin_game callback - This callback has been deprecated. You must
* implement it, but should ignore the 'game' parameter.
*/
bool (__cdecl *begin_game)(void* context, const char *game);
/*
* save_game_state - The client should allocate a buffer, copy the
* entire contents of the current game state into it, and copy the
@ -340,10 +334,8 @@ typedef struct GGPONetworkStats {
*/
GGPO_API GGPOErrorCode __cdecl ggpo_start_session(GGPOSession **session,
GGPOSessionCallbacks *cb,
const char *game,
int num_players,
int input_size,
unsigned short localport,
int maxPrediction);
@ -389,7 +381,6 @@ GGPO_API GGPOErrorCode __cdecl ggpo_add_player(GGPOSession *session,
*/
GGPO_API GGPOErrorCode __cdecl ggpo_start_synctest(GGPOSession **session,
GGPOSessionCallbacks *cb,
char *game,
int num_players,
int input_size,
int frames);
@ -421,12 +412,10 @@ GGPO_API GGPOErrorCode __cdecl ggpo_start_synctest(GGPOSession **session,
*/
GGPO_API GGPOErrorCode __cdecl ggpo_start_spectating(GGPOSession **session,
GGPOSessionCallbacks *cb,
const char *game,
int num_players,
int input_size,
unsigned short local_port,
char *host_ip,
unsigned short host_port);
ENetPeer* host);
/*
* ggpo_close_session --

View File

@ -10,8 +10,6 @@
static const int RECOMMENDATION_INTERVAL = 120;
Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
const char *gamename,
uint16 localport,
int num_players,
int input_size, int nframes) :
_num_players(num_players),
@ -39,11 +37,6 @@ Peer2PeerBackend::Peer2PeerBackend(GGPOSessionCallbacks *cb,
for (int i = 0; i < ARRAY_SIZE(_local_connect_status); i++) {
_local_connect_status[i].last_frame = -1;
}
/*
* Preload the ROM
*/
_callbacks.begin_game(_callbacks.context, gamename);
}
Peer2PeerBackend::~Peer2PeerBackend()
@ -299,6 +292,11 @@ Peer2PeerBackend::AddPlayer(GGPOPlayer *player,
if (player->type == GGPO_PLAYERTYPE_REMOTE) {
AddRemotePlayer(player->u.remote.peer, queue);
}
// no other players in this session?
if (player->type == GGPO_PLAYERTYPE_LOCAL && _num_players == 1)
_synchronizing = false;
return GGPO_OK;
}

View File

@ -17,7 +17,7 @@
class Peer2PeerBackend final : public GGPOSession
{
public:
Peer2PeerBackend(GGPOSessionCallbacks *cb, const char *gamename, uint16 localport, int num_players, int input_size, int nframes);
Peer2PeerBackend(GGPOSessionCallbacks *cb, int num_players, int input_size, int nframes);
virtual ~Peer2PeerBackend();

View File

@ -7,16 +7,8 @@
#include "spectator.h"
SpectatorBackend::SpectatorBackend(GGPOSessionCallbacks *cb,
const char* gamename,
uint16 localport,
int num_players,
int input_size,
char *hostip,
u_short hostport) :
_num_players(num_players),
_input_size(input_size),
_next_input_to_send(0)
SpectatorBackend::SpectatorBackend(GGPOSessionCallbacks* cb, int num_players, int input_size, ENetPeer* peer)
: _num_players(num_players), _input_size(input_size), _next_input_to_send(0)
{
_callbacks = *cb;
_synchronizing = true;
@ -37,11 +29,6 @@ SpectatorBackend::SpectatorBackend(GGPOSessionCallbacks *cb,
*/
//_host.Init(&_udp, _poll, 0, hostip, hostport, NULL);
_host.Synchronize();
/*
* Preload the ROM
*/
_callbacks.begin_game(_callbacks.context, gamename);
}
SpectatorBackend::~SpectatorBackend()

View File

@ -18,7 +18,7 @@
class SpectatorBackend final : public GGPOSession {
public:
SpectatorBackend(GGPOSessionCallbacks *cb, const char *gamename, uint16 localport, int num_players, int input_size, char *hostip, u_short hostport);
SpectatorBackend(GGPOSessionCallbacks *cb, int num_players, int input_size, ENetPeer* peer);
virtual ~SpectatorBackend();

View File

@ -8,7 +8,6 @@
#include "synctest.h"
SyncTestBackend::SyncTestBackend(GGPOSessionCallbacks *cb,
char *gamename,
int frames,
int num_players) :
_sync(NULL, MAX_PREDICTION_FRAMES)
@ -21,7 +20,6 @@ SyncTestBackend::SyncTestBackend(GGPOSessionCallbacks *cb,
_running = false;
_logfp = NULL;
_current_input.erase();
strcpy_s(_game, gamename);
/*
* Initialize the synchronziation layer
@ -30,11 +28,6 @@ SyncTestBackend::SyncTestBackend(GGPOSessionCallbacks *cb,
config.callbacks = _callbacks;
config.num_prediction_frames = MAX_PREDICTION_FRAMES;
_sync.Init(config);
/*
* Preload the ROM
*/
_callbacks.begin_game(_callbacks.context, gamename);
}
SyncTestBackend::~SyncTestBackend()

View File

@ -15,7 +15,7 @@
class SyncTestBackend final : public GGPOSession {
public:
SyncTestBackend(GGPOSessionCallbacks *cb, char *gamename, int frames, int num_players);
SyncTestBackend(GGPOSessionCallbacks *cb, int frames, int num_players);
virtual ~SyncTestBackend();
virtual GGPOErrorCode DoPoll();
@ -52,7 +52,6 @@ protected:
bool _rollingback;
bool _running;
FILE *_logfp;
char _game[128];
GameInput _current_input;
GameInput _last_input;

View File

@ -28,6 +28,7 @@ void Log(const char *fmt, ...)
void Logv(const char *fmt, va_list args)
{
#if 1
if (!Platform::GetConfigBool("ggpo.log") || Platform::GetConfigBool("ggpo.log.ignore")) {
return;
}
@ -36,6 +37,9 @@ void Logv(const char *fmt, va_list args)
fopen_s(&logfile, logbuf, "w");
}
Logv(logfile, fmt, args);
#else
vfprintf(stderr, fmt, args);
#endif
}
void Logv(FILE *fp, const char *fmt, va_list args)

View File

@ -40,15 +40,11 @@ ggpo_logv(GGPOSession *ggpo, const char *fmt, va_list args)
GGPOErrorCode
ggpo_start_session(GGPOSession **session,
GGPOSessionCallbacks *cb,
const char *game,
int num_players,
int input_size,
unsigned short localport,
int maxPrediction)
{
*session= new Peer2PeerBackend(cb,
game,
localport,
num_players,
input_size,
maxPrediction);
@ -71,12 +67,11 @@ ggpo_add_player(GGPOSession *ggpo,
GGPOErrorCode
ggpo_start_synctest(GGPOSession **ggpo,
GGPOSessionCallbacks *cb,
char *game,
int num_players,
int input_size,
int frames)
{
*ggpo = new SyncTestBackend(cb, game, frames, num_players);
*ggpo = new SyncTestBackend(cb, frames, num_players);
return GGPO_OK;
}
@ -209,19 +204,10 @@ ggpo_set_disconnect_notify_start(GGPOSession *ggpo, int timeout)
GGPOErrorCode ggpo_start_spectating(GGPOSession **session,
GGPOSessionCallbacks *cb,
const char *game,
int num_players,
int input_size,
unsigned short local_port,
char *host_ip,
unsigned short host_port)
ENetPeer* host)
{
*session= new SpectatorBackend(cb,
game,
local_port,
num_players,
input_size,
host_ip,
host_port);
return GGPO_OK;
*session = new SpectatorBackend(cb, num_players, input_size, host);
return GGPO_OK;
}

File diff suppressed because it is too large Load Diff

View File

@ -78,8 +78,6 @@ SystemBootParameters::~SystemBootParameters() = default;
namespace System {
static std::optional<ExtendedSaveStateInfo> InternalGetExtendedSaveStateInfo(ByteStream* stream);
static bool InternalSaveState(ByteStream* state, u32 screenshot_size = 256,
u32 compression_method = SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE);
static bool LoadEXE(const char* filename);
@ -95,7 +93,6 @@ static void InternalReset();
static void ClearRunningGame();
static void DestroySystem();
static std::string GetMediaPathFromSaveState(const char* path);
static bool DoLoadState(ByteStream* stream, bool force_software_renderer, bool update_display);
static bool DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_display, bool is_memory_state);
static void WrappedRunFrame();
static void RunFramesToNow();
@ -1019,7 +1016,7 @@ bool System::LoadState(const char* filename)
SaveUndoLoadState();
if (!DoLoadState(stream.get(), false, true))
if (!LoadStateFromStream(stream.get(), true))
{
Host::ReportFormattedErrorAsync(
"Load State Error", Host::TranslateString("OSDMessage", "Loading state from '%s' failed. Resetting."), filename);
@ -1057,7 +1054,7 @@ bool System::SaveState(const char* filename, bool backup_existing_save)
Log_InfoPrintf("Saving state to '%s'...", filename);
const u32 screenshot_size = 256;
const bool result = InternalSaveState(stream.get(), screenshot_size,
const bool result = SaveStateToStream(stream.get(), screenshot_size,
g_settings.compress_save_states ? SAVE_STATE_HEADER::COMPRESSION_TYPE_ZSTD :
SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE);
if (!result)
@ -1312,7 +1309,7 @@ bool System::BootSystem(SystemBootParameters parameters)
return false;
}
if (!DoLoadState(stream.get(), false, true))
if (!LoadStateFromStream(stream.get(), true))
{
DestroySystem();
return false;
@ -1573,7 +1570,7 @@ void System::RecreateSystem()
const bool was_paused = System::IsPaused();
std::unique_ptr<ByteStream> stream = ByteStream::CreateGrowableMemoryStream(nullptr, 8 * 1024);
if (!System::InternalSaveState(stream.get(), 0, SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE) || !stream->SeekAbsolute(0))
if (!System::SaveStateToStream(stream.get(), 0, SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE) || !stream->SeekAbsolute(0))
{
Host::ReportErrorAsync("Error", "Failed to save state before system recreation. Shutting down.");
DestroySystem();
@ -1589,7 +1586,7 @@ void System::RecreateSystem()
return;
}
if (!DoLoadState(stream.get(), false, false))
if (!LoadStateFromStream(stream.get(), false))
{
DestroySystem();
return;
@ -1890,7 +1887,7 @@ std::string System::GetMediaPathFromSaveState(const char* path)
return ret;
}
bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool update_display)
bool System::LoadStateFromStream(ByteStream* state, bool update_display)
{
Assert(IsValid());
@ -2043,7 +2040,7 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u
return true;
}
bool System::InternalSaveState(ByteStream* state, u32 screenshot_size /* = 256 */,
bool System::SaveStateToStream(ByteStream* state, u32 screenshot_size /* = 256 */,
u32 compression_method /* = SAVE_STATE_HEADER::COMPRESSION_TYPE_NONE*/)
{
if (IsShutdown())
@ -3801,7 +3798,7 @@ bool System::UndoLoadState()
Assert(IsValid());
m_undo_load_state->SeekAbsolute(0);
if (!DoLoadState(m_undo_load_state.get(), false, true))
if (!LoadStateFromStream(m_undo_load_state.get(), true))
{
Host::ReportErrorAsync("Error", "Failed to load undo state, resetting system.");
m_undo_load_state.reset();
@ -3820,7 +3817,7 @@ bool System::SaveUndoLoadState()
m_undo_load_state.reset();
m_undo_load_state = ByteStream::CreateGrowableMemoryStream(nullptr, System::MAX_SAVE_STATE_SIZE);
if (!InternalSaveState(m_undo_load_state.get()))
if (!SaveStateToStream(m_undo_load_state.get()))
{
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Failed to save undo load state."), 15.0f);
m_undo_load_state.reset();

View File

@ -235,6 +235,8 @@ struct MemorySaveState
};
bool SaveMemoryState(MemorySaveState* mss);
bool LoadMemoryState(const MemorySaveState& mss);
bool LoadStateFromStream(ByteStream* stream, bool update_display);
bool SaveStateToStream(ByteStream* state, u32 screenshot_size = 256, u32 compression_method = 0);
/// Runs the VM until the CPU execution is canceled.
void Execute();