mirror of
https://github.com/libretro/RetroArch
synced 2025-02-28 22:13:51 +00:00
Netplay commands
This commit is contained in:
parent
c2d85238c2
commit
e5a3b89e6d
175
netplay.c
175
netplay.c
@ -18,6 +18,7 @@
|
||||
#pragma comment(lib, "ws2_32")
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -30,6 +31,7 @@
|
||||
#include "dynamic.h"
|
||||
#include "msg_hash.h"
|
||||
#include "system.h"
|
||||
#include "runloop.h"
|
||||
|
||||
struct delta_frame
|
||||
{
|
||||
@ -112,6 +114,19 @@ struct netplay
|
||||
* well after flip_frame before allowing another flip. */
|
||||
bool flip;
|
||||
uint32_t flip_frame;
|
||||
|
||||
/* Netplay pausing
|
||||
*/
|
||||
bool pause;
|
||||
uint32_t pause_frame;
|
||||
};
|
||||
|
||||
enum {
|
||||
CMD_OPT_ALLOWED_IN_SPECTATE_MODE = 0x1,
|
||||
CMD_OPT_REQUIRE_ACK = 0x2,
|
||||
CMD_OPT_HOST_ONLY = 0x4,
|
||||
CMD_OPT_CLIENT_ONLY = 0x8,
|
||||
CMD_OPT_REQUIRE_SYNC = 0x10,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -125,6 +140,17 @@ static void warn_hangup(void)
|
||||
rarch_main_msg_queue_push("Netplay has disconnected. Will continue without connection.", 0, 480, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* check_netplay_synched:
|
||||
* @netplay: pointer to the netplay object.
|
||||
* Checks to see if the host and client have synchronized states. Returns true
|
||||
* on success and false on failure.
|
||||
*/
|
||||
bool check_netplay_synched(netplay_t* netplay)
|
||||
{
|
||||
assert(netplay);
|
||||
return netplay->frame_count < (netplay->flip_frame + 2 * UDP_FRAME_PACKETS);
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_should_skip:
|
||||
@ -300,8 +326,39 @@ static bool netplay_get_cmd(netplay_t *netplay)
|
||||
|
||||
return netplay_cmd_ack(netplay);
|
||||
|
||||
default:
|
||||
break;
|
||||
case NETPLAY_CMD_SPECTATE:
|
||||
RARCH_ERR("NETPLAY_CMD_SPECTATE unimplemented.\n");
|
||||
return netplay_cmd_nak(netplay);
|
||||
|
||||
case NETPLAY_CMD_DISCONNECT:
|
||||
warn_hangup();
|
||||
return netplay_cmd_ack(netplay);
|
||||
|
||||
case NETPLAY_CMD_LOAD_SAVESTATE:
|
||||
RARCH_ERR("NETPLAY_CMD_LOAD_SAVESTATE unimplemented.\n");
|
||||
return netplay_cmd_nak(netplay);
|
||||
|
||||
case NETPLAY_CFG_SWAP_INPUT:
|
||||
RARCH_ERR("NETPLAY_CFG_SWAP_INPUT unimplemented.\n");
|
||||
return netplay_cmd_nak(netplay);
|
||||
|
||||
case NETPLAY_CFG_DELAY_FRAMES:
|
||||
RARCH_ERR("NETPLAY_CFG_DELAY_FRAMES unimplemented.\n");
|
||||
return netplay_cmd_nak(netplay);
|
||||
|
||||
case NETPLAY_CFG_CHEATS:
|
||||
RARCH_ERR("NETPLAY_CFG_CHEATS unimplemented.\n");
|
||||
return netplay_cmd_nak(netplay);
|
||||
|
||||
case NETPLAY_CMD_PAUSE:
|
||||
event_command(EVENT_CMD_PAUSE);
|
||||
return netplay_cmd_ack(netplay);
|
||||
|
||||
case NETPLAY_CMD_RESUME:
|
||||
event_command(EVENT_CMD_UNPAUSE);
|
||||
return netplay_cmd_ack(netplay);
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
RARCH_ERR("Unknown netplay command received.\n");
|
||||
@ -1244,7 +1301,7 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool netplay_send_cmd(netplay_t *netplay, uint32_t cmd,
|
||||
static bool netplay_send_raw_cmd(netplay_t *netplay, uint32_t cmd,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
cmd = (cmd << 16) | (size & 0xffff);
|
||||
@ -1259,6 +1316,67 @@ static bool netplay_send_cmd(netplay_t *netplay, uint32_t cmd,
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_command:
|
||||
* @netplay : pointer to netplay object
|
||||
* @cmd : command to send
|
||||
* @data : data to send as argument
|
||||
* @sz : size of data
|
||||
* @flags : flags of CMD_OPT_*
|
||||
* @command_str : name of action
|
||||
* @success_msg : message to display upon success
|
||||
*
|
||||
* Sends a single netplay command and waits for response.
|
||||
*/
|
||||
bool netplay_command(netplay_t* netplay, enum netplay_cmd cmd,
|
||||
void* data, size_t sz,
|
||||
uint32_t flags,
|
||||
const char* command_str,
|
||||
const char* success_msg)
|
||||
{
|
||||
assert(netplay);
|
||||
const char* msg = NULL;
|
||||
|
||||
bool allowed_spectate = !!(flags & CMD_OPT_ALLOWED_IN_SPECTATE_MODE);
|
||||
bool host_only = !!(flags & CMD_OPT_HOST_ONLY);
|
||||
bool require_sync = !!(flags & CMD_OPT_REQUIRE_SYNC);
|
||||
|
||||
if (netplay->spectate && !allowed_spectate)
|
||||
{
|
||||
msg = "Cannot %s in spectate mode.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (host_only && netplay->port == 0)
|
||||
{
|
||||
msg = "Cannot %s as a client.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(require_sync && check_netplay_synched(netplay))
|
||||
{
|
||||
msg = "Cannot %s while host and client are not in sync.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(netplay_send_raw_cmd(netplay, cmd, data, sz)) {
|
||||
if(netplay_get_response(netplay))
|
||||
rarch_main_msg_queue_push(success_msg, 1, 180, false);
|
||||
else
|
||||
{
|
||||
msg = "Failed to send command \"%s\"";
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
error: ;
|
||||
char m[256];
|
||||
snprintf(m, 255, msg, command_str);
|
||||
RARCH_WARN("%s\n", m);
|
||||
rarch_main_msg_queue_push(m, 1, 180, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_flip_users:
|
||||
* @netplay : pointer to netplay object
|
||||
@ -1267,51 +1385,19 @@ static bool netplay_send_cmd(netplay_t *netplay, uint32_t cmd,
|
||||
**/
|
||||
void netplay_flip_users(netplay_t *netplay)
|
||||
{
|
||||
uint32_t flip_frame = netplay->frame_count + 2 * UDP_FRAME_PACKETS;
|
||||
uint32_t flip_frame_net = htonl(flip_frame);
|
||||
const char *msg = NULL;
|
||||
|
||||
if (netplay->spectate)
|
||||
assert(netplay);
|
||||
uint32_t flip_frame_net = htonl(netplay->frame_count + 2 * UDP_FRAME_PACKETS);
|
||||
bool command = netplay_command(
|
||||
netplay, NETPLAY_CMD_FLIP_PLAYERS,
|
||||
&flip_frame_net, sizeof flip_frame_net,
|
||||
CMD_OPT_HOST_ONLY | CMD_OPT_REQUIRE_SYNC,
|
||||
"flip users", "Succesfully flipped users.\n");
|
||||
|
||||
if(command)
|
||||
{
|
||||
msg = "Cannot flip users in spectate mode.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (netplay->port == 0)
|
||||
{
|
||||
msg = "Cannot flip users if you're not the host.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Make sure both clients are definitely synced up. */
|
||||
if (netplay->frame_count < (netplay->flip_frame + 2 * UDP_FRAME_PACKETS))
|
||||
{
|
||||
msg = "Cannot flip users yet. Wait a second or two before attempting flip.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (netplay_send_cmd(netplay, NETPLAY_CMD_FLIP_PLAYERS,
|
||||
&flip_frame_net, sizeof(flip_frame_net))
|
||||
&& netplay_get_response(netplay))
|
||||
{
|
||||
RARCH_LOG("Netplay users are flipped.\n");
|
||||
rarch_main_msg_queue_push("Netplay users are flipped.", 1, 180, false);
|
||||
|
||||
/* Queue up a flip well enough in the future. */
|
||||
netplay->flip ^= true;
|
||||
netplay->flip_frame = flip_frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = "Failed to flip users.";
|
||||
goto error;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
RARCH_WARN("%s\n", msg);
|
||||
rarch_main_msg_queue_push(msg, 1, 180, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1764,5 +1850,4 @@ error:
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
23
netplay.h
23
netplay.h
@ -28,11 +28,21 @@ typedef struct netplay netplay_t;
|
||||
|
||||
enum netplay_cmd
|
||||
{
|
||||
NETPLAY_CMD_ACK = 0x0000, /**< Acknowlegement response */
|
||||
NETPLAY_CMD_NAK = 0x0001, /**< Failed acknowlegement response */
|
||||
NETPLAY_CMD_FLIP_PLAYERS = 0x0002, /**< Swap inputs between p1 and p2. */
|
||||
NETPLAY_CMD_SPECTATE = 0x0003, /**< Toggle spectate/join mode [PLANNED] */
|
||||
NETPLAY_CMD_LOAD_SAVESTATE = 0x0004 /**< Load a save state between players [PLANNED] */
|
||||
/* misc. commands */
|
||||
NETPLAY_CMD_ACK = 0x0000, /**< Acknowlegement response */
|
||||
NETPLAY_CMD_NAK = 0x0001, /**< Failed acknowlegement response */
|
||||
NETPLAY_CMD_FLIP_PLAYERS = 0x0002, /**< Swap inputs between p1 and p2. */
|
||||
NETPLAY_CMD_SPECTATE = 0x0003, /**< Toggle spectate/join mode */
|
||||
NETPLAY_CMD_DISCONNECT = 0x0004, /**< Gracefully disconnects from host. */
|
||||
/* loading and synchronization */
|
||||
NETPLAY_CMD_LOAD_SAVESTATE = 0x0012, /** Send a savestate for the client to load */
|
||||
/* configuration synchronization */
|
||||
NETPLAY_CFG_SWAP_INPUT = 0x0020, /**< input.netplay_client_swap_input */
|
||||
NETPLAY_CFG_DELAY_FRAMES = 0x0021, /**< netplay.sync_frames */
|
||||
NETPLAY_CFG_CHEATS = 0x0022, /**< Sends the cheats enabled on the host. */
|
||||
/* controlling game playback */
|
||||
NETPLAY_CMD_PAUSE = 0x0030, /**< Pauses the game, takes no args. */
|
||||
NETPLAY_CMD_RESUME = 0x0031, /**< Resumes the game, takes no args. */
|
||||
};
|
||||
|
||||
void input_poll_net(void);
|
||||
@ -120,5 +130,4 @@ bool init_netplay(void);
|
||||
|
||||
void deinit_netplay(void);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user