// called from ---CPU--- thread bool NetPlayClient::GetNetPads(const u8 pad_nb, GCPadStatus* pad_status) { // The interface for this is extremely silly. // // Imagine a physical device that links three GameCubes together // and emulates NetPlay that way. Which GameCube controls which // in-game controllers can be configured on the device (m_pad_map) // but which sockets on each individual GameCube should be used // to control which players? The solution that Dolphin uses is // that we hardcode the knowledge that they go in order, so if // you have a 3P game with three GameCubes, then every single // controller should be plugged into slot 1. // // If you have a 4P game, then one of the GameCubes will have // a controller plugged into slot 1, and another in slot 2. // // The slot number is the "local" pad number, and what player // it actually means is the "in-game" pad number. // // The interface here gives us the status of local pads, and // expects to get back "in-game" pad numbers back in response. // e.g. it asks "here's the input that slot 1 has, and by the // way, what's the state of P1?" // // We should add this split between "in-game" pads and "local" // pads higher up. int in_game_num = LocalPadToInGamePad(pad_nb); // If this in-game pad is one of ours, then update from the // information given. if (in_game_num < 4) { // adjust the buffer either up or down // inserting multiple padstates or dropping states while (m_pad_buffer[in_game_num].Size() <= m_target_buffer_size) { // add to buffer m_pad_buffer[in_game_num].Push(*pad_status); // send SendPadState(in_game_num, *pad_status); } } // Now, we need to swap out the local value with the values // retrieved from NetPlay. This could be the value we pushed // above if we're configured as P1 and the code is trying // to retrieve data for slot 1. while (!m_pad_buffer[pad_nb].Pop(*pad_status)) { if (!m_is_running.load()) return false; // TODO: use a condition instead of sleeping Common::SleepCurrentThread(1); } if (Movie::IsRecordingInput()) { Movie::RecordInput(pad_status, pad_nb); Movie::InputUpdate(); } else { Movie::CheckPadStatus(pad_status, pad_nb); } return true; }
// called from ---CPU--- thread bool NetPlayClient::GetNetPads(const int pad_nb, GCPadStatus* pad_status) { // The interface for this is extremely silly. // // Imagine a physical device that links three GameCubes together // and emulates NetPlay that way. Which GameCube controls which // in-game controllers can be configured on the device (m_pad_map) // but which sockets on each individual GameCube should be used // to control which players? The solution that Dolphin uses is // that we hardcode the knowledge that they go in order, so if // you have a 3P game with three GameCubes, then every single // controller should be plugged into slot 1. // // If you have a 4P game, then one of the GameCubes will have // a controller plugged into slot 1, and another in slot 2. // // The slot number is the "local" pad number, and what player // it actually means is the "in-game" pad number. // When the 1st in-game pad is polled, we assume the others will // will be polled as well. To reduce latency, we poll all local // controllers at once and then send the status to the other // clients. if (IsFirstInGamePad(pad_nb)) { const int num_local_pads = NumLocalPads(); for (int local_pad = 0; local_pad < num_local_pads; local_pad++) { switch (SConfig::GetInstance().m_SIDevice[local_pad]) { case SIDEVICE_WIIU_ADAPTER: *pad_status = GCAdapter::Input(local_pad); break; case SIDEVICE_GC_CONTROLLER: default: *pad_status = Pad::GetStatus(local_pad); break; } int ingame_pad = LocalPadToInGamePad(local_pad); // adjust the buffer either up or down // inserting multiple padstates or dropping states while (m_pad_buffer[ingame_pad].Size() <= m_target_buffer_size) { // add to buffer m_pad_buffer[ingame_pad].Push(*pad_status); // send SendPadState(ingame_pad, *pad_status); } } } // Now, we either use the data pushed earlier, or wait for the // other clients to send it to us while (m_pad_buffer[pad_nb].Size() == 0) { if (!m_is_running.IsSet()) { return false; } m_gc_pad_event.Wait(); } m_pad_buffer[pad_nb].Pop(*pad_status); if (Movie::IsRecordingInput()) { Movie::RecordInput(pad_status, pad_nb); Movie::InputUpdate(); } else { Movie::CheckPadStatus(pad_status, pad_nb); } return true; }