diff --git a/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/internal/SentPacketCache.h b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/internal/SentPacketCache.h new file mode 100644 index 00000000..5018b20f --- /dev/null +++ b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/internal/SentPacketCache.h @@ -0,0 +1,31 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#pragma once + +#include "oxygen_netcore/network/internal/SentPacket.h" + + +class SentPacketCache +{ +public: + void clear(); + uint32 getNextUniquePacketID() const; + + void addPacket(SentPacket& sentPacket, uint64 currentTimestamp, bool isStartConnectionPacket = false); + + void onPacketReceiveConfirmed(uint32 uniquePacketID); + + inline bool hasUnconfirmedPackets() const { return !mQueue.empty(); } + void updateResend(std::vector& outPacketsToResend, uint64 currentTimestamp); + +private: + uint32 mQueueStartUniquePacketID = 1; + uint32 mNextUniquePacketID = 1; // This should always be "mQueueStartUniquePacketID + mQueue.size()" + std::deque mQueue; // Can contain null pointers, anmely at the positions of packets that were already confirmed by the receiver +}; diff --git a/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/internal/WebSocketClient.cpp b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/internal/WebSocketClient.cpp new file mode 100644 index 00000000..2600fbf0 --- /dev/null +++ b/sonic3air-main/Oxygen/oxygenengine/source/oxygen_netcore/network/internal/WebSocketClient.cpp @@ -0,0 +1,142 @@ +/* +* Part of the Oxygen Engine / Sonic 3 A.I.R. software distribution. +* Copyright (C) 2017-2024 by Eukaryot +* +* Published under the GNU GPLv3 open source software license, see license.txt +* or https://www.gnu.org/licenses/gpl-3.0.en.html +*/ + +#include "oxygen_netcore/pch.h" +#include "oxygen_netcore/network/internal/WebSocketClient.h" +#include "oxygen_netcore/network/ConnectionManager.h" +#include "oxygen_netcore/network/NetConnection.h" + + +namespace +{ + //static const char* WEBSOCKET_PROTOCOL = "ws://"; // Only for testing + static const char* WEBSOCKET_PROTOCOL = "wss://"; // This is required for the web build when running on a https website +} + + +WebSocketClient::WebSocketClient(NetConnection& connection) : + mConnection(connection) +{ +} + +bool WebSocketClient::isAvailable() const +{ + // Web socket usage via emscripten is only available for the web version +#ifdef PLATFORM_WEB + return true; +#else + return false; +#endif +} + +void WebSocketClient::clear() +{ +#ifdef PLATFORM_WEB + if (mWebSocket != 0) + { + emscripten_websocket_close(mWebSocket, 1000, "Connection cleared"); + emscripten_websocket_delete(mWebSocket); + mWebSocket = 0; + } +#endif +} + +bool WebSocketClient::connectTo(const SocketAddress& remoteAddress) +{ +#ifdef PLATFORM_WEB + // Use web socket for communication (client-side only) + if (!emscripten_websocket_is_supported()) + return false; + + const std::string url = WEBSOCKET_PROTOCOL + remoteAddress.getIP() + ":" + std::to_string(remoteAddress.getPort()); + EmscriptenWebSocketCreateAttributes attributes = + { + url.c_str(), + nullptr, + EM_TRUE + }; + mWebSocket = emscripten_websocket_new(&attributes); + if (mWebSocket == 0) + return false; + + RMX_LOG_INFO("Successfully created web socket"); + emscripten_websocket_set_onopen_callback (mWebSocket, this, WebSocketClient::webSocketOpenCallback); + emscripten_websocket_set_onerror_callback (mWebSocket, this, WebSocketClient::webSocketErrorCallback); + emscripten_websocket_set_onclose_callback (mWebSocket, this, WebSocketClient::webSocketCloseCallback); + emscripten_websocket_set_onmessage_callback(mWebSocket, this, WebSocketClient::webSocketMessageCallback); + return true; +#else + return false; +#endif +} + +bool WebSocketClient::sendPacket(const std::vector& content) +{ +#ifdef PLATFORM_WEB + if (mWebSocket == 0) + return false; + + const EMSCRIPTEN_RESULT result = emscripten_websocket_send_binary(mWebSocket, (void*)&content[0], (uint32_t)content.size()); + RMX_ASSERT(result == 0, "emscripten_websocket_send_binary failed with result " << result); + return (result == 0); +#else + return false; +#endif +} + + +#ifdef PLATFORM_WEB + +EM_BOOL WebSocketClient::webSocketOpenCallback(int eventType, const EmscriptenWebSocketOpenEvent* webSocketEvent, void* userData) +{ + return reinterpret_cast(userData)->onWebSocketOpen(webSocketEvent); +} + +EM_BOOL WebSocketClient::webSocketErrorCallback(int eventType, const EmscriptenWebSocketErrorEvent* webSocketEvent, void* userData) +{ + return reinterpret_cast(userData)->onWebSocketError(webSocketEvent); +} + +EM_BOOL WebSocketClient::webSocketCloseCallback(int eventType, const EmscriptenWebSocketCloseEvent* webSocketEvent, void* userData) +{ + // TODO: Implement this + return EM_TRUE; +} + +EM_BOOL WebSocketClient::webSocketMessageCallback(int eventType, const EmscriptenWebSocketMessageEvent* webSocketEvent, void* userData) +{ + return reinterpret_cast(userData)->onWebSocketMessage(webSocketEvent); +} + +bool WebSocketClient::onWebSocketOpen(const EmscriptenWebSocketOpenEvent* webSocketEvent) +{ + RMX_LOG_INFO("onWebSocketOpen"); + return mConnection.finishStartConnect(); +} + +bool WebSocketClient::onWebSocketError(const EmscriptenWebSocketErrorEvent* webSocketEvent) +{ + RMX_LOG_INFO("onWebSocketError"); + return true; +} + +bool WebSocketClient::onWebSocketMessage(const EmscriptenWebSocketMessageEvent* webSocketEvent) +{ + RMX_LOG_INFO("onWebSocketMessage"); + if (webSocketEvent->numBytes == 0) // Ignore empty messages + return true; + if (webSocketEvent->isText) // Only care for binary data + return true; + + static std::vector buffer; + buffer.resize((size_t)webSocketEvent->numBytes); + memcpy(&buffer[0], webSocketEvent->data, webSocketEvent->numBytes); + return mConnection.receivedWebSocketPacket(buffer); +} + +#endif