// TODO: Somewhat better prediction. :P static void simulate_input(netplay_t *handle) { size_t ptr = PREV_PTR(handle->self_ptr); size_t prev = PREV_PTR(handle->read_ptr); handle->buffer[ptr].simulated_input_state = handle->buffer[prev].real_input_state; handle->buffer[ptr].is_simulated = true; handle->buffer[ptr].used_real = false; }
/** * Removes this block from the list of * free blocks. * @param - bp - pointer to a block that is on the list of free blocks. */ static void mm_remove(void *bp){ /* if prev bp, then set it's next to bp next */ if (PREV_PTR(bp)){ SET_NEXT(PREV_PTR(bp), NEXT_PTR(bp)); } else{ /* set listp to bp's next */ listp = NEXT_PTR(bp); } /* set prev of next after bp to prev of bp */ SET_PREV(NEXT_PTR(bp), PREV_PTR(bp)); }
/* TODO: Somewhat better prediction. :P */ static void simulate_input(netplay_t *netplay) { size_t ptr = PREV_PTR(netplay->self_ptr); size_t prev = PREV_PTR(netplay->read_ptr); memcpy(netplay->buffer[ptr].simulated_input_state, netplay->buffer[prev].real_input_state, sizeof(netplay->buffer[prev].real_input_state)); netplay->buffer[ptr].is_simulated = true; netplay->buffer[ptr].used_real = false; }
/** * netplay_simulate_input: * @netplay : pointer to netplay object * @sim_ptr : frame index for which to simulate input * * "Simulate" input by assuming it hasn't changed since the last read input. */ void netplay_simulate_input(netplay_t *netplay, uint32_t sim_ptr) { 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)); }
static int16_t netplay_input_state(netplay_t *netplay, bool port, unsigned device, unsigned idx, unsigned id) { size_t ptr = netplay->is_replay ? netplay->tmp_ptr : PREV_PTR(netplay->self_ptr); const uint32_t *curr_input_state = netplay->buffer[ptr].self_state; if (netplay->port == (netplay_flip_port(netplay, port) ? 1 : 0)) { if (netplay->buffer[ptr].is_simulated) curr_input_state = netplay->buffer[ptr].simulated_input_state; else curr_input_state = netplay->buffer[ptr].real_input_state; } switch (device) { case RETRO_DEVICE_JOYPAD: return ((1 << id) & curr_input_state[0]) ? 1 : 0; case RETRO_DEVICE_ANALOG: { uint32_t state = curr_input_state[1 + idx]; return (int16_t)(uint16_t)(state >> (id * 16)); } default: return 0; } }
static void del(void *bp){ /* if previous bp exists, then we link it to next to bp next */ if (PREV_PTR(bp)) NEXT_PTR(PREV_PTR(bp))=NEXT_PTR(bp); else /* else our new top will be next of bp, if we are deleting our top */ lptr = NEXT_PTR(bp); /* relinking */ PREV_PTR(NEXT_PTR(bp))=PREV_PTR(bp); }
static void insert(void *bp){ /* linking bp to our stack top */ NEXT_PTR(bp)=lptr; /* previous of our stack top will be our bp */ PREV_PTR(lptr)=bp; /* prev bp will be NULL because it is our new top */ PREV_PTR(bp)=NULL; /* now we need to save our new top */ lptr = bp; }
/* put header, footer and free_list pointers in a free block */ void free_ptr(void *ptr, void *prev, void *next, unsigned size) { unsigned header = PACK(size, 0); // header PUT_INT(HDRP(ptr), header); // footer PUT_INT(FTRP(ptr), header); // prev and next PUT(PREV_PTR(ptr), prev); PUT(NEXT_PTR(ptr), next); }
/* * mm_init - initialize the malloc package. * * Allocates free_list next and prev pointers as a circular linked list. * Creates an "allocated" footer to prevent coalescing at the start of the * heap. */ int mm_init(void) { // for free_list head and tail mem_sbrk(2 * DSIZE); // set head and tail to point to free_list PUT(PREV_PTR(free_list), free_list); PUT(NEXT_PTR(free_list), free_list); // ensure there is space for an extra double word before the heap begins void *heap_start = mem_sbrk(DSIZE); // set allocation before the first block PUT_INT(heap_start, 1); return 0; }
/** * 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, size_t sim_ptr, bool resim) { uint32_t player; size_t prev; struct delta_frame *simframe, *pframe; simframe = &netplay->buffer[sim_ptr]; for (player = 0; player < MAX_USERS; player++) { if (!(netplay->connected_players & (1<<player))) continue; if (simframe->have_real[player]) continue; prev = PREV_PTR(netplay->read_ptr[player]); pframe = &netplay->buffer[prev]; 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[player][0] & keep; sim_state |= pframe->real_input_state[player][0] & ~keep; simframe->simulated_input_state[player][0] = sim_state; } else { memcpy(simframe->simulated_input_state[player], pframe->real_input_state[player], WORDS_PER_INPUT * sizeof(uint32_t)); } } }
static int16_t netplay_input_state(netplay_t *netplay, bool port, unsigned device, unsigned idx, unsigned id) { size_t ptr = netplay->is_replay ? netplay->tmp_ptr : PREV_PTR(netplay->self_ptr); uint16_t curr_input_state = netplay->buffer[ptr].self_state; if (netplay->port == (netplay_flip_port(netplay, port) ? 1 : 0)) { if (netplay->buffer[ptr].is_simulated) curr_input_state = netplay->buffer[ptr].simulated_input_state; else curr_input_state = netplay->buffer[ptr].real_input_state; } return ((1 << id) & curr_input_state) ? 1 : 0; }
int16_t netplay_input_state(netplay_t *handle, bool port, unsigned device, unsigned index, unsigned id) { uint16_t input_state = 0; size_t ptr = handle->is_replay ? handle->tmp_ptr : PREV_PTR(handle->self_ptr); port = netplay_flip_port(handle, port); if ((port ? 1 : 0) == handle->port) { if (handle->buffer[ptr].is_simulated) input_state = handle->buffer[ptr].simulated_input_state; else input_state = handle->buffer[ptr].real_input_state; } else input_state = handle->buffer[ptr].self_state; return ((1 << id) & input_state) ? 1 : 0; }
/** * Prints the block payload which * bp is pointing to. * @param bp - pointer to block */ static void printBlock(void *bp){ size_t hsize = GET_HSIZE(bp); size_t halloc = GET_HALLOC(bp); size_t fsize = GET_HSIZE(bp); size_t falloc = GET_HALLOC(bp); if (hsize == 0) { printf("%p: EOL\n", bp); return; } if (halloc) printf("%p: header:[%d:%c] footer:[%d:%c]\n", bp, hsize, (halloc ? 'a' : 'f'), fsize, (falloc ? 'a' : 'f')); else printf("%p:header:[%d:%c] prev:%p next:%p footer:[%d:%c]\n", bp, hsize, (halloc ? 'a' : 'f'), PREV_PTR(bp), NEXT_PTR(bp), fsize, (falloc ? 'a' : 'f')); }
/** * netplay_net_post_frame: * @netplay : pointer to netplay object * * Post-frame for Netplay (normal version). * We check if we have new input and replay from recorded input. **/ static void netplay_net_post_frame(netplay_t *netplay) { netplay->self_ptr = NEXT_PTR(netplay->self_ptr); netplay->self_frame_count++; /* Only relevant if we're connected */ if (!netplay->has_connection) { netplay->read_frame_count = netplay->other_frame_count = netplay->self_frame_count; netplay->read_ptr = netplay->other_ptr = netplay->self_ptr; return; } #ifndef DEBUG_NONDETERMINISTIC_CORES if (!netplay->force_rewind) { /* Skip ahead if we predicted correctly. * Skip until our simulation failed. */ while (netplay->other_frame_count < netplay->read_frame_count && netplay->other_frame_count < netplay->self_frame_count) { struct delta_frame *ptr = &netplay->buffer[netplay->other_ptr]; if (memcmp(ptr->simulated_input_state, ptr->real_input_state, sizeof(ptr->real_input_state)) != 0 && !ptr->used_real) break; netplay_handle_frame_hash(netplay, ptr); netplay->other_ptr = NEXT_PTR(netplay->other_ptr); netplay->other_frame_count++; } } #endif /* Now replay the real input if we've gotten ahead of it */ if (netplay->force_rewind || (netplay->other_frame_count < netplay->read_frame_count && netplay->other_frame_count < netplay->self_frame_count)) { retro_ctx_serialize_info_t serial_info; /* Replay frames. */ netplay->is_replay = true; netplay->replay_ptr = netplay->other_ptr; netplay->replay_frame_count = netplay->other_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->self_frame_count) { 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; /* Remember the current state */ memset(serial_info.data, 0, serial_info.size); core_serialize(&serial_info); if (netplay->replay_frame_count < netplay->read_frame_count) netplay_handle_frame_hash(netplay, ptr); /* Simulate this frame's input */ if (netplay->replay_frame_count >= netplay->read_frame_count) netplay_simulate_input(netplay, netplay->replay_ptr); 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 } if (netplay->read_frame_count < netplay->self_frame_count) { netplay->other_ptr = netplay->read_ptr; netplay->other_frame_count = netplay->read_frame_count; } else { netplay->other_ptr = netplay->self_ptr; netplay->other_frame_count = netplay->self_frame_count; } netplay->is_replay = false; netplay->force_rewind = false; } /* If we're supposed to stall, rewind (we shouldn't get this far if we're * stalled, so this is a last resort) */ if (netplay->stall) { retro_ctx_serialize_info_t serial_info; netplay->self_ptr = PREV_PTR(netplay->self_ptr); netplay->self_frame_count--; serial_info.data = NULL; serial_info.data_const = netplay->buffer[netplay->self_ptr].state; serial_info.size = netplay->state_size; core_unserialize(&serial_info); } }
/** * netplay_poll: * @netplay : pointer to netplay object * * Polls network to see if we have anything new. If our * network buffer is full, we simply have to block * for new input data. * * Returns: true (1) if successful, otherwise false (0). **/ static bool netplay_poll(netplay_t *netplay) { int res; if (!netplay->has_connection) return false; netplay->can_poll = false; if (!get_self_input_state(netplay)) return false; /* We skip reading the first frame so the host has a chance to grab * our host info so we don't block forever :') */ if (netplay->frame_count == 0) { netplay->buffer[0].used_real = true; netplay->buffer[0].is_simulated = false; memset(netplay->buffer[0].real_input_state, 0, sizeof(netplay->buffer[0].real_input_state)); netplay->read_ptr = NEXT_PTR(netplay->read_ptr); netplay->read_frame_count++; return true; } /* We might have reached the end of the buffer, where we * simply have to block. */ res = poll_input(netplay, netplay->other_ptr == netplay->self_ptr); if (res == -1) { netplay->has_connection = false; warn_hangup(); return false; } if (res == 1) { uint32_t first_read = netplay->read_frame_count; do { uint32_t buffer[UDP_FRAME_PACKETS * UDP_WORDS_PER_FRAME]; if (!receive_data(netplay, buffer, sizeof(buffer))) { warn_hangup(); netplay->has_connection = false; return false; } parse_packet(netplay, buffer, UDP_FRAME_PACKETS); } while ((netplay->read_frame_count <= netplay->frame_count) && poll_input(netplay, (netplay->other_ptr == netplay->self_ptr) && (first_read == netplay->read_frame_count)) == 1); } else { /* Cannot allow this. Should not happen though. */ if (netplay->self_ptr == netplay->other_ptr) { warn_hangup(); return false; } } if (netplay->read_ptr != netplay->self_ptr) simulate_input(netplay); else netplay->buffer[PREV_PTR(netplay->self_ptr)].used_real = true; return true; }
/* * mm_malloc - Allocate as an explicit free list. * * This is done using a first fit policy on the minimum size of the block. * The first fitting block may be split up into a smaller free block and an * allocated free block to fit the block requested by malloc. The free block * will precede the allocated block to prevent the need to move pointers. * * If the block does not fit, a new allocated block is created, which may * round the block size up to the nearest power of 2. An "allocated" block is * created at the end to prevent coalescing past the heap end. */ void *mm_malloc(size_t size) { // use malloc_size or fit_size unsigned newsize = fit_size(size); // finger = free_list->next size_t *finger = (size_t *)GET(NEXT_PTR(free_list)); size_t *p = (size_t *)free_list; unsigned fitSize = ~0; // iterate linked list while (finger != free_list) { unsigned blkSize = GET_SIZE(HDRP(finger)); // look for best fit, exit early if perfect fit if (blkSize == newsize) { p = finger; break; } // remember best fit if (blkSize > newsize && blkSize < fitSize) { p = finger; fitSize = blkSize; } // finger = finger->next finger = (size_t *)GET(NEXT_PTR(finger)); } // if block doesn't fit in any free block, allocate more heap space if (p == free_list) { // round up with malloc_size newsize = malloc_size(size); // if block before end of heap is a free block, coalesce void *prevPtr = PREV_BLKP(ADD_PTR(mem_heap_hi(), 1)); if (prevPtr < mem_heap_hi() && !GET_ALLOC(HDRP(prevPtr))) { // calculate new size to sbrk unsigned allocSize = newsize - GET_SIZE(HDRP(prevPtr)); p = mem_sbrk(allocSize); if ((int)(p) == -1) return NULL; // prevPtr->prev->next = prevPtr->next; PUT(NEXT_PTR(GET(PREV_PTR(prevPtr))), GET(NEXT_PTR(prevPtr))); // prevPtr->next->prev = prevPtr->prev; PUT(PREV_PTR(GET(NEXT_PTR(prevPtr))), GET(PREV_PTR(prevPtr))); // allocate prevPtr to new size alloc_ptr(prevPtr, newsize); // set allocation before the last block PUT_INT(HDRP(NEXT_BLKP(prevPtr)), 1); return prevPtr; } p = mem_sbrk(newsize); if ((int)(p) == -1) return NULL; alloc_ptr(p, newsize); // set allocation before the last block PUT_INT(HDRP(NEXT_BLKP(p)), 1); return p; } unsigned freeSize = GET_SIZE(HDRP(p)); if (freeSize - newsize >= FREE_OVERHEAD) { // split free block into free and allocated blocks unsigned header = freeSize - newsize; // header PUT_INT(HDRP(p), header); // footer PUT_INT(FTRP(p), header); p = ADD_PTR(p, freeSize - newsize); } else { // allocate the whole free block newsize = freeSize; // p->prev->next = p->next PUT(NEXT_PTR(GET(PREV_PTR(p))), GET(NEXT_PTR(p))); // p->next->prev = p->prev PUT(PREV_PTR(GET(NEXT_PTR(p))), GET(PREV_PTR(p))); } // sets header and footer with allocate bit set alloc_ptr(p, newsize); return p; }
/* coalesce a pointer and free it */ void coalesce_free_ptr(void *ptr, unsigned size) { // check next ptr if (!GET_ALLOC(HDRP(NEXT_BLKP(ptr)))) { unsigned nextSize = GET_SIZE(HDRP(NEXT_BLKP(ptr))); void *nextPtr = NEXT_BLKP(ptr); // check prev ptr if (!GET_ALLOC(HDRP(PREV_BLKP(ptr)))) { // coalesce with both adjacent blocks unsigned prevSize = GET_SIZE(HDRP(PREV_BLKP(ptr))); void *prevPtr = ADD_PTR(ptr, -prevSize); // prevPtr->next->prev = prevPtr->prev; PUT(PREV_PTR(GET(NEXT_PTR(prevPtr))), GET(PREV_PTR(prevPtr))); // prevPtr->prev->next = prevPtr->next; PUT(NEXT_PTR(GET(PREV_PTR(prevPtr))), GET(NEXT_PTR(prevPtr))); // nextPtr->next->prev = nextPtr->prev; PUT(PREV_PTR(GET(NEXT_PTR(nextPtr))), GET(PREV_PTR(nextPtr))); // nextPtr->prev->next = nextPtr->next; PUT(NEXT_PTR(GET(PREV_PTR(nextPtr))), GET(NEXT_PTR(nextPtr))); // create coalesced free block, stick block at end of free_list free_ptr(prevPtr, (void *)GET(PREV_PTR(free_list)), free_list, prevSize + size + nextSize); // tail->prev->next = prevPtr; PUT(NEXT_PTR(GET(PREV_PTR(free_list))), prevPtr); // tail->prev = prevPtr; PUT(PREV_PTR(free_list), prevPtr); return; } // coalesce with next block // nextPtr->prev->next = ptr; PUT(NEXT_PTR(GET(PREV_PTR(nextPtr))), ptr); // nextPtr->next->prev = ptr; PUT(PREV_PTR(GET(NEXT_PTR(nextPtr))), ptr); // create coalesced free block free_ptr(ptr, (void *)GET(PREV_PTR(nextPtr)), (void *)GET(NEXT_PTR(nextPtr)), size + nextSize); return; } if (!GET_ALLOC(HDRP(PREV_BLKP(ptr)))) { // coalesce with previous block unsigned prevSize = GET_SIZE(HDRP(PREV_BLKP(ptr))); void *prevPtr = ADD_PTR(ptr, -prevSize); // create coalesced free block free_ptr(prevPtr, (void *)GET(PREV_PTR(prevPtr)), (void *)GET(NEXT_PTR(prevPtr)), prevSize + size); } else { // don't coalesce // tail->prev->next = ptr; PUT(NEXT_PTR(GET(PREV_PTR(free_list))), ptr); // create free block free_ptr(ptr, (void *)GET(PREV_PTR(free_list)), free_list, size); // tail->prev = ptr; PUT(PREV_PTR(free_list), ptr); } }
// Poll network to see if we have anything new. If our network buffer is full, we simply have to block for new input data. static bool netplay_poll(netplay_t *handle) { if (!handle->has_connection) return false; handle->can_poll = false; if (!get_self_input_state(handle)) return false; // We skip reading the first frame so the host has a chance to grab our host info so we don't block forever :') if (handle->frame_count == 0) { handle->buffer[0].used_real = true; handle->buffer[0].is_simulated = false; handle->buffer[0].real_input_state = 0; handle->read_ptr = NEXT_PTR(handle->read_ptr); handle->read_frame_count++; return true; } // We might have reached the end of the buffer, where we simply have to block. int res = poll_input(handle, handle->other_ptr == handle->self_ptr); if (res == -1) { handle->has_connection = false; warn_hangup(); return false; } if (res == 1) { uint32_t first_read = handle->read_frame_count; do { uint32_t buffer[UDP_FRAME_PACKETS * 2]; if (!receive_data(handle, buffer, sizeof(buffer))) { warn_hangup(); handle->has_connection = false; return false; } parse_packet(handle, buffer, UDP_FRAME_PACKETS); } while ((handle->read_frame_count <= handle->frame_count) && poll_input(handle, (handle->other_ptr == handle->self_ptr) && (first_read == handle->read_frame_count)) == 1); } else { // Cannot allow this. Should not happen though. if (handle->self_ptr == handle->other_ptr) { warn_hangup(); return false; } } if (handle->read_ptr != handle->self_ptr) simulate_input(handle); else handle->buffer[PREV_PTR(handle->self_ptr)].used_real = true; return true; }
static bool netplay_get_cmd(netplay_t *netplay) { uint32_t cmd; uint32_t flip_frame; uint32_t cmd_size; /* FIXME: This depends on delta_frame_ready */ netplay->timeout_cnt = 0; if (!socket_receive_all_blocking(netplay->fd, &cmd, sizeof(cmd))) return false; cmd = ntohl(cmd); if (!socket_receive_all_blocking(netplay->fd, &cmd_size, sizeof(cmd))) return false; cmd_size = ntohl(cmd_size); switch (cmd) { case NETPLAY_CMD_ACK: /* Why are we even bothering? */ return true; case NETPLAY_CMD_NAK: /* Disconnect now! */ return false; case NETPLAY_CMD_INPUT: { uint32_t buffer[WORDS_PER_FRAME]; unsigned i; if (cmd_size != WORDS_PER_FRAME * sizeof(uint32_t)) { RARCH_ERR("NETPLAY_CMD_INPUT received an unexpected payload size.\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking(netplay->fd, buffer, sizeof(buffer))) { RARCH_ERR("Failed to receive NETPLAY_CMD_INPUT input.\n"); return netplay_cmd_nak(netplay); } for (i = 0; i < WORDS_PER_FRAME; i++) buffer[i] = ntohl(buffer[i]); if (buffer[0] < netplay->read_frame_count) { /* We already had this, so ignore the new transmission */ return true; } else if (buffer[0] > netplay->read_frame_count) { /* Out of order = out of luck */ return netplay_cmd_nak(netplay); } /* The data's good! */ netplay->buffer[netplay->read_ptr].have_remote = true; memcpy(netplay->buffer[netplay->read_ptr].real_input_state, buffer + 1, sizeof(buffer) - sizeof(uint32_t)); netplay->read_ptr = NEXT_PTR(netplay->read_ptr); netplay->read_frame_count++; return true; } case NETPLAY_CMD_FLIP_PLAYERS: if (cmd_size != sizeof(uint32_t)) { RARCH_ERR("CMD_FLIP_PLAYERS received an unexpected command size.\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking( netplay->fd, &flip_frame, sizeof(flip_frame))) { RARCH_ERR("Failed to receive CMD_FLIP_PLAYERS argument.\n"); return netplay_cmd_nak(netplay); } flip_frame = ntohl(flip_frame); if (flip_frame < netplay->read_frame_count) { RARCH_ERR("Host asked us to flip users in the past. Not possible ...\n"); return netplay_cmd_nak(netplay); } netplay->flip ^= true; netplay->flip_frame = flip_frame; /* Force a rewind to assure the flip happens: This just prevents us * from skipping other past the flip because our prediction was * correct */ if (flip_frame < netplay->self_frame_count) netplay->force_rewind = true; RARCH_LOG("Netplay users are flipped.\n"); runloop_msg_queue_push("Netplay users are flipped.", 1, 180, false); return true; case NETPLAY_CMD_SPECTATE: RARCH_ERR("NETPLAY_CMD_SPECTATE unimplemented.\n"); return netplay_cmd_nak(netplay); case NETPLAY_CMD_DISCONNECT: hangup(netplay); return true; case NETPLAY_CMD_CRC: { uint32_t buffer[2]; size_t tmp_ptr = netplay->self_ptr; bool found = false; if (cmd_size != sizeof(buffer)) { RARCH_ERR("NETPLAY_CMD_CRC received unexpected payload size.\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking(netplay->fd, buffer, sizeof(buffer))) { RARCH_ERR("NETPLAY_CMD_CRC failed to receive payload.\n"); return netplay_cmd_nak(netplay); } buffer[0] = ntohl(buffer[0]); buffer[1] = ntohl(buffer[1]); /* Received a CRC for some frame. If we still have it, check if it * matched. This approach could be improved with some quick modular * arithmetic. */ do { if ( netplay->buffer[tmp_ptr].used && netplay->buffer[tmp_ptr].frame == buffer[0]) { found = true; break; } tmp_ptr = PREV_PTR(tmp_ptr); } while (tmp_ptr != netplay->self_ptr); if (!found) { /* Oh well, we got rid of it! */ return true; } if (buffer[0] <= netplay->other_frame_count) { /* We've already replayed up to this frame, so we can check it * directly */ uint32_t local_crc = netplay_delta_frame_crc( netplay, &netplay->buffer[tmp_ptr]); if (buffer[1] != local_crc) { /* Problem! */ netplay_cmd_request_savestate(netplay); } } else { /* We'll have to check it when we catch up */ netplay->buffer[tmp_ptr].crc = buffer[1]; } return true; } case NETPLAY_CMD_REQUEST_SAVESTATE: /* Delay until next frame so we don't send the savestate after the * input */ netplay->force_send_savestate = true; return true; case NETPLAY_CMD_LOAD_SAVESTATE: { uint32_t frame; /* Make sure we're ready for it */ if (netplay->quirks & NETPLAY_QUIRK_INITIALIZATION) { if (!netplay->is_replay) { netplay->is_replay = true; netplay->replay_ptr = netplay->self_ptr; netplay->replay_frame_count = netplay->self_frame_count; netplay_wait_and_init_serialization(netplay); netplay->is_replay = false; } else { netplay_wait_and_init_serialization(netplay); } } /* There is a subtlty in whether the load comes before or after the * current frame: * * If it comes before the current frame, then we need to force a * rewind to that point. * * If it comes after the current frame, we need to jump ahead, then * (strangely) force a rewind to the frame we're already on, so it * gets loaded. This is just to avoid having reloading implemented in * too many places. */ if (cmd_size > netplay->state_size + sizeof(uint32_t)) { RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking(netplay->fd, &frame, sizeof(frame))) { RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate frame.\n"); return netplay_cmd_nak(netplay); } frame = ntohl(frame); if (frame != netplay->read_frame_count) { RARCH_ERR("CMD_LOAD_SAVESTATE loading a state out of order!\n"); return netplay_cmd_nak(netplay); } if (!socket_receive_all_blocking(netplay->fd, netplay->buffer[netplay->read_ptr].state, cmd_size - sizeof(uint32_t))) { RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n"); return netplay_cmd_nak(netplay); } /* Skip ahead if it's past where we are */ if (frame > netplay->self_frame_count) { /* This is squirrely: We need to assure that when we advance the * frame in post_frame, THEN we're referring to the frame to * load into. If we refer directly to read_ptr, then we'll end * up never reading the input for read_frame_count itself, which * will make the other side unhappy. */ netplay->self_ptr = PREV_PTR(netplay->read_ptr); netplay->self_frame_count = frame - 1; } /* And force rewind to it */ netplay->force_rewind = true; netplay->savestate_request_outstanding = false; netplay->other_ptr = netplay->read_ptr; netplay->other_frame_count = frame; return true; } case NETPLAY_CMD_PAUSE: netplay->remote_paused = true; return true; case NETPLAY_CMD_RESUME: netplay->remote_paused = false; return true; default: break; } RARCH_ERR("Unknown netplay command received.\n"); return netplay_cmd_nak(netplay); }