mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-25 07:56:33 +00:00
Change remote server to have server thread & client threads
* This lets us respond to multiple clients (although all but one will get a busy signal).
This commit is contained in:
+361
-226
@@ -45,6 +45,7 @@ enum RemoteServerPacket
|
||||
{
|
||||
eRemoteServer_Noop,
|
||||
eRemoteServer_Handshake,
|
||||
eRemoteServer_Busy,
|
||||
eRemoteServer_RemoteDriverList,
|
||||
eRemoteServer_TakeOwnershipCapture,
|
||||
eRemoteServer_CopyCapture,
|
||||
@@ -87,6 +88,290 @@ static void ProgressTicker(void *d)
|
||||
}
|
||||
}
|
||||
|
||||
struct ClientThread
|
||||
{
|
||||
ClientThread()
|
||||
: socket(NULL), allowExecution(false), killThread(false), killServer(false), thread(0)
|
||||
{
|
||||
}
|
||||
|
||||
Network::Socket *socket;
|
||||
|
||||
bool allowExecution;
|
||||
bool killThread;
|
||||
bool killServer;
|
||||
|
||||
Threading::ThreadHandle thread;
|
||||
};
|
||||
|
||||
static void InactiveRemoteClientThread(void *data)
|
||||
{
|
||||
ClientThread *threadData = (ClientThread *)data;
|
||||
|
||||
uint32_t ip = threadData->socket->GetRemoteIP();
|
||||
|
||||
// this thread just handles receiving the handshake and sending a busy signal without blocking the
|
||||
// server thread
|
||||
RemoteServerPacket type = (RemoteServerPacket)RecvPacket(threadData->socket);
|
||||
|
||||
if(type != eRemoteServer_Handshake)
|
||||
{
|
||||
RDCWARN("Didn't receive proper handshake");
|
||||
SAFE_DELETE(threadData->socket);
|
||||
return;
|
||||
}
|
||||
|
||||
SendPacket(threadData->socket, eRemoteServer_Busy);
|
||||
|
||||
SAFE_DELETE(threadData->socket);
|
||||
|
||||
RDCLOG("Closed inactive connection from %u.%u.%u.%u.", Network::GetIPOctet(ip, 0),
|
||||
Network::GetIPOctet(ip, 1), Network::GetIPOctet(ip, 2), Network::GetIPOctet(ip, 3));
|
||||
}
|
||||
|
||||
static void ActiveRemoteClientThread(void *data)
|
||||
{
|
||||
ClientThread *threadData = (ClientThread *)data;
|
||||
|
||||
Network::Socket *&client = threadData->socket;
|
||||
|
||||
uint32_t ip = client->GetRemoteIP();
|
||||
|
||||
RemoteServerPacket type = (RemoteServerPacket)RecvPacket(client);
|
||||
|
||||
if(type != eRemoteServer_Handshake)
|
||||
{
|
||||
RDCWARN("Didn't receive proper handshake");
|
||||
SAFE_DELETE(client);
|
||||
return;
|
||||
}
|
||||
|
||||
SendPacket(client, eRemoteServer_Handshake);
|
||||
|
||||
vector<string> tempFiles;
|
||||
IRemoteDriver *driver = NULL;
|
||||
ProxySerialiser *proxy = NULL;
|
||||
|
||||
Serialiser sendSer("", Serialiser::WRITING, false);
|
||||
|
||||
while(client)
|
||||
{
|
||||
if(client && !client->Connected())
|
||||
break;
|
||||
|
||||
if(threadData->killThread)
|
||||
break;
|
||||
|
||||
RemoteServerPacket sendType = eRemoteServer_Noop;
|
||||
sendSer.Rewind();
|
||||
|
||||
Threading::Sleep(4);
|
||||
|
||||
if(client->IsRecvDataWaiting())
|
||||
{
|
||||
RemoteServerPacket type;
|
||||
Serialiser *recvser = NULL;
|
||||
|
||||
if(!RecvPacket(client, type, &recvser))
|
||||
break;
|
||||
|
||||
if(client == NULL)
|
||||
{
|
||||
SAFE_DELETE(recvser);
|
||||
continue;
|
||||
}
|
||||
else if(type == eRemoteServer_RemoteDriverList)
|
||||
{
|
||||
map<RDCDriver, string> drivers = RenderDoc::Inst().GetRemoteDrivers();
|
||||
|
||||
sendType = eRemoteServer_RemoteDriverList;
|
||||
|
||||
uint32_t count = (uint32_t)drivers.size();
|
||||
sendSer.Serialise("", count);
|
||||
|
||||
for(auto it = drivers.begin(); it != drivers.end(); ++it)
|
||||
{
|
||||
RDCDriver driver = it->first;
|
||||
sendSer.Serialise("", driver);
|
||||
sendSer.Serialise("", (*it).second);
|
||||
}
|
||||
}
|
||||
else if(type == eRemoteServer_CopyCapture)
|
||||
{
|
||||
string cap_file;
|
||||
string dummy, dummy2;
|
||||
FileIO::GetDefaultFiles("remotecopy", cap_file, dummy, dummy2);
|
||||
|
||||
Serialiser *fileRecv = NULL;
|
||||
|
||||
RDCLOG("Copying file to local path '%s'.", cap_file.c_str());
|
||||
|
||||
if(!RecvChunkedFile(client, type, cap_file.c_str(), fileRecv, NULL))
|
||||
{
|
||||
FileIO::Delete(cap_file.c_str());
|
||||
|
||||
RDCERR("Network error receiving file");
|
||||
|
||||
SAFE_DELETE(fileRecv);
|
||||
break;
|
||||
}
|
||||
|
||||
RDCLOG("File received.");
|
||||
|
||||
tempFiles.push_back(cap_file);
|
||||
|
||||
SAFE_DELETE(fileRecv);
|
||||
|
||||
sendType = eRemoteServer_CopyCapture;
|
||||
sendSer.Serialise("path", cap_file);
|
||||
}
|
||||
else if(type == eRemoteServer_TakeOwnershipCapture)
|
||||
{
|
||||
string cap_file;
|
||||
recvser->Serialise("filename", cap_file);
|
||||
|
||||
RDCLOG("Taking ownership of '%s'.", cap_file.c_str());
|
||||
|
||||
tempFiles.push_back(cap_file);
|
||||
}
|
||||
else if(type == eRemoteServer_ShutdownServer)
|
||||
{
|
||||
RDCLOG("Requested to shut down.");
|
||||
|
||||
threadData->killServer = true;
|
||||
threadData->killThread = true;
|
||||
|
||||
sendType = eRemoteServer_ShutdownServer;
|
||||
}
|
||||
else if(type == eRemoteServer_OpenLog)
|
||||
{
|
||||
string cap_file;
|
||||
recvser->Serialise("filename", cap_file);
|
||||
|
||||
RDCASSERT(driver == NULL && proxy == NULL);
|
||||
|
||||
RDCDriver driverType = RDC_Unknown;
|
||||
string driverName = "";
|
||||
ReplayCreateStatus status =
|
||||
RenderDoc::Inst().FillInitParams(cap_file.c_str(), driverType, driverName, NULL);
|
||||
|
||||
if(status != eReplayCreate_Success)
|
||||
{
|
||||
RDCERR("Failed to open %s", cap_file.c_str());
|
||||
}
|
||||
else if(RenderDoc::Inst().HasRemoteDriver(driverType))
|
||||
{
|
||||
ProgressLoopData data;
|
||||
|
||||
data.sock = client;
|
||||
data.killsignal = false;
|
||||
data.progress = 0.0f;
|
||||
|
||||
RenderDoc::Inst().SetProgressPtr(&data.progress);
|
||||
|
||||
Threading::ThreadHandle ticker = Threading::CreateThread(ProgressTicker, &data);
|
||||
|
||||
status = RenderDoc::Inst().CreateRemoteDriver(driverType, cap_file.c_str(), &driver);
|
||||
|
||||
if(status != eReplayCreate_Success || driver == NULL)
|
||||
{
|
||||
RDCERR("Failed to create remote driver for driver type %d name %s", driverType,
|
||||
driverName.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
driver->ReadLogInitialisation();
|
||||
|
||||
RenderDoc::Inst().SetProgressPtr(NULL);
|
||||
|
||||
data.killsignal = true;
|
||||
Threading::JoinThread(ticker);
|
||||
Threading::CloseThread(ticker);
|
||||
|
||||
proxy = new ProxySerialiser(client, driver);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("File needs driver for %s which isn't supported!", driverName.c_str());
|
||||
|
||||
status = eReplayCreate_APIUnsupported;
|
||||
}
|
||||
|
||||
sendType = eRemoteServer_LogOpened;
|
||||
sendSer.Serialise("status", status);
|
||||
}
|
||||
else if(type == eRemoteServer_CloseLog)
|
||||
{
|
||||
if(driver)
|
||||
driver->Shutdown();
|
||||
driver = NULL;
|
||||
|
||||
SAFE_DELETE(proxy);
|
||||
}
|
||||
else if(type == eRemoteServer_ExecuteAndInject)
|
||||
{
|
||||
string app, workingDir, cmdLine, logfile;
|
||||
CaptureOptions opts;
|
||||
recvser->Serialise("app", app);
|
||||
recvser->Serialise("workingDir", workingDir);
|
||||
recvser->Serialise("cmdLine", cmdLine);
|
||||
recvser->Serialise("opts", opts);
|
||||
|
||||
uint32_t ident = eReplayCreate_NetworkIOFailed;
|
||||
|
||||
if(threadData->allowExecution)
|
||||
{
|
||||
ident = Process::LaunchAndInjectIntoProcess(app.c_str(), workingDir.c_str(),
|
||||
cmdLine.c_str(), "", &opts, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCWARN("Requested to execute program - disallowing based on configuration");
|
||||
}
|
||||
|
||||
sendType = eRemoteServer_ExecuteAndInject;
|
||||
sendSer.Serialise("ident", ident);
|
||||
}
|
||||
else if((int)type >= eReplayProxy_First && proxy)
|
||||
{
|
||||
bool ok = proxy->Tick(type, recvser);
|
||||
|
||||
if(!ok)
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
SAFE_DELETE(recvser);
|
||||
|
||||
if(type != eRemoteServer_Noop && !SendPacket(client, sendType, sendSer))
|
||||
{
|
||||
RDCERR("Network error sending supported driver list");
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(driver)
|
||||
driver->Shutdown();
|
||||
SAFE_DELETE(proxy);
|
||||
|
||||
for(size_t i = 0; i < tempFiles.size(); i++)
|
||||
{
|
||||
FileIO::Delete(tempFiles[i].c_str());
|
||||
}
|
||||
|
||||
RDCLOG("Closing active connection from %u.%u.%u.%u.", Network::GetIPOctet(ip, 0),
|
||||
Network::GetIPOctet(ip, 1), Network::GetIPOctet(ip, 2), Network::GetIPOctet(ip, 3));
|
||||
|
||||
RDCLOG("Ready for new active connection...");
|
||||
|
||||
SAFE_DELETE(client);
|
||||
}
|
||||
|
||||
void RenderDoc::BecomeRemoteServer(const char *listenhost, uint16_t port, volatile bool32 &killReplay)
|
||||
{
|
||||
Network::Socket *sock = Network::CreateServerSocket(listenhost, port, 1);
|
||||
@@ -173,12 +458,40 @@ void RenderDoc::BecomeRemoteServer(const char *listenhost, uint16_t port, volati
|
||||
|
||||
RDCLOG("Replay host ready for requests...");
|
||||
|
||||
bool shutdownServer = false;
|
||||
ClientThread *activeClientData = NULL;
|
||||
|
||||
while(!killReplay && !shutdownServer)
|
||||
std::vector<ClientThread *> inactives;
|
||||
|
||||
while(!killReplay)
|
||||
{
|
||||
Network::Socket *client = sock->AcceptClient(false);
|
||||
|
||||
if(activeClientData && activeClientData->killServer)
|
||||
break;
|
||||
|
||||
// reap any dead inactive threads
|
||||
for(size_t i = 0; i < inactives.size(); i++)
|
||||
{
|
||||
if(inactives[i]->socket == NULL)
|
||||
{
|
||||
Threading::JoinThread(inactives[i]->thread);
|
||||
Threading::CloseThread(inactives[i]->thread);
|
||||
delete inactives[i];
|
||||
inactives.erase(inactives.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// reap our active connection possibly
|
||||
if(activeClientData && activeClientData->socket == NULL)
|
||||
{
|
||||
Threading::JoinThread(activeClientData->thread);
|
||||
Threading::CloseThread(activeClientData->thread);
|
||||
|
||||
delete activeClientData;
|
||||
activeClientData = NULL;
|
||||
}
|
||||
|
||||
if(client == NULL)
|
||||
{
|
||||
if(!sock->Connected())
|
||||
@@ -220,229 +533,44 @@ void RenderDoc::BecomeRemoteServer(const char *listenhost, uint16_t port, volati
|
||||
continue;
|
||||
}
|
||||
|
||||
RemoteServerPacket type = (RemoteServerPacket)RecvPacket(client);
|
||||
|
||||
if(type != eRemoteServer_Handshake)
|
||||
if(activeClientData == NULL)
|
||||
{
|
||||
RDCWARN("Didn't receive proper handshake");
|
||||
SAFE_DELETE(client);
|
||||
continue;
|
||||
activeClientData = new ClientThread();
|
||||
activeClientData->socket = client;
|
||||
activeClientData->allowExecution = allowExecution;
|
||||
|
||||
activeClientData->thread = Threading::CreateThread(ActiveRemoteClientThread, activeClientData);
|
||||
|
||||
RDCLOG("Making active connection");
|
||||
}
|
||||
|
||||
SendPacket(client, eRemoteServer_Handshake);
|
||||
|
||||
vector<string> tempFiles;
|
||||
IRemoteDriver *driver = NULL;
|
||||
ProxySerialiser *proxy = NULL;
|
||||
|
||||
Serialiser sendSer("", Serialiser::WRITING, false);
|
||||
|
||||
while(client)
|
||||
else
|
||||
{
|
||||
if(client && !client->Connected())
|
||||
break;
|
||||
ClientThread *inactive = new ClientThread();
|
||||
inactive->socket = client;
|
||||
inactive->allowExecution = false;
|
||||
|
||||
if(killReplay)
|
||||
break;
|
||||
inactive->thread = Threading::CreateThread(InactiveRemoteClientThread, inactive);
|
||||
|
||||
RemoteServerPacket sendType = eRemoteServer_Noop;
|
||||
sendSer.Rewind();
|
||||
inactives.push_back(inactive);
|
||||
|
||||
Threading::Sleep(4);
|
||||
|
||||
if(client->IsRecvDataWaiting())
|
||||
{
|
||||
RemoteServerPacket type;
|
||||
Serialiser *recvser = NULL;
|
||||
|
||||
if(!RecvPacket(client, type, &recvser))
|
||||
break;
|
||||
|
||||
if(client == NULL)
|
||||
{
|
||||
SAFE_DELETE(recvser);
|
||||
continue;
|
||||
}
|
||||
else if(type == eRemoteServer_RemoteDriverList)
|
||||
{
|
||||
map<RDCDriver, string> drivers = RenderDoc::Inst().GetRemoteDrivers();
|
||||
|
||||
sendType = eRemoteServer_RemoteDriverList;
|
||||
|
||||
uint32_t count = (uint32_t)drivers.size();
|
||||
sendSer.Serialise("", count);
|
||||
|
||||
for(auto it = drivers.begin(); it != drivers.end(); ++it)
|
||||
{
|
||||
RDCDriver driver = it->first;
|
||||
sendSer.Serialise("", driver);
|
||||
sendSer.Serialise("", (*it).second);
|
||||
}
|
||||
}
|
||||
else if(type == eRemoteServer_CopyCapture)
|
||||
{
|
||||
string cap_file;
|
||||
string dummy, dummy2;
|
||||
FileIO::GetDefaultFiles("remotecopy", cap_file, dummy, dummy2);
|
||||
|
||||
Serialiser *fileRecv = NULL;
|
||||
|
||||
RDCLOG("Copying file to local path '%s'.", cap_file.c_str());
|
||||
|
||||
if(!RecvChunkedFile(client, type, cap_file.c_str(), fileRecv, NULL))
|
||||
{
|
||||
FileIO::Delete(cap_file.c_str());
|
||||
|
||||
RDCERR("Network error receiving file");
|
||||
|
||||
SAFE_DELETE(fileRecv);
|
||||
break;
|
||||
}
|
||||
|
||||
RDCLOG("File received.");
|
||||
|
||||
tempFiles.push_back(cap_file);
|
||||
|
||||
SAFE_DELETE(fileRecv);
|
||||
|
||||
sendType = eRemoteServer_CopyCapture;
|
||||
sendSer.Serialise("path", cap_file);
|
||||
}
|
||||
else if(type == eRemoteServer_TakeOwnershipCapture)
|
||||
{
|
||||
string cap_file;
|
||||
recvser->Serialise("filename", cap_file);
|
||||
|
||||
RDCLOG("Taking ownership of '%s'.", cap_file.c_str());
|
||||
|
||||
tempFiles.push_back(cap_file);
|
||||
}
|
||||
else if(type == eRemoteServer_ShutdownServer)
|
||||
{
|
||||
RDCLOG("Requested to shut down.");
|
||||
|
||||
shutdownServer = true;
|
||||
|
||||
sendType = eRemoteServer_ShutdownServer;
|
||||
}
|
||||
else if(type == eRemoteServer_OpenLog)
|
||||
{
|
||||
string cap_file;
|
||||
recvser->Serialise("filename", cap_file);
|
||||
|
||||
RDCASSERT(driver == NULL && proxy == NULL);
|
||||
|
||||
RDCDriver driverType = RDC_Unknown;
|
||||
string driverName = "";
|
||||
ReplayCreateStatus status =
|
||||
RenderDoc::Inst().FillInitParams(cap_file.c_str(), driverType, driverName, NULL);
|
||||
|
||||
if(status != eReplayCreate_Success)
|
||||
{
|
||||
RDCERR("Failed to open %s", cap_file.c_str());
|
||||
}
|
||||
else if(RenderDoc::Inst().HasRemoteDriver(driverType))
|
||||
{
|
||||
ProgressLoopData data;
|
||||
|
||||
data.sock = client;
|
||||
data.killsignal = false;
|
||||
data.progress = 0.0f;
|
||||
|
||||
RenderDoc::Inst().SetProgressPtr(&data.progress);
|
||||
|
||||
Threading::ThreadHandle ticker = Threading::CreateThread(ProgressTicker, &data);
|
||||
|
||||
status = RenderDoc::Inst().CreateRemoteDriver(driverType, cap_file.c_str(), &driver);
|
||||
|
||||
if(status != eReplayCreate_Success || driver == NULL)
|
||||
{
|
||||
RDCERR("Failed to create remote driver for driver type %d name %s", driverType,
|
||||
driverName.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
driver->ReadLogInitialisation();
|
||||
|
||||
RenderDoc::Inst().SetProgressPtr(NULL);
|
||||
|
||||
data.killsignal = true;
|
||||
Threading::JoinThread(ticker);
|
||||
Threading::CloseThread(ticker);
|
||||
|
||||
proxy = new ProxySerialiser(client, driver);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCERR("File needs driver for %s which isn't supported!", driverName.c_str());
|
||||
|
||||
status = eReplayCreate_APIUnsupported;
|
||||
}
|
||||
|
||||
sendType = eRemoteServer_LogOpened;
|
||||
sendSer.Serialise("status", status);
|
||||
}
|
||||
else if(type == eRemoteServer_CloseLog)
|
||||
{
|
||||
if(driver)
|
||||
driver->Shutdown();
|
||||
driver = NULL;
|
||||
|
||||
SAFE_DELETE(proxy);
|
||||
}
|
||||
else if(type == eRemoteServer_ExecuteAndInject)
|
||||
{
|
||||
string app, workingDir, cmdLine, logfile;
|
||||
CaptureOptions opts;
|
||||
recvser->Serialise("app", app);
|
||||
recvser->Serialise("workingDir", workingDir);
|
||||
recvser->Serialise("cmdLine", cmdLine);
|
||||
recvser->Serialise("opts", opts);
|
||||
|
||||
uint32_t ident = Process::LaunchAndInjectIntoProcess(app.c_str(), workingDir.c_str(),
|
||||
cmdLine.c_str(), "", &opts, false);
|
||||
|
||||
sendType = eRemoteServer_ExecuteAndInject;
|
||||
sendSer.Serialise("ident", ident);
|
||||
}
|
||||
else if((int)type >= eReplayProxy_First && proxy)
|
||||
{
|
||||
bool ok = proxy->Tick(type, recvser);
|
||||
|
||||
if(!ok)
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
SAFE_DELETE(recvser);
|
||||
|
||||
if(type != eRemoteServer_Noop && !SendPacket(client, sendType, sendSer))
|
||||
{
|
||||
RDCERR("Network error sending supported driver list");
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
RDCLOG("Refusing inactive connection");
|
||||
}
|
||||
}
|
||||
|
||||
SAFE_DELETE(client);
|
||||
if(activeClientData && activeClientData->socket == NULL)
|
||||
{
|
||||
Threading::JoinThread(activeClientData->thread);
|
||||
Threading::CloseThread(activeClientData->thread);
|
||||
|
||||
if(driver)
|
||||
driver->Shutdown();
|
||||
SAFE_DELETE(proxy);
|
||||
delete activeClientData;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < tempFiles.size(); i++)
|
||||
{
|
||||
FileIO::Delete(tempFiles[i].c_str());
|
||||
}
|
||||
|
||||
RDCLOG("Closing replay connection from %u.%u.%u.%u.", Network::GetIPOctet(ip, 0),
|
||||
Network::GetIPOctet(ip, 1), Network::GetIPOctet(ip, 2), Network::GetIPOctet(ip, 3));
|
||||
|
||||
RDCLOG("Ready for new connection...");
|
||||
// shut down client threads
|
||||
for(size_t i = 0; i < inactives.size(); i++)
|
||||
{
|
||||
Threading::JoinThread(inactives[i]->thread);
|
||||
Threading::CloseThread(inactives[i]->thread);
|
||||
delete inactives[i];
|
||||
}
|
||||
|
||||
SAFE_DELETE(sock);
|
||||
@@ -458,17 +586,6 @@ public:
|
||||
m_Proxies.reserve(m.size());
|
||||
for(auto it = m.begin(); it != m.end(); ++it)
|
||||
m_Proxies.push_back(*it);
|
||||
|
||||
Serialiser sendData("", Serialiser::WRITING, false);
|
||||
SendPacket(m_Socket, eRemoteServer_Handshake);
|
||||
|
||||
RemoteServerPacket type = (RemoteServerPacket)RecvPacket(m_Socket);
|
||||
|
||||
if(type != eRemoteServer_Handshake)
|
||||
{
|
||||
RDCWARN("Didn't get proper handshake");
|
||||
SAFE_DELETE(m_Socket);
|
||||
}
|
||||
}
|
||||
virtual ~RemoteServer() { SAFE_DELETE(m_Socket); }
|
||||
void ShutdownConnection() { delete this; }
|
||||
@@ -804,6 +921,24 @@ RENDERDOC_CreateRemoteServerConnection(const char *host, uint32_t port, RemoteSe
|
||||
return eReplayCreate_NetworkIOFailed;
|
||||
}
|
||||
|
||||
Serialiser sendData("", Serialiser::WRITING, false);
|
||||
SendPacket(sock, eRemoteServer_Handshake);
|
||||
|
||||
RemoteServerPacket type = (RemoteServerPacket)RecvPacket(sock);
|
||||
|
||||
if(type == eRemoteServer_Busy)
|
||||
{
|
||||
SAFE_DELETE(sock);
|
||||
return eReplayCreate_NetworkRemoteBusy;
|
||||
}
|
||||
|
||||
if(type != eRemoteServer_Handshake)
|
||||
{
|
||||
RDCWARN("Didn't get proper handshake");
|
||||
SAFE_DELETE(sock);
|
||||
return eReplayCreate_NetworkIOFailed;
|
||||
}
|
||||
|
||||
*rend = new RemoteServer(sock);
|
||||
|
||||
return eReplayCreate_Success;
|
||||
|
||||
Reference in New Issue
Block a user