[IPC] Use iw4x as worker process instead of creating a separate binary

This commit is contained in:
momo5502 2017-01-27 22:03:35 +01:00
parent fc0bf1acc1
commit 4e84c297e4
15 changed files with 251 additions and 515 deletions

View File

@ -33,6 +33,5 @@
| `--disable-bitmessage` | Disable use of BitMessage completely. |
| `--disable-node-log` | Disable debugging messages for Nodes in Debug builds. |
| `--disable-base128` | Disable base128 encoding for minidumps. |
| `--disable-steam-game` | Disable Steam's in-game setting. |
| `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). |
| `--enable-dxsdk` | Enable DirectX SDK (required for GfxMap exporting). |

View File

@ -88,11 +88,6 @@ newoption {
description = "Disable base128 encoding for minidumps."
}
newoption {
trigger = "disable-steam-game",
description = "Disable Steam's in-game setting."
}
newoption {
trigger = "enable-dxsdk",
description = "Enable DirectX SDK (required for GfxMap exporting)."
@ -307,9 +302,6 @@ workspace "iw4x"
"./src/**.cpp",
--"./src/**.proto",
}
removefiles {
"./src/Worker/**.*",
}
includedirs {
"%{prj.location}/src",
"./src",
@ -349,9 +341,6 @@ workspace "iw4x"
if _OPTIONS["disable-base128"] then
defines { "DISABLE_BASE128" }
end
if _OPTIONS["disable-steam-game"] then
defines { "DISABLE_STEAM_GAME" }
end
if _OPTIONS["enable-dxsdk"] then
defines { "ENABLE_DXSDK" }
includedirs { "%DXSDK_DIR%Include" }
@ -480,117 +469,6 @@ workspace "iw4x"
filter {}
]]
project "iw4xworker"
language "C++"
flags { "C++14" }
files {
"./src/Worker/**.rc",
"./src/Worker/**.hpp",
"./src/Worker/**.cpp",
"./src/Utils/**.hpp",
"./src/Utils/**.cpp",
--"./src/**.proto",
}
removefiles {
"./src/Utils/CSV.*",
"./src/Utils/Cache.*",
"./src/Utils/Stream.*",
"./src/Utils/Hooking.*",
"./src/Utils/InfoString.*",
"./src/Utils/Cryptography.*",
}
includedirs {
"%{prj.location}/src",
--"./src",
"./src/Worker",
}
resincludedirs {
"$(ProjectDir)src/Worker" -- fix for VS IDE
}
-- Pre-compiled header
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
pchsource "src/Worker/STDInclude.cpp" -- real path
buildoptions { "/Zm200" }
protobuf.import()
zlib.import()
boost.import()
-- fix vpaths for protobuf sources
vpaths
{
["*"] = { "./src/Worker/**" },
--["Proto/Generated"] = { "**.pb.*" }, -- meh.
}
-- Virtual paths
if not _OPTIONS["no-new-structure"] then
vpaths
{
["Headers/*"] = { "./src/Worker/**.hpp" },
["Headers/Utils/*"] = { "./src/Utils/**.hpp" },
["Sources/*"] = { "./src/Worker/**.cpp" },
["Sources/Utils/*"] = { "./src/Utils/**.cpp" },
["Resource/*"] = { "./src/Worker/**.rc" },
}
else
vpaths
{
["Utils/*"] = { "./src/Utils/**" },
}
end
vpaths
{
["Docs/*"] = { "**.txt","**.md" },
}
-- Pre-build
prebuildcommands
{
"pushd %{_MAIN_SCRIPT_DIR}",
"tools\\premake5 generate-buildinfo",
"popd",
}
-- Post-build
if _OPTIONS["copy-to"] then
saneCopyToPath = string.gsub(_OPTIONS["copy-to"] .. "\\", "\\\\", "\\")
postbuildcommands {
"if not exist \"" .. saneCopyToPath .. "\" mkdir \"" .. saneCopyToPath .. "iw4x\\\"",
}
if _OPTIONS["copy-pdb"] then
postbuildcommands {
"copy /y \"$(TargetDir)*.pdb\" \"" .. saneCopyToPath .. "iw4x\\\"",
}
end
-- This has to be the last one, as otherwise VisualStudio will succeed building even if copying fails
postbuildcommands {
"copy /y \"$(TargetDir)*.exe\" \"" .. saneCopyToPath .. "iw4x\\\"",
}
end
-- Specific configurations
flags { "UndefinedIdentifiers", "ExtraWarnings" }
if symbols ~= nil then
symbols "On"
else
flags { "Symbols" }
end
kind "ConsoleApp"
configuration "Release*"
kind "WindowedApp"
flags {
"FatalCompileWarnings",
"FatalLinkWarnings",
}
configuration {}
group "External dependencies"
if not _OPTIONS["disable-bitmessage"] then
bitmrc.project()

View File

@ -236,8 +236,7 @@ namespace Components
IPCPipe::Write("ping", "");
});
static Utils::IPC::Channel channel("iw4xChannel");
static Utils::IPC::Channel channel2("iw4xChannel2");
static Utils::IPC::BidirectionalChannel channel("iw4xChannel", !Worker::IsWorker());
channel.send("Hello world!");
Command::Add("ipcchan", [](Command::Params* params)
@ -245,10 +244,32 @@ namespace Components
channel.send(params->join(1));
});
Command::Add("ipcchantest", [](Command::Params*)
{
std::string buffer;
buffer.reserve(2048);
for(int i = 0; i < 1020; ++i)
{
buffer.append("a", 1);
}
buffer.append("lolsnakerules!");
for (int i = 0; i < 1020; ++i)
{
buffer.append("a", 1);
}
buffer.append("lolsnakerules!");
channel.send(buffer);
});
QuickPatch::OnFrame([]()
{
std::string buffer;
if(channel2.receive(&buffer))
if(channel.receive(&buffer))
{
Logger::Print("Data received: %s\n", buffer.data());
}
@ -261,7 +282,7 @@ namespace Components
ZeroMemory(&pInfo, sizeof(pInfo));
sInfo.cb = sizeof(sInfo);
CreateProcessA("iw4x/iw4xworker.exe", const_cast<char*>(Utils::String::VA("-parentProc %d", GetCurrentProcessId())), nullptr, nullptr, false, NULL, nullptr, nullptr, &sInfo, &pInfo);
CreateProcessA("iw4x.exe", const_cast<char*>(Utils::String::VA("-parent %d", GetCurrentProcessId())), nullptr, nullptr, false, NULL, nullptr, nullptr, &sInfo, &pInfo);
if (pInfo.hThread && pInfo.hThread != INVALID_HANDLE_VALUE) CloseHandle(pInfo.hThread);
if (pInfo.hProcess && pInfo.hProcess != INVALID_HANDLE_VALUE) CloseHandle(pInfo.hProcess);

View File

@ -20,8 +20,14 @@ namespace Main
Main::EntryPointHook.uninstall();
Main::SetEnvironment();
Utils::Cryptography::Initialize();
if(Worker::IsWorker())
{
Worker::Initialize();
}
else
{
Components::Loader::Initialize();
#if defined(DEBUG) || defined(FORCE_UNIT_TESTS)
@ -38,10 +44,19 @@ namespace Main
}
#endif
}
}
void Uninitialize()
{
if(Worker::IsWorker())
{
Worker::Uninitialize();
}
else
{
Components::Loader::Uninitialize();
}
Utils::Cache::Uninitialize();
google::protobuf::ShutdownProtobufLibrary();
}
@ -51,8 +66,6 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
Steam::Proxy::RunMod();
// Ensure we're working with our desired binary
if (Utils::Hook::Get<DWORD>(0x4C0FFF) != 0x6824748B)
{

View File

@ -117,6 +117,7 @@ template <size_t S> class Sizer { };
#include "Utils/Stream.hpp"
#include "Components/Loader.hpp"
#include "Worker/Worker.hpp"
// Libraries
#pragma comment(lib, "Winmm.lib")

View File

@ -4,9 +4,9 @@ namespace Utils
{
namespace IPC
{
Channel::Channel(std::string _name, int queueSize, int bufferSize) : name(_name)
Channel::Channel(std::string _name, int queueSize, int bufferSize, bool creator) : name(_name)
{
//boost::interprocess::message_queue::remove(this->name.data());
if(creator) boost::interprocess::message_queue::remove(this->name.data());
queue.reset(new boost::interprocess::message_queue(boost::interprocess::open_or_create, this->name.data(), queueSize, bufferSize + sizeof(Channel::Header)));
}
@ -43,7 +43,7 @@ namespace Utils
while (true)
{
this->queue->receive(const_cast<char*>(packet.data()), packet.size(), recvd_size, priority);
if (!this->queue->try_receive(const_cast<char*>(packet.data()), packet.size(), recvd_size, priority)) return false;
if (header->packetId != mainHeader.packetId || header->totalSize != mainHeader.totalSize || header->fragmentPart != (++part))
{

View File

@ -29,7 +29,7 @@ namespace Utils
class Channel
{
public:
Channel(std::string _name, int queueSize = 100, int bufferSize = 20);
Channel(std::string _name, int queueSize = 100, int bufferSize = 1024, bool creator = false);
~Channel();
bool receive(std::string* data);
@ -51,5 +51,43 @@ namespace Utils
std::string packet;
std::string name;
};
class BidirectionalChannel
{
public:
BidirectionalChannel(std::string name, bool server, int queueSize = 100, int bufferSize = 1024) : isServer(server),
channel1(name, queueSize, bufferSize, server),
channel2(name + "2", queueSize, bufferSize, server)
{}
bool receive(std::string* data)
{
if(this->isServer)
{
return channel1.receive(data);
}
else
{
return channel2.receive(data);
}
}
void send(std::string data)
{
if (this->isServer)
{
return channel2.send(data);
}
else
{
return channel1.send(data);
}
}
private:
const bool isServer;
Channel channel1;
Channel channel2;
};
}
}

View File

@ -1,67 +0,0 @@
#include "STDInclude.hpp"
#include <conio.h>
void worker(bool* terminator)
{
printf("Worker started\n");
Utils::IPC::Channel channel("iw4xChannel");
Utils::IPC::Channel channel2("iw4xChannel2");
while(!*terminator)
{
std::string buffer;
if(channel.receive(&buffer))
{
printf("Data received: %s\n", buffer.data());
channel2.send("OK " + buffer);
}
std::this_thread::sleep_for(1ms);
}
printf("Terminating worker\n");
}
int main()
{
bool terminator = false;
char* command = "-parentProc ";
char* parentProc = strstr(GetCommandLineA(), command);
if (!parentProc)
{
printf("No parent process argument found\n");
return 0;
}
parentProc += strlen(command);
int pid = atoi(parentProc);
printf("Attaching to process %d...\n", pid);
HANDLE processHandle = OpenProcess(SYNCHRONIZE, FALSE, pid);
if (!processHandle || processHandle == INVALID_HANDLE_VALUE)
{
printf("Unable to attach to parent process\n");
return 0;
}
printf("Successfully attached to parent process\n");
printf("Starting worker...\n");
std::thread runner(worker, &terminator);
WaitForSingleObject(processHandle, INFINITE);
CloseHandle(processHandle);
terminator = true;
printf("Awaiting worker termination...\n");
if (runner.joinable()) runner.join();
printf("Worker terminated\n");
//_getch();
google::protobuf::ShutdownProtobufLibrary();
return 0;
}

View File

@ -1,99 +0,0 @@
// Microsoft Visual C++ generated resource script.
//
#pragma code_page(65001)
#include "STDInclude.hpp"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "windows.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"#include ""windows.h""\r\n"
"\0"
END
2 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_RC
PRODUCTVERSION VERSION_RC
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "IW4x"
#ifdef _DEBUG
VALUE "FileDescription", "IW4 client modification (DEBUG)"
#else
VALUE "FileDescription", "IW4 client modification"
#endif
VALUE "FileVersion", SHORTVERSION
VALUE "InternalName", "iw4x"
VALUE "LegalCopyright", "No rights reserved."
VALUE "OriginalFilename", "iw4x.dll"
VALUE "ProductName", "IW4x"
VALUE "ProductVersion", SHORTVERSION
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

60
src/Worker/Runner.cpp Normal file
View File

@ -0,0 +1,60 @@
#include "STDInclude.hpp"
namespace Worker
{
Runner::Runner(int pid) : processId(pid), terminate(false)
{
}
Runner::~Runner()
{
}
void Runner::run()
{
HANDLE processHandle = OpenProcess(SYNCHRONIZE, FALSE, this->processId);
if (!processHandle || processHandle == INVALID_HANDLE_VALUE)
{
printf("Unable to attach to parent process\n");
}
else
{
printf("Successfully attached to parent process\n");
printf("Starting worker...\n");
std::thread workerThread(&Runner::worker, this);
WaitForSingleObject(processHandle, INFINITE);
CloseHandle(processHandle);
this->terminate = true;
printf("Awaiting worker termination...\n");
if (workerThread.joinable()) workerThread.join();
printf("Worker terminated\n");
}
}
void Runner::worker()
{
printf("Worker started\n");
Utils::IPC::BidirectionalChannel channel("iw4xChannel", !Worker::IsWorker());
while (!this->terminate)
{
Steam::Proxy::RunFrame();
std::string buffer;
if (channel.receive(&buffer))
{
printf("Data received: %s\n", buffer.data());
channel.send("OK " + buffer);
}
std::this_thread::sleep_for(1ms);
}
printf("Terminating worker\n");
}
}

19
src/Worker/Runner.hpp Normal file
View File

@ -0,0 +1,19 @@
#pragma once
namespace Worker
{
class Runner
{
public:
Runner(int pid);
~Runner();
void run();
private:
void worker();
int processId;
bool terminate;
};
}

View File

@ -1,77 +0,0 @@
#include "STDInclude.hpp"
// Rename sections
#ifndef DEBUG
#pragma comment(linker, "/merge:.text=.UPX0")
#pragma comment(linker, "/merge:.data=.UPX1")
#pragma comment(linker, "/merge:.rdata=.UPX2")
#pragma comment(linker, "/merge:.tls=.UPX3")
#pragma comment(linker, "/merge:.gfids=.UPX4")
#endif
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Do necessary assertions here
// Some compilers treat them differently which causes a size mismatch
// WinAPI types
AssertSize(DWORD, 4);
AssertSize(WORD, 2);
AssertSize(BYTE, 1);
// 128 bit integers (only x64)
//AssertSize(__int128, 16);
//AssertSize(unsigned __int128, 16);
// 64 bit integers
AssertSize(__int64, 8);
AssertSize(unsigned __int64, 8);
AssertSize(long long, 8);
AssertSize(unsigned long long, 8);
AssertSize(int64_t, 8);
AssertSize(uint64_t, 8);
AssertSize(std::int64_t, 8);
AssertSize(std::uint64_t, 8);
// 32 bit integers
AssertSize(__int32, 4);
AssertSize(unsigned __int32, 4);
AssertSize(int, 4);
AssertSize(unsigned int, 4);
AssertSize(int32_t, 4);
AssertSize(uint32_t, 4);
AssertSize(std::int32_t, 4);
AssertSize(std::uint32_t, 4);
// 16 bit integers
AssertSize(__int16, 2);
AssertSize(unsigned __int16, 2);
AssertSize(short, 2);
AssertSize(unsigned short, 2);
AssertSize(int16_t, 2);
AssertSize(uint16_t, 2);
AssertSize(std::int16_t, 2);
AssertSize(std::uint16_t, 2);
// 8 bit integers
AssertSize(bool, 1);
AssertSize(__int8, 1);
AssertSize(unsigned __int8, 1);
AssertSize(char, 1);
AssertSize(unsigned char, 1);
AssertSize(int8_t, 1);
AssertSize(uint8_t, 1);
AssertSize(std::int8_t, 1);
AssertSize(std::uint8_t, 1);
// Ensure pointers are 4 bytes in size (32-bit)
static_assert(sizeof(intptr_t) == 4 && sizeof(void*) == 4 && sizeof(size_t) == 4, "This doesn't seem to be a 32-bit environment!");
extern "C"
{
// Disable telemetry data logging
void __cdecl __vcrt_initialize_telemetry_provider() {}
void __cdecl __telemetry_main_invoke_trigger() {}
void __cdecl __telemetry_main_return_trigger() {}
void __cdecl __vcrt_uninitialize_telemetry_provider() {}
};

View File

@ -1,126 +0,0 @@
#pragma once
// Version number
#include "version.h"
#ifndef RC_INVOKED
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
// Requires Visual Leak Detector plugin: http://vld.codeplex.com/
//#include <vld.h>
#include <windows.h>
#include <timeapi.h>
#include <shellapi.h>
#include <Wininet.h>
#include <d3d9.h>
#include <Aclapi.h>
#pragma warning(push)
#pragma warning(disable: 4996)
#include <xutility>
#pragma warning(pop)
#include <sstream>
#include <fstream>
#include <cctype>
#include <regex>
#include <thread>
#include <future>
#include <queue>
#include <unordered_map>
// Experimental C++17 features
#include <filesystem>
// Usefull for debugging
template <size_t S> class Sizer { };
#define BindNum(x, y) Sizer<x> y;
#define SizeOf(x, y) BindNum(sizeof(x), y)
#define OffsetOf(x, y, z) BindNum(offsetof(x, y), z)
// Submodules
// Ignore the warnings, it's no our code!
#pragma warning(push)
#pragma warning(disable: 4005)
#pragma warning(disable: 4100)
#pragma warning(disable: 4389)
#pragma warning(disable: 4702)
#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS
#pragma warning(disable: 6001)
#pragma warning(disable: 6011)
#pragma warning(disable: 6031)
#pragma warning(disable: 6255)
#pragma warning(disable: 6258)
#pragma warning(disable: 6386)
#pragma warning(disable: 6387)
#include <zlib.h>
#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
// Protobuf
#include "proto/network.pb.h"
#include "proto/party.pb.h"
#include "proto/auth.pb.h"
#include "proto/node.pb.h"
#include "proto/rcon.pb.h"
#pragma warning(pop)
#include "../Utils/IO.hpp"
#include "../Utils/IPC.hpp"
#include "../Utils/Time.hpp"
#include "../Utils/Chain.hpp"
#include "../Utils/Utils.hpp"
#include "../Utils/WebIO.hpp"
#include "../Utils/Memory.hpp"
#include "../Utils/String.hpp"
#include "../Utils/Library.hpp"
#include "../Utils/Compression.hpp"
// Libraries
#pragma comment(lib, "Winmm.lib")
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "Wininet.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(lib, "Urlmon.lib")
#pragma comment(lib, "Advapi32.lib")
#pragma comment(lib, "rpcrt4.lib")
// Enable additional literals
using namespace std::literals;
#endif
#define STRINGIZE_(x) #x
#define STRINGIZE(x) STRINGIZE_(x)
#define AssertSize(x, size) static_assert(sizeof(x) == size, STRINGIZE(x) " structure has an invalid size.")
#define AssertOffset(x, y, offset) static_assert(offsetof(x, y) == offset, STRINGIZE(x) "::" STRINGIZE(y) " is not at the right offset.")
// Resource stuff
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
// Defines below make accessing the resources from the code easier.
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
// Enables custom map code
#ifdef DEBUG
#define ENABLE_EXPERIMENTAL_MAP_CODE
#endif

65
src/Worker/Worker.cpp Normal file
View File

@ -0,0 +1,65 @@
#include "STDInclude.hpp"
namespace Worker
{
int ProcessId;
int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, char* /*lpCmdLine*/, int /*nCmdShow*/)
{
Runner runner(Worker::ProcessId);
runner.run();
return 0;
}
void Initialize()
{
if(!Steam::Proxy::Inititalize())
{
printf("Failed to initialize worker!\n");
ExitProcess(1);
}
else
{
#ifdef DEBUG
SetConsoleTitleA("IW4x Worker");
#else
FreeConsole();
#endif
Utils::Hook(0x6BABA1, Worker::EntryPoint, HOOK_CALL).install()->quick();
}
}
void Uninitialize()
{
Steam::Proxy::Uninititalize();
}
bool ParseWorkerFlag()
{
char* command = "-parent ";
char* parentProc = strstr(GetCommandLineA(), command);
if (parentProc)
{
parentProc += strlen(command);
Worker::ProcessId = atoi(parentProc);
return true;
}
return false;
}
bool IsWorker()
{
static Utils::Value<bool> flag;
if (!flag.isValid())
{
flag.set(Worker::ParseWorkerFlag());
}
return flag.get();
}
}

11
src/Worker/Worker.hpp Normal file
View File

@ -0,0 +1,11 @@
#pragma once
namespace Worker
{
void Initialize();
void Uninitialize();
bool IsWorker();
}
#include "Runner.hpp"