iw4x-client/src/Components/Modules/IPCPipe.cpp

244 lines
6.0 KiB
C++
Raw Normal View History

2022-02-27 07:53:44 -05:00
#include <STDInclude.hpp>
2017-02-12 13:38:56 -05:00
namespace Components
{
Pipe IPCPipe::ServerPipe;
Pipe IPCPipe::ClientPipe;
#pragma region Pipe
Pipe::Pipe() : connectCallback(0), pipe(INVALID_HANDLE_VALUE), threadAttached(false), type(IPCTYPE_NONE), reconnectAttempt(0)
{
this->destroy();
}
Pipe::~Pipe()
{
this->destroy();
}
2018-12-17 08:29:18 -05:00
bool Pipe::connect(const std::string& name)
2017-02-12 13:38:56 -05:00
{
this->destroy();
this->type = IPCTYPE_CLIENT;
this->setName(name);
this->pipe = CreateFileA(this->pipeFile, GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
if (INVALID_HANDLE_VALUE == this->pipe)
{
Logger::Print("Failed to connect to the pipe\n");
if (this->reconnectAttempt < IPC_MAX_RECONNECTS)
{
Logger::Print("Attempting to reconnect to the pipe.\n");
++this->reconnectAttempt;
std::this_thread::sleep_for(500ms);
return this->connect(name);
}
else
{
this->destroy();
return false;
}
}
this->reconnectAttempt = 0;
Logger::Print("Successfully connected to the pipe\n");
return true;
}
2018-12-17 08:29:18 -05:00
bool Pipe::create(const std::string& name)
2017-02-12 13:38:56 -05:00
{
this->destroy();
this->type = IPCTYPE_SERVER;
this->setName(name);
this->pipe = CreateNamedPipeA(this->pipeFile, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof(this->packet), sizeof(this->packet), NMPWAIT_USE_DEFAULT_WAIT, nullptr);
if (INVALID_HANDLE_VALUE != this->pipe && this->pipe)
{
// Only create the thread, when not performing unit tests!
2017-07-03 09:40:32 -04:00
if (!Loader::IsPerformingUnitTests())
2017-02-12 13:38:56 -05:00
{
this->threadAttached = true;
this->thread = std::thread(Pipe::ReceiveThread, this);
}
Logger::Print("Pipe successfully created\n");
return true;
}
Logger::Print("Failed to create the pipe\n");
this->destroy();
return false;
}
void Pipe::onConnect(Pipe::Callback callback)
{
this->connectCallback = callback;
}
2018-12-17 08:29:18 -05:00
void Pipe::setCallback(const std::string& command, Utils::Slot<Pipe::PacketCallback> callback)
2017-02-12 13:38:56 -05:00
{
this->packetCallbacks[command] = callback;
}
2018-12-17 08:29:18 -05:00
bool Pipe::write(const std::string& command, const std::string& data)
2017-02-12 13:38:56 -05:00
{
if (this->type != IPCTYPE_CLIENT || this->pipe == INVALID_HANDLE_VALUE) return false;
Pipe::Packet _packet;
strcpy_s(_packet.command, command.data());
strcpy_s(_packet.buffer, data.data());
DWORD cbBytes;
return (WriteFile(this->pipe, &_packet, sizeof(_packet), &cbBytes, nullptr) || GetLastError() == ERROR_IO_PENDING);
}
void Pipe::destroy()
{
//this->Type = IPCTYPE_NONE;
//*this->PipeFile = 0;
//*this->PipeName = 0;
if (this->pipe && INVALID_HANDLE_VALUE != this->pipe)
{
CancelIoEx(this->pipe, nullptr);
//DeleteFileA(this->pipeFile);
if (this->type == IPCTYPE_SERVER) DisconnectNamedPipe(this->pipe);
CloseHandle(this->pipe);
Logger::Print("Disconnected from the pipe.\n");
}
this->pipe = nullptr;
this->threadAttached = false;
if (this->thread.joinable())
{
Logger::Print("Terminating pipe thread...\n");
this->thread.join();
Logger::Print("Pipe thread terminated.\n");
}
}
2018-12-17 08:29:18 -05:00
void Pipe::setName(const std::string& name)
2017-02-12 13:38:56 -05:00
{
memset(this->pipeName, 0, sizeof(this->pipeName));
memset(this->pipeFile, 0, sizeof(this->pipeFile));
strncpy_s(this->pipeName, name.data(), sizeof(this->pipeName));
sprintf_s(this->pipeFile, sizeof(this->pipeFile), "\\\\.\\Pipe\\%s", this->pipeName);
}
void Pipe::ReceiveThread(Pipe* pipe)
{
if (!pipe || pipe->type != IPCTYPE_SERVER || pipe->pipe == INVALID_HANDLE_VALUE || !pipe->pipe) return;
if (ConnectNamedPipe(pipe->pipe, nullptr) == FALSE)
{
Logger::Print("Failed to initialize pipe reading.\n");
return;
}
Logger::Print("Client connected to the pipe\n");
pipe->connectCallback();
DWORD cbBytes;
while (pipe->threadAttached && pipe->pipe && pipe->pipe != INVALID_HANDLE_VALUE)
{
BOOL bResult = ReadFile(pipe->pipe, &pipe->packet, sizeof(pipe->packet), &cbBytes, nullptr);
if (bResult && cbBytes)
{
2022-05-31 12:38:09 -04:00
if (pipe->packetCallbacks.contains(pipe->packet.command))
2017-02-12 13:38:56 -05:00
{
pipe->packetCallbacks[pipe->packet.command](pipe->packet.buffer);
}
}
else if (pipe->threadAttached && pipe->pipe != INVALID_HANDLE_VALUE)
{
Logger::Print("Failed to read from client through pipe\n");
DisconnectNamedPipe(pipe->pipe);
ConnectNamedPipe(pipe->pipe, nullptr);
pipe->connectCallback();
}
ZeroMemory(&pipe->packet, sizeof(pipe->packet));
}
}
#pragma endregion
// Callback to connect first instance's client pipe to the second instance's server pipe
void IPCPipe::ConnectClient()
{
if (Singleton::IsFirstInstance())
{
IPCPipe::ClientPipe.connect(IPC_PIPE_NAME_CLIENT);
}
}
// Writes to the process on the other end of the pipe
2018-12-17 08:29:18 -05:00
bool IPCPipe::Write(const std::string& command, const std::string& data)
2017-02-12 13:38:56 -05:00
{
return IPCPipe::ClientPipe.write(command, data);
}
// Installs a callback for receiving commands from the process on the other end of the pipe
2018-12-17 08:29:18 -05:00
void IPCPipe::On(const std::string& command, Utils::Slot<Pipe::PacketCallback> callback)
2017-02-12 13:38:56 -05:00
{
IPCPipe::ServerPipe.setCallback(command, callback);
}
IPCPipe::IPCPipe()
{
2022-10-15 16:31:16 -04:00
if (Dedicated::IsEnabled() || Loader::IsPerformingUnitTests() || ZoneBuilder::IsEnabled()) return;
2017-02-12 13:38:56 -05:00
// Server pipe
IPCPipe::ServerPipe.onConnect(IPCPipe::ConnectClient);
IPCPipe::ServerPipe.create((Singleton::IsFirstInstance() ? IPC_PIPE_NAME_SERVER : IPC_PIPE_NAME_CLIENT));
// Connect second instance's client pipe to first instance's server pipe
if (!Singleton::IsFirstInstance())
{
IPCPipe::ClientPipe.connect(IPC_PIPE_NAME_SERVER);
}
2018-12-17 08:29:18 -05:00
IPCPipe::On("ping", [](const std::string& data)
2017-02-12 13:38:56 -05:00
{
Logger::Print("Received ping form pipe, sending pong!\n");
IPCPipe::Write("pong", data);
});
2018-12-17 08:29:18 -05:00
IPCPipe::On("pong", [](const std::string& /*data*/)
2017-02-12 13:38:56 -05:00
{
Logger::Print("Received pong form pipe!\n");
});
// Test pipe functionality by sending pings
Command::Add("ipcping", [](Command::Params*)
2017-02-12 13:38:56 -05:00
{
Logger::Print("Sending ping to pipe!\n");
IPCPipe::Write("ping", "");
});
}
void IPCPipe::preDestroy()
{
IPCPipe::ServerPipe.destroy();
IPCPipe::ClientPipe.destroy();
}
}