Add CLI support for NetPlay join/host
https://bugs.dolphin-emu.org/issues/5697
This commit is contained in:
parent
90eba2b1a0
commit
1a6876b5bf
|
@ -173,6 +173,11 @@ Options:
|
|||
-b, --batch Run Dolphin without the user interface (Requires
|
||||
--exec or --nand-title)
|
||||
-c, --confirm Set Confirm on Stop
|
||||
--netplay_host=<port>
|
||||
Host a netplay session on the specified port (Requires --exec)
|
||||
--netplay_join=<ip:port> OR <host code>
|
||||
Join a netplay session at the specified IP address and
|
||||
port or using a host code
|
||||
-v VIDEO_BACKEND, --video_backend=VIDEO_BACKEND
|
||||
Specify a video backend
|
||||
-a AUDIO_EMULATION, --audio_emulation=AUDIO_EMULATION
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "DolphinQt/Updater.h"
|
||||
|
||||
#include "UICommon/CommandLineParse.h"
|
||||
#include "UICommon/GameFile.h"
|
||||
#include "UICommon/UICommon.h"
|
||||
|
||||
static bool QtMsgAlertHandler(const char* caption, const char* text, bool yes_no,
|
||||
|
@ -190,14 +191,22 @@ int main(int argc, char* argv[])
|
|||
|
||||
std::unique_ptr<BootParameters> boot;
|
||||
bool game_specified = false;
|
||||
std::optional<UICommon::GameFile> netplay_game = std::nullopt;
|
||||
if (options.is_set("exec"))
|
||||
{
|
||||
const std::list<std::string> paths_list = options.all("exec");
|
||||
const std::vector<std::string> paths{std::make_move_iterator(std::begin(paths_list)),
|
||||
std::make_move_iterator(std::end(paths_list))};
|
||||
boot = BootParameters::GenerateFromFile(
|
||||
paths, BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
|
||||
game_specified = true;
|
||||
if (options.is_set("netplay_host"))
|
||||
{
|
||||
netplay_game = UICommon::GameFile(paths[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
boot = BootParameters::GenerateFromFile(
|
||||
paths, BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
|
||||
game_specified = true;
|
||||
}
|
||||
}
|
||||
else if (options.is_set("nand_title"))
|
||||
{
|
||||
|
@ -220,9 +229,48 @@ int main(int argc, char* argv[])
|
|||
game_specified = true;
|
||||
}
|
||||
|
||||
// FIXME: All those if/else-if clauses except for the last `else` SEGFAULT.
|
||||
// This is "fine" since they mean to exit dolphin anyways, but it probably shouldn't do that.
|
||||
int retval;
|
||||
|
||||
if (save_state_path && !game_specified)
|
||||
if (options.is_set("netplay_join") && options.is_set("netplay_host"))
|
||||
{
|
||||
ModalMessageBox::critical(
|
||||
nullptr, QObject::tr("Error"),
|
||||
QObject::tr("The --netplay_host and --netplay_join flags are mutually exclusive."));
|
||||
retval = 1;
|
||||
}
|
||||
else if (options.is_set("netplay_join") && game_specified)
|
||||
{
|
||||
ModalMessageBox::critical(
|
||||
nullptr, QObject::tr("Error"),
|
||||
QObject::tr("You cannot select the game for the NetPlay session you are joining."));
|
||||
retval = 1;
|
||||
}
|
||||
else if (options.is_set("netplay_join") && save_state_path)
|
||||
{
|
||||
ModalMessageBox::critical(nullptr, QObject::tr("Error"),
|
||||
QObject::tr("NetPlay does not support NAND titles."));
|
||||
retval = 1;
|
||||
}
|
||||
else if (!netplay_game && options.is_set("netplay_host"))
|
||||
{
|
||||
ModalMessageBox::critical(nullptr, QObject::tr("Error"),
|
||||
QObject::tr("You must specify a game to host with NetPlay."));
|
||||
retval = 1;
|
||||
}
|
||||
else if (netplay_game && game_specified)
|
||||
{
|
||||
ModalMessageBox::critical(nullptr, QObject::tr("Error"),
|
||||
QObject::tr("NetPlay does not support NAND titles."));
|
||||
retval = 1;
|
||||
}
|
||||
else if (netplay_game && save_state_path)
|
||||
{
|
||||
ModalMessageBox::critical(nullptr, QObject::tr("Error"),
|
||||
QObject::tr("NetPlay does not support savestates."));
|
||||
retval = 1;
|
||||
}
|
||||
else if (save_state_path && !game_specified)
|
||||
{
|
||||
ModalMessageBox::critical(
|
||||
nullptr, QObject::tr("Error"),
|
||||
|
@ -250,7 +298,8 @@ int main(int argc, char* argv[])
|
|||
Settings::Instance().ApplyStyle();
|
||||
|
||||
MainWindow win{Core::System::GetInstance(), std::move(boot),
|
||||
static_cast<const char*>(options.get("movie"))};
|
||||
static_cast<const char*>(options.get("movie")), options.is_set("netplay_join"),
|
||||
netplay_game};
|
||||
|
||||
#if defined(USE_ANALYTICS) && USE_ANALYTICS
|
||||
if (!Config::Get(Config::MAIN_ANALYTICS_PERMISSION_ASKED))
|
||||
|
|
|
@ -218,7 +218,8 @@ static std::vector<std::string> StringListToStdVector(QStringList list)
|
|||
}
|
||||
|
||||
MainWindow::MainWindow(Core::System& system, std::unique_ptr<BootParameters> boot_parameters,
|
||||
const std::string& movie_path)
|
||||
const std::string& movie_path, const bool netplay_join,
|
||||
const std::optional<UICommon::GameFile> netplay_host)
|
||||
: QMainWindow(nullptr), m_system(system)
|
||||
{
|
||||
setWindowTitle(QString::fromStdString(Common::GetScmRevStr()));
|
||||
|
@ -330,7 +331,15 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr<BootParameters> boo
|
|||
|
||||
Host::GetInstance()->SetMainWindowHandle(reinterpret_cast<void*>(winId()));
|
||||
|
||||
if (m_pending_boot != nullptr)
|
||||
if (netplay_join)
|
||||
{
|
||||
NetPlayJoin();
|
||||
}
|
||||
else if (netplay_host)
|
||||
{
|
||||
NetPlayHost(netplay_host.value());
|
||||
}
|
||||
else if (m_pending_boot != nullptr)
|
||||
{
|
||||
StartGame(std::move(m_pending_boot));
|
||||
m_pending_boot.reset();
|
||||
|
|
|
@ -80,7 +80,8 @@ class MainWindow final : public QMainWindow
|
|||
|
||||
public:
|
||||
explicit MainWindow(Core::System& system, std::unique_ptr<BootParameters> boot_parameters,
|
||||
const std::string& movie_path);
|
||||
const std::string& movie_path, const bool netplay_join,
|
||||
const std::optional<UICommon::GameFile> netplay_host);
|
||||
~MainWindow();
|
||||
|
||||
WindowSystemInfo GetWindowSystemInfo() const;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "UICommon/CommandLineParse.h"
|
||||
|
||||
#include <fmt/ostream.h>
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
|
@ -15,6 +16,7 @@
|
|||
#include "Common/StringUtil.h"
|
||||
#include "Common/Version.h"
|
||||
#include "Core/Config/MainSettings.h"
|
||||
#include "Core/Config/NetplaySettings.h"
|
||||
|
||||
namespace CommandLineParse
|
||||
{
|
||||
|
@ -22,7 +24,9 @@ class CommandLineConfigLayerLoader final : public Config::ConfigLayerLoader
|
|||
{
|
||||
public:
|
||||
CommandLineConfigLayerLoader(const std::list<std::string>& args, const std::string& video_backend,
|
||||
const std::string& audio_backend, bool batch, bool debugger)
|
||||
const std::string& audio_backend,
|
||||
const std::optional<u16> netplay_host,
|
||||
const std::string& netplay_join, bool batch, bool debugger)
|
||||
: ConfigLayerLoader(Config::LayerType::CommandLine)
|
||||
{
|
||||
if (!video_backend.empty())
|
||||
|
@ -34,6 +38,54 @@ public:
|
|||
ValueToString(audio_backend == "HLE"));
|
||||
}
|
||||
|
||||
if (netplay_host.has_value())
|
||||
{
|
||||
const std::string traversal_choice = Config::Get(Config::NETPLAY_TRAVERSAL_CHOICE);
|
||||
const bool is_traversal = traversal_choice == "traversal";
|
||||
|
||||
Config::Location config = is_traversal ? Config::NETPLAY_TRAVERSAL_PORT.GetLocation() :
|
||||
Config::NETPLAY_HOST_PORT.GetLocation();
|
||||
|
||||
m_values.emplace_back(config, ValueToString(netplay_host.value()));
|
||||
}
|
||||
|
||||
if (!netplay_join.empty())
|
||||
{
|
||||
std::vector<std::string> join_parts = SplitString(netplay_join, ':');
|
||||
if (join_parts.size() < 2)
|
||||
{
|
||||
// The user is submitting a host code
|
||||
const std::string& host_code = join_parts[0];
|
||||
m_values.emplace_back(Config::NETPLAY_TRAVERSAL_CHOICE.GetLocation(), "traversal");
|
||||
m_values.emplace_back(Config::NETPLAY_HOST_CODE.GetLocation(), host_code);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ther user is submitting an IP address
|
||||
const std::string& host = join_parts[0];
|
||||
const std::string& port_str = join_parts[1];
|
||||
if (!std::all_of(port_str.begin(), port_str.end(), ::isdigit) || port_str.length() > 5)
|
||||
{
|
||||
fmt::println(std::cerr, "Error: the port must be a number between 0-{}.",
|
||||
std::numeric_limits<uint16_t>::max());
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const u64 port = std::stoul(port_str);
|
||||
if (port > std::numeric_limits<uint16_t>::max() || port < 1)
|
||||
{
|
||||
fmt::println(std::cerr, "Error: the port must be a number between 0-{}.",
|
||||
std::numeric_limits<uint16_t>::max());
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const u16 cast_port = static_cast<uint16_t>(port);
|
||||
|
||||
m_values.emplace_back(Config::NETPLAY_ADDRESS.GetLocation(), host);
|
||||
m_values.emplace_back(Config::NETPLAY_CONNECT_PORT.GetLocation(), ValueToString(cast_port));
|
||||
}
|
||||
}
|
||||
|
||||
// Batch mode hides the main window, and render to main hides the render window. To avoid a
|
||||
// situation where we would have no window at all, disable render to main when using batch mode.
|
||||
if (batch)
|
||||
|
@ -116,6 +168,16 @@ std::unique_ptr<optparse::OptionParser> CreateParser(ParserOptions options)
|
|||
.action("store_true")
|
||||
.help("Run Dolphin without the user interface (Requires --exec or --nand-title)");
|
||||
parser->add_option("-c", "--confirm").action("store_true").help("Set Confirm on Stop");
|
||||
parser->add_option("--netplay_host")
|
||||
.action("store")
|
||||
.metavar("<port>")
|
||||
.type("int")
|
||||
.help("Host a netplay session on the specified port (Requires --exec)");
|
||||
parser->add_option("--netplay_join")
|
||||
.action("store")
|
||||
.metavar("<ip:port> OR <host code>")
|
||||
.type("string")
|
||||
.help("Join a netplay session at the specified IP address and port or using a host code");
|
||||
}
|
||||
|
||||
parser->set_defaults("video_backend", "");
|
||||
|
@ -137,6 +199,10 @@ static void AddConfigLayer(const optparse::Values& options)
|
|||
Config::AddLayer(std::make_unique<CommandLineConfigLayerLoader>(
|
||||
std::move(config_args), static_cast<const char*>(options.get("video_backend")),
|
||||
static_cast<const char*>(options.get("audio_emulation")),
|
||||
options.is_set("netplay_host") ?
|
||||
std::optional<u16>(static_cast<u16>(options.get("netplay_host"))) :
|
||||
std::nullopt,
|
||||
static_cast<const char*>(options.get("netplay_join")),
|
||||
static_cast<bool>(options.get("batch")), static_cast<bool>(options.get("debugger"))));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue