Skip to content

Commit

Permalink
Added 'TakeOver' Option
Browse files Browse the repository at this point in the history
NetImguiServerApp can now forcefully take over a connection to a client already connected to another server. A button with 'TakeOver' appear in the Client List letting the user connect despite an active connection.
  • Loading branch information
sammyfreg committed May 26, 2024
1 parent 051d8cd commit 7a44e10
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 113 deletions.
8 changes: 4 additions & 4 deletions Code/Client/NetImgui_Api.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
//! @Name : NetImgui
//=================================================================================================
//! @author : Sammy Fatnassi
//! @date : 2023/12/2
//! @date : 2024/05/26
//! @version : v1.9.7
//! @Details : For integration info : https://github.com/sammyfreg/netImgui/wiki
//=================================================================================================
#define NETIMGUI_VERSION "1.9.7" // Update to samples, texture creation fix
#define NETIMGUI_VERSION_NUM 10907
#define NETIMGUI_VERSION "1.9.8" // Added 'TakeOver' connection option
#define NETIMGUI_VERSION_NUM 10908



Expand Down Expand Up @@ -62,7 +62,7 @@
// (either always included in NetImgui_config.h or have it included after Imgui.h in your cpp)
#if !defined(IMGUI_VERSION)
#undef NETIMGUI_ENABLED
#define NETIMGUI_ENABLED 0
#define NETIMGUI_ENABLED 0
#endif

#if NETIMGUI_ENABLED
Expand Down
8 changes: 4 additions & 4 deletions Code/Client/Private/NetImgui_Api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ bool ConnectToApp(const char* clientName, const char* ServerHost, uint32_t serve
if (client.mpSocketPending.load() != nullptr)
{
client.ContextInitialize();
threadFunction = threadFunction == nullptr ? DefaultStartCommunicationThread : threadFunction;
threadFunction(Client::CommunicationsClient, &client);
threadFunction = threadFunction == nullptr ? DefaultStartCommunicationThread : threadFunction;
threadFunction(Client::CommunicationsConnect, &client);
}

return client.IsActive();
Expand All @@ -81,11 +81,11 @@ bool ConnectFromApp(const char* clientName, uint32_t serverPort, ThreadFunctPtr
StringCopy(client.mName, (clientName == nullptr || clientName[0] == 0 ? "Unnamed" : clientName));
client.mpSocketPending = Network::ListenStart(serverPort);
client.mFontCreationFunction = FontCreateFunction;
client.mThreadFunction = (threadFunction == nullptr) ? DefaultStartCommunicationThread : threadFunction;
if (client.mpSocketPending.load() != nullptr)
{
client.ContextInitialize();
threadFunction = threadFunction == nullptr ? DefaultStartCommunicationThread : threadFunction;
threadFunction(Client::CommunicationsHost, &client);
client.mThreadFunction(Client::CommunicationsHost, &client);
}

return client.IsActive();
Expand Down
128 changes: 80 additions & 48 deletions Code/Client/Private/NetImgui_Client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,35 +75,6 @@ static void SetClipboardTextFn_NetImguiImpl(void* user_data_ctx, const char* tex
}
}

//=================================================================================================
// COMMUNICATIONS INITIALIZE
// Initialize a new connection to a RemoteImgui server
//=================================================================================================
bool Communications_Initialize(ClientInfo& client)
{
CmdVersion cmdVersionSend, cmdVersionRcv;
StringCopy(cmdVersionSend.mClientName, client.mName);
bool bResultSend = Network::DataSend(client.mpSocketPending, &cmdVersionSend, cmdVersionSend.mHeader.mSize);
bool bResultRcv = Network::DataReceive(client.mpSocketPending, &cmdVersionRcv, sizeof(cmdVersionRcv));
bool mbConnected = bResultRcv && bResultSend &&
cmdVersionRcv.mHeader.mType == cmdVersionSend.mHeader.mType &&
cmdVersionRcv.mVersion == cmdVersionSend.mVersion &&
cmdVersionRcv.mWCharSize == cmdVersionSend.mWCharSize;
if(mbConnected)
{
for(auto& texture : client.mTextures)
{
texture.mbSent = false;
}

client.mbHasTextureUpdate = true; // Force sending the client textures
client.mBGSettingSent.mTextureId = client.mBGSetting.mTextureId-1u; // Force sending the Background settings (by making different than current settings)
client.mpSocketComs = client.mpSocketPending.exchange(nullptr);
client.mFrameIndex = 0;
}
return client.mpSocketComs.load() != nullptr;
}

//=================================================================================================
// INCOM: INPUT
// Receive new keyboard/mouse/screen resolution input to pass on to dearImgui
Expand Down Expand Up @@ -332,16 +303,66 @@ bool Communications_Outgoing(ClientInfo& client)
}

//=================================================================================================
// COMMUNICATIONS THREAD
// COMMUNICATIONS INITIALIZE
// Initialize a new connection to a RemoteImgui server
//=================================================================================================
void CommunicationsClient(void* pClientVoid)
{
bool Communications_Initialize(ClientInfo& client)
{
CmdVersion cmdVersionSend, cmdVersionRcv;
bool bResultRcv = Network::DataReceive(client.mpSocketPending, &cmdVersionRcv, sizeof(cmdVersionRcv));
bool bForceConnect = client.mServerForceConnectEnabled && (cmdVersionRcv.mFlags & static_cast<uint8_t>(CmdVersion::eFlags::ConnectForce)) != 0;
bool bCanConnect = bResultRcv &&
cmdVersionRcv.mHeader.mType == cmdVersionSend.mHeader.mType &&
cmdVersionRcv.mVersion == cmdVersionSend.mVersion &&
cmdVersionRcv.mWCharSize == cmdVersionSend.mWCharSize &&
(!client.IsConnected() || bForceConnect);

StringCopy(cmdVersionSend.mClientName, client.mName);
cmdVersionSend.mFlags = client.IsConnected() && !bCanConnect ? static_cast<uint8_t>(CmdVersion::eFlags::IsConnected): 0;
cmdVersionSend.mFlags |= client.IsConnected() && !client.mServerForceConnectEnabled ? static_cast<uint8_t>(CmdVersion::eFlags::IsUnavailable) : 0;
bool bResultSend = Network::DataSend(client.mpSocketPending, &cmdVersionSend, cmdVersionSend.mHeader.mSize);

if(bCanConnect && bResultSend)
{
Network::SocketInfo* pNewConnect = client.mpSocketPending.exchange(nullptr);
if( client.IsConnected() )
{
if( bForceConnect )
{
client.mbDisconnectRequest = true;
while( client.IsConnected() );
}
else
{
NetImgui::Internal::Network::Disconnect(pNewConnect);
return false;
}
}

for(auto& texture : client.mTextures)
{
texture.mbSent = false;
}
client.mbHasTextureUpdate = true; // Force sending the client textures
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.mServerForceConnectEnabled = (cmdVersionRcv.mFlags & static_cast<uint8_t>(CmdVersion::eFlags::ConnectExclusive)) == 0;
}
return client.mpSocketPending.load() == nullptr;
}

//=================================================================================================
// COMMUNICATIONS MAIN LOOP
//=================================================================================================
void Communications_Loop(void* pClientVoid)
{
IM_ASSERT(pClientVoid != nullptr);
ClientInfo* pClient = reinterpret_cast<ClientInfo*>(pClientVoid);
pClient->mbClientThreadActive = true;
pClient->mbDisconnectRequest = false;
Communications_Initialize(*pClient);
bool bConnected = pClient->IsConnected();

pClient->mbDisconnectRequest = false;
pClient->mbClientThreadActive = true;

while( bConnected && !pClient->mbDisconnectRequest )
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
Expand All @@ -350,11 +371,27 @@ void CommunicationsClient(void* pClientVoid)
}

pClient->KillSocketComs();
pClient->mbClientThreadActive = false;
pClient->mbClientThreadActive = false;
}

//=================================================================================================
// COMMUNICATIONS CONNECT THREAD : Reach out and connect to a NetImGuiServer
//=================================================================================================
void CommunicationsConnect(void* pClientVoid)
{
IM_ASSERT(pClientVoid != nullptr);
ClientInfo* pClient = reinterpret_cast<ClientInfo*>(pClientVoid);
pClient->mbClientThreadActive = true;
if( Communications_Initialize(*pClient) )
{
Communications_Loop(pClientVoid);
}
pClient->mbClientThreadActive = false;
}

//=================================================================================================
// COMMUNICATIONS WAIT THREAD
// COMMUNICATIONS HOST THREAD : Waiting NetImGuiServer reaching out to us.
// Launch a new com loop when connection is established
//=================================================================================================
void CommunicationsHost(void* pClientVoid)
{
Expand All @@ -366,18 +403,13 @@ void CommunicationsHost(void* pClientVoid)
while( pClient->mpSocketListen.load() != nullptr && !pClient->mbDisconnectRequest )
{
pClient->mpSocketPending = Network::ListenConnect(pClient->mpSocketListen);
if( pClient->mpSocketPending.load() != nullptr )
if( !pClient->mbDisconnectRequest &&
pClient->mpSocketPending.load() != nullptr &&
Communications_Initialize(*pClient) )
{
bool bConnected = Communications_Initialize(*pClient);
while (bConnected && !pClient->mbDisconnectRequest)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
//std::this_thread::yield();
bConnected = Communications_Outgoing(*pClient) && Communications_Incoming(*pClient);
}
pClient->KillSocketComs();
pClient->mThreadFunction(Client::Communications_Loop, pClient);
}
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Prevents this thread from taking entire core, waiting on server connection
std::this_thread::sleep_for(std::chrono::milliseconds(16)); // Prevents this thread from taking entire core, waiting on server connection
}
pClient->KillSocketListen();
pClient->mbListenThreadActive = false;
Expand Down
7 changes: 4 additions & 3 deletions Code/Client/Private/NetImgui_Client.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ struct ClientInfo
uint8_t mClientCompressionMode = eCompressionMode::kUseServerSetting;
bool mServerCompressionEnabled = false; // If Server would like compression to be enabled (mClientCompressionMode value can override this value)
bool mServerCompressionSkip = false; // Force ignore compression setting for 1 frame
char PADDING[1];
bool mServerForceConnectEnabled = true; // If another NetImguiServer can take connection away from the one currently active
ThreadFunctPtr mThreadFunction = nullptr; // Function to use when laucnhing new threads
FontCreationFuncPtr mFontCreationFunction = nullptr; // Method to call to generate the remote ImGui font. By default, re-use the local font, but this doesn't handle native DPI scaling on remote server
float mFontCreationScaling = 1.f; // Last font scaling used when generating the NetImgui font
InputState mPreviousInputState; // Keeping track of last keyboard/mouse state
Expand All @@ -136,9 +137,9 @@ struct ClientInfo
};

//=============================================================================
// Main communication thread, that should be started in its own thread
// Main communication loop threads that are run in separate threads
//=============================================================================
void CommunicationsClient(void* pClientVoid);
void CommunicationsConnect(void* pClientVoid);
void CommunicationsHost(void* pClientVoid);

}}} //namespace NetImgui::Internal::Client
Expand Down
12 changes: 10 additions & 2 deletions Code/Client/Private/NetImgui_CmdPackets.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,20 @@ struct alignas(8) CmdVersion
CustomTexture = 12, // Added a 'custom' texture format to let user potentially handle their how format
DPIScale = 13, // Server now handle monitor DPI
Clipboard = 14, // Added clipboard support between server/client
ForceReconnect = 15, // Server can now take over the connection from another server
// Insert new version here

//--------------------------------
_count,
_current = _count -1
};

enum class eFlags : uint8_t
{
IsUnavailable = 0x01, // Client telling Server it cannot be used
IsConnected = 0x02, // Client telling Server there's already a valid connection (can potentially be taken over if !IsUnavailable)
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));
eVersion mVersion = eVersion::_current;
char mClientName[64] = {};
Expand All @@ -61,7 +68,8 @@ struct alignas(8) CmdVersion
uint32_t mImguiVerID = IMGUI_VERSION_NUM;
uint32_t mNetImguiVerID = NETIMGUI_VERSION_NUM;
uint8_t mWCharSize = static_cast<uint8_t>(sizeof(ImWchar));
char PADDING[3];
uint8_t mFlags = 0;
char PADDING[2];
};

struct alignas(8) CmdInput
Expand Down
1 change: 1 addition & 0 deletions Code/Client/Private/NetImgui_Shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ class ExchangePtr
inline TType* Release();
inline void Assign(TType*& pNewData);
inline void Free();
inline bool IsNull()const { return mpData.load() != nullptr; }
private:
std::atomic<TType*> mpData;

Expand Down
2 changes: 1 addition & 1 deletion Code/Sample/SampleCompression/SampleCompression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ void CustomThreadLauncher(void ComFunctPtr(void*), void* pClient)
#endif
//=========================================================================================
// @SAMPLE_EDIT
if( ComFunctPtr == NetImgui::Internal::Client::CommunicationsClient ){
if( ComFunctPtr == NetImgui::Internal::Client::CommunicationsConnect ){
std::thread(CustomCommunicationsClient, pClient).detach();
gMetric_ConnectTo = true;
return;
Expand Down
Loading

0 comments on commit 7a44e10

Please sign in to comment.