mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-25 17:10:12 +00:00
1035 lines
34 KiB
C
1035 lines
34 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
* Copyright (C) 2016-2017 - Gregor Richards
|
|
*
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <boolean.h>
|
|
|
|
#include "netplay_private.h"
|
|
|
|
#include "../../autosave.h"
|
|
#include "../../driver.h"
|
|
#include "../../input/input_driver.h"
|
|
|
|
#if 0
|
|
#define DEBUG_NONDETERMINISTIC_CORES
|
|
#endif
|
|
|
|
/**
|
|
* netplay_update_unread_ptr
|
|
*
|
|
* Update the global unread_ptr and unread_frame_count to correspond to the
|
|
* earliest unread frame count of any connected player
|
|
*/
|
|
void netplay_update_unread_ptr(netplay_t *netplay)
|
|
{
|
|
if (netplay->is_server && netplay->connected_players<=1)
|
|
{
|
|
/* Nothing at all to read! */
|
|
netplay->unread_ptr = netplay->self_ptr;
|
|
netplay->unread_frame_count = netplay->self_frame_count;
|
|
|
|
}
|
|
else
|
|
{
|
|
size_t new_unread_ptr = 0;
|
|
uint32_t new_unread_frame_count = (uint32_t) -1;
|
|
uint32_t client;
|
|
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
|
{
|
|
if (!(netplay->connected_players & (1<<client))) continue;
|
|
if ((netplay->connected_slaves & (1<<client))) continue;
|
|
if (netplay->read_frame_count[client] < new_unread_frame_count)
|
|
{
|
|
new_unread_ptr = netplay->read_ptr[client];
|
|
new_unread_frame_count = netplay->read_frame_count[client];
|
|
}
|
|
}
|
|
|
|
if (!netplay->is_server && netplay->server_frame_count < new_unread_frame_count)
|
|
{
|
|
new_unread_ptr = netplay->server_ptr;
|
|
new_unread_frame_count = netplay->server_frame_count;
|
|
}
|
|
|
|
if (new_unread_frame_count != (uint32_t) -1)
|
|
{
|
|
netplay->unread_ptr = new_unread_ptr;
|
|
netplay->unread_frame_count = new_unread_frame_count;
|
|
}
|
|
else
|
|
{
|
|
netplay->unread_ptr = netplay->self_ptr;
|
|
netplay->unread_frame_count = netplay->self_frame_count;
|
|
}
|
|
}
|
|
}
|
|
|
|
struct vote_count {
|
|
uint16_t votes[32];
|
|
};
|
|
|
|
/**
|
|
* netplay_device_client_state
|
|
* @netplay : pointer to netplay object
|
|
* @simframe : frame in which merging is being performed
|
|
* @device : device being merged
|
|
* @client : client to find state for
|
|
*/
|
|
netplay_input_state_t netplay_device_client_state(netplay_t *netplay,
|
|
struct delta_frame *simframe, uint32_t device, uint32_t client)
|
|
{
|
|
uint32_t dsize = netplay_expected_input_size(netplay, 1 << device);
|
|
netplay_input_state_t simstate =
|
|
netplay_input_state_for(
|
|
&simframe->real_input[device], client,
|
|
dsize, false, true);
|
|
if (!simstate)
|
|
{
|
|
if (netplay->read_frame_count[client] > simframe->frame)
|
|
return NULL;
|
|
simstate = netplay_input_state_for(&simframe->simlated_input[device],
|
|
client, dsize, false, true);
|
|
}
|
|
return simstate;
|
|
}
|
|
|
|
/**
|
|
* netplay_merge_digital
|
|
* @netplay : pointer to netplay object
|
|
* @resstate : state being resolved
|
|
* @simframe : frame in which merging is being performed
|
|
* @device : device being merged
|
|
* @clients : bitmap of clients being merged
|
|
* @digital : bitmap of digital bits
|
|
*/
|
|
static void netplay_merge_digital(netplay_t *netplay,
|
|
netplay_input_state_t resstate, struct delta_frame *simframe,
|
|
uint32_t device, uint32_t clients, const uint32_t *digital)
|
|
{
|
|
netplay_input_state_t simstate;
|
|
uint32_t word, bit, client;
|
|
uint8_t share_mode = netplay->device_share_modes[device]
|
|
& NETPLAY_SHARE_DIGITAL_BITS;
|
|
|
|
/* Make sure all real clients are accounted for */
|
|
for (simstate = simframe->real_input[device];
|
|
simstate; simstate = simstate->next)
|
|
{
|
|
if (!simstate->used || simstate->size != resstate->size)
|
|
continue;
|
|
clients |= 1<<simstate->client_num;
|
|
}
|
|
|
|
if (share_mode == NETPLAY_SHARE_DIGITAL_VOTE)
|
|
{
|
|
unsigned i, j;
|
|
/* This just assumes we have no more than
|
|
* three words, will need to be adjusted for new devices */
|
|
struct vote_count votes[3];
|
|
/* Vote mode requires counting all the bits */
|
|
uint32_t client_count = 0;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
for (j = 0; j < 32; j++)
|
|
votes[i].votes[j] = 0;
|
|
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
|
{
|
|
if (!(clients & (1<<client)))
|
|
continue;
|
|
|
|
simstate = netplay_device_client_state(
|
|
netplay, simframe, device, client);
|
|
|
|
if (!simstate)
|
|
continue;
|
|
client_count++;
|
|
|
|
for (word = 0; word < resstate->size; word++)
|
|
{
|
|
if (!digital[word])
|
|
continue;
|
|
for (bit = 0; bit < 32; bit++)
|
|
{
|
|
if (!(digital[word] & (1<<bit)))
|
|
continue;
|
|
if (simstate->data[word] & (1<<bit))
|
|
votes[word].votes[bit]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now count all the bits */
|
|
client_count /= 2;
|
|
for (word = 0; word < resstate->size; word++)
|
|
{
|
|
for (bit = 0; bit < 32; bit++)
|
|
{
|
|
if (votes[word].votes[bit] > client_count)
|
|
resstate->data[word] |= (1<<bit);
|
|
}
|
|
}
|
|
}
|
|
else /* !VOTE */
|
|
{
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
|
{
|
|
if (!(clients & (1<<client)))
|
|
continue;
|
|
simstate = netplay_device_client_state(
|
|
netplay, simframe, device, client);
|
|
|
|
if (!simstate)
|
|
continue;
|
|
for (word = 0; word < resstate->size; word++)
|
|
{
|
|
uint32_t part;
|
|
if (!digital[word])
|
|
continue;
|
|
part = simstate->data[word];
|
|
|
|
if (digital[word] == (uint32_t) -1)
|
|
{
|
|
/* Combine the whole word */
|
|
switch (share_mode)
|
|
{
|
|
case NETPLAY_SHARE_DIGITAL_XOR:
|
|
resstate->data[word] ^= part;
|
|
break;
|
|
default:
|
|
resstate->data[word] |= part;
|
|
}
|
|
|
|
}
|
|
else /* !whole word */
|
|
{
|
|
for (bit = 0; bit < 32; bit++)
|
|
{
|
|
if (!(digital[word] & (1<<bit)))
|
|
continue;
|
|
switch (share_mode)
|
|
{
|
|
case NETPLAY_SHARE_DIGITAL_XOR:
|
|
resstate->data[word] ^= part & (1<<bit);
|
|
break;
|
|
default:
|
|
resstate->data[word] |= part & (1<<bit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* merge_analog_part
|
|
* @netplay : pointer to netplay object
|
|
* @resstate : state being resolved
|
|
* @simframe : frame in which merging is being performed
|
|
* @device : device being merged
|
|
* @clients : bitmap of clients being merged
|
|
* @word : word to merge
|
|
* @bit : first bit to merge
|
|
*/
|
|
static void merge_analog_part(netplay_t *netplay,
|
|
netplay_input_state_t resstate, struct delta_frame *simframe,
|
|
uint32_t device, uint32_t clients, uint32_t word, uint8_t bit)
|
|
{
|
|
netplay_input_state_t simstate;
|
|
uint32_t client, client_count = 0;
|
|
uint8_t share_mode = netplay->device_share_modes[device]
|
|
& NETPLAY_SHARE_ANALOG_BITS;
|
|
int32_t value = 0, new_value;
|
|
|
|
/* Make sure all real clients are accounted for */
|
|
for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next)
|
|
{
|
|
if (!simstate->used || simstate->size != resstate->size)
|
|
continue;
|
|
clients |= 1<<simstate->client_num;
|
|
}
|
|
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
|
{
|
|
if (!(clients & (1<<client)))
|
|
continue;
|
|
simstate = netplay_device_client_state(
|
|
netplay, simframe, device, client);
|
|
if (!simstate)
|
|
continue;
|
|
client_count++;
|
|
new_value = (int16_t) ((simstate->data[word]>>bit) & 0xFFFF);
|
|
switch (share_mode)
|
|
{
|
|
case NETPLAY_SHARE_ANALOG_AVERAGE:
|
|
value += (int32_t) new_value;
|
|
break;
|
|
default:
|
|
if (abs(new_value) > abs(value) ||
|
|
(abs(new_value) == abs(value) && new_value > value))
|
|
value = new_value;
|
|
}
|
|
}
|
|
|
|
if (share_mode == NETPLAY_SHARE_ANALOG_AVERAGE)
|
|
if (client_count > 0) /* Prevent potential divide by zero */
|
|
value /= client_count;
|
|
|
|
resstate->data[word] |= ((uint32_t) (uint16_t) value) << bit;
|
|
}
|
|
|
|
/**
|
|
* netplay_merge_analog
|
|
* @netplay : pointer to netplay object
|
|
* @resstate : state being resolved
|
|
* @simframe : frame in which merging is being performed
|
|
* @device : device being merged
|
|
* @clients : bitmap of clients being merged
|
|
* @dtype : device type
|
|
*/
|
|
static void netplay_merge_analog(netplay_t *netplay,
|
|
netplay_input_state_t resstate, struct delta_frame *simframe,
|
|
uint32_t device, uint32_t clients, unsigned dtype)
|
|
{
|
|
/* Devices with no analog parts */
|
|
if (dtype == RETRO_DEVICE_JOYPAD || dtype == RETRO_DEVICE_KEYBOARD)
|
|
return;
|
|
|
|
/* All other devices have at least one analog word */
|
|
merge_analog_part(netplay, resstate, simframe, device, clients, 1, 0);
|
|
merge_analog_part(netplay, resstate, simframe, device, clients, 1, 16);
|
|
|
|
/* And the ANALOG device has two (two sticks) */
|
|
if (dtype == RETRO_DEVICE_ANALOG)
|
|
{
|
|
merge_analog_part(netplay, resstate, simframe, device, clients, 2, 0);
|
|
merge_analog_part(netplay, resstate, simframe, device, clients, 2, 16);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* netplay_resolve_input
|
|
* @netplay : pointer to netplay object
|
|
* @sim_ptr : frame pointer for which to resolve input
|
|
* @resim : are we resimulating, or simulating this frame for the
|
|
* first time?
|
|
*
|
|
* "Simulate" input by assuming it hasn't changed since the last read input.
|
|
* Returns true if the resolved input changed from the last time it was
|
|
* resolved.
|
|
*/
|
|
bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim)
|
|
{
|
|
size_t prev;
|
|
uint32_t device;
|
|
uint32_t clients, client, client_count;
|
|
netplay_input_state_t simstate, client_state = NULL,
|
|
resstate, oldresstate, pstate;
|
|
bool ret = false;
|
|
struct delta_frame *pframe = NULL;
|
|
struct delta_frame *simframe = &netplay->buffer[sim_ptr];
|
|
|
|
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
|
{
|
|
unsigned dtype = netplay->config_devices[device]&RETRO_DEVICE_MASK;
|
|
uint32_t dsize = netplay_expected_input_size(netplay, 1 << device);
|
|
clients = netplay->device_clients[device];
|
|
client_count = 0;
|
|
|
|
/* Make sure all real clients are accounted for */
|
|
for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next)
|
|
{
|
|
if (!simstate->used || simstate->size != dsize)
|
|
continue;
|
|
clients |= 1<<simstate->client_num;
|
|
}
|
|
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
|
{
|
|
if (!(clients & (1<<client)))
|
|
continue;
|
|
|
|
/* Resolve this client-device */
|
|
simstate = netplay_input_state_for(
|
|
&simframe->real_input[device], client, dsize, false, true);
|
|
if (!simstate)
|
|
{
|
|
/* Don't already have this input, so must
|
|
* simulate if we're supposed to have it at all */
|
|
if (netplay->read_frame_count[client] > simframe->frame)
|
|
continue;
|
|
simstate = netplay_input_state_for(&simframe->simlated_input[device], client, dsize, false, false);
|
|
if (!simstate)
|
|
continue;
|
|
|
|
prev = PREV_PTR(netplay->read_ptr[client]);
|
|
pframe = &netplay->buffer[prev];
|
|
pstate = netplay_input_state_for(&pframe->real_input[device], client, dsize, false, true);
|
|
if (!pstate)
|
|
continue;
|
|
|
|
if (resim && (dtype == RETRO_DEVICE_JOYPAD || dtype == RETRO_DEVICE_ANALOG))
|
|
{
|
|
/* In resimulation mode, we only copy the buttons. The reason for this
|
|
* is nonobvious:
|
|
*
|
|
* If we resimulated nothing, then the /duration/ with which any input
|
|
* was pressed would be approximately correct, since the original
|
|
* simulation came in as the input came in, but the /number of times/
|
|
* the input was pressed would be wrong, as there would be an
|
|
* advancing wavefront of real data overtaking the simulated data
|
|
* (which is really just real data offset by some frames).
|
|
*
|
|
* That's acceptable for arrows in most situations, since the amount
|
|
* you move is tied to the duration, but unacceptable for buttons,
|
|
* which will seem to jerkily be pressed numerous times with those
|
|
* wavefronts.
|
|
*/
|
|
const uint32_t keep =
|
|
(1U<<RETRO_DEVICE_ID_JOYPAD_UP) |
|
|
(1U<<RETRO_DEVICE_ID_JOYPAD_DOWN) |
|
|
(1U<<RETRO_DEVICE_ID_JOYPAD_LEFT) |
|
|
(1U<<RETRO_DEVICE_ID_JOYPAD_RIGHT);
|
|
simstate->data[0] &= keep;
|
|
simstate->data[0] |= pstate->data[0] & ~keep;
|
|
}
|
|
else
|
|
memcpy(simstate->data, pstate->data,
|
|
dsize * sizeof(uint32_t));
|
|
}
|
|
|
|
client_state = simstate;
|
|
client_count++;
|
|
}
|
|
|
|
/* The frontend always uses the first resolved input,
|
|
* so make sure it's right */
|
|
while (simframe->resolved_input[device]
|
|
&& (simframe->resolved_input[device]->size != dsize
|
|
|| simframe->resolved_input[device]->client_num != 0))
|
|
{
|
|
/* The default resolved input is of the wrong size! */
|
|
netplay_input_state_t nextistate =
|
|
simframe->resolved_input[device]->next;
|
|
free(simframe->resolved_input[device]);
|
|
simframe->resolved_input[device] = nextistate;
|
|
}
|
|
|
|
/* Now we copy the state, whether real or simulated,
|
|
* out into the resolved state */
|
|
resstate = netplay_input_state_for(
|
|
&simframe->resolved_input[device], 0,
|
|
dsize, false, false);
|
|
if (!resstate)
|
|
continue;
|
|
|
|
if (client_count == 1)
|
|
{
|
|
/* Trivial in the common 1-client case */
|
|
if (memcmp(resstate->data, client_state->data,
|
|
dsize * sizeof(uint32_t)))
|
|
ret = true;
|
|
memcpy(resstate->data, client_state->data,
|
|
dsize * sizeof(uint32_t));
|
|
|
|
}
|
|
else if (client_count == 0)
|
|
{
|
|
uint32_t word;
|
|
for (word = 0; word < dsize; word++)
|
|
{
|
|
if (resstate->data[word])
|
|
ret = true;
|
|
resstate->data[word] = 0;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
/* Merge them */
|
|
/* Most devices have all the digital parts in the first word. */
|
|
static const uint32_t digital_common[3] = {~0u, 0u, 0u};
|
|
static const uint32_t digital_keyboard[5] = {~0u, ~0u, ~0u, ~0u, ~0u};
|
|
const uint32_t *digital;
|
|
if (dtype == RETRO_DEVICE_KEYBOARD)
|
|
digital = digital_keyboard;
|
|
else
|
|
digital = digital_common;
|
|
oldresstate = netplay_input_state_for(
|
|
&simframe->resolved_input[device], 1, dsize, false, false);
|
|
if (!oldresstate)
|
|
continue;
|
|
memcpy(oldresstate->data, resstate->data, dsize * sizeof(uint32_t));
|
|
memset(resstate->data, 0, dsize * sizeof(uint32_t));
|
|
|
|
netplay_merge_digital(netplay, resstate, simframe,
|
|
device, clients, digital);
|
|
netplay_merge_analog(netplay, resstate, simframe,
|
|
device, clients, dtype);
|
|
|
|
if (memcmp(resstate->data, oldresstate->data,
|
|
dsize * sizeof(uint32_t)))
|
|
ret = true;
|
|
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void netplay_handle_frame_hash(netplay_t *netplay,
|
|
struct delta_frame *delta)
|
|
{
|
|
if (netplay->is_server)
|
|
{
|
|
if (netplay->check_frames &&
|
|
delta->frame % abs(netplay->check_frames) == 0)
|
|
{
|
|
delta->crc = netplay_delta_frame_crc(netplay, delta);
|
|
netplay_cmd_crc(netplay, delta);
|
|
}
|
|
}
|
|
else if (delta->crc && netplay->crcs_valid)
|
|
{
|
|
/* We have a remote CRC, so check it */
|
|
uint32_t local_crc = netplay_delta_frame_crc(netplay, delta);
|
|
if (local_crc != delta->crc)
|
|
{
|
|
/* If the very first check frame is wrong,
|
|
* they probably just don't work */
|
|
if (!netplay->crc_validity_checked)
|
|
netplay->crcs_valid = false;
|
|
else if (netplay->crcs_valid)
|
|
{
|
|
/* Fix this! */
|
|
if (netplay->check_frames < 0)
|
|
{
|
|
/* Just report */
|
|
RARCH_ERR("Netplay CRCs mismatch!\n");
|
|
}
|
|
else
|
|
netplay_cmd_request_savestate(netplay);
|
|
}
|
|
}
|
|
else if (!netplay->crc_validity_checked)
|
|
netplay->crc_validity_checked = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* netplay_sync_pre_frame
|
|
* @netplay : pointer to netplay object
|
|
*
|
|
* Pre-frame for Netplay synchronization.
|
|
*/
|
|
bool netplay_sync_pre_frame(netplay_t *netplay)
|
|
{
|
|
retro_ctx_serialize_info_t serial_info;
|
|
|
|
if (netplay_delta_frame_ready(netplay,
|
|
&netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
|
|
{
|
|
serial_info.data_const = NULL;
|
|
serial_info.data = netplay->buffer[netplay->run_ptr].state;
|
|
serial_info.size = netplay->state_size;
|
|
|
|
memset(serial_info.data, 0, serial_info.size);
|
|
if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
|
|| netplay->run_frame_count == 0)
|
|
{
|
|
/* Don't serialize until it's safe */
|
|
}
|
|
else if (!(netplay->quirks & NETPLAY_QUIRK_NO_SAVESTATES)
|
|
&& core_serialize(&serial_info))
|
|
{
|
|
if (netplay->force_send_savestate && !netplay->stall
|
|
&& !netplay->remote_paused)
|
|
{
|
|
/* Bring our running frame and input frames into
|
|
* parity so we don't send old info. */
|
|
if (netplay->run_ptr != netplay->self_ptr)
|
|
{
|
|
memcpy(netplay->buffer[netplay->self_ptr].state,
|
|
netplay->buffer[netplay->run_ptr].state,
|
|
netplay->state_size);
|
|
netplay->run_ptr = netplay->self_ptr;
|
|
netplay->run_frame_count = netplay->self_frame_count;
|
|
}
|
|
|
|
/* Send this along to the other side */
|
|
serial_info.data_const = netplay->buffer[netplay->run_ptr].state;
|
|
netplay_load_savestate(netplay, &serial_info, false);
|
|
netplay->force_send_savestate = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* If the core can't serialize properly, we must stall for the
|
|
* remote input on EVERY frame, because we can't recover */
|
|
netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES;
|
|
netplay->stateless_mode = true;
|
|
}
|
|
|
|
/* If we can't transmit savestates, we must stall
|
|
* until the client is ready. */
|
|
if (netplay->run_frame_count > 0 &&
|
|
(netplay->quirks & (NETPLAY_QUIRK_NO_SAVESTATES|NETPLAY_QUIRK_NO_TRANSMISSION)) &&
|
|
(netplay->connections_size == 0 || !netplay->connections[0].active ||
|
|
netplay->connections[0].mode < NETPLAY_CONNECTION_CONNECTED))
|
|
netplay->stall = NETPLAY_STALL_NO_CONNECTION;
|
|
}
|
|
|
|
if (netplay->is_server)
|
|
{
|
|
fd_set fds;
|
|
struct timeval tmp_tv = {0};
|
|
int new_fd;
|
|
struct sockaddr_storage their_addr;
|
|
socklen_t addr_size;
|
|
struct netplay_connection *connection;
|
|
size_t connection_num;
|
|
|
|
/* Check for a connection */
|
|
FD_ZERO(&fds);
|
|
FD_SET(netplay->listen_fd, &fds);
|
|
if (socket_select(netplay->listen_fd + 1,
|
|
&fds, NULL, NULL, &tmp_tv) > 0 &&
|
|
FD_ISSET(netplay->listen_fd, &fds))
|
|
{
|
|
addr_size = sizeof(their_addr);
|
|
new_fd = accept(netplay->listen_fd,
|
|
(struct sockaddr*)&their_addr, &addr_size);
|
|
|
|
if (new_fd < 0)
|
|
{
|
|
RARCH_ERR("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED));
|
|
goto process;
|
|
}
|
|
|
|
/* Set the socket nonblocking */
|
|
if (!socket_nonblock(new_fd))
|
|
{
|
|
/* Catastrophe! */
|
|
socket_close(new_fd);
|
|
goto process;
|
|
}
|
|
|
|
#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
|
|
{
|
|
int flag = 1;
|
|
if (setsockopt(new_fd, IPPROTO_TCP, TCP_NODELAY,
|
|
#ifdef _WIN32
|
|
(const char*)
|
|
#else
|
|
(const void*)
|
|
#endif
|
|
&flag,
|
|
sizeof(int)) < 0)
|
|
RARCH_WARN("Could not set netplay TCP socket to nodelay. Expect jitter.\n");
|
|
}
|
|
#endif
|
|
|
|
#if defined(F_SETFD) && defined(FD_CLOEXEC)
|
|
/* Don't let any inherited processes keep open our port */
|
|
if (fcntl(new_fd, F_SETFD, FD_CLOEXEC) < 0)
|
|
RARCH_WARN("Cannot set Netplay port to close-on-exec. It may fail to reopen if the client disconnects.\n");
|
|
#endif
|
|
|
|
/* Allocate a connection */
|
|
for (connection_num = 0; connection_num < netplay->connections_size; connection_num++)
|
|
if (!netplay->connections[connection_num].active &&
|
|
netplay->connections[connection_num].mode != NETPLAY_CONNECTION_DELAYED_DISCONNECT) break;
|
|
if (connection_num == netplay->connections_size)
|
|
{
|
|
if (connection_num == 0)
|
|
{
|
|
netplay->connections = (struct netplay_connection*)
|
|
malloc(sizeof(struct netplay_connection));
|
|
|
|
if (!netplay->connections)
|
|
{
|
|
socket_close(new_fd);
|
|
goto process;
|
|
}
|
|
netplay->connections_size = 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
size_t new_connections_size = netplay->connections_size * 2;
|
|
struct netplay_connection
|
|
*new_connections = (struct netplay_connection*)
|
|
|
|
realloc(netplay->connections,
|
|
new_connections_size*sizeof(struct netplay_connection));
|
|
|
|
if (!new_connections)
|
|
{
|
|
socket_close(new_fd);
|
|
goto process;
|
|
}
|
|
|
|
memset(new_connections + netplay->connections_size, 0,
|
|
netplay->connections_size * sizeof(struct netplay_connection));
|
|
netplay->connections = new_connections;
|
|
netplay->connections_size = new_connections_size;
|
|
|
|
}
|
|
}
|
|
connection = &netplay->connections[connection_num];
|
|
|
|
/* Set it up */
|
|
memset(connection, 0, sizeof(*connection));
|
|
connection->active = true;
|
|
connection->fd = new_fd;
|
|
connection->mode = NETPLAY_CONNECTION_INIT;
|
|
|
|
if (!netplay_init_socket_buffer(&connection->send_packet_buffer,
|
|
netplay->packet_buffer_size) ||
|
|
!netplay_init_socket_buffer(&connection->recv_packet_buffer,
|
|
netplay->packet_buffer_size))
|
|
{
|
|
if (connection->send_packet_buffer.data)
|
|
netplay_deinit_socket_buffer(&connection->send_packet_buffer);
|
|
connection->active = false;
|
|
socket_close(new_fd);
|
|
goto process;
|
|
}
|
|
|
|
netplay_handshake_init_send(netplay, connection);
|
|
|
|
}
|
|
}
|
|
|
|
process:
|
|
netplay->can_poll = true;
|
|
input_poll_net();
|
|
|
|
return (netplay->stall != NETPLAY_STALL_NO_CONNECTION);
|
|
}
|
|
|
|
/**
|
|
* netplay_sync_post_frame
|
|
* @netplay : pointer to netplay object
|
|
*
|
|
* Post-frame for Netplay synchronization.
|
|
* We check if we have new input and replay from recorded input.
|
|
*/
|
|
void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
|
{
|
|
uint32_t lo_frame_count, hi_frame_count;
|
|
|
|
/* Unless we're stalling, we've just finished running a frame */
|
|
if (!stalled)
|
|
{
|
|
netplay->run_ptr = NEXT_PTR(netplay->run_ptr);
|
|
netplay->run_frame_count++;
|
|
}
|
|
|
|
/* We've finished an input frame even if we're stalling */
|
|
if ((!stalled || netplay->stall == NETPLAY_STALL_INPUT_LATENCY) &&
|
|
netplay->self_frame_count <
|
|
netplay->run_frame_count + netplay->input_latency_frames)
|
|
{
|
|
netplay->self_ptr = NEXT_PTR(netplay->self_ptr);
|
|
netplay->self_frame_count++;
|
|
}
|
|
|
|
/* Only relevant if we're connected and not in a desynching operation */
|
|
if ((netplay->is_server && (netplay->connected_players<=1)) ||
|
|
(netplay->self_mode < NETPLAY_CONNECTION_CONNECTED) ||
|
|
(netplay->desync))
|
|
{
|
|
netplay->other_frame_count = netplay->self_frame_count;
|
|
netplay->other_ptr = netplay->self_ptr;
|
|
/* FIXME: Duplication */
|
|
if (netplay->catch_up)
|
|
{
|
|
netplay->catch_up = false;
|
|
input_driver_unset_nonblock_state();
|
|
driver_set_nonblock_state();
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Reset if it was requested */
|
|
if (netplay->force_reset)
|
|
{
|
|
core_reset();
|
|
netplay->force_reset = false;
|
|
}
|
|
|
|
netplay->replay_ptr = netplay->other_ptr;
|
|
netplay->replay_frame_count = netplay->other_frame_count;
|
|
|
|
#ifndef DEBUG_NONDETERMINISTIC_CORES
|
|
if (!netplay->force_rewind)
|
|
{
|
|
bool cont = true;
|
|
|
|
/* Skip ahead if we predicted correctly.
|
|
* Skip until our simulation failed. */
|
|
while (netplay->other_frame_count < netplay->unread_frame_count &&
|
|
netplay->other_frame_count < netplay->run_frame_count)
|
|
{
|
|
struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr];
|
|
|
|
/* If resolving the input changes it, we used bad input */
|
|
if (netplay_resolve_input(netplay, netplay->other_ptr, true))
|
|
{
|
|
cont = false;
|
|
break;
|
|
}
|
|
|
|
netplay_handle_frame_hash(netplay, ptr);
|
|
netplay->other_ptr = NEXT_PTR(netplay->other_ptr);
|
|
netplay->other_frame_count++;
|
|
}
|
|
netplay->replay_ptr = netplay->other_ptr;
|
|
netplay->replay_frame_count = netplay->other_frame_count;
|
|
|
|
if (cont)
|
|
{
|
|
while (netplay->replay_frame_count < netplay->run_frame_count)
|
|
{
|
|
if (netplay_resolve_input(netplay, netplay->replay_ptr, true))
|
|
break;
|
|
netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr);
|
|
netplay->replay_frame_count++;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Now replay the real input if we've gotten ahead of it */
|
|
if (netplay->force_rewind ||
|
|
netplay->replay_frame_count < netplay->run_frame_count)
|
|
{
|
|
retro_ctx_serialize_info_t serial_info;
|
|
|
|
/* Replay frames. */
|
|
netplay->is_replay = true;
|
|
|
|
/* If we have a keyboard device, we replay the previous frame's input
|
|
* just to assert that the keydown/keyup events work if the core
|
|
* translates them in that way */
|
|
if (netplay->have_updown_device)
|
|
{
|
|
netplay->replay_ptr = PREV_PTR(netplay->replay_ptr);
|
|
netplay->replay_frame_count--;
|
|
autosave_lock();
|
|
core_run();
|
|
autosave_unlock();
|
|
netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr);
|
|
netplay->replay_frame_count++;
|
|
}
|
|
|
|
if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
|
/* Make sure we're initialized before we start loading things */
|
|
netplay_wait_and_init_serialization(netplay);
|
|
|
|
serial_info.data = NULL;
|
|
serial_info.data_const = netplay->buffer[netplay->replay_ptr].state;
|
|
serial_info.size = netplay->state_size;
|
|
|
|
if (!core_unserialize(&serial_info))
|
|
{
|
|
RARCH_ERR("Netplay savestate loading failed: Prepare for desync!\n");
|
|
}
|
|
|
|
while (netplay->replay_frame_count < netplay->run_frame_count)
|
|
{
|
|
retro_time_t start, tm;
|
|
|
|
struct delta_frame *ptr = &netplay->buffer[netplay->replay_ptr];
|
|
serial_info.data = ptr->state;
|
|
serial_info.size = netplay->state_size;
|
|
serial_info.data_const = NULL;
|
|
|
|
start = cpu_features_get_time_usec();
|
|
|
|
/* Remember the current state */
|
|
memset(serial_info.data, 0, serial_info.size);
|
|
core_serialize(&serial_info);
|
|
if (netplay->replay_frame_count < netplay->unread_frame_count)
|
|
netplay_handle_frame_hash(netplay, ptr);
|
|
|
|
/* Re-simulate this frame's input */
|
|
netplay_resolve_input(netplay, netplay->replay_ptr, true);
|
|
|
|
autosave_lock();
|
|
core_run();
|
|
autosave_unlock();
|
|
netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr);
|
|
netplay->replay_frame_count++;
|
|
|
|
#ifdef DEBUG_NONDETERMINISTIC_CORES
|
|
if (ptr->have_remote && netplay_delta_frame_ready(netplay, &netplay->buffer[netplay->replay_ptr], netplay->replay_frame_count))
|
|
{
|
|
RARCH_LOG("PRE %u: %X\n", netplay->replay_frame_count-1, netplay_delta_frame_crc(netplay, ptr));
|
|
if (netplay->is_server)
|
|
RARCH_LOG("INP %X %X\n", ptr->real_input_state[0], ptr->self_state[0]);
|
|
else
|
|
RARCH_LOG("INP %X %X\n", ptr->self_state[0], ptr->real_input_state[0]);
|
|
ptr = &netplay->buffer[netplay->replay_ptr];
|
|
serial_info.data = ptr->state;
|
|
memset(serial_info.data, 0, serial_info.size);
|
|
core_serialize(&serial_info);
|
|
RARCH_LOG("POST %u: %X\n", netplay->replay_frame_count-1, netplay_delta_frame_crc(netplay, ptr));
|
|
}
|
|
#endif
|
|
|
|
/* Get our time window */
|
|
tm = cpu_features_get_time_usec() - start;
|
|
netplay->frame_run_time_sum -= netplay->frame_run_time[netplay->frame_run_time_ptr];
|
|
netplay->frame_run_time[netplay->frame_run_time_ptr] = tm;
|
|
netplay->frame_run_time_sum += tm;
|
|
netplay->frame_run_time_ptr++;
|
|
if (netplay->frame_run_time_ptr >= NETPLAY_FRAME_RUN_TIME_WINDOW)
|
|
netplay->frame_run_time_ptr = 0;
|
|
}
|
|
|
|
/* Average our time */
|
|
netplay->frame_run_time_avg = netplay->frame_run_time_sum / NETPLAY_FRAME_RUN_TIME_WINDOW;
|
|
|
|
if (netplay->unread_frame_count < netplay->run_frame_count)
|
|
{
|
|
netplay->other_ptr = netplay->unread_ptr;
|
|
netplay->other_frame_count = netplay->unread_frame_count;
|
|
}
|
|
else
|
|
{
|
|
netplay->other_ptr = netplay->run_ptr;
|
|
netplay->other_frame_count = netplay->run_frame_count;
|
|
}
|
|
netplay->is_replay = false;
|
|
netplay->force_rewind = false;
|
|
}
|
|
|
|
if (netplay->is_server)
|
|
{
|
|
uint32_t client;
|
|
|
|
lo_frame_count = hi_frame_count = netplay->unread_frame_count;
|
|
|
|
/* Look for players that are ahead of us */
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
|
{
|
|
if (!(netplay->connected_players & (1<<client))) continue;
|
|
if (netplay->read_frame_count[client] > hi_frame_count)
|
|
hi_frame_count = netplay->read_frame_count[client];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lo_frame_count = hi_frame_count = netplay->server_frame_count;
|
|
}
|
|
|
|
/* If we're behind, try to catch up */
|
|
if (netplay->catch_up)
|
|
{
|
|
/* Are we caught up? */
|
|
if (netplay->self_frame_count + 1 >= lo_frame_count)
|
|
{
|
|
netplay->catch_up = false;
|
|
input_driver_unset_nonblock_state();
|
|
driver_set_nonblock_state();
|
|
}
|
|
|
|
}
|
|
else if (!stalled)
|
|
{
|
|
if (netplay->self_frame_count + 3 < lo_frame_count)
|
|
{
|
|
retro_time_t cur_time = cpu_features_get_time_usec();
|
|
uint32_t cur_behind = lo_frame_count - netplay->self_frame_count;
|
|
|
|
/* We're behind, but we'll only try to catch up if we're actually
|
|
* falling behind, i.e. if we're more behind after some time */
|
|
if (netplay->catch_up_time == 0)
|
|
{
|
|
/* Record our current time to check for catch-up later */
|
|
netplay->catch_up_time = cur_time;
|
|
netplay->catch_up_behind = cur_behind;
|
|
|
|
}
|
|
else if (cur_time - netplay->catch_up_time > CATCH_UP_CHECK_TIME_USEC)
|
|
{
|
|
/* Time to check how far behind we are */
|
|
if (netplay->catch_up_behind <= cur_behind)
|
|
{
|
|
/* We're definitely falling behind! */
|
|
netplay->catch_up = true;
|
|
netplay->catch_up_time = 0;
|
|
input_driver_set_nonblock_state();
|
|
driver_set_nonblock_state();
|
|
}
|
|
else
|
|
{
|
|
/* Check again in another period */
|
|
netplay->catch_up_time = cur_time;
|
|
netplay->catch_up_behind = cur_behind;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if (netplay->self_frame_count + 3 < hi_frame_count)
|
|
{
|
|
size_t i;
|
|
netplay->catch_up_time = 0;
|
|
|
|
/* We're falling behind some clients but not others, so request that
|
|
* clients ahead of us stall */
|
|
for (i = 0; i < netplay->connections_size; i++)
|
|
{
|
|
uint32_t client_num;
|
|
struct netplay_connection *connection = &netplay->connections[i];
|
|
|
|
if (!connection->active ||
|
|
connection->mode != NETPLAY_CONNECTION_PLAYING)
|
|
continue;
|
|
|
|
client_num = (uint32_t)(i + 1);
|
|
|
|
/* Are they ahead? */
|
|
if (netplay->self_frame_count + 3 < netplay->read_frame_count[client_num])
|
|
{
|
|
/* Tell them to stall */
|
|
if (connection->stall_frame + NETPLAY_MAX_REQ_STALL_FREQUENCY <
|
|
netplay->self_frame_count)
|
|
{
|
|
connection->stall_frame = netplay->self_frame_count;
|
|
netplay_cmd_stall(netplay, connection,
|
|
netplay->read_frame_count[client_num] -
|
|
netplay->self_frame_count + 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
netplay->catch_up_time = 0;
|
|
}
|
|
else
|
|
netplay->catch_up_time = 0;
|
|
}
|