mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-08-07 04:39:26 -05:00
Now SerialInterfaceManager::UpdateDevices reads the configured SI devices directly from Config instead. The main reason why I wanted to do this is so that Android can change SI devices while emulation is running. (Android didn't have the code for calling ChangeDevice.) But when implementing the change, I noticed that NetPlay and Movie were using ChangeDevice as a way of overriding the SI devices configured by the user. Replacing this ended up making the change larger than I first anticipated. For Wii Remotes, there was no equivalent to ChangeDevice, so NetPlay and Movie were using Config::SetCurrent to override the Wii Remote source configured by the user. If we can use the config system to override Wii Remote sources, why not do the same for SI devices? This commit makes NetPlay and Movie set SI devices and Wii Remote sources in the NetPlay and Movie config layers, as that is the conceptually appropriate place to do it. As far as I can tell, the old Movie code for overriding SI devices and Wii Remote sources didn't actually work. This new code does. I didn't investigate exactly why it didn't work, but maybe it's because loading a movie happens before emulation actually starts.
354 lines
12 KiB
C++
354 lines
12 KiB
C++
// Copyright 2010 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <SFML/Network/Packet.hpp>
|
|
#include <array>
|
|
#include <chrono>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <span>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
#include "Common/Event.h"
|
|
#include "Common/SPSCQueue.h"
|
|
#include "Common/TraversalClient.h"
|
|
#include "Core/NetPlayProto.h"
|
|
#include "Core/SyncIdentifier.h"
|
|
#include "InputCommon/GCPadStatus.h"
|
|
|
|
class BootSessionData;
|
|
|
|
namespace IOS::HLE::FS
|
|
{
|
|
class FileSystem;
|
|
}
|
|
|
|
namespace UICommon
|
|
{
|
|
class GameFile;
|
|
}
|
|
|
|
namespace WiimoteEmu
|
|
{
|
|
struct SerializedWiimoteState;
|
|
}
|
|
|
|
namespace NetPlay
|
|
{
|
|
class NetPlayUI
|
|
{
|
|
public:
|
|
virtual ~NetPlayUI() {}
|
|
virtual void BootGame(const std::string& filename,
|
|
std::unique_ptr<BootSessionData> boot_session_data) = 0;
|
|
virtual void StopGame() = 0;
|
|
virtual bool IsHosting() const = 0;
|
|
|
|
virtual void Update() = 0;
|
|
virtual void AppendChat(const std::string& msg) = 0;
|
|
|
|
virtual void OnMsgChangeGame(const SyncIdentifier& sync_identifier,
|
|
const std::string& netplay_name) = 0;
|
|
virtual void OnMsgChangeGBARom(int pad, const NetPlay::GBAConfig& config) = 0;
|
|
virtual void OnMsgStartGame() = 0;
|
|
virtual void OnMsgStopGame() = 0;
|
|
virtual void OnMsgPowerButton() = 0;
|
|
virtual void OnPlayerConnect(const std::string& player) = 0;
|
|
virtual void OnPlayerDisconnect(const std::string& player) = 0;
|
|
virtual void OnPadBufferChanged(u32 buffer) = 0;
|
|
virtual void OnHostInputAuthorityChanged(bool enabled) = 0;
|
|
virtual void OnDesync(u32 frame, const std::string& player) = 0;
|
|
virtual void OnConnectionLost() = 0;
|
|
virtual void OnConnectionError(const std::string& message) = 0;
|
|
virtual void OnTraversalError(Common::TraversalClient::FailureReason error) = 0;
|
|
virtual void OnTraversalStateChanged(Common::TraversalClient::State state) = 0;
|
|
virtual void OnGameStartAborted() = 0;
|
|
virtual void OnGolferChanged(bool is_golfer, const std::string& golfer_name) = 0;
|
|
virtual void OnTtlDetermined(u8 ttl) = 0;
|
|
|
|
virtual bool IsRecording() = 0;
|
|
virtual std::shared_ptr<const UICommon::GameFile>
|
|
FindGameFile(const SyncIdentifier& sync_identifier,
|
|
SyncIdentifierComparison* found = nullptr) = 0;
|
|
virtual std::string FindGBARomPath(const std::array<u8, 20>& hash, std::string_view title,
|
|
int device_number) = 0;
|
|
virtual void ShowGameDigestDialog(const std::string& title) = 0;
|
|
virtual void SetGameDigestProgress(int pid, int progress) = 0;
|
|
virtual void SetGameDigestResult(int pid, const std::string& result) = 0;
|
|
virtual void AbortGameDigest() = 0;
|
|
|
|
virtual void OnIndexAdded(bool success, std::string error) = 0;
|
|
virtual void OnIndexRefreshFailed(std::string error) = 0;
|
|
|
|
virtual void ShowChunkedProgressDialog(const std::string& title, u64 data_size,
|
|
std::span<const int> players) = 0;
|
|
virtual void HideChunkedProgressDialog() = 0;
|
|
virtual void SetChunkedProgress(int pid, u64 progress) = 0;
|
|
|
|
virtual void SetHostWiiSyncData(std::vector<u64> titles, std::string redirect_folder) = 0;
|
|
};
|
|
|
|
class Player
|
|
{
|
|
public:
|
|
PlayerId pid{};
|
|
std::string name;
|
|
std::string revision;
|
|
u32 ping = 0;
|
|
SyncIdentifierComparison game_status = SyncIdentifierComparison::Unknown;
|
|
|
|
bool IsHost() const { return pid == 1; }
|
|
};
|
|
|
|
class NetPlayClient : public Common::TraversalClientClient
|
|
{
|
|
public:
|
|
void ThreadFunc();
|
|
void SendAsync(sf::Packet&& packet, u8 channel_id = DEFAULT_CHANNEL);
|
|
|
|
NetPlayClient(const std::string& address, const u16 port, NetPlayUI* dialog, std::string name,
|
|
const NetTraversalConfig& traversal_config);
|
|
~NetPlayClient() override;
|
|
|
|
std::vector<const Player*> GetPlayers();
|
|
const NetSettings& GetNetSettings() const;
|
|
|
|
// Called from the GUI thread.
|
|
bool IsConnected() const { return m_is_connected; }
|
|
bool StartGame(const std::string& path);
|
|
void InvokeStop();
|
|
bool StopGame();
|
|
void Stop();
|
|
bool ChangeGame(const std::string& game);
|
|
void SendChatMessage(const std::string& msg);
|
|
void RequestStopGame();
|
|
void SendPowerButtonEvent();
|
|
void RequestGolfControl(PlayerId pid);
|
|
void RequestGolfControl();
|
|
std::string GetCurrentGolfer();
|
|
|
|
// Send and receive pads values
|
|
struct WiimoteDataBatchEntry
|
|
{
|
|
int wiimote;
|
|
WiimoteEmu::SerializedWiimoteState* state;
|
|
};
|
|
bool WiimoteUpdate(const std::span<WiimoteDataBatchEntry>& entries);
|
|
bool GetNetPads(int pad_nb, bool from_vi, GCPadStatus* pad_status);
|
|
|
|
u64 GetInitialRTCValue() const;
|
|
|
|
void OnTraversalStateChanged() override;
|
|
void OnConnectReady(ENetAddress addr) override;
|
|
void OnConnectFailed(Common::TraversalConnectFailedReason reason) override;
|
|
void OnTtlDetermined(u8 ttl) override {}
|
|
|
|
bool IsFirstInGamePad(int ingame_pad) const;
|
|
int NumLocalPads() const;
|
|
int NumLocalWiimotes() const;
|
|
|
|
int InGamePadToLocalPad(int ingame_pad) const;
|
|
int LocalPadToInGamePad(int local_pad) const;
|
|
int InGameWiimoteToLocalWiimote(int ingame_wiimote) const;
|
|
int LocalWiimoteToInGameWiimote(int local_wiimote) const;
|
|
|
|
bool PlayerHasControllerMapped(PlayerId pid) const;
|
|
bool LocalPlayerHasControllerMapped() const;
|
|
bool IsLocalPlayer(PlayerId pid) const;
|
|
const PlayerId& GetLocalPlayerId() const;
|
|
|
|
static void SendTimeBase();
|
|
bool DoAllPlayersHaveGame();
|
|
|
|
const PadMappingArray& GetPadMapping() const;
|
|
const GBAConfigArray& GetGBAConfig() const;
|
|
const PadMappingArray& GetWiimoteMapping() const;
|
|
|
|
void AdjustPadBufferSize(unsigned int size);
|
|
|
|
void SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs, std::vector<u64> titles,
|
|
std::string redirect_folder);
|
|
|
|
static SyncIdentifier GetSDCardIdentifier();
|
|
|
|
protected:
|
|
struct AsyncQueueEntry
|
|
{
|
|
sf::Packet packet;
|
|
u8 channel_id = 0;
|
|
};
|
|
|
|
void ClearBuffers();
|
|
|
|
struct
|
|
{
|
|
std::recursive_mutex game;
|
|
// lock order
|
|
std::recursive_mutex players;
|
|
std::recursive_mutex async_queue_write;
|
|
} m_crit;
|
|
|
|
Common::SPSCQueue<AsyncQueueEntry> m_async_queue;
|
|
|
|
std::array<Common::SPSCQueue<GCPadStatus>, 4> m_pad_buffer;
|
|
std::array<Common::SPSCQueue<WiimoteEmu::SerializedWiimoteState>, 4> m_wiimote_buffer;
|
|
|
|
std::array<GCPadStatus, 4> m_last_pad_status{};
|
|
std::array<bool, 4> m_first_pad_status_received{};
|
|
|
|
std::chrono::time_point<std::chrono::steady_clock> m_buffer_under_target_last;
|
|
|
|
NetPlayUI* m_dialog = nullptr;
|
|
|
|
ENetHost* m_client = nullptr;
|
|
ENetPeer* m_server = nullptr;
|
|
std::thread m_thread;
|
|
|
|
SyncIdentifier m_selected_game;
|
|
Common::Flag m_is_running{false};
|
|
Common::Flag m_do_loop{true};
|
|
|
|
// In non-host input authority mode, this is how many packets each client should
|
|
// try to keep in-flight to the other clients. In host input authority mode, this is how
|
|
// many incoming input packets need to be queued up before the client starts
|
|
// speeding up the game to drain the buffer.
|
|
unsigned int m_target_buffer_size = 20;
|
|
bool m_host_input_authority = false;
|
|
PlayerId m_current_golfer = 1;
|
|
|
|
// This bool will stall the client at the start of GetNetPads, used for switching input control
|
|
// without deadlocking. Use the correspondingly named Event to wake it up.
|
|
bool m_wait_on_input;
|
|
bool m_wait_on_input_received;
|
|
|
|
Player* m_local_player = nullptr;
|
|
|
|
u32 m_current_game = 0;
|
|
|
|
bool m_is_recording = false;
|
|
|
|
private:
|
|
enum class ConnectionState
|
|
{
|
|
WaitingForTraversalClientConnection,
|
|
WaitingForTraversalClientConnectReady,
|
|
Connecting,
|
|
WaitingForHelloResponse,
|
|
Connected,
|
|
Failure
|
|
};
|
|
|
|
void SendStartGamePacket();
|
|
void SendStopGamePacket();
|
|
|
|
void SyncSaveDataResponse(bool success);
|
|
void SyncCodeResponse(bool success);
|
|
|
|
bool PollLocalPad(int local_pad, sf::Packet& packet);
|
|
void SendPadHostPoll(PadIndex pad_num);
|
|
|
|
bool AddLocalWiimoteToBuffer(int local_wiimote, const WiimoteEmu::SerializedWiimoteState& state,
|
|
sf::Packet& packet);
|
|
|
|
void AddPadStateToPacket(int in_game_pad, const GCPadStatus& np, sf::Packet& packet);
|
|
void AddWiimoteStateToPacket(int in_game_pad, const WiimoteEmu::SerializedWiimoteState& np,
|
|
sf::Packet& packet);
|
|
void Send(const sf::Packet& packet, u8 channel_id = DEFAULT_CHANNEL);
|
|
void Disconnect();
|
|
bool Connect();
|
|
void SendGameStatus();
|
|
void ComputeGameDigest(const SyncIdentifier& sync_identifier);
|
|
void DisplayPlayersPing();
|
|
u32 GetPlayersMaxPing() const;
|
|
|
|
void OnData(sf::Packet& packet);
|
|
void OnPlayerJoin(sf::Packet& packet);
|
|
void OnPlayerLeave(sf::Packet& packet);
|
|
void OnChatMessage(sf::Packet& packet);
|
|
void OnChunkedDataStart(sf::Packet& packet);
|
|
void OnChunkedDataEnd(sf::Packet& packet);
|
|
void OnChunkedDataPayload(sf::Packet& packet);
|
|
void OnChunkedDataAbort(sf::Packet& packet);
|
|
void OnPadMapping(sf::Packet& packet);
|
|
void OnWiimoteMapping(sf::Packet& packet);
|
|
void OnGBAConfig(sf::Packet& packet);
|
|
void OnPadData(sf::Packet& packet);
|
|
void OnPadHostData(sf::Packet& packet);
|
|
void OnWiimoteData(sf::Packet& packet);
|
|
void OnPadBuffer(sf::Packet& packet);
|
|
void OnHostInputAuthority(sf::Packet& packet);
|
|
void OnGolfSwitch(sf::Packet& packet);
|
|
void OnGolfPrepare(sf::Packet& packet);
|
|
void OnChangeGame(sf::Packet& packet);
|
|
void OnGameStatus(sf::Packet& packet);
|
|
void OnStartGame(sf::Packet& packet);
|
|
void OnStopGame(sf::Packet& packet);
|
|
void OnPowerButton();
|
|
void OnPing(sf::Packet& packet);
|
|
void OnPlayerPingData(sf::Packet& packet);
|
|
void OnDesyncDetected(sf::Packet& packet);
|
|
void OnSyncSaveData(sf::Packet& packet);
|
|
void OnSyncSaveDataNotify(sf::Packet& packet);
|
|
void OnSyncSaveDataRaw(sf::Packet& packet);
|
|
void OnSyncSaveDataGCI(sf::Packet& packet);
|
|
void OnSyncSaveDataWii(sf::Packet& packet);
|
|
void OnSyncSaveDataGBA(sf::Packet& packet);
|
|
void OnSyncCodes(sf::Packet& packet);
|
|
void OnSyncCodesNotify();
|
|
void OnSyncCodesNotifyGecko(sf::Packet& packet);
|
|
void OnSyncCodesDataGecko(sf::Packet& packet);
|
|
void OnSyncCodesNotifyAR(sf::Packet& packet);
|
|
void OnSyncCodesDataAR(sf::Packet& packet);
|
|
void OnComputeGameDigest(sf::Packet& packet);
|
|
void OnGameDigestProgress(sf::Packet& packet);
|
|
void OnGameDigestResult(sf::Packet& packet);
|
|
void OnGameDigestError(sf::Packet& packet);
|
|
void OnGameDigestAbort();
|
|
|
|
bool m_is_connected = false;
|
|
ConnectionState m_connection_state = ConnectionState::Failure;
|
|
|
|
PlayerId m_pid = 0;
|
|
NetSettings m_net_settings{};
|
|
std::map<PlayerId, Player> m_players;
|
|
std::string m_host_spec;
|
|
std::string m_player_name;
|
|
bool m_connecting = false;
|
|
Common::TraversalClient* m_traversal_client = nullptr;
|
|
std::thread m_game_digest_thread;
|
|
bool m_should_compute_game_digest = false;
|
|
Common::Event m_gc_pad_event;
|
|
Common::Event m_wii_pad_event;
|
|
Common::Event m_first_pad_status_received_event;
|
|
Common::Event m_wait_on_input_event;
|
|
u8 m_sync_save_data_count = 0;
|
|
u8 m_sync_save_data_success_count = 0;
|
|
u16 m_sync_gecko_codes_count = 0;
|
|
u16 m_sync_gecko_codes_success_count = 0;
|
|
bool m_sync_gecko_codes_complete = false;
|
|
u16 m_sync_ar_codes_count = 0;
|
|
u16 m_sync_ar_codes_success_count = 0;
|
|
bool m_sync_ar_codes_complete = false;
|
|
std::unordered_map<u32, sf::Packet> m_chunked_data_receive_queue;
|
|
|
|
u64 m_initial_rtc = 0;
|
|
u32 m_timebase_frame = 0;
|
|
|
|
std::unique_ptr<IOS::HLE::FS::FileSystem> m_wii_sync_fs;
|
|
std::vector<u64> m_wii_sync_titles;
|
|
std::string m_wii_sync_redirect_folder;
|
|
};
|
|
|
|
void NetPlay_Enable(NetPlayClient* const np);
|
|
void NetPlay_Disable();
|
|
bool NetPlay_GetWiimoteData(const std::span<NetPlayClient::WiimoteDataBatchEntry>& entries);
|
|
unsigned int NetPlay_GetLocalWiimoteForSlot(unsigned int slot);
|
|
} // namespace NetPlay
|