diff --git a/Code/Client/NetImgui_Api.h b/Code/Client/NetImgui_Api.h index 444544e..2cb823b 100644 --- a/Code/Client/NetImgui_Api.h +++ b/Code/Client/NetImgui_Api.h @@ -4,12 +4,12 @@ //! @Name : NetImgui //================================================================================================= //! @author : Sammy Fatnassi -//! @date : 2024/11/19 -//! @version : v1.11.3 +//! @date : 2024/12/01 +//! @version : v1.11.4 //! @Details : For integration info : https://github.com/sammyfreg/netImgui/wiki //================================================================================================= -#define NETIMGUI_VERSION "1.11.3" // Upgrade to Dear ImGui 1.Improved connection speed -#define NETIMGUI_VERSION_NUM 11103 +#define NETIMGUI_VERSION "1.11.4" // Handle partial com instead of blocking +#define NETIMGUI_VERSION_NUM 11104 diff --git a/Code/Client/Private/NetImgui_Api.cpp b/Code/Client/Private/NetImgui_Api.cpp index b200be6..bb0c659 100644 --- a/Code/Client/Private/NetImgui_Api.cpp +++ b/Code/Client/Private/NetImgui_Api.cpp @@ -101,7 +101,7 @@ void Disconnect(void) // Attempt fake connection on local socket waiting for a Server connection, // so the blocking operation can terminate and release the communication thread Client::ClientInfo& client = *gpClientInfo; - client.mbDisconnectRequest = true; + client.mbDisconnectPending = true; client.mbDisconnectListen = true; if( client.mpSocketListen.load() != nullptr ) { @@ -335,7 +335,7 @@ void SendDataTexture(ImTextureID textureId, void* pData, uint16_t width, uint16_ pCmdTexture->mpTextureData.SetPtr(reinterpret_cast(&pCmdTexture[1])); memcpy(pCmdTexture->mpTextureData.Get(), pData, dataSize); - pCmdTexture->mHeader.mSize = SizeNeeded; + pCmdTexture->mSize = SizeNeeded; pCmdTexture->mWidth = width; pCmdTexture->mHeight = height; pCmdTexture->mTextureId = texId64; diff --git a/Code/Client/Private/NetImgui_Client.cpp b/Code/Client/Private/NetImgui_Client.cpp index 701a00d..8bd21ee 100644 --- a/Code/Client/Private/NetImgui_Client.cpp +++ b/Code/Client/Private/NetImgui_Client.cpp @@ -113,32 +113,26 @@ static void SetClipboardTextFn_NetImguiImpl(ImGuiContext* ctx, const char* text) // INCOM: INPUT // Receive new keyboard/mouse/screen resolution input to pass on to dearImgui //================================================================================================= -void Communications_Incoming_Input(ClientInfo& client, uint8_t*& pCmdData) +void Communications_Incoming_Input(ClientInfo& client) { - if( pCmdData ) - { - auto pCmdInput = reinterpret_cast(pCmdData); - pCmdData = nullptr; // Take ownership of the data, prevent Free - size_t keyCount(pCmdInput->mKeyCharCount); - client.mDesiredFps = pCmdInput->mDesiredFps > 0.f ? pCmdInput->mDesiredFps : 0.f; - client.mPendingKeyIn.AddData(pCmdInput->mKeyChars, keyCount); - client.mPendingInputIn.Assign(pCmdInput); - } + auto pCmdInput = static_cast(client.mPendingRcv.pCommand); + client.mPendingRcv.bAutoFree = false; // Taking ownership of the data + client.mDesiredFps = pCmdInput->mDesiredFps > 0.f ? pCmdInput->mDesiredFps : 0.f; + size_t keyCount(pCmdInput->mKeyCharCount); + client.mPendingKeyIn.AddData(pCmdInput->mKeyChars, keyCount); + client.mPendingInputIn.Assign(pCmdInput); } //================================================================================================= // INCOM: CLIPBOARD // Receive server new clipboard content, updating internal cache //================================================================================================= -void Communications_Incoming_Clipboard(ClientInfo& client, uint8_t*& pCmdData) +void Communications_Incoming_Clipboard(ClientInfo& client) { - if( pCmdData ) - { - auto pCmdClipboard = reinterpret_cast(pCmdData); - pCmdData = nullptr; // Take ownership of the data, prevent Free - pCmdClipboard->ToPointers(); - client.mPendingClipboardIn.Assign(pCmdClipboard); - } + auto pCmdClipboard = static_cast(client.mPendingRcv.pCommand); + client.mPendingRcv.bAutoFree = false; // Taking ownership of the data + pCmdClipboard->ToPointers(); + client.mPendingClipboardIn.Assign(pCmdClipboard); } //================================================================================================= @@ -146,22 +140,21 @@ void Communications_Incoming_Clipboard(ClientInfo& client, uint8_t*& pCmdData) // Transmit all pending new/updated texture //================================================================================================= void Communications_Outgoing_Textures(ClientInfo& client) -{ +{ client.ProcessTexturePending(); if( client.mbHasTextureUpdate ) { - bool bSuccess(true); for(auto& cmdTexture : client.mTextures) { if( !cmdTexture.mbSent && cmdTexture.mpCmdTexture ) { - bSuccess &= Network::DataSend(client.mpSocketComs, cmdTexture.mpCmdTexture, cmdTexture.mpCmdTexture->mHeader.mSize); - cmdTexture.mbSent = bSuccess; - if( cmdTexture.mbSent && cmdTexture.mpCmdTexture->mFormat == eTexFormat::kTexFmt_Invalid ) - netImguiDeleteSafe(cmdTexture.mpCmdTexture); + client.mPendingSend.pCommand = cmdTexture.mpCmdTexture; + client.mPendingSend.bAutoFree = cmdTexture.mpCmdTexture->mFormat == eTexFormat::kTexFmt_Invalid; // Texture as been marked for deletion, can now free memory allocated for this + cmdTexture.mbSent = true; + return; // Exit as soon as we find a texture to send, so thread can proceed with transmitting it } } - client.mbHasTextureUpdate = !bSuccess; + client.mbHasTextureUpdate = false; // No pending texture detected, mark as 'all sent' } } @@ -174,8 +167,8 @@ void Communications_Outgoing_Background(ClientInfo& client) CmdBackground* pPendingBackground = client.mPendingBackgroundOut.Release(); if( pPendingBackground ) { - Network::DataSend(client.mpSocketComs, pPendingBackground, pPendingBackground->mHeader.mSize); - netImguiDeleteSafe(pPendingBackground); + client.mPendingSend.pCommand = pPendingBackground; + client.mPendingSend.bAutoFree = false; } } @@ -210,29 +203,10 @@ void Communications_Outgoing_Frame(ClientInfo& client) client.mServerCompressionSkip = false; //--------------------------------------------------------------------- - // Send Command to server + // Ready to send command to server pPendingDraw->ToOffsets(); - Network::DataSend(client.mpSocketComs, pPendingDraw, pPendingDraw->mHeader.mSize); - - //--------------------------------------------------------------------- - // Free created data once sent (when not used in next frame) - if( client.mpCmdDrawLast != pPendingDraw ){ - netImguiDeleteSafe(pPendingDraw); - } - } -} - -//================================================================================================= -// OUTCOM: DISCONNECT -// Signal that we will be disconnecting -//================================================================================================= -void Communications_Outgoing_Disconnect(ClientInfo& client) -{ - if( client.mbDisconnectRequest ) - { - CmdDisconnect cmdDisconnect; - Network::DataSend(client.mpSocketComs, &cmdDisconnect, cmdDisconnect.mHeader.mSize); - client.mbDisconnectProcessed = true; + client.mPendingSend.pCommand = pPendingDraw; + client.mPendingSend.bAutoFree = client.mpCmdDrawLast != pPendingDraw; } } @@ -245,8 +219,8 @@ void Communications_Outgoing_Clipboard(ClientInfo& client) CmdClipboard* pPendingClipboard = client.mPendingClipboardOut.Release(); if( pPendingClipboard ){ pPendingClipboard->ToOffsets(); - Network::DataSend(client.mpSocketComs, pPendingClipboard, pPendingClipboard->mHeader.mSize); - netImguiDeleteSafe(pPendingClipboard); + client.mPendingSend.pCommand = pPendingClipboard; + client.mPendingSend.bAutoFree = true; } } @@ -257,42 +231,67 @@ void Communications_Incoming(ClientInfo& client) { if( Network::DataReceivePending(client.mpSocketComs) ) { - CmdHeader cmdHeader; - uint8_t* pCmdData(nullptr); - bool bCmdRcv = Network::DataReceive(client.mpSocketComs, &cmdHeader, sizeof(cmdHeader)); - bCmdRcv &= cmdHeader.mType < NetImgui::Internal::CmdHeader::eCommands::Count; - - if( bCmdRcv && cmdHeader.mSize > sizeof(CmdHeader) ) + //----------------------------------------------------------------------------------------- + // 1. Ready to receive new command, starts the process by reading Header + //----------------------------------------------------------------------------------------- + if( client.mPendingRcv.IsReady() ) { - pCmdData = netImguiSizedNew(cmdHeader.mSize); - *reinterpret_cast(pCmdData) = cmdHeader; - bCmdRcv = Network::DataReceive(client.mpSocketComs, &pCmdData[sizeof(cmdHeader)], cmdHeader.mSize-sizeof(cmdHeader)); + client.mCmdPendingRead = CmdPendingRead(); + client.mPendingRcv.pCommand = &client.mCmdPendingRead; + client.mPendingRcv.bAutoFree= false; } - - if( bCmdRcv ) + + //----------------------------------------------------------------------------------------- + // 2. Read incoming command from server + //----------------------------------------------------------------------------------------- + if( client.mPendingRcv.IsPending() ) { - switch( cmdHeader.mType ) + Network::DataReceive(client.mpSocketComs, client.mPendingRcv); + + // Detected a new command bigger than header, allocate memory for it + if( client.mPendingRcv.pCommand->mSize > sizeof(CmdPendingRead) && + client.mPendingRcv.pCommand == &client.mCmdPendingRead ) { - case CmdHeader::eCommands::Disconnect: client.mbDisconnectProcessed = true; break; - case CmdHeader::eCommands::Input: Communications_Incoming_Input(client, pCmdData); break; - case CmdHeader::eCommands::Clipboard: Communications_Incoming_Clipboard(client, pCmdData); break; - // Commands not received in main loop, by Client - case CmdHeader::eCommands::Version: - case CmdHeader::eCommands::Texture: - case CmdHeader::eCommands::DrawFrame: - case CmdHeader::eCommands::Background: - case CmdHeader::eCommands::Count: break; + CmdPendingRead* pCmdHeader = reinterpret_cast(netImguiSizedNew(client.mPendingRcv.pCommand->mSize)); + *pCmdHeader = client.mCmdPendingRead; + client.mPendingRcv.pCommand = pCmdHeader; + client.mPendingRcv.bAutoFree= true; } } - else + + //----------------------------------------------------------------------------------------- + // 3. Command fully received from Server, process it + //----------------------------------------------------------------------------------------- + if( client.mPendingRcv.IsDone() ) { - client.mbDisconnectRequest = true; + if( !client.mPendingRcv.IsError() ) + { + switch( client.mPendingRcv.pCommand->mType ) + { + case CmdHeader::eCommands::Input: Communications_Incoming_Input(client); break; + case CmdHeader::eCommands::Clipboard: Communications_Incoming_Clipboard(client); break; + // Commands not received in main loop, by Client + case CmdHeader::eCommands::Version: + case CmdHeader::eCommands::Texture: + case CmdHeader::eCommands::DrawFrame: + case CmdHeader::eCommands::Background: + case CmdHeader::eCommands::Count: break; + } + } + + // Cleanup after read completion + if( client.mPendingRcv.IsError() ){ + client.mbDisconnectPending = true; + } + if( client.mPendingRcv.bAutoFree ){ + netImguiDeleteSafe(client.mPendingRcv.pCommand); + } + client.mPendingRcv = PendingCom(); } - netImguiDeleteSafe(pCmdData); } + // Prevent high CPU usage when waiting for new data else { - // Prevent high CPU usage when waiting for new data //std::this_thread::yield(); std::this_thread::sleep_for(std::chrono::microseconds(250)); } @@ -303,11 +302,53 @@ void Communications_Incoming(ClientInfo& client) //================================================================================================= void Communications_Outgoing(ClientInfo& client) { - Communications_Outgoing_Textures(client); - Communications_Outgoing_Background(client); - Communications_Outgoing_Clipboard(client); - Communications_Outgoing_Frame(client); - Communications_Outgoing_Disconnect(client); + //--------------------------------------------------------------------------------------------- + // Try finishing sending a pending command to Server + //--------------------------------------------------------------------------------------------- + if( client.mPendingSend.IsPending() ) + { + Network::DataSend(client.mpSocketComs, client.mPendingSend); + + // Free allocated memory for command + if( client.mPendingSend.IsDone() ) + { + if( client.mPendingSend.IsError() ){ + client.mbDisconnectPending = true; + } + if( client.mPendingSend.bAutoFree ){ + netImguiDeleteSafe(client.mPendingSend.pCommand); + } + client.mPendingSend = PendingCom(); + } + } + //--------------------------------------------------------------------------------------------- + // Initiate sending next command to Server, when none are in flight + // Keep track of what command was last send to prevent 1 type to monopolize coms + //--------------------------------------------------------------------------------------------- + constexpr CmdHeader::eCommands kCommandsOrder[] = { + CmdHeader::eCommands::Texture, CmdHeader::eCommands::Background, + CmdHeader::eCommands::Clipboard, CmdHeader::eCommands::DrawFrame}; + constexpr uint32_t kCommandCounts = static_cast(sizeof(kCommandsOrder)/sizeof(CmdHeader::eCommands)); + + uint32_t Index(client.mPendingSendNext); + while( client.mPendingSend.IsReady() && (Index-client.mPendingSendNext)(&cmdVersionRcv); + while( !PendingRcv.IsDone() && cmdVersionRcv.mType == CmdHeader::eCommands::Version ) { - std::this_thread::yield(); // Idle until we receive the remote data + while( !Network::DataReceivePending(client.mpSocketPending) ){ + std::this_thread::yield(); // Idle until we receive the remote data + } + Network::DataReceive(client.mpSocketPending, PendingRcv); } - bool bResultRcv = Network::DataReceive(client.mpSocketPending, &cmdVersionRcv, sizeof(cmdVersionRcv)); + bool bForceConnect = client.mServerForceConnectEnabled && (cmdVersionRcv.mFlags & static_cast(CmdVersion::eFlags::ConnectForce)) != 0; - bool bCanConnect = bResultRcv && - cmdVersionRcv.mHeader.mType == cmdVersionSend.mHeader.mType && - cmdVersionRcv.mVersion == cmdVersionSend.mVersion && - cmdVersionRcv.mWCharSize == cmdVersionSend.mWCharSize && - (!client.IsConnected() || bForceConnect); + bool bCanConnect = !PendingRcv.IsError() && + cmdVersionRcv.mType == cmdVersionSend.mType && + cmdVersionRcv.mVersion == cmdVersionSend.mVersion && + cmdVersionRcv.mWCharSize == cmdVersionSend.mWCharSize && + (!client.IsConnected() || bForceConnect); StringCopy(cmdVersionSend.mClientName, client.mName); - cmdVersionSend.mFlags = client.IsConnected() && !bCanConnect ? static_cast(CmdVersion::eFlags::IsConnected): 0; - cmdVersionSend.mFlags |= client.IsConnected() && !client.mServerForceConnectEnabled ? static_cast(CmdVersion::eFlags::IsUnavailable) : 0; - bool bResultSend = Network::DataSend(client.mpSocketPending, &cmdVersionSend, cmdVersionSend.mHeader.mSize); + cmdVersionSend.mFlags = client.IsConnected() && !bCanConnect ? static_cast(CmdVersion::eFlags::IsConnected): 0; + cmdVersionSend.mFlags |= client.IsConnected() && !client.mServerForceConnectEnabled ? static_cast(CmdVersion::eFlags::IsUnavailable) : 0; + + PendingSend.pCommand = reinterpret_cast(&cmdVersionSend); + while( !PendingSend.IsDone() ){ + Network::DataSend(client.mpSocketPending, PendingSend); + } //--------------------------------------------------------------------- // Connection established, init client //--------------------------------------------------------------------- - if(bCanConnect && bResultSend) + if( bCanConnect && !PendingSend.IsError() ) { Network::SocketInfo* pNewConnect = client.mpSocketPending.exchange(nullptr); if( client.IsConnected() ) { - if( bForceConnect ) - { - client.mbDisconnectRequest = true; + // Force connect requested, disconnect current client and wait until it is free to connect to new server + if( bForceConnect ){ + client.mbDisconnectPending = true; while( client.IsConnected() ); } - else - { + // Close communication with server, client isn't available to connect to it + else{ NetImgui::Internal::Network::Disconnect(pNewConnect); return false; } @@ -366,7 +416,10 @@ bool Communications_Initialize(ClientInfo& client) client.mBGSettingSent.mTextureId = client.mBGSetting.mTextureId-1u; // Force sending the Background settings (by making different than current settings) client.mpSocketComs = pNewConnect; client.mFrameIndex = 0; + client.mPendingSendNext = 0; client.mServerForceConnectEnabled = (cmdVersionRcv.mFlags & static_cast(CmdVersion::eFlags::ConnectExclusive)) == 0; + client.mPendingRcv = PendingCom(); + client.mPendingSend = PendingCom(); } return client.mpSocketPending.load() == nullptr; } @@ -378,18 +431,17 @@ void Communications_Loop(void* pClientVoid) { IM_ASSERT(pClientVoid != nullptr); ClientInfo* pClient = reinterpret_cast(pClientVoid); - pClient->mbDisconnectRequest = false; - pClient->mbDisconnectProcessed = false; + pClient->mbDisconnectPending = false; pClient->mbClientThreadActive = true; - - while( !pClient->mbDisconnectProcessed ) + + while( !pClient->mbDisconnectPending ) { Communications_Outgoing(*pClient); Communications_Incoming(*pClient); } pClient->KillSocketComs(); - pClient->mbClientThreadActive = false; + pClient->mbClientThreadActive = false; } //================================================================================================= @@ -398,13 +450,11 @@ void Communications_Loop(void* pClientVoid) void CommunicationsConnect(void* pClientVoid) { IM_ASSERT(pClientVoid != nullptr); - ClientInfo* pClient = reinterpret_cast(pClientVoid); - pClient->mbClientThreadActive = true; + ClientInfo* pClient = reinterpret_cast(pClientVoid); if( Communications_Initialize(*pClient) ) { Communications_Loop(pClientVoid); } - pClient->mbClientThreadActive = false; } //================================================================================================= @@ -415,7 +465,6 @@ void CommunicationsHost(void* pClientVoid) { ClientInfo* pClient = reinterpret_cast(pClientVoid); pClient->mbListenThreadActive = true; - pClient->mbDisconnectRequest = false; pClient->mbDisconnectListen = false; pClient->mpSocketListen = pClient->mpSocketPending.exchange(nullptr); diff --git a/Code/Client/Private/NetImgui_Client.h b/Code/Client/Private/NetImgui_Client.h index 1f37737..c60d24d 100644 --- a/Code/Client/Private/NetImgui_Client.h +++ b/Code/Client/Private/NetImgui_Client.h @@ -82,6 +82,8 @@ struct ClientInfo std::atomic mpSocketPending; // Hold socket info until communication is established std::atomic mpSocketComs; // Socket used for communications with server std::atomic mpSocketListen; // Socket used to wait for communication request from server + std::atomic_bool mbDisconnectPending; // Terminate Client/Server coms + std::atomic_bool mbDisconnectListen; // Terminate waiting connection from Server uint32_t mSocketListenPort = 0; // Socket Port number used to wait for communication request from server VecTexture mTextures; // List if textures created by this client (used un main thread) char mName[64] = {}; @@ -93,6 +95,10 @@ struct ClientInfo ExchangePtr mPendingClipboardIn; // Clipboard content received from Server and waiting to be taken by client ExchangePtr mPendingClipboardOut; // Clipboard content copied on Client and waiting to be sent to Server ImGuiContext* mpContext = nullptr; // Context that the remote drawing should use (either the one active when connection request happened, or a clone) + PendingCom mPendingRcv; // Data being currently received from Server + PendingCom mPendingSend; // Data being currently sent to Server + uint32_t mPendingSendNext = 0; // Type of Cmd to next attempt sending, when channel is available + CmdPendingRead mCmdPendingRead; // Used to get info on the next incoming command from Server CmdInput* mpCmdInputPending = nullptr; // Last Input Command from server, waiting to be processed by client CmdClipboard* mpCmdClipboard = nullptr; // Last received clipboad command CmdDrawFrame* mpCmdDrawLast = nullptr; // Last sent Draw Command. Used by data compression, to generate delta between previous and current frame @@ -107,12 +113,9 @@ struct ClientInfo SavedImguiContext mSavedContextValues; std::atomic_uint32_t mTexturesPendingSent; std::atomic_uint32_t mTexturesPendingCreated; - - bool mbDisconnectListen = false; // Waiting to Stop listening to connection request - bool mbDisconnectRequest = false; // Waiting to Disconnect - bool mbDisconnectProcessed = false; // Disconnect command sent to server, ready to disconnect - bool mbClientThreadActive = false; - bool mbListenThreadActive = false; + + bool mbClientThreadActive = false; // True when connected and communicating with Server + bool mbListenThreadActive = false; // True when listening from connection request from Server bool mbHasTextureUpdate = false; bool mbIsDrawing = false; // We are inside a 'NetImgui::NewFrame' / 'NetImgui::EndFrame' (even if not for a remote draw) bool mbIsRemoteDrawing = false; // True if the rendering it meant for the remote netImgui server diff --git a/Code/Client/Private/NetImgui_CmdPackets.h b/Code/Client/Private/NetImgui_CmdPackets.h index 6236c6d..1c8d5c1 100644 --- a/Code/Client/Private/NetImgui_CmdPackets.h +++ b/Code/Client/Private/NetImgui_CmdPackets.h @@ -7,23 +7,22 @@ namespace NetImgui { namespace Internal { //Note: If updating any of these commands data structure, increase 'CmdVersion::eVersion' - struct alignas(8) CmdHeader { - enum class eCommands : uint8_t { Disconnect, Version, Texture, Input, DrawFrame, Background, Clipboard, Count }; - CmdHeader(){} + enum class eCommands : uint8_t { Version, Texture, Input, DrawFrame, Background, Clipboard, Count }; CmdHeader(eCommands CmdType, uint16_t Size) : mSize(Size), mType(CmdType){} uint32_t mSize = 0; eCommands mType = eCommands::Count; uint8_t mPadding[3] = {}; }; -struct alignas(8) CmdDisconnect +// Used as step 1 of 2 of reading incoming transmission between Client/Server, to get header whose size we know +struct alignas(8) CmdPendingRead : public CmdHeader { - CmdHeader mHeader = CmdHeader(CmdHeader::eCommands::Disconnect, sizeof(CmdDisconnect)); + CmdPendingRead() : CmdHeader(eCommands::Count, sizeof(CmdPendingRead) ){} }; -struct alignas(8) CmdVersion +struct alignas(8) CmdVersion : public CmdHeader { enum class eVersion : uint32_t { @@ -43,6 +42,7 @@ struct alignas(8) CmdVersion Clipboard = 14, // Added clipboard support between server/client ForceReconnect = 15, // Server can now take over the connection from another server UpdatedComs = 16, // Faster protocol by removing blocking coms + RemDisconnect = 17, // Removed Disconnect command // Insert new version here //-------------------------------- @@ -56,7 +56,7 @@ struct alignas(8) CmdVersion ConnectForce = 0x04, // Server telling Client it want to take over connection if there's already one ConnectExclusive = 0x08, // Server telling Client that once connected, others servers should be denied access }; - CmdHeader mHeader = CmdHeader(CmdHeader::eCommands::Version, sizeof(CmdVersion)); + CmdVersion() : CmdHeader(CmdHeader::eCommands::Version, sizeof(CmdVersion)){} char mClientName[64] = {}; char mImguiVerName[16] = {IMGUI_VERSION}; char mNetImguiVerName[16] = {NETIMGUI_VERSION}; @@ -68,7 +68,7 @@ struct alignas(8) CmdVersion char PADDING[2]; }; -struct alignas(8) CmdInput +struct alignas(8) CmdInput : public CmdHeader { // Identify a mouse button. // Those values are guaranteed to be stable and we frequently use 0/1 directly. Named enums provided for convenience. @@ -188,7 +188,7 @@ struct alignas(8) CmdInput static constexpr uint32_t kAnalog_Last = ImGuiKey_GamepadRStickDown; static constexpr uint32_t kAnalog_Count = kAnalog_Last - kAnalog_First + 1; - CmdHeader mHeader = CmdHeader(CmdHeader::eCommands::Input, sizeof(CmdInput)); + CmdInput() : CmdHeader(CmdHeader::eCommands::Input, sizeof(CmdInput)){} uint16_t mScreenSize[2] = {}; int16_t mMousePos[2] = {}; float mMouseWheelVert = 0.f; @@ -205,9 +205,9 @@ struct alignas(8) CmdInput inline bool IsKeyDown(NetImguiKeys netimguiKey) const; }; -struct alignas(8) CmdTexture +struct alignas(8) CmdTexture : public CmdHeader { - CmdHeader mHeader = CmdHeader(CmdHeader::eCommands::Texture, sizeof(CmdTexture)); + CmdTexture() : CmdHeader(CmdHeader::eCommands::Texture, sizeof(CmdTexture)){} OffsetPointer mpTextureData; uint64_t mTextureId = 0; uint16_t mWidth = 0; @@ -216,9 +216,9 @@ struct alignas(8) CmdTexture uint8_t PADDING[3] = {}; }; -struct alignas(8) CmdDrawFrame +struct alignas(8) CmdDrawFrame : public CmdHeader { - CmdHeader mHeader = CmdHeader(CmdHeader::eCommands::DrawFrame, sizeof(CmdDrawFrame)); + CmdDrawFrame() : CmdHeader(CmdHeader::eCommands::DrawFrame, sizeof(CmdDrawFrame)){} uint64_t mFrameIndex = 0; uint32_t mMouseCursor = 0; // ImGuiMouseCursor value float mDisplayArea[4] = {}; @@ -235,10 +235,10 @@ struct alignas(8) CmdDrawFrame inline void ToOffsets(); }; -struct alignas(8) CmdBackground +struct alignas(8) CmdBackground : public CmdHeader { + CmdBackground() : CmdHeader(CmdHeader::eCommands::Background, sizeof(CmdBackground)){} static constexpr uint64_t kDefaultTexture = ~0u; - CmdHeader mHeader = CmdHeader(CmdHeader::eCommands::Background, sizeof(CmdBackground)); float mClearColor[4] = {0.2f, 0.2f, 0.2f, 1.f}; // Background color float mTextureTint[4] = {1.f, 1.f, 1.f, 0.5f}; // Tint/alpha applied to texture uint64_t mTextureId = kDefaultTexture; // Texture rendered in background, use server texture by default @@ -246,9 +246,9 @@ struct alignas(8) CmdBackground inline bool operator!=(const CmdBackground& cmp)const; }; -struct alignas(8) CmdClipboard +struct alignas(8) CmdClipboard : public CmdHeader { - CmdHeader mHeader = CmdHeader(CmdHeader::eCommands::Clipboard, sizeof(CmdClipboard)); + CmdClipboard() : CmdHeader(CmdHeader::eCommands::Clipboard, sizeof(CmdClipboard)){} size_t mByteSize = 0; OffsetPointer mContentUTF8; inline void ToPointers(); @@ -256,6 +256,21 @@ struct alignas(8) CmdClipboard inline static CmdClipboard* Create(const char* clipboard); }; +//============================================================================= +// Keeping track of partial incoming/outgoing transmissions +//============================================================================= +struct PendingCom +{ + size_t SizeCurrent = 0; // Amount of data sent or received so far + bool bAutoFree = false; // Need to free data buffer at the end of processing + bool bError = false; // If an error occurs during coms + CmdHeader* pCommand = nullptr; // Where to store incoming data or read to send data + inline bool IsError()const{ return bError; } + inline bool IsDone()const { return IsError() || (pCommand && pCommand->mSize == SizeCurrent); } + inline bool IsReady()const{ return !IsError() && pCommand == nullptr; } + inline bool IsPending()const{ return !IsError() && !IsDone() && !IsReady(); } +}; + }} // namespace NetImgui::Internal #include "NetImgui_CmdPackets.inl" diff --git a/Code/Client/Private/NetImgui_CmdPackets.inl b/Code/Client/Private/NetImgui_CmdPackets.inl index 9d1a034..e03b622 100644 --- a/Code/Client/Private/NetImgui_CmdPackets.inl +++ b/Code/Client/Private/NetImgui_CmdPackets.inl @@ -89,11 +89,10 @@ CmdClipboard* CmdClipboard::Create(const char* clipboard) while(clipboard[clipboardByteSize++] != 0); size_t totalDataCount = sizeof(CmdClipboard) + DivUp(clipboardByteSize, ComDataSize); auto pNewClipboard = NetImgui::Internal::netImguiSizedNew(totalDataCount*ComDataSize); - pNewClipboard->mHeader.mSize = static_cast(totalDataCount*ComDataSize); + pNewClipboard->mSize = static_cast(totalDataCount*ComDataSize); pNewClipboard->mByteSize = clipboardByteSize; pNewClipboard->mContentUTF8.SetPtr(reinterpret_cast(&pNewClipboard[1])); memcpy(pNewClipboard->mContentUTF8.Get(), clipboard, clipboardByteSize); - return pNewClipboard; } return nullptr; diff --git a/Code/Client/Private/NetImgui_CmdPackets_DrawFrame.cpp b/Code/Client/Private/NetImgui_CmdPackets_DrawFrame.cpp index c37add4..e0f1173 100644 --- a/Code/Client/Private/NetImgui_CmdPackets_DrawFrame.cpp +++ b/Code/Client/Private/NetImgui_CmdPackets_DrawFrame.cpp @@ -205,7 +205,7 @@ CmdDrawFrame* CompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const Cmd //----------------------------------------------------------------------------------------- // Allocate memory for worst case scenario (no compression possible) // New DrawFrame size + 2 'compression block info' per data stream - size_t neededDataCount = DivUp(pDrawFrameNew->mHeader.mSize, ComDataSize) + 6*static_cast(pDrawFrameNew->mDrawGroupCount); + size_t neededDataCount = DivUp(pDrawFrameNew->mSize, ComDataSize) + 6*static_cast(pDrawFrameNew->mDrawGroupCount); CmdDrawFrame* pDrawFramePacked = netImguiSizedNew(neededDataCount*ComDataSize); *pDrawFramePacked = *pDrawFrameNew; pDrawFramePacked->mCompressed = true; @@ -259,7 +259,7 @@ CmdDrawFrame* CompressCmdDrawFrame(const CmdDrawFrame* pDrawFramePrev, const Cmd } // Adjust data transfert amount to memory that has been actually needed - pDrawFramePacked->mHeader.mSize = static_cast((pDataOutput - reinterpret_cast(pDrawFramePacked)))*static_cast(sizeof(uint64_t)); + pDrawFramePacked->mSize = static_cast((pDataOutput - reinterpret_cast(pDrawFramePacked)))*static_cast(sizeof(uint64_t)); return pDrawFramePacked; } @@ -370,8 +370,8 @@ CmdDrawFrame* ConvertToCmdDrawFrame(const ImDrawData* pDearImguiData, ImGuiMouse pDrawFrame->mTotalDrawCount += drawGroup.mDrawCount; } - pDrawFrame->mHeader.mSize = static_cast(pDataOutput - reinterpret_cast(pDrawFrame)) * ComDataSize; - pDrawFrame->mUncompressedSize = pDrawFrame->mHeader.mSize; // No compression with this item, so same value + pDrawFrame->mSize = static_cast(pDataOutput - reinterpret_cast(pDrawFrame)) * ComDataSize; + pDrawFrame->mUncompressedSize = pDrawFrame->mSize; // No compression with this item, so same value return pDrawFrame; } diff --git a/Code/Client/Private/NetImgui_Network.h b/Code/Client/Private/NetImgui_Network.h index e952593..6aa3fb4 100644 --- a/Code/Client/Private/NetImgui_Network.h +++ b/Code/Client/Private/NetImgui_Network.h @@ -1,6 +1,8 @@ #pragma once -namespace NetImgui { namespace Internal { namespace Network +namespace NetImgui { namespace Internal { struct PendingCom; }} + +namespace NetImgui { namespace Internal { namespace Network { struct SocketInfo; @@ -14,7 +16,7 @@ SocketInfo* ListenStart (uint32_t ListenPort); // Listening Socket expec void Disconnect (SocketInfo* pClientSocket); bool DataReceivePending (SocketInfo* pClientSocket); // True if some new data if waiting to be processed from remote connection -bool DataReceive (SocketInfo* pClientSocket, void* pDataIn, size_t Size); // Read X amount of bytes to remote connection. Will idle until size request is fullfilled. -bool DataSend (SocketInfo* pClientSocket, void* pDataOut, size_t Size); // Send x amount of bytes to remote connection. Will idle until size request is fullfilled. +void DataReceive (SocketInfo* pClientSocket, PendingCom& PendingComRcv); // Try reading X amount of bytes from remote connection, but can fall short. +void DataSend (SocketInfo* pClientSocket, PendingCom& PendingComSend); // Try sending X amount of bytes to remote connection, but can fall short. }}} //namespace NetImgui::Internal::Network diff --git a/Code/Client/Private/NetImgui_NetworkWin32.cpp b/Code/Client/Private/NetImgui_NetworkWin32.cpp index 7167cc5..d1fc685 100644 --- a/Code/Client/Private/NetImgui_NetworkWin32.cpp +++ b/Code/Client/Private/NetImgui_NetworkWin32.cpp @@ -9,6 +9,7 @@ #pragma comment(lib, "ws2_32") #endif +#include "NetImgui_CmdPackets.h" namespace NetImgui { namespace Internal { namespace Network { @@ -31,12 +32,6 @@ struct SocketInfo //constexpr int kComsRcvBuffer = 1014*1024; //setsockopt(mSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&kComsRcvBuffer), sizeof(kComsRcvBuffer)); - - #if 0 // @sammyfreg : No timeout useful when debugging, to keep connection alive while code breakpoint - constexpr DWORD kComsTimeoutMs = 10000; - setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&kComsTimeoutMs), sizeof(kComsTimeoutMs)); - setsockopt(mSocket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&kComsTimeoutMs), sizeof(kComsTimeoutMs)); - #endif } SOCKET mSocket; @@ -178,57 +173,53 @@ bool DataReceivePending(SocketInfo* pClientSocket) } //================================================================================================= -// Block until all requested data has been received from the remote connection +// Try receiving data from remote connection //================================================================================================= -bool DataReceive(SocketInfo* pClientSocket, void* pDataIn, size_t Size) +void DataReceive(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComRcv) { - if( !pClientSocket ) return false; + // Invalid command + if( !pClientSocket || !PendingComRcv.pCommand ){ + PendingComRcv.bError = true; + } - int totalRcv(0); - while( totalRcv < static_cast(Size) ) - { - int resultRcv = recv(pClientSocket->mSocket, &reinterpret_cast(pDataIn)[totalRcv], static_cast(Size)-totalRcv, 0); - if( resultRcv != SOCKET_ERROR ) - { - totalRcv += resultRcv; - } - else - { - if( WSAGetLastError() != WSAEWOULDBLOCK ) - { - return false; // Connection error, abort transmission - } - std::this_thread::yield(); - } + // Receive data from remote connection + int resultRcv = recv( pClientSocket->mSocket, + &reinterpret_cast(PendingComRcv.pCommand)[PendingComRcv.SizeCurrent], + static_cast(PendingComRcv.pCommand->mSize-PendingComRcv.SizeCurrent), + 0); + + if( resultRcv != SOCKET_ERROR ){ + PendingComRcv.SizeCurrent += static_cast(resultRcv); + } + // Connection error, abort transmission + else if( WSAGetLastError() != WSAEWOULDBLOCK ){ + PendingComRcv.bError = true; } - return totalRcv == static_cast(Size); } //================================================================================================= // Block until all requested data has been sent to remote connection //================================================================================================= -bool DataSend(SocketInfo* pClientSocket, void* pDataOut, size_t Size) +void DataSend(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComSend) { - if( !pClientSocket ) return false; - - int totalSent(0); - while( totalSent < static_cast(Size) ) - { - int resultSent = send(pClientSocket->mSocket, &reinterpret_cast(pDataOut)[totalSent], static_cast(Size)-totalSent, 0); - if( resultSent != SOCKET_ERROR ) - { - totalSent += resultSent; - } - else - { - if( WSAGetLastError() != WSAEWOULDBLOCK ) - { - return false; // Connection error, abort transmission - } - std::this_thread::yield(); - } + // Invalid command + if( !pClientSocket || !PendingComSend.pCommand ){ + PendingComSend.bError = true; + } + + // Send data to remote connection + int resultSent = send( pClientSocket->mSocket, + &reinterpret_cast(PendingComSend.pCommand)[PendingComSend.SizeCurrent], + static_cast(PendingComSend.pCommand->mSize-PendingComSend.SizeCurrent), + 0); + + if( resultSent != SOCKET_ERROR ){ + PendingComSend.SizeCurrent += static_cast(resultSent); + } + // Connection error, abort transmission + else if( WSAGetLastError() != WSAEWOULDBLOCK ){ + PendingComSend.bError = true; } - return totalSent == static_cast(Size); } }}} // namespace NetImgui::Internal::Network diff --git a/Code/Sample/Common/main.cpp b/Code/Sample/Common/main.cpp index d1d5831..9558b82 100644 --- a/Code/Sample/Common/main.cpp +++ b/Code/Sample/Common/main.cpp @@ -238,8 +238,8 @@ int main(int, char**) //========================================================================================= // Present - HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync - //HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync + //HRESULT hr = g_pSwapChain->Present(1, 0); // Present with vsync + HRESULT hr = g_pSwapChain->Present(0, 0); // Present without vsync g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED); } diff --git a/Code/Sample/SampleCompression/SampleCompression.cpp b/Code/Sample/SampleCompression/SampleCompression.cpp index e6a1f46..de0337b 100644 --- a/Code/Sample/SampleCompression/SampleCompression.cpp +++ b/Code/Sample/SampleCompression/SampleCompression.cpp @@ -47,26 +47,30 @@ void CustomCommunicationsClient(void* pClientVoid) { IM_ASSERT(pClientVoid != nullptr); ClientInfo* pClient = reinterpret_cast(pClientVoid); - pClient->mbDisconnectRequest = false; - pClient->mbDisconnectProcessed = false; + pClient->mbDisconnectPending = false; pClient->mbClientThreadActive = true; //============================================================================= //@SAMPLE_EDIT - pClient->mbDisconnectProcessed = !Communications_Initialize(*pClient); + pClient->mbDisconnectPending = !Communications_Initialize(*pClient); //============================================================================= - while( !pClient->mbDisconnectProcessed ) + while( !pClient->mbDisconnectPending ) { //============================================================================= //@SAMPLE_EDIT - // Making sure the Communications_Incoming sleeping when there's no data, + // Making sure the Communications_Incoming sleeping when there's no data, // is not included in timing stats - while( !NetImgui::Internal::Network::DataReceivePending(pClient->mpSocketComs) ) - { + auto startComs = std::chrono::steady_clock::now(); + while( !NetImgui::Internal::Network::DataReceivePending(pClient->mpSocketComs) ){ + // Timeout wait after 1 sec + auto elapsed = std::chrono::duration_cast(std::chrono::steady_clock::now() - startComs); + if( elapsed.count() > 1000 ){ + break; + } std::this_thread::yield(); } - auto startComs = std::chrono::steady_clock::now(); + startComs = std::chrono::steady_clock::now(); //============================================================================= Communications_Outgoing(*pClient); diff --git a/Code/Sample/SampleCompression/SampleNetworkWin32.cpp b/Code/Sample/SampleCompression/SampleNetworkWin32.cpp index 6242083..21f5db5 100644 --- a/Code/Sample/SampleCompression/SampleNetworkWin32.cpp +++ b/Code/Sample/SampleCompression/SampleNetworkWin32.cpp @@ -24,213 +24,230 @@ uint64_t gMetric_SentDataUncompressed = 0u; #pragma comment(lib, "ws2_32") #endif +//#include "NetImgui_CmdPackets.h" namespace NetImgui { namespace Internal { namespace Network { - //================================================================================================= - // Wrapper around native socket object and init some socket options - //================================================================================================= - struct SocketInfo +//================================================================================================= +// Wrapper around native socket object and init some socket options +//================================================================================================= +struct SocketInfo +{ + SocketInfo(SOCKET socket) + : mSocket(socket) { - SocketInfo(SOCKET socket) - : mSocket(socket) - { - u_long kNonBlocking = true; - ioctlsocket(mSocket, static_cast(FIONBIO), &kNonBlocking); + u_long kNonBlocking = true; + ioctlsocket(mSocket, static_cast(FIONBIO), &kNonBlocking); - constexpr DWORD kComsNoDelay = 1; - setsockopt(mSocket, SOL_SOCKET, TCP_NODELAY, reinterpret_cast(&kComsNoDelay), sizeof(kComsNoDelay)); - - //constexpr int kComsSendBuffer = 1014*1024; - //setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&kComsSendBuffer), sizeof(kComsSendBuffer)); + constexpr DWORD kComsNoDelay = 1; + setsockopt(mSocket, SOL_SOCKET, TCP_NODELAY, reinterpret_cast(&kComsNoDelay), sizeof(kComsNoDelay)); - //constexpr int kComsRcvBuffer = 1014*1024; - //setsockopt(mSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&kComsRcvBuffer), sizeof(kComsRcvBuffer)); + //constexpr int kComsSendBuffer = 1014*1024; + //setsockopt(mSocket, SOL_SOCKET, SO_SNDBUF, reinterpret_cast(&kComsSendBuffer), sizeof(kComsSendBuffer)); - #if 0 // @sammyfreg : No timeout useful when debugging, to keep connection alive while code breakpoint - constexpr DWORD kComsTimeoutMs = 10000; - setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast(&kComsTimeoutMs), sizeof(kComsTimeoutMs)); - setsockopt(mSocket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast(&kComsTimeoutMs), sizeof(kComsTimeoutMs)); - #endif - } + //constexpr int kComsRcvBuffer = 1014*1024; + //setsockopt(mSocket, SOL_SOCKET, SO_RCVBUF, reinterpret_cast(&kComsRcvBuffer), sizeof(kComsRcvBuffer)); + } - SOCKET mSocket; - }; + SOCKET mSocket; +}; - bool Startup() - { - WSADATA wsa; - if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) +bool Startup() +{ + WSADATA wsa; + if (WSAStartup(MAKEWORD(2,2),&wsa) != 0) return false; - return true; - } + return true; +} - void Shutdown() - { - WSACleanup(); - } +void Shutdown() +{ + WSACleanup(); +} - //================================================================================================= - // Try establishing a connection to a remote client at given address - //================================================================================================= - SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort) - { - SOCKET ClientSocket = socket(AF_INET , SOCK_STREAM , 0); - if(ClientSocket == INVALID_SOCKET) +//================================================================================================= +// Try establishing a connection to a remote client at given address +//================================================================================================= +SocketInfo* Connect(const char* ServerHost, uint32_t ServerPort) +{ + const timeval kConnectTimeout = {1, 0}; // Waiting 1 seconds before failing connection attempt + u_long kNonBlocking = true; + + SOCKET ClientSocket = socket(AF_INET , SOCK_STREAM , 0); + if(ClientSocket == INVALID_SOCKET) return nullptr; - char zPortName[32]={}; - addrinfo* pResults = nullptr; - SocketInfo* pSocketInfo = nullptr; - NetImgui::Internal::StringFormat(zPortName, "%i", ServerPort); - getaddrinfo(ServerHost, zPortName, nullptr, &pResults); - addrinfo* pResultCur = pResults; - while( pResultCur && !pSocketInfo ) + char zPortName[32] = {}; + addrinfo* pResults = nullptr; + SocketInfo* pSocketInfo = nullptr; + NetImgui::Internal::StringFormat(zPortName, "%i", ServerPort); + getaddrinfo(ServerHost, zPortName, nullptr, &pResults); + addrinfo* pResultCur = pResults; + fd_set SocketSet; + + ioctlsocket(ClientSocket, static_cast(FIONBIO), &kNonBlocking); + while( pResultCur && !pSocketInfo ) + { + int Result = connect(ClientSocket, pResultCur->ai_addr, static_cast(pResultCur->ai_addrlen)); + bool Connected = Result != SOCKET_ERROR; + + // Not connected yet, wait some time before bailing out + if( Result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK ) { - if( connect(ClientSocket, pResultCur->ai_addr, static_cast(pResultCur->ai_addrlen)) == 0 ) - { - pSocketInfo = netImguiNew(ClientSocket); - } - pResultCur = pResultCur->ai_next; + FD_ZERO(&SocketSet); + FD_SET(ClientSocket, &SocketSet); + Result = select(0, nullptr, &SocketSet, nullptr, &kConnectTimeout); + Connected = Result != SOCKET_ERROR; } - freeaddrinfo(pResults); - if( !pSocketInfo ) + + if( Connected ) { - closesocket(ClientSocket); + pSocketInfo = netImguiNew(ClientSocket); } - return pSocketInfo; + + pResultCur = pResultCur->ai_next; } - - //================================================================================================= - // Start waiting for connection request on this socket - //================================================================================================= - SocketInfo* ListenStart(uint32_t ListenPort) + freeaddrinfo(pResults); + if( !pSocketInfo ) { - SOCKET ListenSocket = INVALID_SOCKET; - if( (ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != INVALID_SOCKET ) - { - sockaddr_in server; - server.sin_family = AF_INET; - server.sin_addr.s_addr = INADDR_ANY; - server.sin_port = htons(static_cast(ListenPort)); - - #if NETIMGUI_FORCE_TCP_LISTEN_BINDING - constexpr BOOL ReUseAdrValue(true); - setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&ReUseAdrValue), sizeof(ReUseAdrValue)); - #endif - if( bind(ListenSocket, reinterpret_cast(&server), sizeof(server)) != SOCKET_ERROR && - listen(ListenSocket, 0) != SOCKET_ERROR ) - { - u_long kIsNonBlocking = false; - ioctlsocket(ListenSocket, static_cast(FIONBIO), &kIsNonBlocking); - return netImguiNew(ListenSocket); - } - closesocket(ListenSocket); - } - return nullptr; + closesocket(ClientSocket); } + return pSocketInfo; +} - //================================================================================================= - // Establish a new connection to a remote request - //================================================================================================= - SocketInfo* ListenConnect(SocketInfo* ListenSocket) +//================================================================================================= +// Start waiting for connection request on this socket +//================================================================================================= +SocketInfo* ListenStart(uint32_t ListenPort) +{ + SOCKET ListenSocket = INVALID_SOCKET; + if( (ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != INVALID_SOCKET ) { - if( ListenSocket ) + sockaddr_in server; + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + server.sin_port = htons(static_cast(ListenPort)); + + #if NETIMGUI_FORCE_TCP_LISTEN_BINDING + constexpr BOOL ReUseAdrValue(true); + setsockopt(ListenSocket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast(&ReUseAdrValue), sizeof(ReUseAdrValue)); + #endif + if( bind(ListenSocket, reinterpret_cast(&server), sizeof(server)) != SOCKET_ERROR && + listen(ListenSocket, 0) != SOCKET_ERROR ) { - sockaddr ClientAddress; - int Size(sizeof(ClientAddress)); - SOCKET ClientSocket = accept(ListenSocket->mSocket, &ClientAddress, &Size) ; - if (ClientSocket != INVALID_SOCKET) - { - return netImguiNew(ClientSocket); - } + u_long kIsNonBlocking = false; + ioctlsocket(ListenSocket, static_cast(FIONBIO), &kIsNonBlocking); + return netImguiNew(ListenSocket); } - return nullptr; + closesocket(ListenSocket); } + return nullptr; +} - //================================================================================================= - // Close a connection and free allocated object - //================================================================================================= - void Disconnect(SocketInfo* pClientSocket) +//================================================================================================= +// Establish a new connection to a remote request +//================================================================================================= +SocketInfo* ListenConnect(SocketInfo* ListenSocket) +{ + if( ListenSocket ) { - if( pClientSocket ) + sockaddr ClientAddress; + int Size(sizeof(ClientAddress)); + SOCKET ClientSocket = accept(ListenSocket->mSocket, &ClientAddress, &Size) ; + if (ClientSocket != INVALID_SOCKET) { - shutdown(pClientSocket->mSocket, SD_BOTH); - closesocket(pClientSocket->mSocket); - netImguiDelete(pClientSocket); + return netImguiNew(ClientSocket); } } + return nullptr; +} - //================================================================================================= - // Return trie if data has been received, or there's a connection error - //================================================================================================= - bool DataReceivePending(SocketInfo* pClientSocket) +//================================================================================================= +// Close a connection and free allocated object +//================================================================================================= +void Disconnect(SocketInfo* pClientSocket) +{ + if( pClientSocket ) { - char Unused[4]; - int resultRcv = recv(pClientSocket->mSocket, Unused, 1, MSG_PEEK); - // Note: return true on a connection error, to exit code looping on the data wait - return resultRcv > 0 || (resultRcv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK); + shutdown(pClientSocket->mSocket, SD_BOTH); + closesocket(pClientSocket->mSocket); + netImguiDelete(pClientSocket); } +} - //================================================================================================= - // Block until all requested data has been received from the remote connection - //================================================================================================= - bool DataReceive(SocketInfo* pClientSocket, void* pDataIn, size_t Size) - { - int totalRcv(0); - while( totalRcv < static_cast(Size) ) - { - int resultRcv = recv(pClientSocket->mSocket, &reinterpret_cast(pDataIn)[totalRcv], static_cast(Size)-totalRcv, 0); - if( resultRcv != SOCKET_ERROR ) - { - totalRcv += resultRcv; - } - else - { - if( WSAGetLastError() != WSAEWOULDBLOCK ) - { - return false; // Connection error, abort transmission - } - std::this_thread::yield(); - } - } - return totalRcv == static_cast(Size); +//================================================================================================= +// Return trie if data has been received, or there's a connection error +//================================================================================================= +bool DataReceivePending(SocketInfo* pClientSocket) +{ + char Unused[4]; + int resultRcv = recv(pClientSocket->mSocket, Unused, 1, MSG_PEEK); + // Note: return true on a connection error, to exit code looping on the data wait + return !pClientSocket || resultRcv > 0 || (resultRcv == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK); +} + +//================================================================================================= +// Try receiving data from remote connection +//================================================================================================= +void DataReceive(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComRcv) +{ + // Invalid command + if( !pClientSocket || !PendingComRcv.pCommand ){ + PendingComRcv.bError = true; + } + + // Receive data from remote connection + int resultRcv = recv( pClientSocket->mSocket, + &reinterpret_cast(PendingComRcv.pCommand)[PendingComRcv.SizeCurrent], + static_cast(PendingComRcv.pCommand->mSize-PendingComRcv.SizeCurrent), + 0); + + if( resultRcv != SOCKET_ERROR ){ + PendingComRcv.SizeCurrent += static_cast(resultRcv); + } + // Connection error, abort transmission + else if( WSAGetLastError() != WSAEWOULDBLOCK ){ + PendingComRcv.bError = true; + } +} + +//================================================================================================= +// Block until all requested data has been sent to remote connection +//================================================================================================= +void DataSend(SocketInfo* pClientSocket, NetImgui::Internal::PendingCom& PendingComSend) +{ + // Invalid command + if( !pClientSocket || !PendingComSend.pCommand ){ + PendingComSend.bError = true; } - //================================================================================================= - // Block until all requested data has been sent to remote connection - //================================================================================================= - bool DataSend(SocketInfo* pClientSocket, void* pDataOut, size_t Size) + //============================================================================= + //@SAMPLE_EDIT + // Intercept data transmission metrics used in compression sample + if( PendingComSend.SizeCurrent == 0 ) { - //============================================================================= - //@SAMPLE_EDIT - // Intercept data transmission metrics used in compression sample - auto* pCmdHeader = reinterpret_cast(pDataOut); - gMetric_SentDataCompressed += Size; - gMetric_SentDataUncompressed += (pCmdHeader->mType == NetImgui::Internal::CmdHeader::eCommands::DrawFrame) ? - reinterpret_cast(pDataOut)->mUncompressedSize : - Size; - //============================================================================= - int totalSent(0); - while( totalSent < static_cast(Size) ) - { - int resultSent = send(pClientSocket->mSocket, &reinterpret_cast(pDataOut)[totalSent], static_cast(Size)-totalSent, 0); - if( resultSent != SOCKET_ERROR ) - { - totalSent += resultSent; - } - else - { - if( WSAGetLastError() != WSAEWOULDBLOCK ) - { - return false; // Connection error, abort transmission - } - std::this_thread::yield(); - } - } - return totalSent == static_cast(Size); + gMetric_SentDataCompressed += PendingComSend.pCommand->mSize; + gMetric_SentDataUncompressed += (PendingComSend.pCommand->mType == NetImgui::Internal::CmdHeader::eCommands::DrawFrame) ? + reinterpret_cast(PendingComSend.pCommand)->mUncompressedSize : + PendingComSend.pCommand->mSize; + } + //============================================================================= + + // Send data to remote connection + int resultSent = send( pClientSocket->mSocket, + &reinterpret_cast(PendingComSend.pCommand)[PendingComSend.SizeCurrent], + static_cast(PendingComSend.pCommand->mSize-PendingComSend.SizeCurrent), + 0); + + if( resultSent != SOCKET_ERROR ){ + PendingComSend.SizeCurrent += static_cast(resultSent); + } + // Connection error, abort transmission + else if( WSAGetLastError() != WSAEWOULDBLOCK ){ + PendingComSend.bError = true; } +} }}} // namespace NetImgui::Internal::Network diff --git a/Code/ServerApp/Source/NetImguiServer_Config.cpp b/Code/ServerApp/Source/NetImguiServer_Config.cpp index db442b9..1b235ac 100644 --- a/Code/ServerApp/Source/NetImguiServer_Config.cpp +++ b/Code/ServerApp/Source/NetImguiServer_Config.cpp @@ -93,7 +93,7 @@ Client::Client() , mConnectAuto(false) , mConnectRequest(false) , mConnectForce(false) -, mStatus(eStatus::Disconnected) +, mConnectStatus(eStatus::Disconnected) { NetImgui::Internal::StringCopy(mClientName, "New Client"); NetImgui::Internal::StringCopy(mHostName, "localhost"); @@ -195,7 +195,7 @@ void Client::SetProperty_Status(uint32_t configID, eStatus Status) std::lock_guard guard(gConfigLock); int index = FindClientIndex(configID); if( index != -1 ){ - gConfigList[index]->mStatus = Status; + gConfigList[index]->mConnectStatus = Status; } } @@ -217,8 +217,9 @@ void Client::SetProperty_ConnectRequest(uint32_t configID, bool value, bool forc std::lock_guard guard(gConfigLock); int index = FindClientIndex(configID); if (index != -1){ - gConfigList[index]->mConnectRequest = value && !force; - gConfigList[index]->mConnectForce = value && force; + gConfigList[index]->mConnectRequest = value && !force; + gConfigList[index]->mConnectForce = value && force; + gConfigList[index]->mConnectLastTime = std::chrono::steady_clock::now(); } } diff --git a/Code/ServerApp/Source/NetImguiServer_Config.h b/Code/ServerApp/Source/NetImguiServer_Config.h index 5b1575e..6194adf 100644 --- a/Code/ServerApp/Source/NetImguiServer_Config.h +++ b/Code/ServerApp/Source/NetImguiServer_Config.h @@ -17,6 +17,7 @@ class Client { public: using RuntimeID = uint32_t; + using TimeStamp = std::chrono::steady_clock::time_point; static constexpr RuntimeID kInvalidRuntimeID = static_cast(0); enum class eVersion : uint32_t { @@ -44,26 +45,44 @@ class Client ErrorBusy, // Client already taken ErrorVer, // Server/Client network api mismatch }; + + // Config settings + char mClientName[128]; //!< Client display name + char mHostName[128]; //!< Client IP or remote host address to attempt connection at + uint32_t mHostPort; //!< Client Port to attempt connection at + RuntimeID mRuntimeID; //!< Unique RuntimeID used to find this Config + eConfigType mConfigType; //!< Type of the configuration + bool mDPIScaleEnabled; //!< Enable support of Font DPI scaling requests by Server + bool mBlockTakeover; //!< If another NetImguiServer is allowed to forcefully disconnect this client to connect to it + bool mReadOnly; //!< Config comes from read only file, can't be modified + bool mConnectAuto; //!< Try automatically connecting to client + + // Transient values used while running + mutable bool mConnectRequest; //!< Attempt connecting to Client, after user request + mutable bool mConnectForce; //!< Attempt connecting to Client, after user request, even if already connected + mutable eStatus mConnectStatus; //!< Connection status of associated client + mutable TimeStamp mConnectLastTime; //!< Last connection attempt time (avoid quickly retrying same client) + + // Access methods +public: Client(); Client(const Client& Copy) = default; - char mClientName[128]; //!< Client display name - char mHostName[128]; //!< Client IP or remote host address to attempt connection at - uint32_t mHostPort; //!< Client Port to attempt connection at - RuntimeID mRuntimeID; //!< Unique RuntimeID used to find this Config - eConfigType mConfigType; //!< Type of the configuration - bool mDPIScaleEnabled; //!< Enable support of Font DPI scaling requests by Server - bool mBlockTakeover; //!< If another NetImguiServer is allowed to forcefully disconnect this client to connect to it - bool mReadOnly; //!< Config comes from read only file, can't be modified - bool mConnectAuto; //!< Try automatically connecting to client - mutable bool mConnectRequest; //!< Attempt connecting to Client, after user request - mutable bool mConnectForce; //!< Attempt connecting to Client, after user request, even if already connected - mutable eStatus mStatus; //!< Status of associated client - inline bool IsReadOnly()const { return mReadOnly; }; inline bool IsTransient()const { return mConfigType == eConfigType::Transient; }; - inline bool IsConnected()const { return mStatus == eStatus::Connected; } - inline bool IsAvailable()const { return mStatus != eStatus::Connected && mStatus != eStatus::Connecting;} + inline bool IsConnected()const { return mConnectStatus == eStatus::Connected; } + inline bool IsConnecting()const { return mConnectStatus == eStatus::Connecting || mConnectRequest || mConnectForce; } + inline bool IsConnectReady()const { + bool bAvailable = mConnectStatus != eStatus::Connected && mConnectStatus != eStatus::Connecting; + return bAvailable && (mConnectRequest || mConnectForce); + } + inline bool IsAutoConnectReady()const { + bool bAvailable = mConnectStatus != eStatus::Connected && mConnectStatus != eStatus::Connecting; + auto elapsedTime = std::chrono::steady_clock::now() - mConnectLastTime; + int durationSec = static_cast(std::chrono::duration_cast(elapsedTime).count()); + bool elapseOk = durationSec > (mConnectStatus == eStatus::Disconnected ? 5 : 30); + return bAvailable && mConnectAuto && elapseOk; + } // Add/Edit/Remove config static void SetConfig(const Client& config); //!< Add or replace a client configuration info diff --git a/Code/ServerApp/Source/NetImguiServer_Network.cpp b/Code/ServerApp/Source/NetImguiServer_Network.cpp index c1e9cbf..85111b7 100644 --- a/Code/ServerApp/Source/NetImguiServer_Network.cpp +++ b/Code/ServerApp/Source/NetImguiServer_Network.cpp @@ -7,9 +7,11 @@ #include "NetImguiServer_RemoteClient.h" #include "NetImguiServer_Config.h" +using namespace NetImgui::Internal; + namespace NetImguiServer { namespace Network { -using atomic_SocketInfo = std::atomic; +using atomic_SocketInfo = std::atomic<::Network::SocketInfo*>; static bool gbShutdown(false); // Set to true when NetImguiServer exiting static atomic_SocketInfo gListenSocket(nullptr); // Need global access to kill socket on shutdown static std::atomic_uint32_t gActiveClientThreadCount(0); // How many active client connection currently running @@ -21,112 +23,117 @@ static std::atomic_uint64_t gStatsDataRcvd(0); //================================================================================================= // (IN) COMMAND TEXTURE //================================================================================================= -void Communications_Incoming_CmdTexture(RemoteClient::Client* pClient, uint8_t*& pCmdData) +void Communications_Incoming_CmdTexture(RemoteClient::Client& Client) { - if( pCmdData ) - { - auto pCmdTexture = reinterpret_cast(pCmdData); - pCmdData = nullptr; // Take ownership of the data, preventing freeing - pCmdTexture->mpTextureData.ToPointer(); - pClient->ReceiveTexture(pCmdTexture); - } + auto pCmdTexture = reinterpret_cast(Client.mPendingRcv.pCommand); + Client.mPendingRcv.bAutoFree = false; // Taking ownership of the data + pCmdTexture->mpTextureData.ToPointer(); + Client.ReceiveTexture(pCmdTexture); } //================================================================================================= // (IN) COMMAND BACKGROUND //================================================================================================= -void Communications_Incoming_CmdBackground(RemoteClient::Client* pClient, uint8_t*& pCmdData) +void Communications_Incoming_CmdBackground(RemoteClient::Client& Client) { - if( pCmdData ) - { - auto pCmdBackground = reinterpret_cast(pCmdData); - pCmdData = nullptr; // Take ownership of the data, preventing freeing - pClient->mPendingBackgroundIn.Assign(pCmdBackground); - } + auto pCmdBackground = reinterpret_cast(Client.mPendingRcv.pCommand); + Client.mPendingRcv.bAutoFree = false; // Taking ownership of the data + Client.mPendingBackgroundIn.Assign(pCmdBackground); } //================================================================================================= // (IN) COMMAND DRAW FRAME //================================================================================================= -void Communications_Incoming_CmdDrawFrame(RemoteClient::Client* pClient, uint8_t*& pCmdData) +void Communications_Incoming_CmdDrawFrame(RemoteClient::Client& Client) { - if( pCmdData ) - { - auto pCmdDraw = reinterpret_cast(pCmdData); - pCmdData = nullptr; // Take ownership of the data, preventing freeing - pCmdDraw->ToPointers(); - pClient->ReceiveDrawFrame(pCmdDraw); - } + auto pCmdDraw = reinterpret_cast(Client.mPendingRcv.pCommand); + Client.mPendingRcv.bAutoFree = false; // Taking ownership of the data + pCmdDraw->ToPointers(); + Client.ReceiveDrawFrame(pCmdDraw); } //================================================================================================= // (IN) COMMAND CLIPBOARD //================================================================================================= -void Communications_Incoming_CmdClipboard(RemoteClient::Client* pClient, uint8_t*& pCmdData) +void Communications_Incoming_CmdClipboard(RemoteClient::Client& Client) { - if( pCmdData ) - { - auto pCmdClipboard = reinterpret_cast(pCmdData); - pCmdData = nullptr; // Take ownership of the data, preventing freeing - pCmdClipboard->ToPointers(); - pClient->mPendingClipboardIn.Assign(pCmdClipboard); - } + auto pCmdClipboard = reinterpret_cast(Client.mPendingRcv.pCommand); + Client.mPendingRcv.bAutoFree = false; // Taking ownership of the data + pCmdClipboard->ToPointers(); + Client.mPendingClipboardIn.Assign(pCmdClipboard); } //================================================================================================= // Receive every commands sent by remote client and process them // We keep receiving until we detect a ping command (signal end of commands) //================================================================================================= -void Communications_Incoming(NetImgui::Internal::Network::SocketInfo* pClientSocket, RemoteClient::Client* pClient) +void Communications_Incoming(RemoteClient::Client& Client) { - if( NetImgui::Internal::Network::DataReceivePending(pClientSocket) ) + if( ::Network::DataReceivePending(Client.mpSocket) ) { - bool bOk(true); - NetImgui::Internal::CmdHeader cmdHeader; - //--------------------------------------------------------------------- - // Receive all of the data from client, - // and allocate memory to receive it if needed - //--------------------------------------------------------------------- - uint8_t* pCmdData = nullptr; - bOk = NetImgui::Internal::Network::DataReceive(pClientSocket, &cmdHeader, sizeof(cmdHeader)); - bOk &= cmdHeader.mType < NetImgui::Internal::CmdHeader::eCommands::Count; - if( bOk && cmdHeader.mSize > sizeof(cmdHeader) ) + //----------------------------------------------------------------------------------------- + // 1. Ready to receive new command, starts the process by reading Header + //----------------------------------------------------------------------------------------- + if( Client.mPendingRcv.IsReady() ) { - pCmdData = NetImgui::Internal::netImguiSizedNew(cmdHeader.mSize); - *reinterpret_cast(pCmdData) = cmdHeader; - char* pDataRemaining = reinterpret_cast(&pCmdData[sizeof(cmdHeader)]); - bOk = NetImgui::Internal::Network::DataReceive(pClientSocket, pDataRemaining, cmdHeader.mSize - sizeof(cmdHeader)); + Client.mCmdPendingRead = NetImgui::Internal::CmdPendingRead(); + Client.mPendingRcv.pCommand = &Client.mCmdPendingRead; + Client.mPendingRcv.bAutoFree = false; } - //--------------------------------------------------------------------- - // Process the command type - //--------------------------------------------------------------------- - if( bOk ) + //----------------------------------------------------------------------------------------- + // 2. Read incoming command from server + //----------------------------------------------------------------------------------------- + if( Client.mPendingRcv.IsPending() ) { - pClient->mStatsDataRcvd += cmdHeader.mSize; - pClient->mLastIncomingComTime = std::chrono::steady_clock::now(); - switch( cmdHeader.mType ) + ::Network::DataReceive(Client.mpSocket, Client.mPendingRcv); + + // Detected a new command bigger than header, allocate memory for it + if( Client.mPendingRcv.pCommand->mSize > sizeof(NetImgui::Internal::CmdPendingRead) && + Client.mPendingRcv.pCommand == &Client.mCmdPendingRead ) { - case NetImgui::Internal::CmdHeader::eCommands::Disconnect: pClient->mbIsConnected = false; break; - case NetImgui::Internal::CmdHeader::eCommands::Texture: Communications_Incoming_CmdTexture(pClient, pCmdData); break; - case NetImgui::Internal::CmdHeader::eCommands::Background: Communications_Incoming_CmdBackground(pClient, pCmdData); break; - case NetImgui::Internal::CmdHeader::eCommands::DrawFrame: Communications_Incoming_CmdDrawFrame(pClient, pCmdData); break; - case NetImgui::Internal::CmdHeader::eCommands::Clipboard: Communications_Incoming_CmdClipboard(pClient, pCmdData); break; - // Commands not received in main loop, by Server - case NetImgui::Internal::CmdHeader::eCommands::Version: - case NetImgui::Internal::CmdHeader::eCommands::Input: - case NetImgui::Internal::CmdHeader::eCommands::Count: break; + CmdPendingRead* pCmdHeader = reinterpret_cast(netImguiSizedNew(Client.mPendingRcv.pCommand->mSize)); + *pCmdHeader = Client.mCmdPendingRead; + Client.mPendingRcv.pCommand = pCmdHeader; + Client.mPendingRcv.bAutoFree = true; } } - else + + //----------------------------------------------------------------------------------------- + // 3. Command fully received from Server, process it + //----------------------------------------------------------------------------------------- + if( Client.mPendingRcv.IsDone() ) { - pClient->mbDisconnectPending = true; // Connection problem detected, close connection + if( !Client.mPendingRcv.IsError() ) + { + Client.mStatsDataRcvd += Client.mPendingRcv.pCommand->mSize; + Client.mLastIncomingComTime = std::chrono::steady_clock::now(); + switch( Client.mPendingRcv.pCommand->mType ) + { + case NetImgui::Internal::CmdHeader::eCommands::Texture: Communications_Incoming_CmdTexture(Client); break; + case NetImgui::Internal::CmdHeader::eCommands::Background: Communications_Incoming_CmdBackground(Client); break; + case NetImgui::Internal::CmdHeader::eCommands::DrawFrame: Communications_Incoming_CmdDrawFrame(Client); break; + case NetImgui::Internal::CmdHeader::eCommands::Clipboard: Communications_Incoming_CmdClipboard(Client); break; + // Commands not received in main loop, by Server + case NetImgui::Internal::CmdHeader::eCommands::Version: + case NetImgui::Internal::CmdHeader::eCommands::Input: + case NetImgui::Internal::CmdHeader::eCommands::Count: break; + } + } + + // Reset pending read + if( Client.mPendingRcv.IsError() ){ + Client.mbDisconnectPending = true; + } + if( Client.mPendingRcv.bAutoFree ){ + netImguiDeleteSafe(Client.mPendingRcv.pCommand); + } + Client.mPendingRcv = PendingCom(); } - NetImgui::Internal::netImguiDeleteSafe(pCmdData); } + // Prevent high CPU usage when waiting for new data else { - // Prevent high CPU usage when waiting for new data //std::this_thread::yield(); std::this_thread::sleep_for(std::chrono::microseconds(250)); } @@ -136,35 +143,41 @@ void Communications_Incoming(NetImgui::Internal::Network::SocketInfo* pClientSoc // Send the updates to RemoteClient // Ends with a Ping Command (signal a end of commands) //================================================================================================= -void Communications_Outgoing(NetImgui::Internal::Network::SocketInfo* pClientSocket, RemoteClient::Client* pClient) +void Communications_Outgoing(RemoteClient::Client& Client) { - NetImgui::Internal::CmdInput* pInputCmd = pClient->TakePendingInput(); - NetImgui::Internal::CmdClipboard* pClipboardCmd = pClient->TakePendingClipboard(); - - if( pInputCmd ) + //--------------------------------------------------------------------------------------------- + // Try finishing sending a pending command to Server + //--------------------------------------------------------------------------------------------- + if( Client.mPendingSend.IsPending() ) { - if( NetImgui::Internal::Network::DataSend(pClientSocket, reinterpret_cast(pInputCmd), pInputCmd->mHeader.mSize) ) + ::Network::DataSend(Client.mpSocket, Client.mPendingSend); + + // Free allocated memory for command + if( Client.mPendingSend.IsDone() ) { - pClient->mStatsDataSent += pInputCmd->mHeader.mSize; - NetImgui::Internal::netImguiDeleteSafe(pInputCmd); + if( Client.mPendingSend.IsError() ){ + Client.mbDisconnectPending = true; + } + Client.mStatsDataSent += Client.mPendingSend.pCommand->mSize; + if( Client.mPendingSend.bAutoFree ){ + netImguiDeleteSafe(Client.mPendingSend.pCommand); + } + Client.mPendingSend = PendingCom(); } } - if( pClipboardCmd ) + if( Client.mPendingSend.IsReady() ) { - pClipboardCmd->ToOffsets(); - if(NetImgui::Internal::Network::DataSend(pClientSocket, reinterpret_cast(pClipboardCmd), pClipboardCmd->mHeader.mSize) ) - { - pClient->mStatsDataSent += pClipboardCmd->mHeader.mSize; - NetImgui::Internal::netImguiDeleteSafe(pClipboardCmd); - } + NetImgui::Internal::CmdClipboard* pClipboardCmd = Client.TakePendingClipboard(); + Client.mPendingSend.pCommand = pClipboardCmd; + Client.mPendingSend.bAutoFree = true; } - if( gbShutdown || pClient->mbDisconnectPending ) + if( Client.mPendingSend.IsReady() ) { - NetImgui::Internal::CmdDisconnect cmdDisconnect; - NetImgui::Internal::Network::DataSend(pClientSocket, reinterpret_cast(&cmdDisconnect), cmdDisconnect.mHeader.mSize); - pClient->mbIsConnected = false; + NetImgui::Internal::CmdInput* pInputCmd = Client.TakePendingInput(); + Client.mPendingSend.pCommand = pInputCmd; + Client.mPendingSend.bAutoFree = true; } } @@ -196,21 +209,23 @@ void Communications_UpdateClientStats(RemoteClient::Client& Client) //================================================================================================= // Keep sending/receiving commands to Remote Client, until disconnection occurs //================================================================================================= -void Communications_ClientExchangeLoop(NetImgui::Internal::Network::SocketInfo* pClientSocket, RemoteClient::Client* pClient) +void Communications_ClientExchangeLoop(RemoteClient::Client* pClient) { gActiveClientThreadCount++; NetImguiServer::Config::Client::SetProperty_Status(pClient->mClientConfigID, NetImguiServer::Config::Client::eStatus::Connected); - while ( pClient->mbIsConnected ) + pClient->mbDisconnectPending = false; + pClient->mbIsConnected = true; + while ( !gbShutdown && !pClient->mbDisconnectPending ) { - Communications_Outgoing(pClientSocket, pClient); - Communications_Incoming(pClientSocket, pClient); + Communications_Outgoing(*pClient); + Communications_Incoming(*pClient); Communications_UpdateClientStats(*pClient); } - NetImguiServer::Config::Client::SetProperty_Status(pClient->mClientConfigID, NetImguiServer::Config::Client::eStatus::Disconnected); - NetImgui::Internal::Network::Disconnect(pClientSocket); + NetImgui::Internal::Network::Disconnect(pClient->mpSocket); pClient->Release(); + pClient->mbIsConnected = false; gActiveClientThreadCount--; } @@ -220,28 +235,38 @@ void Communications_ClientExchangeLoop(NetImgui::Internal::Network::SocketInfo* //================================================================================================= bool Communications_InitializeClient(NetImgui::Internal::Network::SocketInfo* pClientSocket, RemoteClient::Client* pClient, bool ConnectForce) { - NetImgui::Internal::CmdVersion cmdVersionSend; - NetImgui::Internal::CmdVersion cmdVersionRcv; - NetImgui::Internal::StringCopy(cmdVersionSend.mClientName, "Server"); - bool ConnectExclusive = NetImguiServer::Config::Client::GetProperty_BlockTakeover(pClient->mClientConfigID); - cmdVersionSend.mFlags |= ConnectExclusive ? static_cast(NetImgui::Internal::CmdVersion::eFlags::ConnectExclusive) : 0; - cmdVersionSend.mFlags |= ConnectForce ? static_cast(NetImgui::Internal::CmdVersion::eFlags::ConnectForce) : 0; + NetImgui::Internal::CmdVersion cmdVersionSend, cmdVersionRcv; + NetImgui::Internal::PendingCom PendingRcv, PendingSend; //--------------------------------------------------------------------- // Handshake confirming connection validity //--------------------------------------------------------------------- - if( NetImgui::Internal::Network::DataSend(pClientSocket, reinterpret_cast(&cmdVersionSend), cmdVersionSend.mHeader.mSize) ) + NetImgui::Internal::StringCopy(cmdVersionSend.mClientName, "Server"); + const bool ConnectExclusive = NetImguiServer::Config::Client::GetProperty_BlockTakeover(pClient->mClientConfigID); + cmdVersionSend.mFlags |= ConnectExclusive ? static_cast(NetImgui::Internal::CmdVersion::eFlags::ConnectExclusive) : 0; + cmdVersionSend.mFlags |= ConnectForce ? static_cast(NetImgui::Internal::CmdVersion::eFlags::ConnectForce) : 0; + PendingSend.pCommand = reinterpret_cast(&cmdVersionSend); + while( !PendingSend.IsDone() ){ + ::Network::DataSend(pClientSocket, PendingSend); + } + + if( !PendingSend.IsError() ) { - while( !NetImgui::Internal::Network::DataReceivePending(pClientSocket) ) + PendingRcv.pCommand = reinterpret_cast(&cmdVersionRcv); + while( !PendingRcv.IsDone() && cmdVersionRcv.mType == CmdHeader::eCommands::Version ) { - std::this_thread::yield(); // Idle until we receive the remote data + while( !::Network::DataReceivePending(pClientSocket) ){ + std::this_thread::yield(); // Idle until we receive the remote data + } + ::Network::DataReceive(pClientSocket, PendingRcv); } - if( NetImgui::Internal::Network::DataReceive(pClientSocket, reinterpret_cast(&cmdVersionRcv), sizeof(cmdVersionRcv)) ) + + if( !PendingRcv.IsError() ) { //--------------------------------------------------------------------- // Connection accepted, initialize client //--------------------------------------------------------------------- - if( cmdVersionRcv.mHeader.mType != NetImgui::Internal::CmdHeader::eCommands::Version || + if( cmdVersionRcv.mType != NetImgui::Internal::CmdHeader::eCommands::Version || cmdVersionRcv.mVersion != NetImgui::Internal::CmdVersion::eVersion::_current ) { NetImguiServer::Config::Client::SetProperty_Status(pClient->mClientConfigID, NetImguiServer::Config::Client::eStatus::ErrorVer); @@ -250,14 +275,16 @@ bool Communications_InitializeClient(NetImgui::Internal::Network::SocketInfo* pC else if(cmdVersionRcv.mFlags & static_cast(NetImgui::Internal::CmdVersion::eFlags::IsConnected) ) { bool bAvailable = (cmdVersionRcv.mFlags & static_cast(NetImgui::Internal::CmdVersion::eFlags::IsUnavailable)) == 0; - NetImguiServer::Config::Client::SetProperty_Status(pClient->mClientConfigID, bAvailable ? NetImguiServer::Config::Client::eStatus::Available - : NetImguiServer::Config::Client::eStatus::ErrorBusy); + NetImguiServer::Config::Client::SetProperty_Status(pClient->mClientConfigID, bAvailable ? NetImguiServer::Config::Client::eStatus::Available + : NetImguiServer::Config::Client::eStatus::ErrorBusy); return false; } pClient->Initialize(); pClient->mInfoImguiVerID = cmdVersionRcv.mImguiVerID; pClient->mInfoNetImguiVerID = cmdVersionRcv.mNetImguiVerID; + pClient->mPendingRcv = PendingCom(); + pClient->mPendingSend = PendingCom(); NetImgui::Internal::StringCopy(pClient->mInfoName, cmdVersionRcv.mClientName); NetImgui::Internal::StringCopy(pClient->mInfoImguiVerName, cmdVersionRcv.mImguiVerName); NetImgui::Internal::StringCopy(pClient->mInfoNetImguiVerName, cmdVersionRcv.mNetImguiVerName); @@ -272,6 +299,7 @@ bool Communications_InitializeClient(NetImgui::Internal::Network::SocketInfo* pC return true; } } + NetImguiServer::Config::Client::SetProperty_Status(pClient->mClientConfigID, NetImguiServer::Config::Client::eStatus::Disconnected); return false; } @@ -295,12 +323,11 @@ void NetworkConnectionNew(NetImgui::Internal::Network::SocketInfo* pClientSocket } if (zErrorMsg == nullptr && !gbShutdown){ - pNewClient->mbIsConnected = true; - std::thread(Communications_ClientExchangeLoop, pClientSocket, pNewClient).detach(); + pNewClient->mpSocket = pClientSocket; + std::thread(Communications_ClientExchangeLoop, pNewClient).detach(); } else{ NetImgui::Internal::Network::Disconnect(pClientSocket); - NetImguiServer::Config::Client::SetProperty_Status(pNewClient->mClientConfigID, NetImguiServer::Config::Client::eStatus::Disconnected); if (!gbShutdown) { if (pNewClient){ pNewClient->mbIsFree = true; @@ -381,7 +408,7 @@ void NetworkConnectRequest_Send() if( NetImguiServer::Config::Client::GetConfigByIndex(configIdx, clientConfig) ) { ConnectForce = clientConfig.mConnectForce; - if( (clientConfig.mConnectAuto || clientConfig.mConnectRequest || clientConfig.mConnectForce) && clientConfig.IsAvailable() && clientConfig.mHostPort != NetImguiServer::Config::Server::sPort) + if( (clientConfig.IsConnectReady() || clientConfig.IsAutoConnectReady()) && clientConfig.mHostPort != NetImguiServer::Config::Server::sPort ) { NetImguiServer::Config::Client::SetProperty_ConnectRequest(clientConfig.mRuntimeID, false, false); // Reset the Connection request, we are processing it NetImguiServer::Config::Client::SetProperty_Status(clientConfig.mRuntimeID, NetImguiServer::Config::Client::eStatus::Disconnected); diff --git a/Code/ServerApp/Source/NetImguiServer_RemoteClient.cpp b/Code/ServerApp/Source/NetImguiServer_RemoteClient.cpp index 79993c5..dd7db58 100644 --- a/Code/ServerApp/Source/NetImguiServer_RemoteClient.cpp +++ b/Code/ServerApp/Source/NetImguiServer_RemoteClient.cpp @@ -22,11 +22,9 @@ NetImguiImDrawData::NetImguiImDrawData() Client::Client() : mPendingTextureReadIndex(0) , mPendingTextureWriteIndex(0) -, mbIsVisible(false) , mbIsFree(true) -, mbIsConnected(false) -, mbDisconnectPending(false) , mbCompressionSkipOncePending(false) +, mbDisconnectPending(false) , mClientConfigID(NetImguiServer::Config::Client::kInvalidRuntimeID) { } @@ -91,7 +89,7 @@ void Client::ProcessPendingTextures() { NetImgui::Internal::CmdTexture* pTextureCmd = mpPendingTextures[(mPendingTextureReadIndex++) % IM_ARRAYSIZE(mpPendingTextures)]; bool isRemoval = pTextureCmd->mFormat == NetImgui::eTexFormat::kTexFmt_Invalid; - uint32_t dataSize = pTextureCmd->mHeader.mSize - sizeof(NetImgui::Internal::CmdTexture); + uint32_t dataSize = pTextureCmd->mSize - sizeof(NetImgui::Internal::CmdTexture); auto texIt = mTextureTable.find(pTextureCmd->mTextureId) ; textureChanged |= texIt != mTextureTable.end(); // Delete texture when format/size changed or asked to remove @@ -141,6 +139,8 @@ void Client::Initialize() mbIsReleased = false; mStatsTime = std::chrono::steady_clock::now(); mBGSettings = NetImgui::Internal::CmdBackground(); // Assign background default value, until we receive first update from client + mPendingRcv = NetImgui::Internal::PendingCom(); + mPendingSend = NetImgui::Internal::PendingCom(); NetImgui::Internal::netImguiDeleteSafe(mpImguiDrawData); NetImgui::Internal::netImguiDeleteSafe(mpFrameDrawPrev); } diff --git a/Code/ServerApp/Source/NetImguiServer_RemoteClient.h b/Code/ServerApp/Source/NetImguiServer_RemoteClient.h index 65b346d..b70c8e8 100644 --- a/Code/ServerApp/Source/NetImguiServer_RemoteClient.h +++ b/Code/ServerApp/Source/NetImguiServer_RemoteClient.h @@ -54,68 +54,72 @@ struct Client NetImgui::Internal::CmdInput* TakePendingInput(); NetImgui::Internal::CmdClipboard* TakePendingClipboard(); void ProcessPendingTextures(); - - void* mpHAL_AreaRT = nullptr; - void* mpHAL_AreaTexture = nullptr; - uint16_t mAreaRTSizeX = 0; //!< Currently allocated RenderTarget size - uint16_t mAreaRTSizeY = 0; //!< Currently allocated RenderTarget size - uint16_t mAreaSizeX = 0; //!< Available area size available to remote client - uint16_t mAreaSizeY = 0; //!< Available area size available to remote client - char mInfoName[128] = {}; - char mWindowID[128+16] = {}; - char mInfoImguiVerName[16] = {}; - char mInfoNetImguiVerName[16]= {}; - uint32_t mInfoImguiVerID = 0; - uint32_t mInfoNetImguiVerID = 0; - char mConnectHost[64] = {}; //!< Connected Hostname of this remote client - int mConnectPort = 0; //!< Connected Port of this remote client - - NetImguiImDrawData* mpImguiDrawData = nullptr; //!< Current Imgui Data that this client is the owner of - NetImgui::Internal::CmdDrawFrame* mpFrameDrawPrev = nullptr; //!< Last valid DrawDrame (used by com thread, to uncompress data) - TextureTable mTextureTable; //!< Table of textures received and used by the client - ExchPtrImguiDraw mPendingImguiDrawDataIn; //!< Pending received Imgui DrawData, waiting to be taken ownership of - ExchPtrBackground mPendingBackgroundIn; //!< Background settings received and waiting to update client setting - ExchPtrClipboard mPendingClipboardIn; //!< Clipboard received from Client and waiting to be processed on Server - ExchPtrInput mPendingInputOut; //!< Input command waiting to be sent out to client - ExchPtrClipboard mPendingClipboardOut; //!< Clipboard command waiting to be sent out to client - std::vector mPendingInputChars; //!< Captured Imgui characters input waiting to be added to new InputCmd - NetImgui::Internal::CmdTexture* mpPendingTextures[64] = {}; //!< Textures commands waiting to be processed in main update loop - std::atomic_uint64_t mPendingTextureReadIndex; - std::atomic_uint64_t mPendingTextureWriteIndex; - bool mbIsVisible = false; //!< If currently shown - bool mbIsActive = false; //!< Is the current active window (will receive input, only one is true at a time) - bool mbIsReleased = false; //!< If released in com thread and main thread should delete resources - std::atomic_bool mbIsFree; //!< If available to use for a new connected client - std::atomic_bool mbIsConnected; //!< If connected to a remote client - std::atomic_bool mbDisconnectPending; //!< Server requested a disconnect on this item - std::atomic_bool mbCompressionSkipOncePending; //!< When we detect invalid previous DrawFrame command, cancel compression for 1 frame, to get good data - std::chrono::steady_clock::time_point mConnectedTime; //!< When the connection was established with this remote client - std::chrono::steady_clock::time_point mLastUpdateTime; //!< When the client last send a content refresh request - std::chrono::steady_clock::time_point mLastDrawFrame; //!< When we last receive a new drawframe commant - std::chrono::steady_clock::time_point mLastIncomingComTime; //!< When we last received a valid command from client (to detect timeout) - uint32_t mClientConfigID = 0; //!< ID of ClientConfig that connected (if connection came from our list of ClientConfigs) - uint32_t mClientIndex = 0; //!< Entry idx into table of connected clients - uint64_t mStatsDataRcvd = 0; //!< Current amount of Bytes received since connected - uint64_t mStatsDataSent = 0; //!< Current amount of Bytes sent to client since connected - uint64_t mStatsDataRcvdPrev = 0; //!< Last amount of Bytes received since connected - uint64_t mStatsDataSentPrev = 0; //!< Last amount of Bytes sent to client since connected - std::chrono::steady_clock::time_point mStatsTime; //!< Time when info was collected (with history of last x values) - uint32_t mStatsRcvdBps = 0; //!< Average Bytes received per second - uint32_t mStatsSentBps = 0; //!< Average Bytes sent per second - float mStatsDrawElapsedMs = 0.f; //!< Average milliseconds between 2 draw requests - uint32_t mStatsIndex = 0; - float mMousePos[2] = {0,0}; - float mMouseWheelPos[2] = {0,0}; - ImGuiMouseCursor mMouseCursor = ImGuiMouseCursor_None; // Last mosue cursor remote client requested - ImGuiContext* mpBGContext = nullptr; // Special Imgui Context used to render the background (only updated when needed) - bool mBGNeedUpdate = true; // Let engine know that we should regenerate the background draw commands - NetImgui::Internal::CmdBackground mBGSettings; // Settings for client background drawing settings static bool Startup(uint32_t clientCountMax); static void Shutdown(); static uint32_t GetCountMax(); static uint32_t GetFreeIndex(); static Client& Get(uint32_t index); + + void* mpHAL_AreaRT = nullptr; + void* mpHAL_AreaTexture = nullptr; + uint16_t mAreaRTSizeX = 0; //!< Currently allocated RenderTarget size + uint16_t mAreaRTSizeY = 0; //!< Currently allocated RenderTarget size + uint16_t mAreaSizeX = 0; //!< Available area size available to remote client + uint16_t mAreaSizeY = 0; //!< Available area size available to remote client + char mInfoName[128] = {}; + char mWindowID[128+16] = {}; + char mInfoImguiVerName[16] = {}; + char mInfoNetImguiVerName[16]= {}; + uint32_t mInfoImguiVerID = 0; + uint32_t mInfoNetImguiVerID = 0; + char mConnectHost[64] = {}; //!< Connected Hostname of this remote client + int mConnectPort = 0; //!< Connected Port of this remote client + + NetImguiImDrawData* mpImguiDrawData = nullptr; //!< Current Imgui Data that this client is the owner of + NetImgui::Internal::CmdDrawFrame* mpFrameDrawPrev = nullptr; //!< Last valid DrawDrame (used by com thread, to uncompress data) + TextureTable mTextureTable; //!< Table of textures received and used by the client + ExchPtrImguiDraw mPendingImguiDrawDataIn; //!< Pending received Imgui DrawData, waiting to be taken ownership of + ExchPtrBackground mPendingBackgroundIn; //!< Background settings received and waiting to update client setting + ExchPtrClipboard mPendingClipboardIn; //!< Clipboard received from Client and waiting to be processed on Server + ExchPtrInput mPendingInputOut; //!< Input command waiting to be sent out to client + ExchPtrClipboard mPendingClipboardOut; //!< Clipboard command waiting to be sent out to client + std::vector mPendingInputChars; //!< Captured Imgui characters input waiting to be added to new InputCmd + NetImgui::Internal::CmdTexture* mpPendingTextures[64] = {}; //!< Textures commands waiting to be processed in main update loop + std::atomic_uint64_t mPendingTextureReadIndex; + std::atomic_uint64_t mPendingTextureWriteIndex; + bool mbIsVisible = false; //!< If currently shown + bool mbIsActive = false; //!< Is the current active window (will receive input, only one is true at a time) + bool mbIsReleased = false; //!< If released in com thread and main thread should delete resources + bool mbIsConnected = false; //!< If connected to a remote client + std::atomic_bool mbIsFree; //!< If available to use for a new connected client + std::atomic_bool mbCompressionSkipOncePending; //!< When we detect invalid previous DrawFrame command, cancel compression for 1 frame, to get good data + std::atomic_bool mbDisconnectPending; //!< Terminate Client/Server coms + std::chrono::steady_clock::time_point mConnectedTime; //!< When the connection was established with this remote client + std::chrono::steady_clock::time_point mLastUpdateTime; //!< When the client last send a content refresh request + std::chrono::steady_clock::time_point mLastDrawFrame; //!< When we last receive a new drawframe commant + std::chrono::steady_clock::time_point mLastIncomingComTime; //!< When we last received a valid command from client (to detect timeout) + uint32_t mClientConfigID = 0; //!< ID of ClientConfig that connected (if connection came from our list of ClientConfigs) + uint32_t mClientIndex = 0; //!< Entry idx into table of connected clients + uint64_t mStatsDataRcvd = 0; //!< Current amount of Bytes received since connected + uint64_t mStatsDataSent = 0; //!< Current amount of Bytes sent to client since connected + uint64_t mStatsDataRcvdPrev = 0; //!< Last amount of Bytes received since connected + uint64_t mStatsDataSentPrev = 0; //!< Last amount of Bytes sent to client since connected + std::chrono::steady_clock::time_point mStatsTime; //!< Time when info was collected (with history of last x values) + uint32_t mStatsRcvdBps = 0; //!< Average Bytes received per second + uint32_t mStatsSentBps = 0; //!< Average Bytes sent per second + float mStatsDrawElapsedMs = 0.f; //!< Average milliseconds between 2 draw requests + uint32_t mStatsIndex = 0; + float mMousePos[2] = {0,0}; + float mMouseWheelPos[2] = {0,0}; + ImGuiMouseCursor mMouseCursor = ImGuiMouseCursor_None; // Last mosue cursor remote client requested + ImGuiContext* mpBGContext = nullptr; // Special Imgui Context used to render the background (only updated when needed) + bool mBGNeedUpdate = true; // Let engine know that we should regenerate the background draw commands + NetImgui::Internal::Network::SocketInfo* mpSocket = nullptr; //!< Socket used for communications + NetImgui::Internal::CmdBackground mBGSettings; //!< Settings for client background drawing settings + NetImgui::Internal::CmdPendingRead mCmdPendingRead; //!< Used to get info on the next incoming command from Client + NetImgui::Internal::PendingCom mPendingRcv; //!< Data being currently received from Client + NetImgui::Internal::PendingCom mPendingSend; //!< Data being currently sent to Client }; }} // namespace NetImguiServer { namespace Client \ No newline at end of file diff --git a/Code/ServerApp/Source/NetImguiServer_UI.cpp b/Code/ServerApp/Source/NetImguiServer_UI.cpp index 15f069b..5e4e08b 100644 --- a/Code/ServerApp/Source/NetImguiServer_UI.cpp +++ b/Code/ServerApp/Source/NetImguiServer_UI.cpp @@ -249,7 +249,7 @@ void Popup_ServerConfig() // --- Port --- ImGui::TextUnformatted("Port waiting for connection requests"); - if (ImGui::InputInt("Port", &sEditPort, 1, 100, ImGuiInputTextFlags_EnterReturnsTrue)) { + if (ImGui::InputInt("Port", &sEditPort, 1, 100, 0)) { sEditPort = std::min(0xFFFF, std::max(1, sEditPort)); } ImGui::SameLine(); @@ -567,7 +567,7 @@ void DrawImguiContent_Clients() } } ImGui::End(); - if(!bOpened && !client.mbDisconnectPending ) + if(!bOpened && client.mbIsConnected ) { gPopup_ConfirmDisconnect_ClientIdx = i; } @@ -667,7 +667,7 @@ void DrawImguiContent_MainMenu_Clients_Entry(RemoteClient::Client* pClient, NetI ImGui::TableSetColumnIndex(0); // Name / Status - auto ConfigStatus = pClientConfig ? pClientConfig->mStatus : NetImguiServer::Config::Client::eStatus::Disconnected; + auto ConfigStatus = pClientConfig ? pClientConfig->mConnectStatus : NetImguiServer::Config::Client::eStatus::Disconnected; ImVec4 StatusColor = pClient && pClient->mbIsConnected ? ImVec4(0.7f, 1.f, 0.25f, 1.f) : ConfigStatus == NetImguiServer::Config::Client::eStatus::ErrorVer ? ImVec4(1.f, 0.7f, 0.25f, 1.f) : ConfigStatus == NetImguiServer::Config::Client::eStatus::ErrorBusy ? ImVec4(1.f, 0.7f, 0.25f, 1.f) @@ -729,37 +729,46 @@ void DrawImguiContent_MainMenu_Clients_Entry(RemoteClient::Client* pClient, NetI ImGui::EndDisabled(); // Config: Connection - if( pClient && !pClient->mbDisconnectPending && ImGui::Button("Disconnect", ImVec2(80 * GetFontDPIScale(),0 )) ) + const float kButtonWidth = 100.f * GetFontDPIScale(); + if( pClient && !pClient->mbDisconnectPending && ImGui::Button("Disconnect", ImVec2(kButtonWidth, 0)) ) { gPopup_ConfirmDisconnect_ClientIdx = pClient->mClientIndex; } else if( pClientConfig ) { - if( pClientConfig->IsTransient() ){ + if( pClientConfig->IsConnecting() ) + { + ImGui::SetNextItemWidth(kButtonWidth); + ImGui::TextUnformatted("(Connecting)"); + } + else if( pClientConfig->IsTransient() ){ + ImGui::SetNextItemWidth(kButtonWidth); ImGui::TextUnformatted("(Request)"); } else if (!pClientConfig->IsConnected() && !pClientConfig->mConnectRequest && !pClientConfig->mConnectForce) { if( ConfigStatus == NetImguiServer::Config::Client::eStatus::Disconnected ) { - if( ImGui::Button("Connect", ImVec2(80 * GetFontDPIScale(),0 )) ){ + if( ImGui::Button("Connect", ImVec2(kButtonWidth,0 )) ){ NetImguiServer::Config::Client::SetProperty_ConnectRequest(pClientConfig->mRuntimeID, true, false); } } else if( ConfigStatus == NetImguiServer::Config::Client::eStatus::Available ) { - if( ImGui::Button("Takeover", ImVec2(80 * GetFontDPIScale(),0 )) ){ + if( ImGui::Button("Takeover", ImVec2(kButtonWidth,0 )) ){ NetImguiServer::Config::Client::SetProperty_ConnectRequest(pClientConfig->mRuntimeID, true, true); } ImGui::SetItemTooltip("Client already connected to another NetImguiServer, disconnect it and force connect this Server"); } else if( ConfigStatus == NetImguiServer::Config::Client::eStatus::ErrorBusy ) { + ImGui::SetNextItemWidth(kButtonWidth); ImGui::TextUnformatted("(In Use)"); ImGui::SetItemTooltip("Client already connected to another NetImguiServer and doesn't allow taking over the connection"); } else if( ConfigStatus == NetImguiServer::Config::Client::eStatus::ErrorVer ) { + ImGui::SetNextItemWidth(kButtonWidth); ImGui::TextUnformatted("(Version)"); ImGui::SetItemTooltip("Client is using a different network protocol than this NetImguiServer. Update the client code to match the Server."); } diff --git a/Code/ServerApp/Source/Sokol/sokol/util/sokol_imgui.h b/Code/ServerApp/Source/Sokol/sokol/util/sokol_imgui.h index d4cad40..8dae74b 100644 --- a/Code/ServerApp/Source/Sokol/sokol/util/sokol_imgui.h +++ b/Code/ServerApp/Source/Sokol/sokol/util/sokol_imgui.h @@ -2,6 +2,9 @@ #define SOKOL_IMGUI_IMPL #endif #ifndef SOKOL_IMGUI_INCLUDED + +// @SAMPLE_EDIT NOTE: This needs to be updated to latest Solol Version. +// for now, just fixed 2 compile errors linked to clipboard /* sokol_imgui.h -- drop-in Dear ImGui renderer/event-handler for sokol_gfx.h @@ -1672,13 +1675,11 @@ static const char* _simgui_fs_source_dummy = ""; #endif #if !defined(SOKOL_IMGUI_NO_SOKOL_APP) -static void _simgui_set_clipboard(void* user_data, const char* text) { - (void)user_data; +static void _simgui_set_clipboard(ImGuiContext *, const char* text) { sapp_set_clipboard_string(text); } -static const char* _simgui_get_clipboard(void* user_data) { - (void)user_data; +static const char* _simgui_get_clipboard(ImGuiContext *) { return sapp_get_clipboard_string(); } #endif @@ -1782,8 +1783,11 @@ SOKOL_API_IMPL void simgui_setup(const simgui_desc_t* desc) { if (!_simgui.desc.disable_set_mouse_cursor) { io->BackendFlags |= ImGuiBackendFlags_HasMouseCursors; } - io->SetClipboardTextFn = _simgui_set_clipboard; - io->GetClipboardTextFn = _simgui_get_clipboard; + // @SAMPLE_EDIT Disabled Clipboard + // Latest Imgui moved clipboard functionalities to + // ImGui::GetPlatformIO(), and changed the signature + ImGui::GetPlatformIO().Platform_SetClipboardTextFn = _simgui_set_clipboard; + ImGui::GetPlatformIO().Platform_GetClipboardTextFn = _simgui_get_clipboard; #endif io->ConfigWindowsResizeFromEdges = !_simgui.desc.disable_windows_resize_from_edges;