// Copyright 2013 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include #include #include #include #include #include #include "Common/Event.h" #include "Common/QoSSession.h" #include "Common/SPSCQueue.h" #include "Common/Timer.h" #include "Common/TraversalClient.h" #include "Core/NetPlayProto.h" #include "Core/SyncIdentifier.h" #include "InputCommon/GCPadStatus.h" #include "UICommon/NetPlayIndex.h" namespace NetPlay { class NetPlayUI; class NetPlayServer : public TraversalClientClient { public: void ThreadFunc(); void SendAsync(sf::Packet&& packet, PlayerId pid, u8 channel_id = DEFAULT_CHANNEL); void SendAsyncToClients(sf::Packet&& packet, PlayerId skip_pid = 0, u8 channel_id = DEFAULT_CHANNEL); void SendChunked(sf::Packet&& packet, PlayerId pid, const std::string& title = ""); void SendChunkedToClients(sf::Packet&& packet, PlayerId skip_pid = 0, const std::string& title = ""); NetPlayServer(u16 port, bool forward_port, NetPlayUI* dialog, const NetTraversalConfig& traversal_config); ~NetPlayServer(); bool ChangeGame(const SyncIdentifier& sync_identifier, const std::string& netplay_name); bool ComputeMD5(const SyncIdentifier& sync_identifier); bool AbortMD5(); void SendChatMessage(const std::string& msg); bool DoAllPlayersHaveIPLDump() const; bool DoAllPlayersHaveHardwareFMA() const; bool StartGame(); bool RequestStartGame(); void AbortGameStart(); PadMappingArray GetPadMapping() const; void SetPadMapping(const PadMappingArray& mappings); GBAConfigArray GetGBAConfig() const; void SetGBAConfig(const GBAConfigArray& configs, bool update_rom); PadMappingArray GetWiimoteMapping() const; void SetWiimoteMapping(const PadMappingArray& mappings); void AdjustPadBufferSize(unsigned int size); void SetHostInputAuthority(bool enable); void KickPlayer(PlayerId player); u16 GetPort() const; std::unordered_set GetInterfaceSet() const; std::string GetInterfaceHost(const std::string& inter) const; bool is_connected = false; private: class Client { public: PlayerId pid{}; std::string name; std::string revision; SyncIdentifierComparison game_status = SyncIdentifierComparison::Unknown; bool has_ipl_dump = false; bool has_hardware_fma = false; ENetPeer* socket = nullptr; u32 ping = 0; u32 current_game = 0; Common::QoSSession qos_session; bool operator==(const Client& other) const { return this == &other; } bool IsHost() const { return pid == 1; } }; enum class TargetMode { Only, AllExcept }; struct AsyncQueueEntry { sf::Packet packet; PlayerId target_pid{}; TargetMode target_mode{}; u8 channel_id = 0; }; struct ChunkedDataQueueEntry { sf::Packet packet; PlayerId target_pid{}; TargetMode target_mode{}; std::string title; }; bool SetupNetSettings(); bool SyncSaveData(); bool SyncCodes(); void CheckSyncAndStartGame(); u64 GetInitialNetPlayRTC() const; template void SendResponseToPlayer(const Client& player, const MessageID message_id, Data&&... data_to_send); template void SendResponseToAllPlayers(const MessageID message_id, Data&&... data_to_send); void SendToClients(const sf::Packet& packet, PlayerId skip_pid = 0, u8 channel_id = DEFAULT_CHANNEL); void Send(ENetPeer* socket, const sf::Packet& packet, u8 channel_id = DEFAULT_CHANNEL); ConnectionError OnConnect(ENetPeer* socket, sf::Packet& received_packet); unsigned int OnDisconnect(const Client& player); unsigned int OnData(sf::Packet& packet, Client& player); void OnTraversalStateChanged() override; void OnConnectReady(ENetAddress) override {} void OnConnectFailed(TraversalConnectFailedReason) override {} void UpdatePadMapping(); void UpdateGBAConfig(); void UpdateWiimoteMapping(); std::vector> GetInterfaceListInternal() const; void ChunkedDataThreadFunc(); void ChunkedDataSend(sf::Packet&& packet, PlayerId pid, const TargetMode target_mode); void ChunkedDataAbort(); void SetupIndex(); bool PlayerHasControllerMapped(PlayerId pid) const; // pulled from OnConnect() void AssignNewUserAPad(const Client& player); // pulled from OnConnect() // returns the PID given PlayerId GiveFirstAvailableIDTo(ENetPeer* player); NetSettings m_settings; bool m_is_running = false; bool m_do_loop = false; Common::Timer m_ping_timer; u32 m_ping_key = 0; bool m_update_pings = false; u32 m_current_game = 0; unsigned int m_target_buffer_size = 0; PadMappingArray m_pad_map; GBAConfigArray m_gba_config; PadMappingArray m_wiimote_map; unsigned int m_save_data_synced_players = 0; unsigned int m_codes_synced_players = 0; bool m_saves_synced = true; bool m_codes_synced = true; bool m_start_pending = false; bool m_host_input_authority = false; PlayerId m_current_golfer = 1; PlayerId m_pending_golfer = 0; std::map m_players; std::unordered_map>> m_timebase_by_frame; bool m_desync_detected = false; struct { std::recursive_mutex game; // lock order std::recursive_mutex players; std::recursive_mutex async_queue_write; std::recursive_mutex chunked_data_queue_write; } m_crit; Common::SPSCQueue m_async_queue; Common::SPSCQueue m_chunked_data_queue; SyncIdentifier m_selected_game_identifier; std::string m_selected_game_name; std::thread m_thread; Common::Event m_chunked_data_event; Common::Event m_chunked_data_complete_event; std::thread m_chunked_data_thread; u32 m_next_chunked_data_id = 0; std::unordered_map m_chunked_data_complete_count; bool m_abort_chunked_data = false; ENetHost* m_server = nullptr; TraversalClient* m_traversal_client = nullptr; NetPlayUI* m_dialog = nullptr; NetPlayIndex m_index; }; } // namespace NetPlay