IPCPipe implemented for inter-process-communication.

This commit is contained in:
momo5502 2015-12-31 13:37:25 +01:00
parent 75db36aba9
commit fb8aa17ab1
10 changed files with 396 additions and 2 deletions

View File

@ -7,6 +7,7 @@ namespace Components
void Loader::Initialize()
{
Loader::Register(new Dedicated());
Loader::Register(new Singleton());
Loader::Register(new Dvar());
Loader::Register(new Maps());
@ -18,6 +19,7 @@ namespace Components
Loader::Register(new Window());
Loader::Register(new Command());
Loader::Register(new Console());
Loader::Register(new IPCPipe());
Loader::Register(new Network());
Loader::Register(new RawFiles());
Loader::Register(new Renderer());
@ -25,7 +27,6 @@ namespace Components
Loader::Register(new UIScript());
Loader::Register(new FastFiles());
Loader::Register(new Materials());
Loader::Register(new Singleton());
Loader::Register(new FileSystem());
Loader::Register(new QuickPatch());
Loader::Register(new ServerList());

View File

@ -29,6 +29,7 @@ namespace Components
#include "Modules\Window.hpp"
#include "Modules\Command.hpp"
#include "Modules\Console.hpp"
#include "Modules\IPCPipe.hpp"
#include "Modules\Network.hpp"
#include "Modules\Party.hpp" // Destroys the order, but requires network classes :D
#include "Modules\RawFiles.hpp"

View File

@ -4,6 +4,8 @@ namespace Components
{
public:
ConnectProtocol();
const char* GetName() { return "ConnectProtocol"; };
void EvaluateProtocol();
static BOOL InvokeConnect();

View File

@ -0,0 +1,251 @@
#include "..\..\STDInclude.hpp"
namespace Components
{
Pipe* IPCPipe::ServerPipe = 0;
Pipe* IPCPipe::ClientPipe = 0;
#pragma region Pipe
Pipe::Pipe() : mType(IPCTYPE_NONE), ReconnectAttempt(0), hPipe(INVALID_HANDLE_VALUE), mThread(0), ConnectCallback(0), mThreadAttached(false)
{
this->Destroy();
}
Pipe::~Pipe()
{
this->Destroy();
}
bool Pipe::Connect(std::string name)
{
this->Destroy();
this->mType = IPCTYPE_CLIENT;
this->SetName(name);
this->hPipe = CreateFile(this->PipeFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (INVALID_HANDLE_VALUE == this->hPipe)
{
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++;
Sleep(500);
return this->Connect(name);
}
else
{
this->Destroy();
return false;
}
}
this->ReconnectAttempt = 0;
Logger::Print("Successfully connected to the pipe\n");
return true;
}
bool Pipe::Create(std::string name)
{
this->Destroy();
this->mType = IPCTYPE_SERVER;
this->SetName(name);
this->hPipe = CreateNamedPipe(this->PipeFile, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof(this->mPacket), sizeof(this->mPacket), NMPWAIT_USE_DEFAULT_WAIT, NULL);
if (INVALID_HANDLE_VALUE != this->hPipe)
{
this->mThreadAttached = true;
this->mThread = new std::thread(this->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;
}
void Pipe::SetCallback(std::string command, Pipe::PacketCallback callback)
{
this->PacketCallbacks[command] = callback;
}
bool Pipe::Write(std::string command, std::string data)
{
if (this->mType != IPCTYPE_CLIENT || this->hPipe == 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->hPipe, &packet, sizeof(packet), &cbBytes, NULL) || GetLastError() == ERROR_IO_PENDING);
}
void Pipe::Destroy()
{
//this->Type = IPCTYPE_NONE;
//*this->PipeFile = 0;
//*this->PipeName = 0;
if (INVALID_HANDLE_VALUE != this->hPipe)
{
if (this->mType == IPCTYPE_SERVER) DisconnectNamedPipe(this->hPipe);
CloseHandle(this->hPipe);
Logger::Print("Disconnected from the pipe.\n");
}
this->mThreadAttached = false;
if (this->mThread)
{
Logger::Print("Terminating pipe thread...\n");
this->mThread->join();
Logger::Print("Pipe thread terminated.\n");
delete this->mThread;
this->mThread = 0;
}
}
void Pipe::SetName(std::string name)
{
memset(this->PipeName, 0, sizeof(this->PipeName));
memset(this->PipeFile, 0, sizeof(this->PipeFile));
strncpy_s(this->PipeName, name.c_str(), sizeof(this->PipeName));
sprintf_s(this->PipeFile, sizeof(this->PipeFile), "\\\\.\\Pipe\\%s", this->PipeName);
}
void Pipe::ReceiveThread(Pipe* pipe)
{
if (!pipe || pipe->mType != IPCTYPE_SERVER || pipe->hPipe == INVALID_HANDLE_VALUE) return;
if (ConnectNamedPipe(pipe->hPipe, NULL) == FALSE)
{
Logger::Print("Failed to initialize pipe reading.\n");
return;
}
Logger::Print("Client connected to the pipe\n");
if (pipe->ConnectCallback) pipe->ConnectCallback();
DWORD cbBytes;
while (pipe->mThreadAttached && pipe->hPipe != INVALID_HANDLE_VALUE)
{
BOOL bResult = ReadFile(pipe->hPipe, &pipe->mPacket, sizeof(pipe->mPacket), &cbBytes, NULL);
if (bResult && cbBytes)
{
if (pipe->PacketCallbacks.find(pipe->mPacket.Command) != pipe->PacketCallbacks.end())
{
pipe->PacketCallbacks[pipe->mPacket.Command](pipe->mPacket.Buffer);
}
}
else if (pipe->mThreadAttached && pipe->hPipe != INVALID_HANDLE_VALUE)
{
Logger::Print("Failed to read from client through pipe\n");
DisconnectNamedPipe(pipe->hPipe);
ConnectNamedPipe(pipe->hPipe, NULL);
if (pipe->ConnectCallback) pipe->ConnectCallback();
}
ZeroMemory(&pipe->mPacket, sizeof(pipe->mPacket));
}
}
#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)
{
IPCPipe::ClientPipe->Connect(IPC_PIPE_NAME_CLIENT);
}
}
// Writes to the process on the other end of the pipe
bool IPCPipe::Write(std::string command, std::string data)
{
if (IPCPipe::ClientPipe)
{
return IPCPipe::ClientPipe->Write(command, data);
}
return false;
}
// Installs a callback for receiving commands from the process on the other end of the pipe
void IPCPipe::On(std::string command, Pipe::PacketCallback callback)
{
if (IPCPipe::ServerPipe)
{
IPCPipe::ServerPipe->SetCallback(command, callback);
}
}
IPCPipe::IPCPipe()
{
// Server pipe
IPCPipe::ServerPipe = new Pipe();
IPCPipe::ServerPipe->OnConnect(IPCPipe::ConnectClient);
IPCPipe::ServerPipe->Create((Singleton::IsFirstInstance() ? IPC_PIPE_NAME_SERVER : IPC_PIPE_NAME_CLIENT));
// Client pipe
IPCPipe::ClientPipe = new Pipe();
// Connect second instance's client pipe to first instance's server pipe
if (!Singleton::IsFirstInstance())
{
IPCPipe::ClientPipe->Connect(IPC_PIPE_NAME_SERVER);
}
IPCPipe::On("ping", [] (std::string data)
{
Logger::Print("Received ping form pipe, sending pong!\n");
IPCPipe::Write("pong", data);
});
IPCPipe::On("pong", [] (std::string data)
{
Logger::Print("Received pong form pipe!\n");
});
// Test pipe functionality by sending pings
Command::Add("ipcping", [] (Command::Params params)
{
Logger::Print("Sending ping to pipe!\n");
IPCPipe::Write("ping", "");
});
}
IPCPipe::~IPCPipe()
{
if (IPCPipe::ServerPipe) delete IPCPipe::ServerPipe;
if (IPCPipe::ClientPipe) delete IPCPipe::ClientPipe;
IPCPipe::ServerPipe = 0;
IPCPipe::ClientPipe = 0;
}
}

View File

@ -0,0 +1,76 @@
#define IPC_MAX_RECONNECTS 3
#define IPC_COMMAND_SIZE 100
#define IPC_BUFFER_SIZE 0x2000
#define IPC_PIPE_NAME_SERVER "IW4x-Server"
#define IPC_PIPE_NAME_CLIENT "IW4x-Client"
namespace Components
{
class Pipe
{
public:
struct Packet
{
char Command[IPC_COMMAND_SIZE];
char Buffer[IPC_BUFFER_SIZE];
};
enum Type
{
IPCTYPE_NONE,
IPCTYPE_SERVER,
IPCTYPE_CLIENT
};
typedef void(__cdecl* PacketCallback)(std::string data);
typedef void(__cdecl* Callback)();
Pipe();
~Pipe();
bool Connect(std::string name);
bool Create(std::string name);
bool Write(std::string command, std::string data);
void SetCallback(std::string command, PacketCallback callback);
void OnConnect(Callback callback);
private:
Callback ConnectCallback;
std::map<std::string, PacketCallback> PacketCallbacks;
HANDLE hPipe;
std::thread* mThread;
bool mThreadAttached;
Type mType;
Packet mPacket;
char PipeName[MAX_PATH];
char PipeFile[MAX_PATH];
unsigned int ReconnectAttempt;
void Destroy();
void SetName(std::string name);
static void ReceiveThread(Pipe* pipe);
};
class IPCPipe : public Component
{
public:
IPCPipe();
~IPCPipe();
const char* GetName() { return "IPCPipe"; };
static bool Write(std::string command, std::string data);
static void On(std::string command, Pipe::PacketCallback callback);
private:
static Pipe* ServerPipe;
static Pipe* ClientPipe;
static void ConnectClient();
};
}

View File

@ -2,6 +2,9 @@
namespace Components
{
std::mutex Logger::MessageMutex;
std::vector<std::string> Logger::MessageQueue;
bool Logger::IsConsoleReady()
{
return (IsWindow(*(HWND*)0x64A3288) != FALSE);
@ -18,7 +21,14 @@ namespace Components
if (Logger::IsConsoleReady())
{
Game::Com_Printf(0, "%s", buffer);
if (!Game::Sys_IsMainThread())
{
Logger::EnqueueMessage(buffer);
}
else
{
Game::Com_PrintMessage(0, buffer, 0);
}
}
else
{
@ -50,8 +60,43 @@ namespace Components
Game::Com_Error(2, "%s", buffer);
}
void Logger::Frame()
{
Logger::MessageMutex.lock();
for (unsigned int i = 0; i < Logger::MessageQueue.size(); i++)
{
if (Logger::IsConsoleReady())
{
Game::Com_PrintMessage(0, Logger::MessageQueue[i].data(), 0);
}
else
{
OutputDebugStringA(Logger::MessageQueue[i].data());
}
}
Logger::MessageQueue.clear();
Logger::MessageMutex.unlock();
}
void Logger::EnqueueMessage(std::string message)
{
Logger::MessageMutex.lock();
Logger::MessageQueue.push_back(message);
Logger::MessageMutex.unlock();
}
Logger::Logger()
{
Renderer::OnFrame(Logger::Frame); // Client
Dedicated::OnFrame(Logger::Frame); // Dedi
}
Logger::~Logger()
{
Logger::MessageMutex.lock();
Logger::MessageQueue.clear();
Logger::MessageMutex.unlock();
}
}

View File

@ -4,11 +4,19 @@ namespace Components
{
public:
Logger();
~Logger();
const char* GetName() { return "Logger"; };
static void Print(const char* message, ...);
static void Error(const char* message, ...);
static void SoftError(const char* message, ...);
static bool IsConsoleReady();
private:
static std::mutex MessageMutex;
static std::vector<std::string> MessageQueue;
static void Frame();
static void EnqueueMessage(std::string message);
};
}

View File

@ -12,6 +12,7 @@ namespace Game
Com_Error_t Com_Error = (Com_Error_t)0x4B22D0;
Com_Printf_t Com_Printf = (Com_Printf_t)0x402500;
Com_PrintMessage_t Com_PrintMessage = (Com_PrintMessage_t)0x4AA830;
Com_Milliseconds_t Com_Milliseconds = (Com_Milliseconds_t)0x42A660;
Com_ParseExt_t Com_ParseExt = (Com_ParseExt_t)0x474D60;
@ -86,6 +87,8 @@ namespace Game
Steam_JoinLobby_t Steam_JoinLobby = (Steam_JoinLobby_t)0x49CF70;
Sys_IsMainThread_t Sys_IsMainThread = (Sys_IsMainThread_t)0x4C37D0;
UI_AddMenuList_t UI_AddMenuList = (UI_AddMenuList_t)0x4533C0;
UI_DrawHandlePic_t UI_DrawHandlePic = (UI_DrawHandlePic_t)0x4D0EA0;

View File

@ -21,6 +21,9 @@ namespace Game
typedef void(__cdecl * Com_Printf_t)(int, const char*, ...);
extern Com_Printf_t Com_Printf;
typedef void(__cdecl * Com_PrintMessage_t)(int, const char*, char);
extern Com_PrintMessage_t Com_PrintMessage;
typedef int(__cdecl * Com_Milliseconds_t)(void);
extern Com_Milliseconds_t Com_Milliseconds;
@ -187,6 +190,9 @@ namespace Game
typedef void(__cdecl * Steam_JoinLobby_t)(SteamID, char);
extern Steam_JoinLobby_t Steam_JoinLobby;
typedef bool(__cdecl * Sys_IsMainThread_t)();
extern Sys_IsMainThread_t Sys_IsMainThread;
typedef void(__cdecl * UI_AddMenuList_t)(UiContext *dc, MenuList *menuList, int close);
extern UI_AddMenuList_t UI_AddMenuList;

View File

@ -18,6 +18,7 @@
#include <utility>
#include <algorithm>
#include <regex>
#include <thread>
#include <version.hpp>