diff options
Diffstat (limited to 'src/yuzu_cmd/yuzu.cpp')
| -rw-r--r-- | src/yuzu_cmd/yuzu.cpp | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index cb301e78b..e10d3f5b4 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <chrono> | 5 | #include <chrono> |
| 6 | #include <iostream> | 6 | #include <iostream> |
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <regex> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <thread> | 10 | #include <thread> |
| 10 | 11 | ||
| @@ -29,6 +30,7 @@ | |||
| 29 | #include "core/loader/loader.h" | 30 | #include "core/loader/loader.h" |
| 30 | #include "core/telemetry_session.h" | 31 | #include "core/telemetry_session.h" |
| 31 | #include "input_common/main.h" | 32 | #include "input_common/main.h" |
| 33 | #include "network/network.h" | ||
| 32 | #include "video_core/renderer_base.h" | 34 | #include "video_core/renderer_base.h" |
| 33 | #include "yuzu_cmd/config.h" | 35 | #include "yuzu_cmd/config.h" |
| 34 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 36 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| @@ -60,6 +62,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; | |||
| 60 | static void PrintHelp(const char* argv0) { | 62 | static void PrintHelp(const char* argv0) { |
| 61 | std::cout << "Usage: " << argv0 | 63 | std::cout << "Usage: " << argv0 |
| 62 | << " [options] <filename>\n" | 64 | << " [options] <filename>\n" |
| 65 | "-m, --multiplayer=nick:password@address:port" | ||
| 66 | " Nickname, password, address and port for multiplayer\n" | ||
| 63 | "-f, --fullscreen Start in fullscreen mode\n" | 67 | "-f, --fullscreen Start in fullscreen mode\n" |
| 64 | "-h, --help Display this help and exit\n" | 68 | "-h, --help Display this help and exit\n" |
| 65 | "-v, --version Output version information and exit\n" | 69 | "-v, --version Output version information and exit\n" |
| @@ -71,6 +75,107 @@ static void PrintVersion() { | |||
| 71 | std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; | 75 | std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; |
| 72 | } | 76 | } |
| 73 | 77 | ||
| 78 | static void OnStateChanged(const Network::RoomMember::State& state) { | ||
| 79 | switch (state) { | ||
| 80 | case Network::RoomMember::State::Idle: | ||
| 81 | LOG_DEBUG(Network, "Network is idle"); | ||
| 82 | break; | ||
| 83 | case Network::RoomMember::State::Joining: | ||
| 84 | LOG_DEBUG(Network, "Connection sequence to room started"); | ||
| 85 | break; | ||
| 86 | case Network::RoomMember::State::Joined: | ||
| 87 | LOG_DEBUG(Network, "Successfully joined to the room"); | ||
| 88 | break; | ||
| 89 | case Network::RoomMember::State::Moderator: | ||
| 90 | LOG_DEBUG(Network, "Successfully joined the room as a moderator"); | ||
| 91 | break; | ||
| 92 | default: | ||
| 93 | break; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | static void OnNetworkError(const Network::RoomMember::Error& error) { | ||
| 98 | switch (error) { | ||
| 99 | case Network::RoomMember::Error::LostConnection: | ||
| 100 | LOG_DEBUG(Network, "Lost connection to the room"); | ||
| 101 | break; | ||
| 102 | case Network::RoomMember::Error::CouldNotConnect: | ||
| 103 | LOG_ERROR(Network, "Error: Could not connect"); | ||
| 104 | exit(1); | ||
| 105 | break; | ||
| 106 | case Network::RoomMember::Error::NameCollision: | ||
| 107 | LOG_ERROR( | ||
| 108 | Network, | ||
| 109 | "You tried to use the same nickname as another user that is connected to the Room"); | ||
| 110 | exit(1); | ||
| 111 | break; | ||
| 112 | case Network::RoomMember::Error::MacCollision: | ||
| 113 | LOG_ERROR(Network, "You tried to use the same MAC-Address as another user that is " | ||
| 114 | "connected to the Room"); | ||
| 115 | exit(1); | ||
| 116 | break; | ||
| 117 | case Network::RoomMember::Error::ConsoleIdCollision: | ||
| 118 | LOG_ERROR(Network, "Your Console ID conflicted with someone else in the Room"); | ||
| 119 | exit(1); | ||
| 120 | break; | ||
| 121 | case Network::RoomMember::Error::WrongPassword: | ||
| 122 | LOG_ERROR(Network, "Room replied with: Wrong password"); | ||
| 123 | exit(1); | ||
| 124 | break; | ||
| 125 | case Network::RoomMember::Error::WrongVersion: | ||
| 126 | LOG_ERROR(Network, | ||
| 127 | "You are using a different version than the room you are trying to connect to"); | ||
| 128 | exit(1); | ||
| 129 | break; | ||
| 130 | case Network::RoomMember::Error::RoomIsFull: | ||
| 131 | LOG_ERROR(Network, "The room is full"); | ||
| 132 | exit(1); | ||
| 133 | break; | ||
| 134 | case Network::RoomMember::Error::HostKicked: | ||
| 135 | LOG_ERROR(Network, "You have been kicked by the host"); | ||
| 136 | break; | ||
| 137 | case Network::RoomMember::Error::HostBanned: | ||
| 138 | LOG_ERROR(Network, "You have been banned by the host"); | ||
| 139 | break; | ||
| 140 | case Network::RoomMember::Error::UnknownError: | ||
| 141 | LOG_ERROR(Network, "UnknownError"); | ||
| 142 | break; | ||
| 143 | case Network::RoomMember::Error::PermissionDenied: | ||
| 144 | LOG_ERROR(Network, "PermissionDenied"); | ||
| 145 | break; | ||
| 146 | case Network::RoomMember::Error::NoSuchUser: | ||
| 147 | LOG_ERROR(Network, "NoSuchUser"); | ||
| 148 | break; | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | static void OnMessageReceived(const Network::ChatEntry& msg) { | ||
| 153 | std::cout << std::endl << msg.nickname << ": " << msg.message << std::endl << std::endl; | ||
| 154 | } | ||
| 155 | |||
| 156 | static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { | ||
| 157 | std::string message; | ||
| 158 | switch (msg.type) { | ||
| 159 | case Network::IdMemberJoin: | ||
| 160 | message = fmt::format("{} has joined", msg.nickname); | ||
| 161 | break; | ||
| 162 | case Network::IdMemberLeave: | ||
| 163 | message = fmt::format("{} has left", msg.nickname); | ||
| 164 | break; | ||
| 165 | case Network::IdMemberKicked: | ||
| 166 | message = fmt::format("{} has been kicked", msg.nickname); | ||
| 167 | break; | ||
| 168 | case Network::IdMemberBanned: | ||
| 169 | message = fmt::format("{} has been banned", msg.nickname); | ||
| 170 | break; | ||
| 171 | case Network::IdAddressUnbanned: | ||
| 172 | message = fmt::format("{} has been unbanned", msg.nickname); | ||
| 173 | break; | ||
| 174 | } | ||
| 175 | if (!message.empty()) | ||
| 176 | std::cout << std::endl << "* " << message << std::endl << std::endl; | ||
| 177 | } | ||
| 178 | |||
| 74 | /// Application entry point | 179 | /// Application entry point |
| 75 | int main(int argc, char** argv) { | 180 | int main(int argc, char** argv) { |
| 76 | Common::Log::Initialize(); | 181 | Common::Log::Initialize(); |
| @@ -92,10 +197,16 @@ int main(int argc, char** argv) { | |||
| 92 | std::optional<std::string> config_path; | 197 | std::optional<std::string> config_path; |
| 93 | std::string program_args; | 198 | std::string program_args; |
| 94 | 199 | ||
| 200 | bool use_multiplayer = false; | ||
| 95 | bool fullscreen = false; | 201 | bool fullscreen = false; |
| 202 | std::string nickname{}; | ||
| 203 | std::string password{}; | ||
| 204 | std::string address{}; | ||
| 205 | u16 port = Network::DefaultRoomPort; | ||
| 96 | 206 | ||
| 97 | static struct option long_options[] = { | 207 | static struct option long_options[] = { |
| 98 | // clang-format off | 208 | // clang-format off |
| 209 | {"multiplayer", required_argument, 0, 'm'}, | ||
| 99 | {"fullscreen", no_argument, 0, 'f'}, | 210 | {"fullscreen", no_argument, 0, 'f'}, |
| 100 | {"help", no_argument, 0, 'h'}, | 211 | {"help", no_argument, 0, 'h'}, |
| 101 | {"version", no_argument, 0, 'v'}, | 212 | {"version", no_argument, 0, 'v'}, |
| @@ -109,6 +220,38 @@ int main(int argc, char** argv) { | |||
| 109 | int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index); | 220 | int arg = getopt_long(argc, argv, "g:fhvp::c:", long_options, &option_index); |
| 110 | if (arg != -1) { | 221 | if (arg != -1) { |
| 111 | switch (static_cast<char>(arg)) { | 222 | switch (static_cast<char>(arg)) { |
| 223 | case 'm': { | ||
| 224 | use_multiplayer = true; | ||
| 225 | const std::string str_arg(optarg); | ||
| 226 | // regex to check if the format is nickname:password@ip:port | ||
| 227 | // with optional :password | ||
| 228 | const std::regex re("^([^:]+)(?::(.+))?@([^:]+)(?::([0-9]+))?$"); | ||
| 229 | if (!std::regex_match(str_arg, re)) { | ||
| 230 | std::cout << "Wrong format for option --multiplayer\n"; | ||
| 231 | PrintHelp(argv[0]); | ||
| 232 | return 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | std::smatch match; | ||
| 236 | std::regex_search(str_arg, match, re); | ||
| 237 | ASSERT(match.size() == 5); | ||
| 238 | nickname = match[1]; | ||
| 239 | password = match[2]; | ||
| 240 | address = match[3]; | ||
| 241 | if (!match[4].str().empty()) | ||
| 242 | port = std::stoi(match[4]); | ||
| 243 | std::regex nickname_re("^[a-zA-Z0-9._\\- ]+$"); | ||
| 244 | if (!std::regex_match(nickname, nickname_re)) { | ||
| 245 | std::cout | ||
| 246 | << "Nickname is not valid. Must be 4 to 20 alphanumeric characters.\n"; | ||
| 247 | return 0; | ||
| 248 | } | ||
| 249 | if (address.empty()) { | ||
| 250 | std::cout << "Address to room must not be empty.\n"; | ||
| 251 | return 0; | ||
| 252 | } | ||
| 253 | break; | ||
| 254 | } | ||
| 112 | case 'f': | 255 | case 'f': |
| 113 | fullscreen = true; | 256 | fullscreen = true; |
| 114 | LOG_INFO(Frontend, "Starting in fullscreen mode..."); | 257 | LOG_INFO(Frontend, "Starting in fullscreen mode..."); |
| @@ -215,6 +358,21 @@ int main(int argc, char** argv) { | |||
| 215 | 358 | ||
| 216 | system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); | 359 | system.TelemetrySession().AddField(Common::Telemetry::FieldType::App, "Frontend", "SDL"); |
| 217 | 360 | ||
| 361 | if (use_multiplayer) { | ||
| 362 | if (auto member = system.GetRoomNetwork().GetRoomMember().lock()) { | ||
| 363 | member->BindOnChatMessageRecieved(OnMessageReceived); | ||
| 364 | member->BindOnStatusMessageReceived(OnStatusMessageReceived); | ||
| 365 | member->BindOnStateChanged(OnStateChanged); | ||
| 366 | member->BindOnError(OnNetworkError); | ||
| 367 | LOG_DEBUG(Network, "Start connection to {}:{} with nickname {}", address, port, | ||
| 368 | nickname); | ||
| 369 | member->Join(nickname, "", address.c_str(), port, 0, Network::NoPreferredMac, password); | ||
| 370 | } else { | ||
| 371 | LOG_ERROR(Network, "Could not access RoomMember"); | ||
| 372 | return 0; | ||
| 373 | } | ||
| 374 | } | ||
| 375 | |||
| 218 | // Core is loaded, start the GPU (makes the GPU contexts current to this thread) | 376 | // Core is loaded, start the GPU (makes the GPU contexts current to this thread) |
| 219 | system.GPU().Start(); | 377 | system.GPU().Start(); |
| 220 | system.GetCpuManager().OnGpuReady(); | 378 | system.GetCpuManager().OnGpuReady(); |