Better netplay input simulation

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.
This commit is contained in:
Gregor Richards 2016-12-18 00:46:02 -05:00
parent 1c0d8f3927
commit 1245d1b5dd
3 changed files with 39 additions and 7 deletions

View File

@ -822,15 +822,47 @@ static int poll_input(netplay_t *netplay, bool block)
* netplay_simulate_input:
* @netplay : pointer to netplay object
* @sim_ptr : frame index for which to simulate 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.
*/
void netplay_simulate_input(netplay_t *netplay, uint32_t sim_ptr)
void netplay_simulate_input(netplay_t *netplay, uint32_t sim_ptr, bool resim)
{
size_t prev = PREV_PTR(netplay->read_ptr);
memcpy(netplay->buffer[sim_ptr].simulated_input_state,
netplay->buffer[prev].real_input_state,
sizeof(netplay->buffer[prev].real_input_state));
struct delta_frame *pframe = &netplay->buffer[prev],
*simframe = &netplay->buffer[sim_ptr];
if (resim)
{
/* 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);
uint32_t sim_state = simframe->simulated_input_state[0] & keep;
sim_state |= pframe->real_input_state[0] & ~keep;
simframe->simulated_input_state[0] = sim_state;
}
else
{
memcpy(simframe->simulated_input_state,
pframe->real_input_state,
sizeof(pframe->real_input_state));
}
}
@ -874,7 +906,7 @@ static bool netplay_poll(void)
/* Simulate the input if we don't have real input */
if (!netplay_data->buffer[netplay_data->self_ptr].have_remote)
netplay_simulate_input(netplay_data, netplay_data->self_ptr);
netplay_simulate_input(netplay_data, netplay_data->self_ptr, false);
/* Consider stalling */
switch (netplay_data->stall)

View File

@ -280,7 +280,7 @@ static void netplay_net_post_frame(netplay_t *netplay)
/* Simulate this frame's input */
if (netplay->replay_frame_count >= netplay->read_frame_count)
netplay_simulate_input(netplay, netplay->replay_ptr);
netplay_simulate_input(netplay, netplay->replay_ptr, true);
autosave_lock();
core_run();

View File

@ -234,7 +234,7 @@ bool netplay_init_serialization(netplay_t *netplay);
/* Force serialization to be ready by fast-forwarding the core */
bool netplay_wait_and_init_serialization(netplay_t *netplay);
void netplay_simulate_input(netplay_t *netplay, uint32_t sim_ptr);
void netplay_simulate_input(netplay_t *netplay, uint32_t sim_ptr, bool resim);
void netplay_log_connection(const struct sockaddr_storage *their_addr,
unsigned slot, const char *nick);