diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 01a70ea39..85d7ab099 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -39,20 +40,69 @@ #include "core/hle/service/am/am.h" #include "core/loader/loader.h" #include "core/settings.h" +#include "network/network.h" static void PrintHelp(const char* argv0) { - std::cout << "Usage: " << argv0 - << " [options] \n" - "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" - "-i, --install=FILE Installs a specified CIA file\n" - "-h, --help Display this help and exit\n" - "-v, --version Output version information and exit\n"; + std::cout << "Usage: " << argv0 << " [options] \n" + "-g, --gdbport=NUMBER Enable gdb stub on port NUMBER\n" + "-i, --install=FILE Installs a specified CIA file\n" + "-m, --multiplayer=nick:password@address:port" + " Nickname, password, address and port for multiplayer\n" + "-h, --help Display this help and exit\n" + "-v, --version Output version information and exit\n"; } static void PrintVersion() { std::cout << "Citra " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; } +static void OnStateChanged(const Network::RoomMember::State& state) { + switch (state) { + case Network::RoomMember::State::Idle: + LOG_DEBUG(Network, "Network is idle"); + break; + case Network::RoomMember::State::Joining: + LOG_DEBUG(Network, "Connection sequence to room started"); + break; + case Network::RoomMember::State::Joined: + LOG_DEBUG(Network, "Successfully joined to the room"); + break; + case Network::RoomMember::State::LostConnection: + LOG_DEBUG(Network, "Lost connection to the room"); + break; + case Network::RoomMember::State::CouldNotConnect: + LOG_ERROR(Network, "State: CouldNotConnect"); + exit(1); + break; + case Network::RoomMember::State::NameCollision: + LOG_ERROR( + Network, + "You tried to use the same nickname then another user that is connected to the Room"); + exit(1); + break; + case Network::RoomMember::State::MacCollision: + LOG_ERROR(Network, "You tried to use the same MAC-Address then another user that is " + "connected to the Room"); + exit(1); + break; + case Network::RoomMember::State::WrongPassword: + LOG_ERROR(Network, "Room replied with: Wrong password"); + exit(1); + break; + case Network::RoomMember::State::WrongVersion: + LOG_ERROR(Network, + "You are using a different version then the room you are trying to connect to"); + exit(1); + break; + default: + break; + } +} + +static void OnMessageReceived(const Network::ChatEntry& msg) { + std::cout << std::endl << msg.nickname << ": " << msg.message << std::endl << std::endl; +} + /// Application entry point int main(int argc, char** argv) { Config config; @@ -71,16 +121,20 @@ int main(int argc, char** argv) { #endif std::string filepath; + bool use_multiplayer = false; + std::string nickname{}; + std::string password{}; + std::string address{}; + u16 port = Network::DefaultRoomPort; + static struct option long_options[] = { - {"gdbport", required_argument, 0, 'g'}, - {"install", required_argument, 0, 'i'}, - {"help", no_argument, 0, 'h'}, - {"version", no_argument, 0, 'v'}, - {0, 0, 0, 0}, + {"gdbport", required_argument, 0, 'g'}, {"install", required_argument, 0, 'i'}, + {"multiplayer", required_argument, 0, 'm'}, {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'v'}, {0, 0, 0, 0}, }; while (optind < argc) { - char arg = getopt_long(argc, argv, "g:i:hv", long_options, &option_index); + char arg = getopt_long(argc, argv, "g:i:m:hv", long_options, &option_index); if (arg != -1) { switch (arg) { case 'g': @@ -103,6 +157,41 @@ int main(int argc, char** argv) { errno = EINVAL; if (errno != 0) exit(1); + } + case 'm': { + use_multiplayer = true; + std::string str_arg(optarg); + // regex to check if the format is nickname:password@ip:port + // with optional :password + std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$"); + if (!std::regex_match(str_arg, re)) { + std::cout << "Wrong format for option --multiplayer\n"; + PrintHelp(argv[0]); + return 0; + } + + std::smatch match; + std::regex_search(str_arg, match, re); + ASSERT(match.size() == 5); + nickname = match[1]; + password = match[2]; + address = match[3]; + if (!match[4].str().empty()) + port = std::stoi(match[4]); + std::regex nickname_re("^[a-zA-Z0-9._- ]+$"); + if (!std::regex_match(nickname, nickname_re)) { + std::cout + << "Nickname is not valid. Must be 4 to 20 alphanumeric characters.\n"; + return 0; + } + if (address.empty()) { + std::cout << "Address to room must not be empty.\n"; + return 0; + } + if (port > 65535) { + std::cout << "Port must be between 0 and 65535.\n"; + return 0; + } break; } case 'h': @@ -183,6 +272,19 @@ int main(int argc, char** argv) { Core::Telemetry().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); + if (use_multiplayer) { + if (auto member = Network::GetRoomMember().lock()) { + member->BindOnChatMessageRecieved(OnMessageReceived); + member->BindOnStateChanged(OnStateChanged); + LOG_DEBUG(Network, "Start connection to %s:%u with nickname %s", address.c_str(), port, + nickname.c_str()); + member->Join(nickname, address.c_str(), port, 0, Network::NoPreferredMac, password); + } else { + LOG_ERROR(Network, "Could not access RoomMember"); + return 0; + } + } + while (emu_window->IsOpen()) { system.RunLoop(); }