2015-12-23 20:25:28 +00:00
|
|
|
/* RetroArch - A frontend for libretro.
|
|
|
|
* Copyright (C) 2010-2014 - Hans-Kristian Arntzen
|
2017-01-22 12:40:32 +00:00
|
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
|
|
* Copyright (C) 2016-2017 - Gregor Richards
|
2015-12-23 20:25:28 +00:00
|
|
|
*
|
|
|
|
* 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/>.
|
|
|
|
*/
|
|
|
|
|
2016-12-16 04:09:55 +00:00
|
|
|
#include <stdio.h>
|
2016-12-14 01:52:44 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/types.h>
|
2016-09-05 22:56:00 +00:00
|
|
|
|
2016-12-14 01:52:44 +00:00
|
|
|
#include <boolean.h>
|
2016-09-21 21:23:36 +00:00
|
|
|
|
2015-12-23 20:25:28 +00:00
|
|
|
#include "netplay_private.h"
|
2016-09-03 05:48:25 +00:00
|
|
|
|
|
|
|
#include "../../autosave.h"
|
2016-12-16 03:34:18 +00:00
|
|
|
#include "../../driver.h"
|
|
|
|
#include "../../input/input_driver.h"
|
2016-09-03 05:48:25 +00:00
|
|
|
|
2016-10-05 11:58:01 +00:00
|
|
|
#if 0
|
|
|
|
#define DEBUG_NONDETERMINISTIC_CORES
|
|
|
|
#endif
|
2016-09-28 00:49:16 +00:00
|
|
|
|
2016-12-14 01:50:17 +00:00
|
|
|
/**
|
|
|
|
* netplay_update_unread_ptr
|
|
|
|
*
|
|
|
|
* Update the global unread_ptr and unread_frame_count to correspond to the
|
2016-12-15 13:42:03 +00:00
|
|
|
* earliest unread frame count of any connected player
|
|
|
|
*/
|
2016-12-14 01:50:17 +00:00
|
|
|
void netplay_update_unread_ptr(netplay_t *netplay)
|
|
|
|
{
|
2017-09-10 13:15:06 +00:00
|
|
|
if (netplay->is_server && netplay->connected_players<=1)
|
2016-12-14 01:50:17 +00:00
|
|
|
{
|
|
|
|
/* Nothing at all to read! */
|
2017-01-20 19:28:18 +00:00
|
|
|
netplay->unread_ptr = netplay->self_ptr;
|
|
|
|
netplay->unread_frame_count = netplay->self_frame_count;
|
2016-12-14 01:50:17 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
size_t new_unread_ptr = 0;
|
|
|
|
uint32_t new_unread_frame_count = (uint32_t) -1;
|
2017-08-25 18:38:21 +00:00
|
|
|
uint32_t client;
|
2016-12-14 01:50:17 +00:00
|
|
|
|
2017-08-25 18:38:21 +00:00
|
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
2016-12-14 01:50:17 +00:00
|
|
|
{
|
2017-09-10 13:15:06 +00:00
|
|
|
if (!(netplay->connected_players & (1<<client))) continue;
|
|
|
|
if ((netplay->connected_slaves & (1<<client))) continue;
|
2017-09-11 14:40:34 +00:00
|
|
|
if (netplay->read_frame_count[client] < new_unread_frame_count)
|
2016-12-14 01:50:17 +00:00
|
|
|
{
|
2017-09-11 14:40:34 +00:00
|
|
|
new_unread_ptr = netplay->read_ptr[client];
|
|
|
|
new_unread_frame_count = netplay->read_frame_count[client];
|
2016-12-14 01:50:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-02-23 02:10:02 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-12-14 01:50:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-10 23:42:32 +00:00
|
|
|
struct vote_count {
|
|
|
|
uint16_t votes[32];
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* netplay_device_client_state
|
2017-09-11 11:29:44 +00:00
|
|
|
* @netplay : pointer to netplay object
|
2017-09-10 23:42:32 +00:00
|
|
|
* @simframe : frame in which merging is being performed
|
|
|
|
* @device : device being merged
|
|
|
|
* @client : client to find state for
|
|
|
|
*/
|
2017-09-11 11:29:44 +00:00
|
|
|
netplay_input_state_t netplay_device_client_state(netplay_t *netplay,
|
|
|
|
struct delta_frame *simframe, uint32_t device, uint32_t client)
|
2017-09-10 23:42:32 +00:00
|
|
|
{
|
2017-09-13 15:39:41 +00:00
|
|
|
uint32_t dsize = netplay_expected_input_size(netplay, 1 << device);
|
2017-09-10 23:42:32 +00:00
|
|
|
netplay_input_state_t simstate =
|
|
|
|
netplay_input_state_for(
|
2017-09-13 15:39:41 +00:00
|
|
|
&simframe->real_input[device], client,
|
|
|
|
dsize, false, true);
|
2017-09-10 23:42:32 +00:00
|
|
|
if (!simstate)
|
|
|
|
{
|
2017-09-11 14:40:34 +00:00
|
|
|
if (netplay->read_frame_count[client] > simframe->frame)
|
2017-09-11 11:29:44 +00:00
|
|
|
return NULL;
|
2017-09-10 23:42:32 +00:00
|
|
|
simstate = netplay_input_state_for(&simframe->simlated_input[device],
|
2017-09-13 15:39:41 +00:00
|
|
|
client, dsize, false, true);
|
2017-09-10 23:42:32 +00:00
|
|
|
}
|
|
|
|
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;
|
2018-04-09 15:35:27 +00:00
|
|
|
uint8_t share_mode = netplay->device_share_modes[device]
|
|
|
|
& NETPLAY_SHARE_DIGITAL_BITS;
|
2017-09-11 11:29:44 +00:00
|
|
|
|
|
|
|
/* Make sure all real clients are accounted for */
|
2018-04-09 15:35:27 +00:00
|
|
|
for (simstate = simframe->real_input[device];
|
|
|
|
simstate; simstate = simstate->next)
|
2017-09-11 11:29:44 +00:00
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!simstate->used || simstate->size != resstate->size)
|
|
|
|
continue;
|
2017-09-11 11:29:44 +00:00
|
|
|
clients |= 1<<simstate->client_num;
|
|
|
|
}
|
|
|
|
|
2017-09-11 02:49:25 +00:00
|
|
|
if (share_mode == NETPLAY_SHARE_DIGITAL_VOTE)
|
2017-09-10 23:42:32 +00:00
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
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];
|
2017-09-10 23:42:32 +00:00
|
|
|
/* Vote mode requires counting all the bits */
|
2018-04-09 15:35:27 +00:00
|
|
|
uint32_t client_count = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
for (j = 0; j < 32; j++)
|
|
|
|
votes[i].votes[j] = 0;
|
2017-09-13 15:39:41 +00:00
|
|
|
|
2017-09-10 23:42:32 +00:00
|
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!(clients & (1<<client)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
simstate = netplay_device_client_state(
|
|
|
|
netplay, simframe, device, client);
|
|
|
|
|
|
|
|
if (!simstate)
|
|
|
|
continue;
|
2017-09-11 11:29:44 +00:00
|
|
|
client_count++;
|
2017-09-10 23:42:32 +00:00
|
|
|
|
2017-09-13 15:39:41 +00:00
|
|
|
for (word = 0; word < resstate->size; word++)
|
2017-09-10 23:42:32 +00:00
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!digital[word])
|
|
|
|
continue;
|
2017-09-10 23:42:32 +00:00
|
|
|
for (bit = 0; bit < 32; bit++)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!(digital[word] & (1<<bit)))
|
|
|
|
continue;
|
2017-09-10 23:42:32 +00:00
|
|
|
if (simstate->data[word] & (1<<bit))
|
|
|
|
votes[word].votes[bit]++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now count all the bits */
|
|
|
|
client_count /= 2;
|
2017-09-13 15:39:41 +00:00
|
|
|
for (word = 0; word < resstate->size; word++)
|
2017-09-10 23:42:32 +00:00
|
|
|
{
|
|
|
|
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++)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!(clients & (1<<client)))
|
|
|
|
continue;
|
|
|
|
simstate = netplay_device_client_state(
|
|
|
|
netplay, simframe, device, client);
|
|
|
|
|
|
|
|
if (!simstate)
|
|
|
|
continue;
|
2017-09-13 15:39:41 +00:00
|
|
|
for (word = 0; word < resstate->size; word++)
|
2017-09-10 23:42:32 +00:00
|
|
|
{
|
|
|
|
uint32_t part;
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!digital[word])
|
|
|
|
continue;
|
2017-09-10 23:42:32 +00:00
|
|
|
part = simstate->data[word];
|
2018-04-09 15:35:27 +00:00
|
|
|
|
2017-09-10 23:42:32 +00:00
|
|
|
if (digital[word] == (uint32_t) -1)
|
|
|
|
{
|
|
|
|
/* Combine the whole word */
|
|
|
|
switch (share_mode)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
case NETPLAY_SHARE_DIGITAL_XOR:
|
|
|
|
resstate->data[word] ^= part;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
resstate->data[word] |= part;
|
2017-09-10 23:42:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
else /* !whole word */
|
|
|
|
{
|
|
|
|
for (bit = 0; bit < 32; bit++)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!(digital[word] & (1<<bit)))
|
|
|
|
continue;
|
2017-09-10 23:42:32 +00:00
|
|
|
switch (share_mode)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
case NETPLAY_SHARE_DIGITAL_XOR:
|
|
|
|
resstate->data[word] ^= part & (1<<bit);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
resstate->data[word] |= part & (1<<bit);
|
2017-09-10 23:42:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
2018-01-23 04:48:37 +00:00
|
|
|
uint32_t client, client_count = 0;
|
2018-04-09 15:35:27 +00:00
|
|
|
uint8_t share_mode = netplay->device_share_modes[device]
|
|
|
|
& NETPLAY_SHARE_ANALOG_BITS;
|
2018-01-23 04:48:37 +00:00
|
|
|
int32_t value = 0, new_value;
|
2017-09-10 23:42:32 +00:00
|
|
|
|
2017-09-11 11:29:44 +00:00
|
|
|
/* Make sure all real clients are accounted for */
|
|
|
|
for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!simstate->used || simstate->size != resstate->size)
|
|
|
|
continue;
|
2017-09-11 11:29:44 +00:00
|
|
|
clients |= 1<<simstate->client_num;
|
|
|
|
}
|
|
|
|
|
2017-09-10 23:42:32 +00:00
|
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!(clients & (1<<client)))
|
|
|
|
continue;
|
|
|
|
simstate = netplay_device_client_state(
|
|
|
|
netplay, simframe, device, client);
|
|
|
|
if (!simstate)
|
|
|
|
continue;
|
2017-09-11 11:29:44 +00:00
|
|
|
client_count++;
|
2017-09-10 23:42:32 +00:00
|
|
|
new_value = (int16_t) ((simstate->data[word]>>bit) & 0xFFFF);
|
|
|
|
switch (share_mode)
|
|
|
|
{
|
2017-09-11 02:49:25 +00:00
|
|
|
case NETPLAY_SHARE_ANALOG_AVERAGE:
|
2017-09-10 23:42:32 +00:00
|
|
|
value += (int32_t) new_value;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (abs(new_value) > abs(value) ||
|
|
|
|
(abs(new_value) == abs(value) && new_value > value))
|
|
|
|
value = new_value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-11 02:49:25 +00:00
|
|
|
if (share_mode == NETPLAY_SHARE_ANALOG_AVERAGE)
|
2018-04-12 19:39:31 +00:00
|
|
|
if (client_count > 0) /* Prevent potential divide by zero */
|
|
|
|
value /= client_count;
|
2017-09-10 23:42:32 +00:00
|
|
|
|
|
|
|
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
|
2017-09-13 15:39:41 +00:00
|
|
|
* @dtype : device type
|
2017-09-10 23:42:32 +00:00
|
|
|
*/
|
|
|
|
static void netplay_merge_analog(netplay_t *netplay,
|
|
|
|
netplay_input_state_t resstate, struct delta_frame *simframe,
|
2017-09-13 15:39:41 +00:00
|
|
|
uint32_t device, uint32_t clients, unsigned dtype)
|
2017-09-10 23:42:32 +00:00
|
|
|
{
|
2017-09-14 00:51:57 +00:00
|
|
|
/* Devices with no analog parts */
|
|
|
|
if (dtype == RETRO_DEVICE_JOYPAD || dtype == RETRO_DEVICE_KEYBOARD)
|
2017-09-13 15:39:41 +00:00
|
|
|
return;
|
|
|
|
|
2017-09-14 00:51:57 +00:00
|
|
|
/* All other devices have at least one analog word */
|
2017-09-10 23:42:32 +00:00
|
|
|
merge_analog_part(netplay, resstate, simframe, device, clients, 1, 0);
|
|
|
|
merge_analog_part(netplay, resstate, simframe, device, clients, 1, 16);
|
2017-09-14 00:51:57 +00:00
|
|
|
|
|
|
|
/* And the ANALOG device has two (two sticks) */
|
2017-09-13 15:39:41 +00:00
|
|
|
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);
|
|
|
|
}
|
2017-09-10 23:42:32 +00:00
|
|
|
}
|
|
|
|
|
2016-12-14 01:50:17 +00:00
|
|
|
/**
|
2017-09-10 00:44:12 +00:00
|
|
|
* netplay_resolve_input
|
2016-12-14 01:50:17 +00:00
|
|
|
* @netplay : pointer to netplay object
|
2017-09-10 00:44:12 +00:00
|
|
|
* @sim_ptr : frame pointer for which to resolve input
|
2016-12-14 01:50:17 +00:00
|
|
|
* @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.
|
2017-09-10 00:44:12 +00:00
|
|
|
* Returns true if the resolved input changed from the last time it was
|
|
|
|
* resolved.
|
2016-12-14 01:50:17 +00:00
|
|
|
*/
|
2017-09-10 00:44:12 +00:00
|
|
|
bool netplay_resolve_input(netplay_t *netplay, size_t sim_ptr, bool resim)
|
2016-12-14 01:50:17 +00:00
|
|
|
{
|
|
|
|
size_t prev;
|
2017-09-10 23:42:32 +00:00
|
|
|
uint32_t device;
|
2018-04-09 15:35:27 +00:00
|
|
|
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];
|
2016-12-14 01:50:17 +00:00
|
|
|
|
2017-09-10 23:42:32 +00:00
|
|
|
for (device = 0; device < MAX_INPUT_DEVICES; device++)
|
2016-12-14 01:50:17 +00:00
|
|
|
{
|
2017-09-13 15:39:41 +00:00
|
|
|
unsigned dtype = netplay->config_devices[device]&RETRO_DEVICE_MASK;
|
|
|
|
uint32_t dsize = netplay_expected_input_size(netplay, 1 << device);
|
2018-04-09 15:48:11 +00:00
|
|
|
clients = netplay->device_clients[device];
|
|
|
|
client_count = 0;
|
2017-08-25 18:38:21 +00:00
|
|
|
|
2017-09-11 11:29:44 +00:00
|
|
|
/* Make sure all real clients are accounted for */
|
|
|
|
for (simstate = simframe->real_input[device]; simstate; simstate = simstate->next)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!simstate->used || simstate->size != dsize)
|
|
|
|
continue;
|
2017-09-11 11:29:44 +00:00
|
|
|
clients |= 1<<simstate->client_num;
|
|
|
|
}
|
|
|
|
|
2017-09-10 23:42:32 +00:00
|
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
2016-12-14 01:50:17 +00:00
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (!(clients & (1<<client)))
|
|
|
|
continue;
|
2017-09-10 00:44:12 +00:00
|
|
|
|
2017-09-10 23:42:32 +00:00
|
|
|
/* Resolve this client-device */
|
2018-04-09 15:35:27 +00:00
|
|
|
simstate = netplay_input_state_for(
|
|
|
|
&simframe->real_input[device], client, dsize, false, true);
|
2017-09-10 13:12:32 +00:00
|
|
|
if (!simstate)
|
2017-09-10 00:44:12 +00:00
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
/* Don't already have this input, so must
|
|
|
|
* simulate if we're supposed to have it at all */
|
2017-09-11 14:40:34 +00:00
|
|
|
if (netplay->read_frame_count[client] > simframe->frame)
|
2017-09-11 11:29:44 +00:00
|
|
|
continue;
|
2017-09-13 15:39:41 +00:00
|
|
|
simstate = netplay_input_state_for(&simframe->simlated_input[device], client, dsize, false, false);
|
2017-09-10 13:12:32 +00:00
|
|
|
if (!simstate)
|
|
|
|
continue;
|
|
|
|
|
2017-09-11 14:40:34 +00:00
|
|
|
prev = PREV_PTR(netplay->read_ptr[client]);
|
2017-09-10 13:12:32 +00:00
|
|
|
pframe = &netplay->buffer[prev];
|
2017-09-13 15:39:41 +00:00
|
|
|
pstate = netplay_input_state_for(&pframe->real_input[device], client, dsize, false, true);
|
2017-09-10 13:12:32 +00:00
|
|
|
if (!pstate)
|
|
|
|
continue;
|
|
|
|
|
2017-09-14 23:48:47 +00:00
|
|
|
if (resim && (dtype == RETRO_DEVICE_JOYPAD || dtype == RETRO_DEVICE_ANALOG))
|
2017-09-10 13:12:32 +00:00
|
|
|
{
|
|
|
|
/* 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.
|
|
|
|
*/
|
2017-09-14 23:48:47 +00:00
|
|
|
const uint32_t keep =
|
2018-04-09 15:35:27 +00:00
|
|
|
(1U<<RETRO_DEVICE_ID_JOYPAD_UP) |
|
|
|
|
(1U<<RETRO_DEVICE_ID_JOYPAD_DOWN) |
|
|
|
|
(1U<<RETRO_DEVICE_ID_JOYPAD_LEFT) |
|
|
|
|
(1U<<RETRO_DEVICE_ID_JOYPAD_RIGHT);
|
2017-09-10 13:12:32 +00:00
|
|
|
simstate->data[0] &= keep;
|
|
|
|
simstate->data[0] |= pstate->data[0] & ~keep;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
memcpy(simstate->data, pstate->data,
|
2017-09-13 15:39:41 +00:00
|
|
|
dsize * sizeof(uint32_t));
|
2017-09-10 00:44:12 +00:00
|
|
|
}
|
2017-09-11 11:29:44 +00:00
|
|
|
|
|
|
|
client_state = simstate;
|
|
|
|
client_count++;
|
2017-09-10 23:42:32 +00:00
|
|
|
}
|
2017-09-10 13:12:32 +00:00
|
|
|
|
2018-04-09 15:35:27 +00:00
|
|
|
/* The frontend always uses the first resolved input,
|
|
|
|
* so make sure it's right */
|
2017-09-13 15:39:41 +00:00
|
|
|
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! */
|
2018-04-09 15:35:27 +00:00
|
|
|
netplay_input_state_t nextistate =
|
|
|
|
simframe->resolved_input[device]->next;
|
2017-09-13 15:39:41 +00:00
|
|
|
free(simframe->resolved_input[device]);
|
|
|
|
simframe->resolved_input[device] = nextistate;
|
|
|
|
}
|
|
|
|
|
2018-04-09 15:35:27 +00:00
|
|
|
/* Now we copy the state, whether real or simulated,
|
|
|
|
* out into the resolved state */
|
|
|
|
resstate = netplay_input_state_for(
|
|
|
|
&simframe->resolved_input[device], 0,
|
2017-09-13 15:39:41 +00:00
|
|
|
dsize, false, false);
|
2017-09-10 23:42:32 +00:00
|
|
|
if (!resstate)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (client_count == 1)
|
|
|
|
{
|
|
|
|
/* Trivial in the common 1-client case */
|
2018-04-09 15:35:27 +00:00
|
|
|
if (memcmp(resstate->data, client_state->data,
|
|
|
|
dsize * sizeof(uint32_t)))
|
2017-09-10 13:12:32 +00:00
|
|
|
ret = true;
|
2018-04-09 15:35:27 +00:00
|
|
|
memcpy(resstate->data, client_state->data,
|
|
|
|
dsize * sizeof(uint32_t));
|
2017-09-11 11:29:44 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
else if (client_count == 0)
|
|
|
|
{
|
|
|
|
uint32_t word;
|
2017-09-13 15:39:41 +00:00
|
|
|
for (word = 0; word < dsize; word++)
|
2017-09-11 11:29:44 +00:00
|
|
|
{
|
|
|
|
if (resstate->data[word])
|
|
|
|
ret = true;
|
|
|
|
resstate->data[word] = 0;
|
|
|
|
}
|
2017-09-10 23:42:32 +00:00
|
|
|
|
2017-09-11 11:29:44 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-09-10 23:42:32 +00:00
|
|
|
/* Merge them */
|
2017-09-14 00:51:57 +00:00
|
|
|
/* Most devices have all the digital parts in the first word. */
|
2018-04-09 15:35:27 +00:00
|
|
|
static const uint32_t digital_common[3] = {~0u, 0u, 0u};
|
2018-02-04 19:03:27 +00:00
|
|
|
static const uint32_t digital_keyboard[5] = {~0u, ~0u, ~0u, ~0u, ~0u};
|
2017-09-14 00:51:57 +00:00
|
|
|
const uint32_t *digital;
|
|
|
|
if (dtype == RETRO_DEVICE_KEYBOARD)
|
|
|
|
digital = digital_keyboard;
|
|
|
|
else
|
|
|
|
digital = digital_common;
|
2018-04-09 15:35:27 +00:00
|
|
|
oldresstate = netplay_input_state_for(
|
|
|
|
&simframe->resolved_input[device], 1, dsize, false, false);
|
2017-09-10 23:42:32 +00:00
|
|
|
if (!oldresstate)
|
|
|
|
continue;
|
2017-09-13 15:39:41 +00:00
|
|
|
memcpy(oldresstate->data, resstate->data, dsize * sizeof(uint32_t));
|
|
|
|
memset(resstate->data, 0, dsize * sizeof(uint32_t));
|
2017-09-10 23:42:32 +00:00
|
|
|
|
2018-04-09 15:35:27 +00:00
|
|
|
netplay_merge_digital(netplay, resstate, simframe,
|
|
|
|
device, clients, digital);
|
|
|
|
netplay_merge_analog(netplay, resstate, simframe,
|
|
|
|
device, clients, dtype);
|
2017-09-10 23:42:32 +00:00
|
|
|
|
2018-04-09 15:35:27 +00:00
|
|
|
if (memcmp(resstate->data, oldresstate->data,
|
|
|
|
dsize * sizeof(uint32_t)))
|
2017-09-10 23:42:32 +00:00
|
|
|
ret = true;
|
|
|
|
|
2016-12-14 01:50:17 +00:00
|
|
|
}
|
|
|
|
}
|
2017-09-10 00:44:12 +00:00
|
|
|
|
|
|
|
return ret;
|
2016-12-14 01:50:17 +00:00
|
|
|
}
|
|
|
|
|
2018-04-09 15:35:27 +00:00
|
|
|
static void netplay_handle_frame_hash(netplay_t *netplay,
|
|
|
|
struct delta_frame *delta)
|
2016-09-15 03:19:47 +00:00
|
|
|
{
|
2016-12-14 02:01:31 +00:00
|
|
|
if (netplay->is_server)
|
2016-09-15 03:19:47 +00:00
|
|
|
{
|
2016-10-05 11:55:30 +00:00
|
|
|
if (netplay->check_frames &&
|
2016-12-19 00:27:51 +00:00
|
|
|
delta->frame % abs(netplay->check_frames) == 0)
|
2016-09-15 03:54:18 +00:00
|
|
|
{
|
|
|
|
delta->crc = netplay_delta_frame_crc(netplay, delta);
|
|
|
|
netplay_cmd_crc(netplay, delta);
|
|
|
|
}
|
2016-09-15 03:19:47 +00:00
|
|
|
}
|
2016-12-15 17:43:29 +00:00
|
|
|
else if (delta->crc && netplay->crcs_valid)
|
2016-09-15 03:19:47 +00:00
|
|
|
{
|
|
|
|
/* We have a remote CRC, so check it */
|
|
|
|
uint32_t local_crc = netplay_delta_frame_crc(netplay, delta);
|
|
|
|
if (local_crc != delta->crc)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
/* If the very first check frame is wrong,
|
|
|
|
* they probably just don't work */
|
2016-12-15 17:43:29 +00:00
|
|
|
if (!netplay->crc_validity_checked)
|
|
|
|
netplay->crcs_valid = false;
|
|
|
|
else if (netplay->crcs_valid)
|
2016-10-05 11:55:30 +00:00
|
|
|
{
|
|
|
|
/* Fix this! */
|
2016-12-19 00:27:51 +00:00
|
|
|
if (netplay->check_frames < 0)
|
|
|
|
{
|
|
|
|
/* Just report */
|
|
|
|
RARCH_ERR("Netplay CRCs mismatch!\n");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
netplay_cmd_request_savestate(netplay);
|
2016-10-05 11:55:30 +00:00
|
|
|
}
|
2016-09-15 03:19:47 +00:00
|
|
|
}
|
2016-12-15 17:43:29 +00:00
|
|
|
else if (!netplay->crc_validity_checked)
|
|
|
|
netplay->crc_validity_checked = true;
|
2016-09-15 03:19:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-23 20:25:28 +00:00
|
|
|
/**
|
2016-12-15 13:42:03 +00:00
|
|
|
* netplay_sync_pre_frame
|
2015-12-23 20:25:28 +00:00
|
|
|
* @netplay : pointer to netplay object
|
|
|
|
*
|
2016-12-10 04:11:18 +00:00
|
|
|
* Pre-frame for Netplay synchronization.
|
2016-12-15 13:42:03 +00:00
|
|
|
*/
|
2016-12-10 04:11:18 +00:00
|
|
|
bool netplay_sync_pre_frame(netplay_t *netplay)
|
2015-12-23 20:25:28 +00:00
|
|
|
{
|
2016-01-27 06:28:03 +00:00
|
|
|
retro_ctx_serialize_info_t serial_info;
|
|
|
|
|
2018-04-09 15:35:27 +00:00
|
|
|
if (netplay_delta_frame_ready(netplay,
|
|
|
|
&netplay->buffer[netplay->run_ptr], netplay->run_frame_count))
|
Multitudinous fixes and updates to Netplay. Had to be one commit since
they're mostly related:
(1) Renamed frame_count to self_frame_count to be consistent with all
other names.
(2) Previously, it was possible to overwrite data in the ring buffer
that hadn't yet been used. Now that's not possible, but that just
changes one breakage for another: It's now possible to miss the NEW
data. The final resolution for this will probably be requesting stalls.
This is accomplished simply by storing frame numbers in the ring buffer
and checking them against the 'other' head.
(3) In TCP packets, separated cmd_size from cmd. It was beyond pointless
for these to be combined, and restricted cmd_size to 16 bits, which
will probably fail when/if state loading is supported.
(4) Readahead is now allowed. In the past, if the peer got ahead of us,
we would simply ignore their data. Thus, if they got too far ahead of
us, we'd stop reading their data altogether. Fabulous. Now, we're happy
to read future input.
(5) If the peer gets too far ahead of us (currently an unconfigurable 10
frames), fast forward to catch up. This should prevent desync due to
clock drift or stutter.
(6) Used frame_count in a few places where ptr was used. Doing a
comparison of pointers on a ring buffer is a far more dangerous way to
assure we're done with a task than simply using the count, since the
ring buffer is... well, a ring.
(7) Renamed tmp_{ptr,frame_count} to replay_{ptr,frame_count} for
clarity.
(8) Slightly changed the protocol version hash, just to assure that
other clients wouldn't think they were compatible with this one.
(9) There was an off-by-one error which, under some circumstances, could
allow the replay engine to run a complete round through the ring buffer,
replaying stale data. Fixed.
2016-09-12 02:01:47 +00:00
|
|
|
{
|
2016-09-13 21:05:28 +00:00
|
|
|
serial_info.data_const = NULL;
|
2018-04-09 15:35:27 +00:00
|
|
|
serial_info.data = netplay->buffer[netplay->run_ptr].state;
|
|
|
|
serial_info.size = netplay->state_size;
|
2016-09-13 21:05:28 +00:00
|
|
|
|
2016-10-05 02:24:33 +00:00
|
|
|
memset(serial_info.data, 0, serial_info.size);
|
2018-04-09 15:35:27 +00:00
|
|
|
if ((netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
|
|
|
|| netplay->run_frame_count == 0)
|
2016-09-30 18:03:18 +00:00
|
|
|
{
|
|
|
|
/* Don't serialize until it's safe */
|
|
|
|
}
|
2018-04-09 15:35:27 +00:00
|
|
|
else if (!(netplay->quirks & NETPLAY_QUIRK_NO_SAVESTATES)
|
|
|
|
&& core_serialize(&serial_info))
|
2016-09-15 03:19:47 +00:00
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
if (netplay->force_send_savestate && !netplay->stall
|
|
|
|
&& !netplay->remote_paused)
|
2016-09-15 03:19:47 +00:00
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
/* Bring our running frame and input frames into
|
|
|
|
* parity so we don't send old info. */
|
2017-01-20 19:28:18 +00:00
|
|
|
if (netplay->run_ptr != netplay->self_ptr)
|
2017-01-18 21:07:17 +00:00
|
|
|
{
|
2017-01-20 19:28:18 +00:00
|
|
|
memcpy(netplay->buffer[netplay->self_ptr].state,
|
2017-01-18 21:07:17 +00:00
|
|
|
netplay->buffer[netplay->run_ptr].state,
|
|
|
|
netplay->state_size);
|
2018-04-09 15:35:27 +00:00
|
|
|
netplay->run_ptr = netplay->self_ptr;
|
2017-01-20 19:28:18 +00:00
|
|
|
netplay->run_frame_count = netplay->self_frame_count;
|
2017-01-18 21:07:17 +00:00
|
|
|
}
|
|
|
|
|
2016-09-15 03:19:47 +00:00
|
|
|
/* Send this along to the other side */
|
2017-01-18 21:07:17 +00:00
|
|
|
serial_info.data_const = netplay->buffer[netplay->run_ptr].state;
|
2016-09-15 03:19:47 +00:00
|
|
|
netplay_load_savestate(netplay, &serial_info, false);
|
2016-12-13 00:34:50 +00:00
|
|
|
netplay->force_send_savestate = false;
|
2016-09-15 03:19:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2016-09-13 21:05:28 +00:00
|
|
|
{
|
|
|
|
/* If the core can't serialize properly, we must stall for the
|
|
|
|
* remote input on EVERY frame, because we can't recover */
|
2016-09-30 17:31:58 +00:00
|
|
|
netplay->quirks |= NETPLAY_QUIRK_NO_SAVESTATES;
|
2016-12-16 04:09:55 +00:00
|
|
|
netplay->stateless_mode = true;
|
2016-09-13 21:05:28 +00:00
|
|
|
}
|
2016-09-30 17:39:00 +00:00
|
|
|
|
2018-04-09 15:35:27 +00:00
|
|
|
/* If we can't transmit savestates, we must stall
|
|
|
|
* until the client is ready. */
|
2017-01-18 21:07:17 +00:00
|
|
|
if (netplay->run_frame_count > 0 &&
|
2016-12-05 05:04:01 +00:00
|
|
|
(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))
|
2016-12-09 18:32:04 +00:00
|
|
|
netplay->stall = NETPLAY_STALL_NO_CONNECTION;
|
Multitudinous fixes and updates to Netplay. Had to be one commit since
they're mostly related:
(1) Renamed frame_count to self_frame_count to be consistent with all
other names.
(2) Previously, it was possible to overwrite data in the ring buffer
that hadn't yet been used. Now that's not possible, but that just
changes one breakage for another: It's now possible to miss the NEW
data. The final resolution for this will probably be requesting stalls.
This is accomplished simply by storing frame numbers in the ring buffer
and checking them against the 'other' head.
(3) In TCP packets, separated cmd_size from cmd. It was beyond pointless
for these to be combined, and restricted cmd_size to 16 bits, which
will probably fail when/if state loading is supported.
(4) Readahead is now allowed. In the past, if the peer got ahead of us,
we would simply ignore their data. Thus, if they got too far ahead of
us, we'd stop reading their data altogether. Fabulous. Now, we're happy
to read future input.
(5) If the peer gets too far ahead of us (currently an unconfigurable 10
frames), fast forward to catch up. This should prevent desync due to
clock drift or stutter.
(6) Used frame_count in a few places where ptr was used. Doing a
comparison of pointers on a ring buffer is a far more dangerous way to
assure we're done with a task than simply using the count, since the
ring buffer is... well, a ring.
(7) Renamed tmp_{ptr,frame_count} to replay_{ptr,frame_count} for
clarity.
(8) Slightly changed the protocol version hash, just to assure that
other clients wouldn't think they were compatible with this one.
(9) There was an off-by-one error which, under some circumstances, could
allow the replay engine to run a complete round through the ring buffer,
replaying stale data. Fixed.
2016-09-12 02:01:47 +00:00
|
|
|
}
|
2016-01-27 06:28:03 +00:00
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
if (netplay->is_server)
|
2016-09-21 21:23:36 +00:00
|
|
|
{
|
|
|
|
fd_set fds;
|
|
|
|
struct timeval tmp_tv = {0};
|
2016-09-22 20:01:35 +00:00
|
|
|
int new_fd;
|
2016-09-21 21:23:36 +00:00
|
|
|
struct sockaddr_storage their_addr;
|
|
|
|
socklen_t addr_size;
|
2016-12-05 05:04:01 +00:00
|
|
|
struct netplay_connection *connection;
|
|
|
|
size_t connection_num;
|
2016-09-21 21:23:36 +00:00
|
|
|
|
|
|
|
/* Check for a connection */
|
|
|
|
FD_ZERO(&fds);
|
2016-12-05 05:04:01 +00:00
|
|
|
FD_SET(netplay->listen_fd, &fds);
|
2018-04-09 15:35:27 +00:00
|
|
|
if (socket_select(netplay->listen_fd + 1,
|
|
|
|
&fds, NULL, NULL, &tmp_tv) > 0 &&
|
2016-12-05 05:04:01 +00:00
|
|
|
FD_ISSET(netplay->listen_fd, &fds))
|
2016-09-21 21:23:36 +00:00
|
|
|
{
|
|
|
|
addr_size = sizeof(their_addr);
|
2018-04-09 15:35:27 +00:00
|
|
|
new_fd = accept(netplay->listen_fd,
|
|
|
|
(struct sockaddr*)&their_addr, &addr_size);
|
|
|
|
|
2016-09-21 21:23:36 +00:00
|
|
|
if (new_fd < 0)
|
|
|
|
{
|
|
|
|
RARCH_ERR("%s\n", msg_hash_to_str(MSG_NETPLAY_FAILED));
|
2016-12-05 05:04:01 +00:00
|
|
|
goto process;
|
2016-09-21 21:23:36 +00:00
|
|
|
}
|
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
/* Set the socket nonblocking */
|
|
|
|
if (!socket_nonblock(new_fd))
|
|
|
|
{
|
|
|
|
/* Catastrophe! */
|
|
|
|
socket_close(new_fd);
|
|
|
|
goto process;
|
|
|
|
}
|
2016-09-21 21:23:36 +00:00
|
|
|
|
2016-09-21 21:31:19 +00:00
|
|
|
#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
|
|
|
|
{
|
|
|
|
int flag = 1;
|
2016-12-05 05:04:01 +00:00
|
|
|
if (setsockopt(new_fd, IPPROTO_TCP, TCP_NODELAY,
|
2006-05-18 11:31:43 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
(const char*)
|
|
|
|
#else
|
|
|
|
(const void*)
|
|
|
|
#endif
|
|
|
|
&flag,
|
|
|
|
sizeof(int)) < 0)
|
2016-09-24 22:48:55 +00:00
|
|
|
RARCH_WARN("Could not set netplay TCP socket to nodelay. Expect jitter.\n");
|
2016-09-21 21:31:19 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-09-26 03:26:32 +00:00
|
|
|
#if defined(F_SETFD) && defined(FD_CLOEXEC)
|
|
|
|
/* Don't let any inherited processes keep open our port */
|
2016-12-05 05:04:01 +00:00
|
|
|
if (fcntl(new_fd, F_SETFD, FD_CLOEXEC) < 0)
|
2016-09-26 03:26:32 +00:00
|
|
|
RARCH_WARN("Cannot set Netplay port to close-on-exec. It may fail to reopen if the client disconnects.\n");
|
|
|
|
#endif
|
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
/* Allocate a connection */
|
|
|
|
for (connection_num = 0; connection_num < netplay->connections_size; connection_num++)
|
2017-06-07 01:35:09 +00:00
|
|
|
if (!netplay->connections[connection_num].active &&
|
|
|
|
netplay->connections[connection_num].mode != NETPLAY_CONNECTION_DELAYED_DISCONNECT) break;
|
2016-12-05 05:04:01 +00:00
|
|
|
if (connection_num == netplay->connections_size)
|
2016-12-03 20:17:38 +00:00
|
|
|
{
|
2016-12-05 05:04:01 +00:00
|
|
|
if (connection_num == 0)
|
|
|
|
{
|
2018-04-09 15:35:27 +00:00
|
|
|
netplay->connections = (struct netplay_connection*)
|
|
|
|
malloc(sizeof(struct netplay_connection));
|
|
|
|
|
|
|
|
if (!netplay->connections)
|
2016-12-05 05:04:01 +00:00
|
|
|
{
|
|
|
|
socket_close(new_fd);
|
|
|
|
goto process;
|
|
|
|
}
|
|
|
|
netplay->connections_size = 1;
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
size_t new_connections_size = netplay->connections_size * 2;
|
2018-04-09 15:35:27 +00:00
|
|
|
struct netplay_connection
|
|
|
|
*new_connections = (struct netplay_connection*)
|
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
realloc(netplay->connections,
|
|
|
|
new_connections_size*sizeof(struct netplay_connection));
|
2018-04-09 15:35:27 +00:00
|
|
|
|
|
|
|
if (!new_connections)
|
2016-12-05 05:04:01 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
2016-12-03 20:17:38 +00:00
|
|
|
}
|
2016-12-05 05:04:01 +00:00
|
|
|
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;
|
|
|
|
|
2016-12-09 19:14:54 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-12-03 20:17:38 +00:00
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
netplay_handshake_init_send(netplay, connection);
|
2016-12-03 20:17:38 +00:00
|
|
|
|
2016-09-21 21:23:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-05 05:04:01 +00:00
|
|
|
process:
|
2016-09-21 21:23:36 +00:00
|
|
|
netplay->can_poll = true;
|
2015-12-23 20:25:28 +00:00
|
|
|
input_poll_net();
|
2016-09-16 03:04:48 +00:00
|
|
|
|
2016-12-09 18:32:04 +00:00
|
|
|
return (netplay->stall != NETPLAY_STALL_NO_CONNECTION);
|
2015-12-23 20:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-12-15 13:42:03 +00:00
|
|
|
* netplay_sync_post_frame
|
2015-12-23 20:25:28 +00:00
|
|
|
* @netplay : pointer to netplay object
|
|
|
|
*
|
2016-12-10 04:11:18 +00:00
|
|
|
* Post-frame for Netplay synchronization.
|
2015-12-23 20:25:28 +00:00
|
|
|
* We check if we have new input and replay from recorded input.
|
2016-12-15 13:42:03 +00:00
|
|
|
*/
|
2016-12-17 21:50:06 +00:00
|
|
|
void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
2015-12-23 20:25:28 +00:00
|
|
|
{
|
2016-12-24 20:25:03 +00:00
|
|
|
uint32_t lo_frame_count, hi_frame_count;
|
2016-12-18 01:08:13 +00:00
|
|
|
|
2016-12-17 21:50:06 +00:00
|
|
|
/* Unless we're stalling, we've just finished running a frame */
|
|
|
|
if (!stalled)
|
|
|
|
{
|
2017-01-18 21:07:17 +00:00
|
|
|
netplay->run_ptr = NEXT_PTR(netplay->run_ptr);
|
|
|
|
netplay->run_frame_count++;
|
|
|
|
}
|
|
|
|
|
2017-01-20 19:28:18 +00:00
|
|
|
/* We've finished an input frame even if we're stalling */
|
|
|
|
if ((!stalled || netplay->stall == NETPLAY_STALL_INPUT_LATENCY) &&
|
2018-04-09 15:35:27 +00:00
|
|
|
netplay->self_frame_count <
|
|
|
|
netplay->run_frame_count + netplay->input_latency_frames)
|
2017-01-18 21:07:17 +00:00
|
|
|
{
|
2017-01-20 19:28:18 +00:00
|
|
|
netplay->self_ptr = NEXT_PTR(netplay->self_ptr);
|
|
|
|
netplay->self_frame_count++;
|
2016-12-17 21:50:06 +00:00
|
|
|
}
|
2015-12-23 20:25:28 +00:00
|
|
|
|
2017-04-18 19:25:58 +00:00
|
|
|
/* Only relevant if we're connected and not in a desynching operation */
|
2017-09-10 13:15:06 +00:00
|
|
|
if ((netplay->is_server && (netplay->connected_players<=1)) ||
|
2017-04-18 19:25:58 +00:00
|
|
|
(netplay->self_mode < NETPLAY_CONNECTION_CONNECTED) ||
|
|
|
|
(netplay->desync))
|
2016-09-30 00:48:39 +00:00
|
|
|
{
|
2017-01-20 19:28:18 +00:00
|
|
|
netplay->other_frame_count = netplay->self_frame_count;
|
|
|
|
netplay->other_ptr = netplay->self_ptr;
|
2016-12-16 15:49:31 +00:00
|
|
|
/* FIXME: Duplication */
|
|
|
|
if (netplay->catch_up)
|
|
|
|
{
|
|
|
|
netplay->catch_up = false;
|
|
|
|
input_driver_unset_nonblock_state();
|
2017-01-22 11:47:17 +00:00
|
|
|
driver_set_nonblock_state();
|
2016-12-16 15:49:31 +00:00
|
|
|
}
|
2016-09-13 21:33:26 +00:00
|
|
|
return;
|
2016-09-30 00:48:39 +00:00
|
|
|
}
|
2016-09-13 21:33:26 +00:00
|
|
|
|
2017-02-15 19:40:37 +00:00
|
|
|
/* Reset if it was requested */
|
|
|
|
if (netplay->force_reset)
|
|
|
|
{
|
|
|
|
core_reset();
|
|
|
|
netplay->force_reset = false;
|
|
|
|
}
|
|
|
|
|
2017-09-11 21:46:16 +00:00
|
|
|
netplay->replay_ptr = netplay->other_ptr;
|
|
|
|
netplay->replay_frame_count = netplay->other_frame_count;
|
|
|
|
|
2016-10-05 11:58:01 +00:00
|
|
|
#ifndef DEBUG_NONDETERMINISTIC_CORES
|
2016-09-14 22:03:40 +00:00
|
|
|
if (!netplay->force_rewind)
|
2015-12-23 20:25:28 +00:00
|
|
|
{
|
2017-09-11 21:46:16 +00:00
|
|
|
bool cont = true;
|
|
|
|
|
2016-09-14 22:03:40 +00:00
|
|
|
/* Skip ahead if we predicted correctly.
|
|
|
|
* Skip until our simulation failed. */
|
2016-12-11 01:36:57 +00:00
|
|
|
while (netplay->other_frame_count < netplay->unread_frame_count &&
|
2017-01-18 21:07:17 +00:00
|
|
|
netplay->other_frame_count < netplay->run_frame_count)
|
2016-09-14 22:03:40 +00:00
|
|
|
{
|
2016-09-15 03:19:47 +00:00
|
|
|
struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr];
|
2016-09-14 22:03:40 +00:00
|
|
|
|
2017-09-10 00:44:12 +00:00
|
|
|
/* If resolving the input changes it, we used bad input */
|
|
|
|
if (netplay_resolve_input(netplay, netplay->other_ptr, true))
|
2017-09-11 21:46:16 +00:00
|
|
|
{
|
|
|
|
cont = false;
|
2017-09-10 00:44:12 +00:00
|
|
|
break;
|
2017-09-11 21:46:16 +00:00
|
|
|
}
|
2017-09-10 00:44:12 +00:00
|
|
|
|
2016-09-15 03:19:47 +00:00
|
|
|
netplay_handle_frame_hash(netplay, ptr);
|
2016-09-14 22:03:40 +00:00
|
|
|
netplay->other_ptr = NEXT_PTR(netplay->other_ptr);
|
|
|
|
netplay->other_frame_count++;
|
|
|
|
}
|
2017-09-11 21:46:16 +00:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
2015-12-23 20:25:28 +00:00
|
|
|
}
|
2016-10-05 11:58:01 +00:00
|
|
|
#endif
|
2015-12-23 20:25:28 +00:00
|
|
|
|
Multitudinous fixes and updates to Netplay. Had to be one commit since
they're mostly related:
(1) Renamed frame_count to self_frame_count to be consistent with all
other names.
(2) Previously, it was possible to overwrite data in the ring buffer
that hadn't yet been used. Now that's not possible, but that just
changes one breakage for another: It's now possible to miss the NEW
data. The final resolution for this will probably be requesting stalls.
This is accomplished simply by storing frame numbers in the ring buffer
and checking them against the 'other' head.
(3) In TCP packets, separated cmd_size from cmd. It was beyond pointless
for these to be combined, and restricted cmd_size to 16 bits, which
will probably fail when/if state loading is supported.
(4) Readahead is now allowed. In the past, if the peer got ahead of us,
we would simply ignore their data. Thus, if they got too far ahead of
us, we'd stop reading their data altogether. Fabulous. Now, we're happy
to read future input.
(5) If the peer gets too far ahead of us (currently an unconfigurable 10
frames), fast forward to catch up. This should prevent desync due to
clock drift or stutter.
(6) Used frame_count in a few places where ptr was used. Doing a
comparison of pointers on a ring buffer is a far more dangerous way to
assure we're done with a task than simply using the count, since the
ring buffer is... well, a ring.
(7) Renamed tmp_{ptr,frame_count} to replay_{ptr,frame_count} for
clarity.
(8) Slightly changed the protocol version hash, just to assure that
other clients wouldn't think they were compatible with this one.
(9) There was an off-by-one error which, under some circumstances, could
allow the replay engine to run a complete round through the ring buffer,
replaying stale data. Fixed.
2016-09-12 02:01:47 +00:00
|
|
|
/* Now replay the real input if we've gotten ahead of it */
|
2016-09-14 22:03:40 +00:00
|
|
|
if (netplay->force_rewind ||
|
2018-03-04 04:03:35 +00:00
|
|
|
netplay->replay_frame_count < netplay->run_frame_count)
|
2015-12-23 20:25:28 +00:00
|
|
|
{
|
2016-01-27 06:28:03 +00:00
|
|
|
retro_ctx_serialize_info_t serial_info;
|
2015-12-23 20:25:28 +00:00
|
|
|
|
|
|
|
/* Replay frames. */
|
|
|
|
netplay->is_replay = true;
|
|
|
|
|
2017-09-14 23:41:44 +00:00
|
|
|
/* 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++;
|
|
|
|
}
|
|
|
|
|
2016-09-30 18:03:18 +00:00
|
|
|
if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION)
|
|
|
|
/* Make sure we're initialized before we start loading things */
|
|
|
|
netplay_wait_and_init_serialization(netplay);
|
|
|
|
|
2016-09-15 03:19:47 +00:00
|
|
|
serial_info.data = NULL;
|
|
|
|
serial_info.data_const = netplay->buffer[netplay->replay_ptr].state;
|
|
|
|
serial_info.size = netplay->state_size;
|
2016-09-13 01:18:00 +00:00
|
|
|
|
2016-09-28 19:45:52 +00:00
|
|
|
if (!core_unserialize(&serial_info))
|
|
|
|
{
|
|
|
|
RARCH_ERR("Netplay savestate loading failed: Prepare for desync!\n");
|
|
|
|
}
|
2015-12-23 20:25:28 +00:00
|
|
|
|
2017-01-18 21:07:17 +00:00
|
|
|
while (netplay->replay_frame_count < netplay->run_frame_count)
|
2015-12-23 20:25:28 +00:00
|
|
|
{
|
2016-12-16 03:34:18 +00:00
|
|
|
retro_time_t start, tm;
|
|
|
|
|
2016-09-15 03:19:47 +00:00
|
|
|
struct delta_frame *ptr = &netplay->buffer[netplay->replay_ptr];
|
|
|
|
serial_info.data = ptr->state;
|
2016-01-27 06:28:03 +00:00
|
|
|
serial_info.size = netplay->state_size;
|
2016-04-10 14:35:25 +00:00
|
|
|
serial_info.data_const = NULL;
|
2016-01-27 06:28:03 +00:00
|
|
|
|
2016-12-16 03:34:18 +00:00
|
|
|
start = cpu_features_get_time_usec();
|
|
|
|
|
2016-09-24 12:12:08 +00:00
|
|
|
/* Remember the current state */
|
2016-10-05 02:24:33 +00:00
|
|
|
memset(serial_info.data, 0, serial_info.size);
|
2016-05-07 23:33:57 +00:00
|
|
|
core_serialize(&serial_info);
|
2016-12-11 01:36:57 +00:00
|
|
|
if (netplay->replay_frame_count < netplay->unread_frame_count)
|
2016-09-24 12:12:08 +00:00
|
|
|
netplay_handle_frame_hash(netplay, ptr);
|
2016-01-27 06:28:03 +00:00
|
|
|
|
2016-12-12 22:23:21 +00:00
|
|
|
/* Re-simulate this frame's input */
|
2017-09-10 00:44:12 +00:00
|
|
|
netplay_resolve_input(netplay, netplay->replay_ptr, true);
|
2016-09-15 03:19:47 +00:00
|
|
|
|
2016-05-09 06:17:35 +00:00
|
|
|
autosave_lock();
|
2016-05-07 23:33:57 +00:00
|
|
|
core_run();
|
2016-05-09 06:17:35 +00:00
|
|
|
autosave_unlock();
|
Multitudinous fixes and updates to Netplay. Had to be one commit since
they're mostly related:
(1) Renamed frame_count to self_frame_count to be consistent with all
other names.
(2) Previously, it was possible to overwrite data in the ring buffer
that hadn't yet been used. Now that's not possible, but that just
changes one breakage for another: It's now possible to miss the NEW
data. The final resolution for this will probably be requesting stalls.
This is accomplished simply by storing frame numbers in the ring buffer
and checking them against the 'other' head.
(3) In TCP packets, separated cmd_size from cmd. It was beyond pointless
for these to be combined, and restricted cmd_size to 16 bits, which
will probably fail when/if state loading is supported.
(4) Readahead is now allowed. In the past, if the peer got ahead of us,
we would simply ignore their data. Thus, if they got too far ahead of
us, we'd stop reading their data altogether. Fabulous. Now, we're happy
to read future input.
(5) If the peer gets too far ahead of us (currently an unconfigurable 10
frames), fast forward to catch up. This should prevent desync due to
clock drift or stutter.
(6) Used frame_count in a few places where ptr was used. Doing a
comparison of pointers on a ring buffer is a far more dangerous way to
assure we're done with a task than simply using the count, since the
ring buffer is... well, a ring.
(7) Renamed tmp_{ptr,frame_count} to replay_{ptr,frame_count} for
clarity.
(8) Slightly changed the protocol version hash, just to assure that
other clients wouldn't think they were compatible with this one.
(9) There was an off-by-one error which, under some circumstances, could
allow the replay engine to run a complete round through the ring buffer,
replaying stale data. Fixed.
2016-09-12 02:01:47 +00:00
|
|
|
netplay->replay_ptr = NEXT_PTR(netplay->replay_ptr);
|
|
|
|
netplay->replay_frame_count++;
|
2016-10-05 11:58:01 +00:00
|
|
|
|
|
|
|
#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
|
2016-12-16 03:34:18 +00:00
|
|
|
|
|
|
|
/* 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;
|
2015-12-23 20:25:28 +00:00
|
|
|
}
|
|
|
|
|
2016-12-16 03:34:18 +00:00
|
|
|
/* Average our time */
|
|
|
|
netplay->frame_run_time_avg = netplay->frame_run_time_sum / NETPLAY_FRAME_RUN_TIME_WINDOW;
|
|
|
|
|
2017-01-18 21:07:17 +00:00
|
|
|
if (netplay->unread_frame_count < netplay->run_frame_count)
|
2016-09-12 11:42:35 +00:00
|
|
|
{
|
2016-12-11 01:36:57 +00:00
|
|
|
netplay->other_ptr = netplay->unread_ptr;
|
|
|
|
netplay->other_frame_count = netplay->unread_frame_count;
|
2016-09-14 14:06:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-01-18 21:07:17 +00:00
|
|
|
netplay->other_ptr = netplay->run_ptr;
|
|
|
|
netplay->other_frame_count = netplay->run_frame_count;
|
2016-09-12 11:42:35 +00:00
|
|
|
}
|
2015-12-23 20:25:28 +00:00
|
|
|
netplay->is_replay = false;
|
2016-09-14 22:03:40 +00:00
|
|
|
netplay->force_rewind = false;
|
2015-12-23 20:25:28 +00:00
|
|
|
}
|
Multitudinous fixes and updates to Netplay. Had to be one commit since
they're mostly related:
(1) Renamed frame_count to self_frame_count to be consistent with all
other names.
(2) Previously, it was possible to overwrite data in the ring buffer
that hadn't yet been used. Now that's not possible, but that just
changes one breakage for another: It's now possible to miss the NEW
data. The final resolution for this will probably be requesting stalls.
This is accomplished simply by storing frame numbers in the ring buffer
and checking them against the 'other' head.
(3) In TCP packets, separated cmd_size from cmd. It was beyond pointless
for these to be combined, and restricted cmd_size to 16 bits, which
will probably fail when/if state loading is supported.
(4) Readahead is now allowed. In the past, if the peer got ahead of us,
we would simply ignore their data. Thus, if they got too far ahead of
us, we'd stop reading their data altogether. Fabulous. Now, we're happy
to read future input.
(5) If the peer gets too far ahead of us (currently an unconfigurable 10
frames), fast forward to catch up. This should prevent desync due to
clock drift or stutter.
(6) Used frame_count in a few places where ptr was used. Doing a
comparison of pointers on a ring buffer is a far more dangerous way to
assure we're done with a task than simply using the count, since the
ring buffer is... well, a ring.
(7) Renamed tmp_{ptr,frame_count} to replay_{ptr,frame_count} for
clarity.
(8) Slightly changed the protocol version hash, just to assure that
other clients wouldn't think they were compatible with this one.
(9) There was an off-by-one error which, under some circumstances, could
allow the replay engine to run a complete round through the ring buffer,
replaying stale data. Fixed.
2016-09-12 02:01:47 +00:00
|
|
|
|
2016-12-18 01:08:13 +00:00
|
|
|
if (netplay->is_server)
|
2016-12-24 20:25:03 +00:00
|
|
|
{
|
2017-09-10 00:44:12 +00:00
|
|
|
uint32_t client;
|
2016-12-24 20:25:03 +00:00
|
|
|
|
|
|
|
lo_frame_count = hi_frame_count = netplay->unread_frame_count;
|
|
|
|
|
|
|
|
/* Look for players that are ahead of us */
|
2017-09-10 00:44:12 +00:00
|
|
|
for (client = 0; client < MAX_CLIENTS; client++)
|
2016-12-24 20:25:03 +00:00
|
|
|
{
|
2017-09-10 13:15:06 +00:00
|
|
|
if (!(netplay->connected_players & (1<<client))) continue;
|
2017-09-11 14:40:34 +00:00
|
|
|
if (netplay->read_frame_count[client] > hi_frame_count)
|
|
|
|
hi_frame_count = netplay->read_frame_count[client];
|
2016-12-24 20:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
2016-12-18 01:08:13 +00:00
|
|
|
else
|
2016-12-24 20:25:03 +00:00
|
|
|
{
|
|
|
|
lo_frame_count = hi_frame_count = netplay->server_frame_count;
|
|
|
|
}
|
2016-12-18 01:08:13 +00:00
|
|
|
|
2016-12-15 16:04:05 +00:00
|
|
|
/* If we're behind, try to catch up */
|
2016-12-16 03:34:18 +00:00
|
|
|
if (netplay->catch_up)
|
|
|
|
{
|
2016-12-16 15:49:31 +00:00
|
|
|
/* Are we caught up? */
|
2017-02-24 00:05:43 +00:00
|
|
|
if (netplay->self_frame_count + 1 >= lo_frame_count)
|
2016-12-16 15:49:31 +00:00
|
|
|
{
|
|
|
|
netplay->catch_up = false;
|
|
|
|
input_driver_unset_nonblock_state();
|
2017-01-22 11:47:17 +00:00
|
|
|
driver_set_nonblock_state();
|
2016-12-16 15:49:31 +00:00
|
|
|
}
|
|
|
|
|
2016-12-16 03:34:18 +00:00
|
|
|
}
|
2016-12-18 01:08:13 +00:00
|
|
|
else if (!stalled)
|
2016-12-16 03:34:18 +00:00
|
|
|
{
|
2017-02-24 00:05:43 +00:00
|
|
|
if (netplay->self_frame_count + 3 < lo_frame_count)
|
2016-12-16 15:49:31 +00:00
|
|
|
{
|
2017-02-24 00:05:43 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-12-24 20:25:03 +00:00
|
|
|
|
|
|
|
}
|
2017-02-24 00:05:43 +00:00
|
|
|
else if (netplay->self_frame_count + 3 < hi_frame_count)
|
2016-12-24 20:25:03 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
2017-02-24 00:05:43 +00:00
|
|
|
netplay->catch_up_time = 0;
|
2016-12-24 20:25:03 +00:00
|
|
|
|
|
|
|
/* 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++)
|
|
|
|
{
|
2017-09-10 00:44:12 +00:00
|
|
|
uint32_t client_num;
|
2018-04-09 13:56:45 +00:00
|
|
|
struct netplay_connection *connection = &netplay->connections[i];
|
|
|
|
|
2016-12-24 20:25:03 +00:00
|
|
|
if (!connection->active ||
|
|
|
|
connection->mode != NETPLAY_CONNECTION_PLAYING)
|
|
|
|
continue;
|
2018-04-09 13:56:45 +00:00
|
|
|
|
|
|
|
client_num = (uint32_t)(i + 1);
|
2016-12-24 20:25:03 +00:00
|
|
|
|
|
|
|
/* Are they ahead? */
|
2017-09-11 14:40:34 +00:00
|
|
|
if (netplay->self_frame_count + 3 < netplay->read_frame_count[client_num])
|
2016-12-24 20:25:03 +00:00
|
|
|
{
|
|
|
|
/* Tell them to stall */
|
|
|
|
if (connection->stall_frame + NETPLAY_MAX_REQ_STALL_FREQUENCY <
|
2017-01-20 19:28:18 +00:00
|
|
|
netplay->self_frame_count)
|
2016-12-24 20:25:03 +00:00
|
|
|
{
|
2017-01-20 19:28:18 +00:00
|
|
|
connection->stall_frame = netplay->self_frame_count;
|
2016-12-24 20:25:03 +00:00
|
|
|
netplay_cmd_stall(netplay, connection,
|
2017-09-11 14:40:34 +00:00
|
|
|
netplay->read_frame_count[client_num] -
|
2017-01-20 19:28:18 +00:00
|
|
|
netplay->self_frame_count + 1);
|
2016-12-24 20:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-12-16 15:49:31 +00:00
|
|
|
}
|
2017-02-24 00:05:43 +00:00
|
|
|
else
|
|
|
|
netplay->catch_up_time = 0;
|
2016-12-16 03:34:18 +00:00
|
|
|
}
|
2017-02-24 00:05:43 +00:00
|
|
|
else
|
|
|
|
netplay->catch_up_time = 0;
|
2015-12-23 20:25:28 +00:00
|
|
|
}
|