diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index d82108f640..53ebcffed3 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -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<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) diff --git a/network/netplay/netplay_net.c b/network/netplay/netplay_net.c index f9cb1c549f..f90d2362ef 100644 --- a/network/netplay/netplay_net.c +++ b/network/netplay/netplay_net.c @@ -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(); diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index e8bf108da7..faff16b4a2 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -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);