mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-12-16 04:09:39 +00:00
Core/HW: Add Broadband Adapter (IPC).
This is a hassle-free BBA option intended for local play with multiple Dolphin instances running *in the same system*. After selecting **Broadband Adapter (IPC)** in the **SP1** slot in the GameCube section in the settings, games that support LAN play will be able to discover each other, without requiring third-party software or relatively complex TAP setups. The implementation is based on cpp-ipc, a high-performance inter-process communication library that uses shared memory as transport layer. Supported platforms are: - [x] Linux - [x] Windows - [ ] macOS (cpp-ipc does not support this platform) - [ ] FreeBSD (cpp-ipc does not support this platform) - [ ] Android (cpp-ipc needs some adjustments; while it could work, launching two Dolphin instances within the same Android system may be both challenging and impractical)
This commit is contained in:
parent
4677a92b13
commit
f5012ef457
@ -797,6 +797,11 @@ if(UNIX)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(WIN32 OR LINUX)
|
||||||
|
target_sources(core PRIVATE HW/EXI/BBA/IPC.cpp)
|
||||||
|
target_link_libraries(core PRIVATE cpp-ipc::ipc)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
# Add precompiled header
|
# Add precompiled header
|
||||||
target_link_libraries(core PRIVATE use_pch)
|
target_link_libraries(core PRIVATE use_pch)
|
||||||
|
|||||||
101
Source/Core/Core/HW/EXI/BBA/IPC.cpp
Normal file
101
Source/Core/Core/HW/EXI/BBA/IPC.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright 2025 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <libipc/ipc.h>
|
||||||
|
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
|
#include "Core/HW/EXI/EXI_DeviceEthernet.h"
|
||||||
|
|
||||||
|
namespace ExpansionInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
bool CEXIETHERNET::IPCBBAInterface::Activate()
|
||||||
|
{
|
||||||
|
if (m_active)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_channel.connect("dolphin-emu-bba-ipc", ipc::sender | ipc::receiver);
|
||||||
|
if (!m_channel.valid())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_read_enabled.Clear();
|
||||||
|
m_read_thread_shutdown.Clear();
|
||||||
|
|
||||||
|
m_active = true;
|
||||||
|
|
||||||
|
return RecvInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::IPCBBAInterface::Deactivate()
|
||||||
|
{
|
||||||
|
m_read_enabled.Clear();
|
||||||
|
m_read_thread_shutdown.Set();
|
||||||
|
|
||||||
|
if (m_read_thread.joinable())
|
||||||
|
{
|
||||||
|
m_read_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_channel.disconnect();
|
||||||
|
|
||||||
|
m_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::IPCBBAInterface::IsActivated()
|
||||||
|
{
|
||||||
|
return m_active;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::IPCBBAInterface::SendFrame(const u8* const frame, const u32 size)
|
||||||
|
{
|
||||||
|
if (!m_active)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
static constexpr u64 TIMEOUT_IN_MS{3000};
|
||||||
|
if (!m_channel.send(frame, size, TIMEOUT_IN_MS))
|
||||||
|
{
|
||||||
|
ERROR_LOG_FMT(SP1, "Failed to send frame");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_eth_ref->SendComplete();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CEXIETHERNET::IPCBBAInterface::RecvInit()
|
||||||
|
{
|
||||||
|
m_read_thread = std::thread(&CEXIETHERNET::IPCBBAInterface::ReadThreadHandler, this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::IPCBBAInterface::RecvStart()
|
||||||
|
{
|
||||||
|
m_read_enabled.Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::IPCBBAInterface::RecvStop()
|
||||||
|
{
|
||||||
|
m_read_enabled.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIETHERNET::IPCBBAInterface::ReadThreadHandler()
|
||||||
|
{
|
||||||
|
while (!m_read_thread_shutdown.IsSet())
|
||||||
|
{
|
||||||
|
const ipc::buff_t buffer{m_channel.recv(50)};
|
||||||
|
if (buffer.empty() || !m_read_enabled.IsSet())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const u8* const frame{reinterpret_cast<const u8*>(buffer.data())};
|
||||||
|
const u64 size{buffer.size()};
|
||||||
|
|
||||||
|
std::memcpy(m_eth_ref->mRecvBuffer.get(), frame, size);
|
||||||
|
m_eth_ref->mRecvBufferLength = static_cast<u32>(size);
|
||||||
|
m_eth_ref->RecvHandlePacket();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ExpansionInterface
|
||||||
@ -160,6 +160,10 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(Core::System& system, const EXIDevi
|
|||||||
result = std::make_unique<CEXIETHERNET>(system, BBADeviceType::BuiltIn);
|
result = std::make_unique<CEXIETHERNET>(system, BBADeviceType::BuiltIn);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case EXIDeviceType::EthernetIPC:
|
||||||
|
result = std::make_unique<CEXIETHERNET>(system, BBADeviceType::IPC);
|
||||||
|
break;
|
||||||
|
|
||||||
case EXIDeviceType::ModemTapServer:
|
case EXIDeviceType::ModemTapServer:
|
||||||
result = std::make_unique<CEXIModem>(system, ModemDeviceType::TAPSERVER);
|
result = std::make_unique<CEXIModem>(system, ModemDeviceType::TAPSERVER);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -40,6 +40,7 @@ enum class EXIDeviceType : int
|
|||||||
EthernetTapServer,
|
EthernetTapServer,
|
||||||
EthernetBuiltIn,
|
EthernetBuiltIn,
|
||||||
ModemTapServer,
|
ModemTapServer,
|
||||||
|
EthernetIPC,
|
||||||
None = 0xFF
|
None = 0xFF
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(Core::System& system, EXIDeviceType
|
|||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct fmt::formatter<ExpansionInterface::EXIDeviceType>
|
struct fmt::formatter<ExpansionInterface::EXIDeviceType>
|
||||||
: EnumFormatter<ExpansionInterface::EXIDeviceType::ModemTapServer>
|
: EnumFormatter<ExpansionInterface::EXIDeviceType::EthernetIPC>
|
||||||
{
|
{
|
||||||
static constexpr array_type names = {
|
static constexpr array_type names = {
|
||||||
_trans("Dummy"),
|
_trans("Dummy"),
|
||||||
@ -104,6 +105,7 @@ struct fmt::formatter<ExpansionInterface::EXIDeviceType>
|
|||||||
_trans("Broadband Adapter (tapserver)"),
|
_trans("Broadband Adapter (tapserver)"),
|
||||||
_trans("Broadband Adapter (HLE)"),
|
_trans("Broadband Adapter (HLE)"),
|
||||||
_trans("Modem Adapter (tapserver)"),
|
_trans("Modem Adapter (tapserver)"),
|
||||||
|
_trans("Broadband Adapter (IPC)"),
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr formatter() : EnumFormatter(names) {}
|
constexpr formatter() : EnumFormatter(names) {}
|
||||||
|
|||||||
@ -60,6 +60,11 @@ CEXIETHERNET::CEXIETHERNET(Core::System& system, BBADeviceType type) : IEXIDevic
|
|||||||
this, Config::Get(Config::MAIN_BBA_BUILTIN_DNS), Config::Get(Config::MAIN_BBA_BUILTIN_IP));
|
this, Config::Get(Config::MAIN_BBA_BUILTIN_DNS), Config::Get(Config::MAIN_BBA_BUILTIN_IP));
|
||||||
INFO_LOG_FMT(SP1, "Created Built in network interface.");
|
INFO_LOG_FMT(SP1, "Created Built in network interface.");
|
||||||
break;
|
break;
|
||||||
|
case BBADeviceType::IPC:
|
||||||
|
mac_addr = Common::GenerateMacAddress(Common::MACConsumer::BBA); // Always randomize
|
||||||
|
m_network_interface = std::make_unique<IPCBBAInterface>(this);
|
||||||
|
INFO_LOG_FMT(SP1, "Created IPC-based network interface.");
|
||||||
|
break;
|
||||||
case BBADeviceType::XLINK:
|
case BBADeviceType::XLINK:
|
||||||
// TODO start BBA with network link down, bring it up after "connected" response from XLink
|
// TODO start BBA with network link down, bring it up after "connected" response from XLink
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,9 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <SFML/Network.hpp>
|
#include <SFML/Network.hpp>
|
||||||
|
#if defined(WIN32) || (defined(__linux__) && !defined(__ANDROID__))
|
||||||
|
#include <libipc/ipc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "Common/Flag.h"
|
#include "Common/Flag.h"
|
||||||
#include "Common/Network.h"
|
#include "Common/Network.h"
|
||||||
@ -210,6 +213,7 @@ enum class BBADeviceType
|
|||||||
XLINK,
|
XLINK,
|
||||||
TAPSERVER,
|
TAPSERVER,
|
||||||
BuiltIn,
|
BuiltIn,
|
||||||
|
IPC,
|
||||||
};
|
};
|
||||||
|
|
||||||
class CEXIETHERNET : public IEXIDevice
|
class CEXIETHERNET : public IEXIDevice
|
||||||
@ -474,6 +478,43 @@ private:
|
|||||||
const Common::MACAddress& ResolveAddress(u32 inet_ip);
|
const Common::MACAddress& ResolveAddress(u32 inet_ip);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class IPCBBAInterface : public NetworkInterface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit IPCBBAInterface(CEXIETHERNET* const eth_ref) : NetworkInterface(eth_ref) {}
|
||||||
|
|
||||||
|
#if defined(WIN32) || (defined(__linux__) && !defined(__ANDROID__))
|
||||||
|
|
||||||
|
bool Activate() override;
|
||||||
|
void Deactivate() override;
|
||||||
|
bool IsActivated() override;
|
||||||
|
bool SendFrame(const u8* frame, u32 size) override;
|
||||||
|
bool RecvInit() override;
|
||||||
|
void RecvStart() override;
|
||||||
|
void RecvStop() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ReadThreadHandler();
|
||||||
|
|
||||||
|
bool m_active{};
|
||||||
|
ipc::channel m_channel;
|
||||||
|
std::thread m_read_thread;
|
||||||
|
Common::Flag m_read_enabled;
|
||||||
|
Common::Flag m_read_thread_shutdown;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
bool Activate() override { return false; }
|
||||||
|
void Deactivate() override {}
|
||||||
|
bool IsActivated() override { return false; }
|
||||||
|
bool SendFrame(const u8* const frame, const u32 size) override { return false; }
|
||||||
|
bool RecvInit() override { return false; }
|
||||||
|
void RecvStart() override {}
|
||||||
|
void RecvStop() override {}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
std::unique_ptr<NetworkInterface> m_network_interface;
|
std::unique_ptr<NetworkInterface> m_network_interface;
|
||||||
|
|
||||||
std::unique_ptr<u8[]> mRecvBuffer;
|
std::unique_ptr<u8[]> mRecvBuffer;
|
||||||
|
|||||||
@ -962,6 +962,7 @@
|
|||||||
<ClCompile Include="Core\HW\DVD\DVDThread.cpp" />
|
<ClCompile Include="Core\HW\DVD\DVDThread.cpp" />
|
||||||
<ClCompile Include="Core\HW\DVD\FileMonitor.cpp" />
|
<ClCompile Include="Core\HW\DVD\FileMonitor.cpp" />
|
||||||
<ClCompile Include="Core\HW\EXI\BBA\BuiltIn.cpp" />
|
<ClCompile Include="Core\HW\EXI\BBA\BuiltIn.cpp" />
|
||||||
|
<ClCompile Include="Core\HW\EXI\BBA\IPC.cpp" />
|
||||||
<ClCompile Include="Core\HW\EXI\BBA\TAP_Win32.cpp" />
|
<ClCompile Include="Core\HW\EXI\BBA\TAP_Win32.cpp" />
|
||||||
<ClCompile Include="Core\HW\EXI\BBA\TAPServerConnection.cpp" />
|
<ClCompile Include="Core\HW\EXI\BBA\TAPServerConnection.cpp" />
|
||||||
<ClCompile Include="Core\HW\EXI\BBA\TAPServerBBA.cpp" />
|
<ClCompile Include="Core\HW\EXI\BBA\TAPServerBBA.cpp" />
|
||||||
|
|||||||
@ -30,6 +30,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(ExternalsDir)Bochs_disasm\exports.props" />
|
<Import Project="$(ExternalsDir)Bochs_disasm\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)bzip2\exports.props" />
|
<Import Project="$(ExternalsDir)bzip2\exports.props" />
|
||||||
|
<Import Project="$(ExternalsDir)cpp-ipc\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)cpp-optparse\exports.props" />
|
<Import Project="$(ExternalsDir)cpp-optparse\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)cubeb\exports.props" />
|
<Import Project="$(ExternalsDir)cubeb\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)curl\exports.props" />
|
<Import Project="$(ExternalsDir)curl\exports.props" />
|
||||||
|
|||||||
@ -145,6 +145,9 @@ void GameCubePane::CreateWidgets()
|
|||||||
EXIDeviceType::EthernetXLink,
|
EXIDeviceType::EthernetXLink,
|
||||||
EXIDeviceType::EthernetTapServer,
|
EXIDeviceType::EthernetTapServer,
|
||||||
EXIDeviceType::EthernetBuiltIn,
|
EXIDeviceType::EthernetBuiltIn,
|
||||||
|
#if defined(WIN32) || (defined(__linux__) && !defined(__ANDROID__))
|
||||||
|
EXIDeviceType::EthernetIPC,
|
||||||
|
#endif
|
||||||
EXIDeviceType::ModemTapServer,
|
EXIDeviceType::ModemTapServer,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user