From 13b5fb82fa2816b30dbb74c0d85a122638685d70 Mon Sep 17 00:00:00 2001 From: staphen Date: Tue, 19 Sep 2023 23:11:24 -0400 Subject: [PATCH] Build ASIO without exceptions --- 3rdParty/asio/CMakeLists.txt | 5 ++- 3rdParty/asio/asio_handle_exception.cpp | 18 +++++++++ 3rdParty/asio/asio_handle_exception.hpp | 17 +++++++++ Source/dvlnet/base.h | 2 +- Source/dvlnet/base_protocol.h | 5 ++- Source/dvlnet/packet.h | 10 +++++ Source/dvlnet/tcp_client.cpp | 50 +++++++++++++++++-------- Source/dvlnet/tcp_client.h | 7 +++- Source/dvlnet/tcp_server.cpp | 24 +++++++++++- Source/dvlnet/tcp_server.h | 5 +++ uwp-project/devilutionx.vcxproj | 8 ++-- 11 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 3rdParty/asio/asio_handle_exception.cpp create mode 100644 3rdParty/asio/asio_handle_exception.hpp diff --git a/3rdParty/asio/CMakeLists.txt b/3rdParty/asio/CMakeLists.txt index 3c1e4bc81..1c8a06ab7 100644 --- a/3rdParty/asio/CMakeLists.txt +++ b/3rdParty/asio/CMakeLists.txt @@ -7,8 +7,9 @@ FetchContent_Declare(asio ) FetchContent_MakeAvailableExcludeFromAll(asio) -add_library(asio INTERFACE) -target_include_directories(asio INTERFACE ${asio_SOURCE_DIR}/asio/include) +add_library(asio STATIC ${CMAKE_CURRENT_LIST_DIR}/asio_handle_exception.cpp) +target_compile_definitions(asio PUBLIC ASIO_NO_EXCEPTIONS) +target_include_directories(asio PUBLIC ${asio_SOURCE_DIR}/asio/include ${CMAKE_CURRENT_LIST_DIR}) if(NINTENDO_3DS OR NINTENDO_SWITCH) include(asio_defs REQUIRED) diff --git a/3rdParty/asio/asio_handle_exception.cpp b/3rdParty/asio/asio_handle_exception.cpp new file mode 100644 index 000000000..20a906c70 --- /dev/null +++ b/3rdParty/asio/asio_handle_exception.cpp @@ -0,0 +1,18 @@ +#include + +#define ErrAsio(message) devilution::ErrDlg("ASIO Error", message, __FILE__, __LINE__) + +namespace devilution { + +extern void ErrDlg(const char* title, std::string_view error, std::string_view logFilePath, int logLineNr); + +} // namespace devilution + +namespace asio::detail { + +void fatal_exception(const char* message) +{ + ErrAsio(message); +} + +} // namespace asio::detail diff --git a/3rdParty/asio/asio_handle_exception.hpp b/3rdParty/asio/asio_handle_exception.hpp new file mode 100644 index 000000000..6742717c6 --- /dev/null +++ b/3rdParty/asio/asio_handle_exception.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace asio::detail { + +void fatal_exception(const char *message); + +template +void throw_exception( + const Exception &e + ASIO_SOURCE_LOCATION_PARAM) +{ + fatal_exception(e.what()); +} + +} // namespace asio::detail diff --git a/Source/dvlnet/base.h b/Source/dvlnet/base.h index 28a85b90d..27abdb327 100644 --- a/Source/dvlnet/base.h +++ b/Source/dvlnet/base.h @@ -29,7 +29,7 @@ public: bool SNetGetOwnerTurnsWaiting(uint32_t *turns) override; bool SNetGetTurnsInTransit(uint32_t *turns) override; - virtual void poll() = 0; + virtual tl::expected poll() = 0; virtual void send(packet &pkt) = 0; virtual void DisconnectNet(plr_t plr); diff --git a/Source/dvlnet/base_protocol.h b/Source/dvlnet/base_protocol.h index 9594cf8e4..b3c525f3b 100644 --- a/Source/dvlnet/base_protocol.h +++ b/Source/dvlnet/base_protocol.h @@ -17,7 +17,7 @@ class base_protocol : public base { public: int create(std::string addrstr) override; int join(std::string addrstr) override; - void poll() override; + tl::expected poll() override; void send(packet &pkt) override; void DisconnectNet(plr_t plr) override; @@ -190,9 +190,10 @@ bool base_protocol

::IsGameHost() } template -void base_protocol

::poll() +tl::expected base_protocol

::poll() { recv(); + return {}; } template diff --git a/Source/dvlnet/packet.h b/Source/dvlnet/packet.h index 246206db2..f3dd76842 100644 --- a/Source/dvlnet/packet.h +++ b/Source/dvlnet/packet.h @@ -76,6 +76,11 @@ public: { } + PacketError(std::string_view message) + : message_(message) + { + } + PacketError(const PacketError &error) : message_(std::string(error.message_)) { @@ -95,6 +100,11 @@ private: StringOrView message_; }; +inline PacketError IoHandlerError(std::string message) +{ + return PacketError(std::move(message)); +} + PacketError PacketTypeError(std::uint8_t unknownPacketType); PacketError PacketTypeError(std::initializer_list expectedTypes, std::uint8_t actual); diff --git a/Source/dvlnet/tcp_client.cpp b/Source/dvlnet/tcp_client.cpp index d3a4e12da..5761a725a 100644 --- a/Source/dvlnet/tcp_client.cpp +++ b/Source/dvlnet/tcp_client.cpp @@ -12,6 +12,7 @@ #include "options.h" #include "utils/language.h" +#include "utils/str_cat.hpp" namespace devilution::net { @@ -55,15 +56,14 @@ int tcp_client::join(std::string addrstr) PLR_BROADCAST, PLR_MASTER, cookie_self, game_init_info); if (!pkt.has_value()) { const std::string_view message = pkt.error().what(); - SDL_SetError("make_packet: %.*s\n", static_cast(message.size()), message.data()); + SDL_SetError("make_packet: %.*s", static_cast(message.size()), message.data()); return -1; } send(**pkt); for (auto i = 0; i < NoSleep; ++i) { - try { - poll(); - } catch (const std::runtime_error &e) { - SDL_SetError("%s", e.what()); + if (tl::expected result = poll(); !result.has_value()) { + const std::string_view message = result.error().what(); + SDL_SetError("%.*s", static_cast(message.size()), message.data()); return -1; } if (plr_self != PLR_BROADCAST) @@ -72,7 +72,8 @@ int tcp_client::join(std::string addrstr) } } if (plr_self == PLR_BROADCAST) { - SDL_SetError("%s", _("Unable to connect").data()); + const std::string_view message = _("Unable to connect"); + SDL_SetError("%.*s", static_cast(message.size()), message.data()); return -1; } @@ -84,21 +85,34 @@ bool tcp_client::IsGameHost() return local_server != nullptr; } -void tcp_client::poll() +tl::expected tcp_client::poll() { - ioc.poll(); + while (ioc.poll_one() > 0) { + if (IsGameHost()) { + tl::expected serverResult = local_server->CheckIoHandlerError(); + if (!serverResult.has_value()) + return serverResult; + } + if (ioHandlerResult == std::nullopt) + continue; + tl::expected packetError = tl::make_unexpected(*ioHandlerResult); + ioHandlerResult = std::nullopt; + return packetError; + } + return {}; } void tcp_client::HandleReceive(const asio::error_code &error, size_t bytesRead) { if (error) { - // error in recv from server - // returning and doing nothing should be the same - // as if all connections to other clients were lost + PacketError packetError = IoHandlerError(error.message()); + RaiseIoHandlerError(packetError); return; } if (bytesRead == 0) { - throw std::runtime_error(_("error: read 0 bytes from server").data()); + PacketError packetError(_("error: read 0 bytes from server")); + RaiseIoHandlerError(packetError); + return; } recv_buffer.resize(bytesRead); recv_queue.Write(std::move(recv_buffer)); @@ -106,11 +120,11 @@ void tcp_client::HandleReceive(const asio::error_code &error, size_t bytesRead) while (recv_queue.PacketReady()) { tl::expected, PacketError> pkt = pktfty->make_packet(recv_queue.ReadPacket()); if (!pkt.has_value()) { - LogError("make_packet: {}", pkt.error().what()); + RaiseIoHandlerError(pkt.error()); return; } if (tl::expected result = RecvLocal(**pkt); !result.has_value()) { - LogError("RecvLocal: {}", result.error().what()); + RaiseIoHandlerError(result.error()); return; } } @@ -126,7 +140,8 @@ void tcp_client::StartReceive() void tcp_client::HandleSend(const asio::error_code &error, size_t bytesSent) { - // empty for now + if (error) + RaiseIoHandlerError(error.message()); } void tcp_client::send(packet &pkt) @@ -153,6 +168,11 @@ std::string tcp_client::make_default_gamename() return std::string(sgOptions.Network.szBindAddress); } +void tcp_client::RaiseIoHandlerError(const PacketError &error) +{ + ioHandlerResult.emplace(error); +} + tcp_client::~tcp_client() = default; diff --git a/Source/dvlnet/tcp_client.h b/Source/dvlnet/tcp_client.h index c0df795a8..2baedc919 100644 --- a/Source/dvlnet/tcp_client.h +++ b/Source/dvlnet/tcp_client.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "dvlnet/base.h" #include "dvlnet/frame_queue.h" @@ -29,7 +30,7 @@ public: int create(std::string addrstr) override; int join(std::string addrstr) override; - void poll() override; + tl::expected poll() override; void send(packet &pkt) override; bool SNetLeaveGame(int type) override; @@ -50,9 +51,13 @@ private: asio::ip::tcp::socket sock = asio::ip::tcp::socket(ioc); std::unique_ptr local_server; // must be declared *after* ioc + std::optional ioHandlerResult; + void HandleReceive(const asio::error_code &error, size_t bytesRead); void StartReceive(); void HandleSend(const asio::error_code &error, size_t bytesSent); + + void RaiseIoHandlerError(const PacketError &error); }; } // namespace devilution::net diff --git a/Source/dvlnet/tcp_server.cpp b/Source/dvlnet/tcp_server.cpp index 61f53956d..f968e8766 100644 --- a/Source/dvlnet/tcp_server.cpp +++ b/Source/dvlnet/tcp_server.cpp @@ -189,7 +189,10 @@ void tcp_server::StartSend(const scc &con, packet &pkt) void tcp_server::HandleSend(const scc &con, const asio::error_code &ec, size_t bytesSent) { - // empty for now + if (ec) { + Log("Network error: {}", ec.message()); + DropConnection(con); + } } void tcp_server::StartAccept() @@ -202,8 +205,11 @@ void tcp_server::StartAccept() void tcp_server::HandleAccept(const scc &con, const asio::error_code &ec) { - if (ec) + if (ec) { + PacketError packetError = IoHandlerError(ec.message()); + RaiseIoHandlerError(packetError); return; + } if (NextFree() == PLR_BROADCAST) { DropConnection(con); } else { @@ -261,6 +267,20 @@ void tcp_server::DropConnection(const scc &con) con->socket.close(); } +void tcp_server::RaiseIoHandlerError(const PacketError &error) +{ + ioHandlerResult.emplace(error); +} + +tl::expected tcp_server::CheckIoHandlerError() +{ + if (ioHandlerResult == std::nullopt) + return {}; + tl::expected packetError = tl::make_unexpected(*ioHandlerResult); + ioHandlerResult = std::nullopt; + return packetError; +} + void tcp_server::Close() { acceptor->close(); diff --git a/Source/dvlnet/tcp_server.h b/Source/dvlnet/tcp_server.h index 5ecbff4de..07a1365b3 100644 --- a/Source/dvlnet/tcp_server.h +++ b/Source/dvlnet/tcp_server.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "dvlnet/abstract_net.h" #include "dvlnet/frame_queue.h" @@ -37,6 +38,7 @@ public: tcp_server(asio::io_context &ioc, const std::string &bindaddr, unsigned short port, packet_factory &pktfty); std::string LocalhostSelf(); + tl::expected CheckIoHandlerError(); void Close(); virtual ~tcp_server(); @@ -66,6 +68,8 @@ private: std::array connections; buffer_t game_init_info; + std::optional ioHandlerResult; + scc MakeConnection(); plr_t NextFree(); bool Empty(); @@ -81,6 +85,7 @@ private: void StartTimeout(const scc &con); void HandleTimeout(const scc &con, const asio::error_code &ec); void DropConnection(const scc &con); + void RaiseIoHandlerError(const PacketError &error); }; } // namespace devilution::net diff --git a/uwp-project/devilutionx.vcxproj b/uwp-project/devilutionx.vcxproj index 8d0d6f43a..b63e5404d 100644 --- a/uwp-project/devilutionx.vcxproj +++ b/uwp-project/devilutionx.vcxproj @@ -73,8 +73,8 @@ - sdl_image.lib;libpng16_staticd.lib;pkware.lib;fmtd.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;sdl2.lib;sdl_audiolib.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) - ..\build\SDL\VisualC-WinRT\x64\Debug\SDL-UWP;..\build\3rdParty\SDL_image\Debug;..\build\_deps\zlib-build\Debug;..\build\3rdParty\PKWare\Debug;..\build\3rdParty\bzip2\Debug;..\build\3rdParty\libsmackerdec\Debug;..\build\3rdParty\libmpq\Debug;..\build\_deps\sdl_audiolib-build\Debug;..\build\_deps\libsodium-build\Debug;..\build\_deps\libzt-build\lib\Debug;..\build\_deps\libfmt-build\Debug;..\build\_deps\libpng-build\Debug;..\build\Source\libdevilutionx.dir\Debug;%(AdditionalLibraryDirectories) + sdl_image.lib;libpng16_staticd.lib;pkware.lib;fmtd.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;sdl2.lib;sdl_audiolib.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) + ..\build\SDL\VisualC-WinRT\x64\Debug\SDL-UWP;..\build\3rdParty\SDL_image\Debug;..\build\_deps\zlib-build\Debug;..\build\3rdParty\PKWare\Debug;..\build\3rdParty\bzip2\Debug;..\build\3rdParty\libsmackerdec\Debug;..\build\3rdParty\libmpq\Debug;..\build\_deps\sdl_audiolib-build\Debug;..\build\3rdParty\asio\Release;..\build\_deps\libsodium-build\Debug;..\build\_deps\libzt-build\lib\Debug;..\build\_deps\libfmt-build\Debug;..\build\_deps\libpng-build\Debug;..\build\Source\libdevilutionx.dir\Debug;%(AdditionalLibraryDirectories) pch.h @@ -92,8 +92,8 @@ - sdl_image.lib;libpng16_static.lib;pkware.lib;fmt.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;sdl2.lib;sdl_audiolib.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) - ..\build\SDL\VisualC-WinRT\x64\Release\SDL-UWP;..\build\3rdParty\SDL_image\Release;..\build\_deps\zlib-build\Release;..\build\3rdParty\PKWare\Release;..\build\3rdParty\bzip2\Release;..\build\3rdParty\libsmackerdec\Release;..\build\3rdParty\libmpq\Release;..\build\_deps\sdl_audiolib-build\Release;..\build\_deps\libsodium-build\Release;..\build\_deps\libzt-build\lib\Release;..\build\_deps\libfmt-build\Release;..\build\_deps\libpng-build\Release;..\build\Source\libdevilutionx.dir\Release;%(AdditionalLibraryDirectories) + sdl_image.lib;libpng16_static.lib;pkware.lib;fmt.lib;zlibstatic.lib;bzip2.lib;libsmackerdec.lib;libmpq.lib;libdevilutionx.lib;sdl2.lib;sdl_audiolib.lib;asio.lib;sodium.lib;zt.lib;lwip_pic.lib;miniupnpc_pic.lib;natpmp_pic.lib;zt_pic.lib;zto_pic.lib;shlwapi.lib;shell32.lib;%(AdditionalDependencies) + ..\build\SDL\VisualC-WinRT\x64\Release\SDL-UWP;..\build\3rdParty\SDL_image\Release;..\build\_deps\zlib-build\Release;..\build\3rdParty\PKWare\Release;..\build\3rdParty\bzip2\Release;..\build\3rdParty\libsmackerdec\Release;..\build\3rdParty\libmpq\Release;..\build\_deps\sdl_audiolib-build\Release;..\build\3rdParty\asio\Release;..\build\_deps\libsodium-build\Release;..\build\_deps\libzt-build\lib\Release;..\build\_deps\libfmt-build\Release;..\build\_deps\libpng-build\Release;..\build\Source\libdevilutionx.dir\Release;%(AdditionalLibraryDirectories) pch.h