diff --git a/tsd/apps/interactive/demos/network/RenderSession.hpp b/tsd/apps/interactive/demos/network/RenderSession.hpp index 9bc7b9de..feeacc4c 100644 --- a/tsd/apps/interactive/demos/network/RenderSession.hpp +++ b/tsd/apps/interactive/demos/network/RenderSession.hpp @@ -45,6 +45,9 @@ enum MessageType // All ping messages PING, + // All disconnections + DISCONNECT, + // All errors ERROR = 255 }; diff --git a/tsd/apps/interactive/demos/network/client/RemoteViewport.cpp b/tsd/apps/interactive/demos/network/client/RemoteViewport.cpp index c2697841..231d47ac 100644 --- a/tsd/apps/interactive/demos/network/client/RemoteViewport.cpp +++ b/tsd/apps/interactive/demos/network/client/RemoteViewport.cpp @@ -42,10 +42,8 @@ void RemoteViewport::buildUI() if (m_viewportSize != viewportSize || m_wasConnected != isConnected) reshape(viewportSize); - if (!m_wasConnected && isConnected) { - m_channel->send( - tsd::network::make_message(MessageType::SERVER_REQUEST_VIEW)); - } + if (!m_wasConnected && isConnected) + m_channel->send(MessageType::SERVER_REQUEST_VIEW); m_wasConnected = isConnected; m_incomingFramePass->setEnabled(isConnected); @@ -136,8 +134,7 @@ void RemoteViewport::reshape(tsd::math::int2 newSize) if (m_channel && m_channel->isConnected()) { tsd::network::RenderSession::Frame::Config frameConfig; frameConfig.size = tsd::math::uint2(newSize.x, newSize.y); - m_channel->send(tsd::network::make_message( - MessageType::SERVER_SET_FRAME_CONFIG, &frameConfig)); + m_channel->send(MessageType::SERVER_SET_FRAME_CONFIG, &frameConfig); } } @@ -153,8 +150,7 @@ void RemoteViewport::updateCamera() viewMsg.azeldist.z = m_arcball->distance(); viewMsg.lookat = m_arcball->at(); - m_channel->send( - tsd::network::make_message(MessageType::SERVER_SET_VIEW, &viewMsg)); + m_channel->send(MessageType::SERVER_SET_VIEW, &viewMsg); } } diff --git a/tsd/apps/interactive/demos/network/client/tsdRemoteViewer.cpp b/tsd/apps/interactive/demos/network/client/tsdRemoteViewer.cpp index c33b4a7d..9509ec17 100644 --- a/tsd/apps/interactive/demos/network/client/tsdRemoteViewer.cpp +++ b/tsd/apps/interactive/demos/network/client/tsdRemoteViewer.cpp @@ -95,11 +95,12 @@ Application::Application() tsd::core::logStatus( "\n%s", tsd::core::objectDBInfo(scene.objectDB()).c_str()); tsd::core::logStatus("[Client] Requesting start of rendering..."); - m_client->send( - tsd::network::make_message(MessageType::SERVER_START_RENDERING)); + m_client->send(MessageType::SERVER_START_RENDERING); + appCore()->tsd.sceneLoadComplete = true; }); core->tsd.scene.setUpdateDelegate(m_updateDelegate.get()); + core->tsd.sceneLoadComplete = false; } Application::~Application() = default; @@ -110,7 +111,6 @@ anari_viewer::WindowArray Application::setupWindows() auto *core = appCore(); auto *manipulator = &core->view.manipulator; - core->tsd.sceneLoadComplete = true; auto *log = new tsd_ui::Log(this); m_viewport = @@ -172,22 +172,19 @@ void Application::uiMainMenuBar() if (ImGui::BeginMenu("Server")) { if (ImGui::MenuItem("Start Rendering")) { tsd::core::logStatus("[Client] Sending START_RENDERING command"); - m_client->send( - tsd::network::make_message(MessageType::SERVER_START_RENDERING)); + m_client->send(MessageType::SERVER_START_RENDERING); } if (ImGui::MenuItem("Pause Rendering")) { tsd::core::logStatus("[Client] Sending STOP_RENDERING command"); - m_client->send( - tsd::network::make_message(MessageType::SERVER_STOP_RENDERING)); + m_client->send(MessageType::SERVER_STOP_RENDERING); } ImGui::Separator(); if (ImGui::MenuItem("Shutdown")) { tsd::core::logStatus("[Client] Sending SHUTDOWN command"); - m_client->send(tsd::network::make_message(MessageType::SERVER_SHUTDOWN)) - .get(); + m_client->send(MessageType::SERVER_SHUTDOWN).get(); disconnect(); } @@ -200,7 +197,7 @@ void Application::uiMainMenuBar() if (ImGui::IsKeyPressed(ImGuiKey_P, false)) { tsd::core::logStatus("[Client] Sending PING"); - m_client->send(tsd::network::make_message(MessageType::PING)); + m_client->send(MessageType::PING); } if (ImGui::IsKeyPressed(ImGuiKey_F1, false)) { @@ -325,9 +322,7 @@ void Application::connect() tsd::core::logStatus( "[Client] Connected to server at %s:%d", m_host.c_str(), m_port); tsd::core::logStatus("[Client] Requesting scene from server"); - m_client->send( - tsd::network::make_message(MessageType::SERVER_REQUEST_SCENE)) - .get(); + m_client->send(MessageType::SERVER_REQUEST_SCENE).get(); } else { tsd::core::logError("[Client] Failed to connect to server at %s:%d", m_host.c_str(), @@ -338,11 +333,14 @@ void Application::connect() void Application::disconnect() { tsd::core::logStatus("[Client] Disconnecting from server..."); - m_client->send(tsd::network::make_message(MessageType::SERVER_STOP_RENDERING)) - .get(); + m_updateDelegate->setEnabled(false); + m_client->send(MessageType::DISCONNECT).get(); m_client->disconnect(); - auto &scene = appCore()->tsd.scene; + auto *core = appCore(); + core->tsd.sceneLoadComplete = false; + core->clearSelected(); + auto &scene = core->tsd.scene; scene.removeAllLayers(); scene.removeAllObjects(); } diff --git a/tsd/apps/interactive/demos/network/client/tsdTestClient.cpp b/tsd/apps/interactive/demos/network/client/tsdTestClient.cpp index f9345a42..df3a2693 100644 --- a/tsd/apps/interactive/demos/network/client/tsdTestClient.cpp +++ b/tsd/apps/interactive/demos/network/client/tsdTestClient.cpp @@ -85,19 +85,19 @@ int main() client->connect("127.0.0.1", 12345); - client->send(make_message(MessageType::SERVER_START_RENDERING)); + client->send(MessageType::SERVER_START_RENDERING); tsd::core::logStatus("[Client] Rendering for 1 second..."); std::this_thread::sleep_for(std::chrono::seconds(1)); - client->send(make_message(MessageType::SERVER_STOP_RENDERING)).get(); - client->send(make_message(MessageType::SERVER_REQUEST_FRAME_CONFIG)).get(); - client->send(make_message(MessageType::SERVER_REQUEST_VIEW)).get(); + client->send(MessageType::SERVER_STOP_RENDERING).get(); + client->send(MessageType::SERVER_REQUEST_FRAME_CONFIG).get(); + client->send(MessageType::SERVER_REQUEST_VIEW).get(); for (int i = 0; i < 3; ++i) { tsd::core::logStatus("[Client] Sending PING #%d", i + 1); std::this_thread::sleep_for(std::chrono::seconds(1)); - client->send(make_message(MessageType::PING)); + client->send(MessageType::PING); } tsd::core::logStatus("[Client] Waiting for 3 seconds..."); @@ -106,7 +106,7 @@ int main() // Shutdown // tsd::core::logStatus("[Client] Sending SHUTDOWN"); - client->send(make_message(MessageType::SERVER_SHUTDOWN)).get(); + client->send(MessageType::SERVER_SHUTDOWN).get(); client->disconnect(); // Store fine frame // diff --git a/tsd/apps/interactive/demos/network/server/RenderServer.cpp b/tsd/apps/interactive/demos/network/server/RenderServer.cpp index 1daccd8f..575cc631 100644 --- a/tsd/apps/interactive/demos/network/server/RenderServer.cpp +++ b/tsd/apps/interactive/demos/network/server/RenderServer.cpp @@ -41,24 +41,25 @@ void RenderServer::run(short port) tsd::core::logStatus("[Server] Listening on port %i...", int(port)); - while (m_mode != ServerMode::SHUTDOWN) { - if (!m_server->isConnected() || m_mode == ServerMode::DISCONNECTED) { + while (m_nextMode != ServerMode::SHUTDOWN) { + bool wasRendering = m_currentMode == ServerMode::RENDERING; + + m_currentMode = + m_server->isConnected() ? m_nextMode : ServerMode::DISCONNECTED; + + if (m_currentMode == ServerMode::DISCONNECTED) { if (m_previousMode != ServerMode::DISCONNECTED) { tsd::core::logStatus("[Server] Listening on port %i...", int(port)); - if (m_lastSentFrame.valid()) - m_lastSentFrame.get(); - m_mode = ServerMode::DISCONNECTED; + m_server->restart(); } - m_wasRenderingBeforeSendScene = false; std::this_thread::sleep_for(std::chrono::seconds(1)); - } else if (m_mode == ServerMode::RENDERING) { + } else if (m_currentMode == ServerMode::RENDERING) { tsd::core::logDebug("[Server] Rendering frame..."); update_FrameConfig(); update_View(); m_renderPipeline.render(); send_FrameBuffer(); - m_wasRenderingBeforeSendScene = true; - } else if (m_mode == ServerMode::SEND_SCENE) { + } else if (m_currentMode == ServerMode::SEND_SCENE) { tsd::core::logStatus("[Server] Serializing + sending scene..."); tsd::core::Timer timer; @@ -69,18 +70,14 @@ void RenderServer::run(short port) timer.end(); tsd::core::logStatus("[Server] ...done! (%.3f s)", timer.seconds()); - m_mode = m_wasRenderingBeforeSendScene ? ServerMode::RENDERING - : ServerMode::PAUSED; + set_Mode(wasRendering ? ServerMode::RENDERING : ServerMode::PAUSED); } else { if (m_previousMode != ServerMode::PAUSED) tsd::core::logStatus("[Server] Rendering paused..."); - m_wasRenderingBeforeSendScene = false; - if (m_lastSentFrame.valid()) - m_lastSentFrame.get(); std::this_thread::sleep_for(std::chrono::seconds(1)); } - m_previousMode = m_mode; + m_previousMode = m_currentMode; } tsd::core::logStatus("[Server] Shutting down..."); @@ -191,6 +188,12 @@ void RenderServer::setup_Messaging() tsd::core::logStatus("[Server] Received PING from client"); }); + m_server->registerHandler( + MessageType::DISCONNECT, [&](const tsd::network::Message &msg) { + tsd::core::logStatus("[Server] Client signaled disconnection."); + set_Mode(ServerMode::DISCONNECTED); + }); + m_server->registerHandler(MessageType::SERVER_START_RENDERING, [&](const tsd::network::Message &msg) { tsd::core::logStatus( @@ -298,21 +301,21 @@ void RenderServer::setup_Messaging() m_server->registerHandler(MessageType::SERVER_REQUEST_FRAME_CONFIG, [s = m_server, session = &m_session](const tsd::network::Message &msg) { tsd::core::logDebug("[Server] Client requested frame config."); - s->send(make_message( - MessageType::CLIENT_RECEIVE_FRAME_CONFIG, &session->frame.config)); + s->send( + MessageType::CLIENT_RECEIVE_FRAME_CONFIG, &session->frame.config); }); m_server->registerHandler(MessageType::SERVER_REQUEST_VIEW, [s = m_server, session = &m_session](const tsd::network::Message &msg) { tsd::core::logDebug("[Server] Client requested view."); - s->send(make_message(MessageType::CLIENT_RECEIVE_VIEW, &session->view)); + s->send(MessageType::CLIENT_RECEIVE_VIEW, &session->view); }); m_server->registerHandler(MessageType::SERVER_REQUEST_SCENE, [this](const tsd::network::Message &msg) { tsd::core::logDebug("[Server] Client requested scene..."); // Notify client a big message is coming... - m_server->send(make_message(MessageType::CLIENT_SCENE_TRANSFER_BEGIN)); + m_server->send(MessageType::CLIENT_SCENE_TRANSFER_BEGIN); set_Mode(ServerMode::SEND_SCENE); }); } @@ -357,16 +360,18 @@ void RenderServer::send_FrameBuffer() return; } - m_lastSentFrame = m_server->send( - tsd::network::make_message(MessageType::CLIENT_RECEIVE_FRAME_BUFFER_COLOR, - m_session.frame.buffers.color)); + m_lastSentFrame = + m_server->send(MessageType::CLIENT_RECEIVE_FRAME_BUFFER_COLOR, + m_session.frame.buffers.color); } void RenderServer::set_Mode(ServerMode mode) { - if (m_mode == ServerMode::SHUTDOWN) // if shutting down, do not change mode + const bool shuttingDown = m_nextMode == ServerMode::SHUTDOWN + || m_currentMode == ServerMode::SHUTDOWN; + if (shuttingDown) // if shutting down, do not change mode return; - m_mode = mode; + m_nextMode = mode; } } // namespace tsd::network diff --git a/tsd/apps/interactive/demos/network/server/RenderServer.hpp b/tsd/apps/interactive/demos/network/server/RenderServer.hpp index 6d57b721..b4bdc431 100644 --- a/tsd/apps/interactive/demos/network/server/RenderServer.hpp +++ b/tsd/apps/interactive/demos/network/server/RenderServer.hpp @@ -61,9 +61,9 @@ struct RenderServer tsd::rendering::Manipulator m_manipulator; tsd::rendering::RenderIndex *m_renderIndex{nullptr}; tsd::rendering::RenderPipeline m_renderPipeline; - ServerMode m_mode{ServerMode::DISCONNECTED}; + ServerMode m_currentMode{ServerMode::DISCONNECTED}; + ServerMode m_nextMode{ServerMode::DISCONNECTED}; ServerMode m_previousMode{ServerMode::DISCONNECTED}; - bool m_wasRenderingBeforeSendScene{false}; struct SessionVersions { diff --git a/tsd/src/tsd/network/Message.hpp b/tsd/src/tsd/network/Message.hpp index b1e18740..6f28004f 100644 --- a/tsd/src/tsd/network/Message.hpp +++ b/tsd/src/tsd/network/Message.hpp @@ -79,15 +79,6 @@ bool payloadRead( const Message &msg, uint32_t &offset, T *data, uint32_t length = 1); bool payloadRead(const Message &msg, uint32_t &offset, std::string &str); -// make_message() // - -Message make_message(uint8_t type); -Message make_message(uint8_t type, const std::string &data); -template -Message make_message(uint8_t type, const T *data, uint32_t count = 1); -template -Message make_message(uint8_t type, const std::vector &data); - /////////////////////////////////////////////////////////////////////////////// // Inlined definitions //////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// @@ -179,34 +170,4 @@ inline bool payloadRead(const Message &msg, uint32_t &offset, std::string &str) return true; } -inline Message make_message(uint8_t type) -{ - Message msg; - msg.header.type = type; - return msg; -} - -inline Message make_message(uint8_t type, const std::string &data) -{ - Message msg = make_message(type); - payloadWrite(msg, data); - return msg; -} - -template -inline Message make_message(uint8_t type, const T *data, uint32_t count) -{ - Message msg = make_message(type); - payloadWrite(msg, data, count); - return msg; -} - -template -inline Message make_message(uint8_t type, const std::vector &data) -{ - static_assert(std::is_standard_layout_v && std::is_trivially_copyable_v, - "Message payload must be a POD type"); - return make_message(type, data.data(), static_cast(data.size())); -} - } // namespace tsd::network diff --git a/tsd/src/tsd/network/NetworkChannel.cpp b/tsd/src/tsd/network/NetworkChannel.cpp index b2ef34a1..c4d7e284 100644 --- a/tsd/src/tsd/network/NetworkChannel.cpp +++ b/tsd/src/tsd/network/NetworkChannel.cpp @@ -17,9 +17,7 @@ static void async_invoke(boost::asio::io_context &io_context, FCN &&f) // NetworkChannel definitions ///////////////////////////////////////////////// -NetworkChannel::NetworkChannel() - : m_work(asio::make_work_guard(m_io_context)), m_socket(m_io_context) -{} +NetworkChannel::NetworkChannel() : m_socket(m_io_context) {} NetworkChannel::~NetworkChannel() { @@ -48,7 +46,8 @@ void NetworkChannel::removeAllHandlers() MessageFuture NetworkChannel::send(Message &&msg) { - auto promise = std::make_shared>(); + using MessagePromise = std::promise; + auto promise = std::make_shared(); auto future = promise->get_future(); if (!isConnected()) { @@ -86,21 +85,27 @@ MessageFuture NetworkChannel::send(Message &&msg) return future; } +MessageFuture NetworkChannel::send(uint8_t type, StructuredMessage &&msg) +{ + Message message = msg.toMessage(type); + return send(std::move(message)); +} + MessageFuture NetworkChannel::send(uint8_t type) { return send(make_message(type)); } -MessageFuture NetworkChannel::send(uint8_t type, StructuredMessage &&msg) +MessageFuture NetworkChannel::send(uint8_t type, const std::string &str) { - Message message = msg.toMessage(type); - return send(std::move(message)); + return send(make_message(type, str)); } void NetworkChannel::start_messaging() { tsd::core::logDebug("[NetworkChannel] starting channel"); stop_messaging(); + m_work.emplace(asio::make_work_guard(m_io_context)); m_io_context.restart(); m_io_thread = std::thread([this]() { tsd::core::logDebug("[NetworkChannel] starting IO thread"); @@ -119,12 +124,20 @@ void NetworkChannel::start_messaging() void NetworkChannel::stop_messaging() { try { - boost::system::error_code ec{}; - m_socket.shutdown(tcp::socket::shutdown_both, ec); - m_socket.close(ec); - m_io_context.stop(); - if (m_io_thread.joinable()) - m_io_thread.join(); + if (m_socket.is_open()) { + boost::system::error_code ec{}; + m_socket.shutdown(tcp::socket::shutdown_both, ec); + m_socket.close(ec); + } + if (!m_io_context.stopped()) { + m_io_context.stop(); + if (m_io_thread.joinable()) + m_io_thread.join(); + m_work.reset(); + + // Ensure all completion handlers have finished before returning + m_io_context.poll(); + } } catch (const std::system_error &e) { tsd::core::logError( "[NetworkChannel] System error during stop: %s", e.what()); @@ -140,58 +153,57 @@ void NetworkChannel::read_header() return; } + auto message = std::make_shared(); auto self = shared_from_this(); asio::async_read(m_socket, - asio::buffer(&m_currentMessage.header, sizeof(Message::Header)), - [this, self](const boost::system::error_code &error, + asio::buffer(&message->header, sizeof(Message::Header)), + [this, self, message](const boost::system::error_code &error, std::size_t bytes_transferred) { log_asio_error(error, "ReadHeader"); if (!error) - read_payload(); // Read next message + read_payload(message); // Read next message }); } -void NetworkChannel::read_payload() +void NetworkChannel::read_payload(std::shared_ptr msg) { if (!isConnected()) { tsd::core::logError("[NetworkChannel] Cannot read payload: not connected"); return; } - if (m_currentMessage.header.payload_length == 0) { - async_invoke(m_io_context, [this]() { - invoke_handler(); - read_header(); // Read next message + if (msg->header.payload_length == 0) { + async_invoke(m_io_context, [this, msg]() { + invoke_handler(msg); + read_header(); // Read next msg }); return; } - m_currentMessage.payload.resize(m_currentMessage.header.payload_length); + msg->payload.resize(msg->header.payload_length); auto self = shared_from_this(); asio::async_read(m_socket, - asio::buffer(m_currentMessage.payload.data(), - m_currentMessage.header.payload_length), - [this, self](const boost::system::error_code &error, + asio::buffer(msg->payload.data(), msg->header.payload_length), + [this, self, msg](const boost::system::error_code &error, std::size_t bytes_transferred) { log_asio_error(error, "ReadPayload"); if (!error) { - invoke_handler(); - read_header(); // Read next message + invoke_handler(msg); + read_header(); // Read next msg } }); } -void NetworkChannel::invoke_handler() +void NetworkChannel::invoke_handler(std::shared_ptr msg) { - Message &msg = m_currentMessage; // Invoke handler if registered - if (auto *handler = m_handlers.at(msg.header.type); handler != nullptr) { - (*handler)(msg); + if (auto *handler = m_handlers.at(msg->header.type); handler != nullptr) { + (*handler)(*msg); } else { tsd::core::logWarning( "[NetworkChannel] No handler registered for message type %d", - static_cast(msg.header.type)); + static_cast(msg->header.type)); } } @@ -214,9 +226,11 @@ void NetworkChannel::log_asio_error( "[NetworkChannel] %s error: %s", context, error.message().c_str()); } - boost::system::error_code ec{}; - m_socket.shutdown(tcp::socket::shutdown_both, ec); - m_socket.close(ec); + if (m_socket.is_open()) { + boost::system::error_code ec{}; + m_socket.shutdown(tcp::socket::shutdown_both, ec); + m_socket.close(ec); + } } // NetworkServer definitions ////////////////////////////////////////////////// @@ -232,6 +246,13 @@ void NetworkServer::start() start_messaging(); } +void NetworkServer::restart() +{ + stop(); + start_accept(); + start(); +} + void NetworkServer::stop() { stop_messaging(); @@ -246,9 +267,9 @@ void NetworkServer::start_accept() tsd::core::logStatus("[NetworkServer] New connection from %s", socket->remote_endpoint().address().to_string().c_str()); m_socket = std::move(*socket); + read_header(); + start_accept(); // Accept next connection } - read_header(); - start_accept(); // Accept next connection }); } @@ -279,20 +300,8 @@ void NetworkClient::connect(const std::string &host, short port) void NetworkClient::disconnect() { - boost::system::error_code ec{}; - m_socket.shutdown(tcp::socket::shutdown_both, ec); - if (ec) { - tsd::core::logError( - "[NetworkClient] Shutdown error: %s", ec.message().c_str()); - } - m_socket.close(ec); - if (ec) { - tsd::core::logError( - "[NetworkClient] Close error: %s", ec.message().c_str()); - } else { - tsd::core::logStatus("[NetworkClient] Disconnected from server"); - } stop_messaging(); + tsd::core::logStatus("[NetworkClient] Disconnected from server"); } } // namespace tsd::network diff --git a/tsd/src/tsd/network/NetworkChannel.hpp b/tsd/src/tsd/network/NetworkChannel.hpp index 21b020fd..f376f0e8 100644 --- a/tsd/src/tsd/network/NetworkChannel.hpp +++ b/tsd/src/tsd/network/NetworkChannel.hpp @@ -7,6 +7,7 @@ // std #include #include +#include #include namespace tsd::network { @@ -20,35 +21,51 @@ struct NetworkChannel : public std::enable_shared_from_this bool isConnected() const; - // Receive messages // + //// Receive messages //// void registerHandler(uint8_t messageType, MessageHandler handler); void removeHandler(uint8_t messageType); void removeAllHandlers(); - // Send messages // + //// Send messages //// MessageFuture send(Message &&msg); - MessageFuture send(uint8_t type); // empty payload MessageFuture send(uint8_t type, StructuredMessage &&msg); + /* No payload */ + MessageFuture send(uint8_t type); + + /* With payloads */ + MessageFuture send(uint8_t type, const std::string &str); + template + MessageFuture send(uint8_t type, const T *data, uint32_t count = 1); + template + MessageFuture send(uint8_t type, const std::vector &data); + protected: void start_messaging(); void stop_messaging(); void read_header(); - void read_payload(); - void invoke_handler(); + void read_payload(std::shared_ptr msg); + void invoke_handler(std::shared_ptr msg); void log_asio_error( const boost::system::error_code &error, const char *context); asio::io_context m_io_context; std::thread m_io_thread; - asio::executor_work_guard m_work; + + using WorkGuard = asio::executor_work_guard; + std::optional m_work; tcp::socket m_socket; - Message m_currentMessage; HandlerMap m_handlers; + + private: + Message make_message(uint8_t type); + Message make_message(uint8_t type, const std::string &data); + template + Message make_message(uint8_t type, const T *data, uint32_t count); }; struct NetworkServer : public NetworkChannel @@ -57,6 +74,7 @@ struct NetworkServer : public NetworkChannel ~NetworkServer() override = default; void start(); + void restart(); // must be running already void stop(); private: @@ -75,6 +93,51 @@ struct NetworkClient : public NetworkChannel void disconnect(); }; +// Inline definitions ///////////////////////////////////////////////////////// + +template +inline MessageFuture NetworkChannel::send( + uint8_t type, const T *data, uint32_t count) +{ + static_assert(std::is_standard_layout_v && std::is_trivially_copyable_v, + "Message payload must be a POD type"); + return send(make_message(type, data, count)); +} + +template +inline MessageFuture NetworkChannel::send( + uint8_t type, const std::vector &data) +{ + return send( + make_message(type, data.data(), static_cast(data.size()))); +} + +inline Message NetworkChannel::make_message(uint8_t type) +{ + Message msg; + msg.header.type = type; + return msg; +} + +inline Message NetworkChannel::make_message( + uint8_t type, const std::string &data) +{ + Message msg = make_message(type); + payloadWrite(msg, data); + return msg; +} + +template +inline Message NetworkChannel::make_message( + uint8_t type, const T *data, uint32_t count) +{ + static_assert(std::is_standard_layout_v && std::is_trivially_copyable_v, + "Message payload must be a POD type"); + Message msg = make_message(type); + payloadWrite(msg, data, count); + return msg; +} + // Inlined helper functions /////////////////////////////////////////////////// template diff --git a/tsd/src/tsd/ui/imgui/windows/LayerTree.cpp b/tsd/src/tsd/ui/imgui/windows/LayerTree.cpp index eafdb03c..5796f3f5 100644 --- a/tsd/src/tsd/ui/imgui/windows/LayerTree.cpp +++ b/tsd/src/tsd/ui/imgui/windows/LayerTree.cpp @@ -32,7 +32,7 @@ LayerTree::LayerTree(Application *app, const char *name) : Window(app, name) {} void LayerTree::buildUI() { if (!appCore()->tsd.sceneLoadComplete) { - ImGui::Text("PLEASE WAIT...LOADING SCENE"); + ImGui::Text("{SCENE NOT AVAILABLE}"); return; }