diff --git a/.gitmodules b/.gitmodules index b0e5a858c..b43a910ab 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,12 +37,6 @@ [submodule "discord-rpc"] path = externals/discord-rpc url = https://github.com/discordapp/discord-rpc.git -[submodule "externals/libzmq"] - path = externals/libzmq - url = https://github.com/zeromq/libzmq -[submodule "externals/cppzmq"] - path = externals/cppzmq - url = https://github.com/zeromq/cppzmq [submodule "cpp-jwt"] path = externals/cpp-jwt url = https://github.com/arun11299/cpp-jwt.git diff --git a/.travis/linux-mingw/docker.sh b/.travis/linux-mingw/docker.sh index a4e75d4ab..2514a5058 100755 --- a/.travis/linux-mingw/docker.sh +++ b/.travis/linux-mingw/docker.sh @@ -5,7 +5,7 @@ cd /citra echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf" mkdir build && cd build -cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_SCRIPTING=ON +cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON make -j4 echo "Tests skipped" diff --git a/.travis/linux/docker.sh b/.travis/linux/docker.sh index 10ce0900c..2a583d6a5 100755 --- a/.travis/linux/docker.sh +++ b/.travis/linux/docker.sh @@ -3,7 +3,7 @@ cd /citra mkdir build && cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_SCRIPTING=ON +cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON make -j4 ctest -VV -C Release diff --git a/.travis/macos/build.sh b/.travis/macos/build.sh index 5b95924ca..bb3c687e6 100755 --- a/.travis/macos/build.sh +++ b/.travis/macos/build.sh @@ -7,7 +7,7 @@ export Qt5_DIR=$(brew --prefix)/opt/qt5 export PATH="/usr/local/opt/ccache/libexec:$PATH" mkdir build && cd build -cmake .. -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h" -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -DENABLE_SCRIPTING=ON +cmake .. -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h" -DCMAKE_BUILD_TYPE=Release -DENABLE_QT_TRANSLATION=ON -DCITRA_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON make -j4 ctest -VV -C Release diff --git a/.travis/transifex/docker.sh b/.travis/transifex/docker.sh index 99b415459..003b298b6 100644 --- a/.travis/transifex/docker.sh +++ b/.travis/transifex/docker.sh @@ -26,7 +26,7 @@ tx --version cd /citra mkdir build && cd build -cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF -DENABLE_SCRIPTING=OFF +cmake .. -DENABLE_QT_TRANSLATION=ON -DGENERATE_QT_TRANSLATION=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_SDL2=OFF make translation cd .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ba175a1d..491a6d6e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,8 +22,6 @@ option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF) -option(ENABLE_SCRIPTING "Enables scripting support" OFF) - if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit) message(STATUS "Copying pre-commit hook") file(COPY hooks/pre-commit @@ -253,10 +251,6 @@ if (ENABLE_QT) endif() endif() -if (ENABLE_SCRIPTING) - add_definitions(-DENABLE_SCRIPTING) -endif() - # Platform-specific library requirements # ====================================== diff --git a/dist/scripting/citra.py b/dist/scripting/citra.py index be6068685..507662033 100644 --- a/dist/scripting/citra.py +++ b/dist/scripting/citra.py @@ -1,22 +1,22 @@ -import zmq import struct import random import enum +import socket CURRENT_REQUEST_VERSION = 1 MAX_REQUEST_DATA_SIZE = 32 +MAX_PACKET_SIZE = 48 class RequestType(enum.IntEnum): ReadMemory = 1, WriteMemory = 2 -CITRA_PORT = "45987" +CITRA_PORT = 45987 class Citra: def __init__(self, address="127.0.0.1", port=CITRA_PORT): - self.context = zmq.Context() - self.socket = self.context.socket(zmq.REQ) - self.socket.connect("tcp://" + address + ":" + port) + self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.address = address def is_connected(self): return self.socket is not None @@ -45,9 +45,9 @@ class Citra: request_data = struct.pack("II", read_address, temp_read_size) request, request_id = self._generate_header(RequestType.ReadMemory, len(request_data)) request += request_data - self.socket.send(request) + self.socket.sendto(request, (self.address, CITRA_PORT)) - raw_reply = self.socket.recv() + raw_reply = self.socket.recv(MAX_PACKET_SIZE) reply_data = self._read_and_validate_header(raw_reply, request_id, RequestType.ReadMemory) if reply_data: @@ -77,9 +77,9 @@ class Citra: request_data += write_contents[:temp_write_size] request, request_id = self._generate_header(RequestType.WriteMemory, len(request_data)) request += request_data - self.socket.send(request) + self.socket.sendto(request, (self.address, CITRA_PORT)) - raw_reply = self.socket.recv() + raw_reply = self.socket.recv(MAX_PACKET_SIZE) reply_data = self._read_and_validate_header(raw_reply, request_id, RequestType.WriteMemory) if None != reply_data: diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 55bf4f8c4..ae92ce861 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -100,98 +100,3 @@ if (ENABLE_WEB_SERVICE) add_library(cpp-jwt INTERFACE) target_include_directories(cpp-jwt INTERFACE ./cpp-jwt/include) endif() - -if (ENABLE_SCRIPTING) - # ZeroMQ - # libzmq includes its own clang-format target, which conflicts with the - # clang-format in Citra if libzmq is added as a subdirectory. An external - # project gets around this issue. Unfortunately, a lot of different - # configuration options are required for each different platform. An - # attempt was made to use CMake variables where possible, but some - # information necessarily had to be repeated. Hopefully there is not - # often a need to change anything. - if (MINGW) - if (${CMAKE_HOST_SYSTEM_NAME} STREQUAL "Windows") - set(LIBZMQ_MAKE mingw32-make) - set(LIBZMQ_COMPILER "") - set(LIBZMQ_TOOLCHAIN_FILE "") - else() - set(LIBZMQ_MAKE make) - set(LIBZMQ_COMPILER -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER};-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}) - set(LIBZMQ_TOOLCHAIN_FILE -DCMAKE_TOOLCHAIN_FILE=${PROJECT_SOURCE_DIR}/CMakeModules/MinGWCross.cmake) - endif() - ExternalProject_Add(libzmq-external - SOURCE_DIR ./libzmq - CMAKE_ARGS -DWITH_PERF_TOOL=OFF;-DZMQ_BUILD_TESTS=OFF;-DENABLE_CPACK=OFF;-DCMAKE_MAKE_PROGRAM=${LIBZMQ_MAKE};-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE};${LIBZMQ_TOOLCHAIN_FILE};${LIBZMQ_COMPILER} - BUILD_COMMAND cmake --build ${CMAKE_CURRENT_BINARY_DIR}/libzmq-external-prefix/src/libzmq-external-build --target libzmq-static --config ${CMAKE_BUILD_TYPE} - GIT_REPOSITORY https://github.com/zeromq/libzmq - GIT_TAG v4.2.5 - INSTALL_COMMAND "") - else() - if (MSVC) - set(LIBZMQ_COMPILER_FLAGS -DCMAKE_C_FLAGS=/GL-;-DCMAKE_CXX_FLAGS=/GL-) - else() - set(LIBZMQ_COMPILER_FLAGS "") - endif() - ExternalProject_Add(libzmq-external - SOURCE_DIR ./libzmq - CMAKE_ARGS -DCMAKE_MACOSX_RPATH=1;-DCMAKE_OSX_ARCHITECTURES=x86_64;-DWITH_PERF_TOOL=OFF;-DZMQ_BUILD_TESTS=OFF;-DENABLE_CPACK=OFF;-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE};${LIBZMQ_COMPILER_FLAGS} - BUILD_COMMAND cmake --build ${CMAKE_CURRENT_BINARY_DIR}/libzmq-external-prefix/src/libzmq-external-build --target libzmq-static --config ${CMAKE_BUILD_TYPE} - GIT_REPOSITORY https://github.com/zeromq/libzmq - GIT_TAG v4.2.5 - INSTALL_COMMAND "") - endif() - set(LIBZMQ_DIR ${CMAKE_CURRENT_BINARY_DIR}/libzmq-external-prefix/src/libzmq-external-build/lib) - # On macOS, we need to build a fat static library containing both x86_64 and x86_64h, since macOS - # targets specify two architectures in their link command line ("-arch x86_64 -arch x86_64h"). - if (APPLE) - ExternalProject_Add(libzmq-external-h - SOURCE_DIR ./libzmq-h - CMAKE_ARGS -DCMAKE_MACOSX_RPATH=1;-DCMAKE_OSX_ARCHITECTURES=x86_64h;-DWITH_PERF_TOOL=OFF;-DZMQ_BUILD_TESTS=OFF;-DENABLE_CPACK=OFF;-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} - BUILD_COMMAND cmake --build ${CMAKE_CURRENT_BINARY_DIR}/libzmq-external-h-prefix/src/libzmq-external-h-build --target libzmq-static --config ${CMAKE_BUILD_TYPE} - GIT_REPOSITORY https://github.com/zeromq/libzmq - GIT_TAG v4.2.5 - INSTALL_COMMAND "") - set(LIBZMQ_H_DIR ${CMAKE_CURRENT_BINARY_DIR}/libzmq-external-h-prefix/src/libzmq-external-h-build/lib) - - add_library(libzmq-external-imported STATIC IMPORTED GLOBAL) - add_library(libzmq-external-imported-h STATIC IMPORTED GLOBAL) - add_dependencies(libzmq-external-imported libzmq-external) - add_dependencies(libzmq-external-imported-h libzmq-external-h) - else() - add_library(libzmq STATIC IMPORTED GLOBAL) - add_dependencies(libzmq libzmq-external) - endif() - # Set up the imported target properties - if (MSVC) - set_target_properties(libzmq PROPERTIES IMPORTED_LOCATION ${LIBZMQ_DIR}/${CMAKE_BUILD_TYPE}/libzmq-v141-mt-s-4_2_5${CMAKE_STATIC_LIBRARY_SUFFIX}) - set_target_properties(libzmq PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES iphlpapi${CMAKE_STATIC_LIBRARY_SUFFIX}) - else() - if (APPLE) - set_target_properties(libzmq-external-imported PROPERTIES IMPORTED_LOCATION ${LIBZMQ_DIR}/libzmq${CMAKE_STATIC_LIBRARY_SUFFIX}) - set_target_properties(libzmq-external-imported-h PROPERTIES IMPORTED_LOCATION ${LIBZMQ_H_DIR}/libzmq${CMAKE_STATIC_LIBRARY_SUFFIX}) - else() - set_target_properties(libzmq PROPERTIES IMPORTED_LOCATION ${LIBZMQ_DIR}/libzmq${CMAKE_STATIC_LIBRARY_SUFFIX}) - if(MINGW) - set_target_properties(libzmq PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES "ws2_32${CMAKE_STATIC_LIBRARY_SUFFIX};iphlpapi${CMAKE_STATIC_LIBRARY_SUFFIX}") - endif() - endif() - endif() - # On macOS, create the combined target - if (APPLE) - set(LIBZMQ_COMBINED_OUTPUT ${LIBZMQ_DIR}/libzmq_combined${CMAKE_STATIC_LIBRARY_SUFFIX}) - add_custom_target(libzmq-combined COMMAND lipo -create ${LIBZMQ_DIR}/libzmq${CMAKE_STATIC_LIBRARY_SUFFIX} ${LIBZMQ_H_DIR}/libzmq${CMAKE_STATIC_LIBRARY_SUFFIX} -o ${LIBZMQ_COMBINED_OUTPUT} - BYPRODUCTS ${LIBZMQ_COMBINED_OUTPUT}) - add_dependencies(libzmq-combined libzmq-external-imported libzmq-external-imported-h) - add_library(libzmq STATIC IMPORTED GLOBAL) - set_target_properties(libzmq PROPERTIES IMPORTED_LOCATION ${LIBZMQ_COMBINED_OUTPUT}) - add_dependencies(libzmq libzmq-combined) - endif() - # C interface to ZeroMQ - add_library(libzmq-headers INTERFACE) - target_include_directories(libzmq-headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/libzmq/include) - # C++ interface to ZeroMQ - add_library(cppzmq-headers INTERFACE) - target_include_directories(cppzmq-headers INTERFACE ./cppzmq) - add_dependencies(cppzmq-headers libzmq) -endif() diff --git a/externals/cppzmq b/externals/cppzmq deleted file mode 160000 index 6aa3ab686..000000000 --- a/externals/cppzmq +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6aa3ab686e916cb0e62df7fa7d12e0b13ae9fae6 diff --git a/externals/libzmq b/externals/libzmq deleted file mode 160000 index d062edd8c..000000000 --- a/externals/libzmq +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d062edd8c142384792955796329baf1e5a3377cd diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1e9f0248b..6904aaaf6 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -423,6 +423,14 @@ add_library(core STATIC movie.h perf_stats.cpp perf_stats.h + rpc/packet.cpp + rpc/packet.h + rpc/rpc_server.cpp + rpc/rpc_server.h + rpc/server.cpp + rpc/server.h + rpc/udp_server.cpp + rpc/udp_server.h settings.cpp settings.h telemetry_session.cpp @@ -431,18 +439,6 @@ add_library(core STATIC tracer/recorder.cpp tracer/recorder.h ) -if (ENABLE_SCRIPTING) - target_sources(core PRIVATE - rpc/packet.cpp - rpc/packet.h - rpc/rpc_server.cpp - rpc/rpc_server.h - rpc/server.cpp - rpc/server.h - rpc/zmq_server.cpp - rpc/zmq_server.h - ) -endif() create_target_directory_groups(core) @@ -462,7 +458,3 @@ if (ARCHITECTURE_x86_64) ) target_link_libraries(core PRIVATE dynarmic) endif() - -if (ENABLE_SCRIPTING) - target_link_libraries(core PUBLIC libzmq-headers cppzmq-headers libzmq) -endif() diff --git a/src/core/core.cpp b/src/core/core.cpp index a6af49b41..671363169 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -27,9 +27,7 @@ #include "core/hw/hw.h" #include "core/loader/loader.h" #include "core/movie.h" -#ifdef ENABLE_SCRIPTING #include "core/rpc/rpc_server.h" -#endif #include "core/settings.h" #include "network/network.h" #include "video_core/video_core.h" @@ -201,9 +199,7 @@ System::ResultStatus System::Init(EmuWindow& emu_window, u32 system_mode) { telemetry_session = std::make_unique(); -#ifdef ENABLE_SCRIPTING rpc_server = std::make_unique(); -#endif service_manager = std::make_shared(*this); archive_manager = std::make_unique(*this); @@ -294,9 +290,7 @@ void System::Shutdown() { kernel.reset(); HW::Shutdown(); telemetry_session.reset(); -#ifdef ENABLE_SCRIPTING rpc_server.reset(); -#endif cheat_engine.reset(); service_manager.reset(); dsp_core.reset(); diff --git a/src/core/core.h b/src/core/core.h index 365b8063c..b8552fe3b 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -24,11 +24,9 @@ namespace AudioCore { class DspInterface; } -#ifdef ENABLE_SCRIPTING namespace RPC { class RPCServer; } -#endif namespace Service { namespace SM { @@ -267,10 +265,8 @@ private: /// Cheats manager std::unique_ptr cheat_engine; -#ifdef ENABLE_SCRIPTING /// RPC Server for scripting support std::unique_ptr rpc_server; -#endif std::unique_ptr archive_manager; diff --git a/src/core/rpc/rpc_server.h b/src/core/rpc/rpc_server.h index bb57bcdae..83fe4f60c 100644 --- a/src/core/rpc/rpc_server.h +++ b/src/core/rpc/rpc_server.h @@ -13,6 +13,9 @@ namespace RPC { +class Packet; +struct PacketHeader; + class RPCServer { public: RPCServer(); diff --git a/src/core/rpc/server.cpp b/src/core/rpc/server.cpp index b3e66d6ff..3474fa7e1 100644 --- a/src/core/rpc/server.cpp +++ b/src/core/rpc/server.cpp @@ -1,27 +1,30 @@ #include - #include "core/core.h" +#include "core/rpc/packet.h" #include "core/rpc/rpc_server.h" #include "core/rpc/server.h" +#include "core/rpc/udp_server.h" namespace RPC { Server::Server(RPCServer& rpc_server) : rpc_server(rpc_server) {} +Server::~Server() = default; + void Server::Start() { - const auto callback = [this](std::unique_ptr new_request) { + const auto callback = [this](std::unique_ptr new_request) { NewRequestCallback(std::move(new_request)); }; try { - zmq_server = std::make_unique(callback); + udp_server = std::make_unique(callback); } catch (...) { - LOG_ERROR(RPC_Server, "Error starting ZeroMQ server"); + LOG_ERROR(RPC_Server, "Error starting UDP server"); } } void Server::Stop() { - zmq_server.reset(); + udp_server.reset(); } void Server::NewRequestCallback(std::unique_ptr new_request) { diff --git a/src/core/rpc/server.h b/src/core/rpc/server.h index 2dfad2ef7..c9f27cd5c 100644 --- a/src/core/rpc/server.h +++ b/src/core/rpc/server.h @@ -4,24 +4,25 @@ #pragma once -#include "core/rpc/packet.h" -#include "core/rpc/zmq_server.h" +#include namespace RPC { class RPCServer; -class ZMQServer; +class UDPServer; +class Packet; class Server { public: Server(RPCServer& rpc_server); + ~Server(); void Start(); void Stop(); - void NewRequestCallback(std::unique_ptr new_request); + void NewRequestCallback(std::unique_ptr new_request); private: RPCServer& rpc_server; - std::unique_ptr zmq_server; + std::unique_ptr udp_server; }; } // namespace RPC diff --git a/src/core/rpc/udp_server.cpp b/src/core/rpc/udp_server.cpp new file mode 100644 index 000000000..185450f5e --- /dev/null +++ b/src/core/rpc/udp_server.cpp @@ -0,0 +1,100 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/rpc/packet.h" +#include "core/rpc/udp_server.h" + +namespace RPC { + +class UDPServer::Impl { +public: + explicit Impl(std::function)> new_request_callback) + // Use a random high port + // TODO: Make configurable or increment port number on failure + : socket(io_context, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 45987)), + new_request_callback(std::move(new_request_callback)) { + + StartReceive(); + worker_thread = std::thread([this] { + io_context.run(); + this->new_request_callback(nullptr); + }); + } + + ~Impl() { + io_context.stop(); + worker_thread.join(); + } + +private: + void StartReceive() { + socket.async_receive_from(boost::asio::buffer(request_buffer), remote_endpoint, + [this](const boost::system::error_code& error, std::size_t size) { + HandleReceive(error, size); + }); + } + + void HandleReceive(const boost::system::error_code& error, std::size_t size) { + if (error) { + LOG_WARNING(RPC_Server, "Failed to receive data on UDP socket: {}", error.message()); + } else if (size >= MIN_PACKET_SIZE && size <= MAX_PACKET_SIZE) { + PacketHeader header; + std::memcpy(&header, request_buffer.data(), sizeof(header)); + if ((size - MIN_PACKET_SIZE) == header.packet_size) { + u8* data = request_buffer.data() + MIN_PACKET_SIZE; + std::function send_reply_callback = + std::bind(&Impl::SendReply, this, remote_endpoint, std::placeholders::_1); + std::unique_ptr new_packet = + std::make_unique(header, data, send_reply_callback); + + // Send the request to the upper layer for handling + new_request_callback(std::move(new_packet)); + } + } else { + LOG_WARNING(RPC_Server, "Received message with wrong size: {}", size); + } + StartReceive(); + } + + void SendReply(boost::asio::ip::udp::endpoint endpoint, Packet& reply_packet) { + std::vector reply_buffer(MIN_PACKET_SIZE + reply_packet.GetPacketDataSize()); + auto reply_header = reply_packet.GetHeader(); + + std::memcpy(reply_buffer.data(), &reply_header, sizeof(reply_header)); + std::memcpy(reply_buffer.data() + (4 * sizeof(u32)), reply_packet.GetPacketData().data(), + reply_packet.GetPacketDataSize()); + + boost::system::error_code error; + socket.send_to(boost::asio::buffer(reply_buffer), endpoint, 0, error); + + if (error) { + LOG_WARNING(RPC_Server, "Failed to send reply: {}", error.message()); + } else { + LOG_INFO(RPC_Server, "Sent reply version({}) id=({}) type=({}) size=({})", + reply_packet.GetVersion(), reply_packet.GetId(), + static_cast(reply_packet.GetPacketType()), + reply_packet.GetPacketDataSize()); + } + } + + std::thread worker_thread; + + boost::asio::io_context io_context; + boost::asio::ip::udp::socket socket; + std::array request_buffer; + boost::asio::ip::udp::endpoint remote_endpoint; + + std::function)> new_request_callback; +}; + +UDPServer::UDPServer(std::function)> new_request_callback) + : impl(std::make_unique(new_request_callback)) {} + +UDPServer::~UDPServer() = default; + +} // namespace RPC diff --git a/src/core/rpc/udp_server.h b/src/core/rpc/udp_server.h new file mode 100644 index 000000000..f4ff2ad62 --- /dev/null +++ b/src/core/rpc/udp_server.h @@ -0,0 +1,24 @@ +// Copyright 2019 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +namespace RPC { + +class Packet; + +class UDPServer { +public: + explicit UDPServer(std::function)> new_request_callback); + ~UDPServer(); + +private: + class Impl; + std::unique_ptr impl; +}; + +} // namespace RPC diff --git a/src/core/rpc/zmq_server.cpp b/src/core/rpc/zmq_server.cpp deleted file mode 100644 index 47885973c..000000000 --- a/src/core/rpc/zmq_server.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "common/common_types.h" -#include "core/core.h" -#include "core/rpc/packet.h" -#include "core/rpc/zmq_server.h" - -namespace RPC { - -ZMQServer::ZMQServer(std::function)> new_request_callback) - : zmq_context(std::move(std::make_unique(1))), - zmq_socket(std::move(std::make_unique(*zmq_context, ZMQ_REP))), - new_request_callback(std::move(new_request_callback)) { - // Use a random high port - // TODO: Make configurable or increment port number on failure - zmq_socket->bind("tcp://127.0.0.1:45987"); - LOG_INFO(RPC_Server, "ZeroMQ listening on port 45987"); - - worker_thread = std::thread(&ZMQServer::WorkerLoop, this); -} - -ZMQServer::~ZMQServer() { - // Triggering the zmq_context destructor will cancel - // any blocking calls to zmq_socket->recv() - running = false; - zmq_context.reset(); - worker_thread.join(); - - LOG_INFO(RPC_Server, "ZeroMQ stopped"); -} - -void ZMQServer::WorkerLoop() { - zmq::message_t request; - while (running) { - try { - if (zmq_socket->recv(&request, 0)) { - if (request.size() >= MIN_PACKET_SIZE && request.size() <= MAX_PACKET_SIZE) { - u8* request_buffer = static_cast(request.data()); - PacketHeader header; - std::memcpy(&header, request_buffer, sizeof(header)); - if ((request.size() - MIN_PACKET_SIZE) == header.packet_size) { - u8* data = request_buffer + MIN_PACKET_SIZE; - std::function send_reply_callback = - std::bind(&ZMQServer::SendReply, this, std::placeholders::_1); - std::unique_ptr new_packet = - std::make_unique(header, data, send_reply_callback); - - // Send the request to the upper layer for handling - new_request_callback(std::move(new_packet)); - } - } - } - } catch (...) { - LOG_WARNING(RPC_Server, "Failed to receive data on ZeroMQ socket"); - } - } - std::unique_ptr end_packet = nullptr; - new_request_callback(std::move(end_packet)); - // Destroying the socket must be done by this thread. - zmq_socket.reset(); -} - -void ZMQServer::SendReply(Packet& reply_packet) { - if (running) { - auto reply_buffer = - std::make_unique(MIN_PACKET_SIZE + reply_packet.GetPacketDataSize()); - auto reply_header = reply_packet.GetHeader(); - - std::memcpy(reply_buffer.get(), &reply_header, sizeof(reply_header)); - std::memcpy(reply_buffer.get() + (4 * sizeof(u32)), reply_packet.GetPacketData().data(), - reply_packet.GetPacketDataSize()); - - zmq_socket->send(reply_buffer.get(), MIN_PACKET_SIZE + reply_packet.GetPacketDataSize()); - - LOG_INFO(RPC_Server, "Sent reply version({}) id=({}) type=({}) size=({})", - reply_packet.GetVersion(), reply_packet.GetId(), - static_cast(reply_packet.GetPacketType()), reply_packet.GetPacketDataSize()); - } -} - -}; // namespace RPC diff --git a/src/core/rpc/zmq_server.h b/src/core/rpc/zmq_server.h deleted file mode 100644 index 784fccf5a..000000000 --- a/src/core/rpc/zmq_server.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#define ZMQ_STATIC -#include - -namespace RPC { - -class Packet; - -class ZMQServer { -public: - explicit ZMQServer(std::function)> new_request_callback); - ~ZMQServer(); - -private: - void WorkerLoop(); - void SendReply(Packet& request); - - std::thread worker_thread; - std::atomic_bool running = true; - - std::unique_ptr zmq_context; - std::unique_ptr zmq_socket; - - std::function)> new_request_callback; -}; - -} // namespace RPC