From 0221d24f079bec7d65582f3716ead2e38dda17c8 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 28 Jan 2017 14:20:28 +0100 Subject: [PATCH 01/16] [Runner] Add handler interface --- src/Worker/Runner.cpp | 21 +++++++++++++++++++-- src/Worker/Runner.hpp | 11 +++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Worker/Runner.cpp b/src/Worker/Runner.cpp index 0800bcca..a2b0c38a 100644 --- a/src/Worker/Runner.cpp +++ b/src/Worker/Runner.cpp @@ -37,6 +37,11 @@ namespace Worker } } + void Runner::attachHandler(std::shared_ptr handler) + { + this->handlers[handler->getCommand()] = handler; + } + void Runner::worker() { printf("Worker started\n"); @@ -49,8 +54,20 @@ namespace Worker std::string buffer; if (channel.receive(&buffer)) { - printf("Data received: %s\n", buffer.data()); - channel.send("OK " + buffer); + Proto::IPC::Command command; + if(command.ParseFromString(buffer)) + { + auto handler = this->handlers.find(command.command()); + if (handler != this->handlers.end()) + { + printf("Handling command %s\n", command.command().data()); + handler->second->handle(&channel, command.data()); + } + else + { + printf("No handler found for command %s\n", command.command().data()); + } + } } std::this_thread::sleep_for(1ms); diff --git a/src/Worker/Runner.hpp b/src/Worker/Runner.hpp index 4c13444b..f882acb2 100644 --- a/src/Worker/Runner.hpp +++ b/src/Worker/Runner.hpp @@ -5,15 +5,26 @@ namespace Worker class Runner { public: + class Handler + { + public: + virtual ~Handler() {}; + virtual std::string getCommand() = 0; + virtual void handle(Utils::IPC::BidirectionalChannel* channel, std::string data) = 0; + }; + Runner(int pid); ~Runner(); void run(); + void attachHandler(std::shared_ptr handler); + private: void worker(); int processId; bool terminate; + std::map> handlers; }; } From 4cf2ca270ca7fbf7b4e6d1be66e3cda75298e23c Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 28 Jan 2017 14:20:39 +0100 Subject: [PATCH 02/16] [Friends] Add friend handler --- src/Worker/Handlers/Friends.cpp | 65 +++++++++++++++++++++++++++++++++ src/Worker/Handlers/Friends.hpp | 11 ++++++ 2 files changed, 76 insertions(+) create mode 100644 src/Worker/Handlers/Friends.cpp create mode 100644 src/Worker/Handlers/Friends.hpp diff --git a/src/Worker/Handlers/Friends.cpp b/src/Worker/Handlers/Friends.cpp new file mode 100644 index 00000000..8fe8d635 --- /dev/null +++ b/src/Worker/Handlers/Friends.cpp @@ -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 flag; + + if (!flag.isValid()) + { + flag.set(Worker::ParseWorkerFlag()); + } + + return flag.get(); + } +} diff --git a/src/Worker/Handlers/Friends.hpp b/src/Worker/Handlers/Friends.hpp new file mode 100644 index 00000000..4f700b23 --- /dev/null +++ b/src/Worker/Handlers/Friends.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace Worker +{ + void Initialize(); + void Uninitialize(); + + bool IsWorker(); +} + +#include "Runner.hpp" From 46d3045d6fcbb30df2f7f3bcdd78bef8a7037a1b Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 28 Jan 2017 15:51:50 +0100 Subject: [PATCH 03/16] [Friends] Experimental ipc function handler --- src/Components/Modules/Friends.cpp | 28 ++++++++++ src/Components/Modules/IPCHandler.cpp | 8 +-- src/Proto/ipc.proto | 8 ++- src/Worker/Handlers/Friends.cpp | 80 ++++++++++++--------------- src/Worker/Handlers/Friends.hpp | 20 +++++-- src/Worker/Runner.cpp | 10 ++-- src/Worker/Runner.hpp | 31 ++++++++++- src/Worker/Worker.cpp | 1 + 8 files changed, 122 insertions(+), 64 deletions(-) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index 48cbfa8b..5b02a8ac 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -167,6 +167,34 @@ namespace Components }); UIFeeder::Add(6.0f, Friends::GetFriendCount, Friends::GetFriendText, Friends::SelectFriend); + + IPCHandler::OnWorker("friends", [](std::string data) + { + Proto::IPC::Function function; + if(function.ParseFromString(data)) + { + if(function.name() == "friendsResponse") + { + auto params = function.params(); + + Logger::Print("Received friendslist: %d\n", params.size()); + + for(auto param : params) + { + Logger::Print("%s\n", param.data()); + } + } + } + }); + + Command::Add("getFriends", [](Command::Params*) + { + Proto::IPC::Function function; + function.set_name("getFriends"); + function.add_params()->append(Utils::String::VA("%d", 4)); + + IPCHandler::SendWorker("friends", function.SerializeAsString()); + }); } Friends::~Friends() diff --git a/src/Components/Modules/IPCHandler.cpp b/src/Components/Modules/IPCHandler.cpp index 952035e4..f0f519fe 100644 --- a/src/Components/Modules/IPCHandler.cpp +++ b/src/Components/Modules/IPCHandler.cpp @@ -13,7 +13,7 @@ namespace Components IPCHandler::InitChannels(); Proto::IPC::Command command; - command.set_command(message); + command.set_name(message); command.set_data(data); IPCHandler::WorkerChannel->send(command.SerializeAsString()); @@ -24,7 +24,7 @@ namespace Components IPCHandler::InitChannels(); Proto::IPC::Command command; - command.set_command(message); + command.set_name(message); command.set_data(data); IPCHandler::ClientChannel->send(command.SerializeAsString()); @@ -78,7 +78,7 @@ namespace Components Proto::IPC::Command command; if(command.ParseFromString(packet)) { - auto callback = IPCHandler::ClientCallbacks.find(command.command()); + auto callback = IPCHandler::ClientCallbacks.find(command.name()); if (callback != IPCHandler::ClientCallbacks.end()) { callback->second(command.data()); @@ -97,7 +97,7 @@ namespace Components Proto::IPC::Command command; if (command.ParseFromString(packet)) { - auto callback = IPCHandler::WorkerCallbacks.find(command.command()); + auto callback = IPCHandler::WorkerCallbacks.find(command.name()); if (callback != IPCHandler::WorkerCallbacks.end()) { callback->second(command.data()); diff --git a/src/Proto/ipc.proto b/src/Proto/ipc.proto index f2e71514..2a5471d1 100644 --- a/src/Proto/ipc.proto +++ b/src/Proto/ipc.proto @@ -4,6 +4,12 @@ package Proto.IPC; message Command { - bytes command = 1; + bytes name = 1; bytes data = 2; } + +message Function +{ + bytes name = 1; + repeated bytes params = 2; +} diff --git a/src/Worker/Handlers/Friends.cpp b/src/Worker/Handlers/Friends.cpp index 8fe8d635..c659839e 100644 --- a/src/Worker/Handlers/Friends.cpp +++ b/src/Worker/Handlers/Friends.cpp @@ -1,65 +1,55 @@ #include "STDInclude.hpp" -namespace Worker +namespace Handlers { - int ProcessId; - - int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, char* /*lpCmdLine*/, int /*nCmdShow*/) + void Friends::handle(Worker::Endpoint endpoint, std::string data) { - Runner runner(Worker::ProcessId); - runner.run(); - return 0; - } - - void Initialize() - { - if(!Steam::Proxy::Inititalize()) + Proto::IPC::Function function; + if (function.ParseFromString(data)) { - printf("Failed to initialize worker!\n"); - ExitProcess(1); - } - else - { -#ifdef DEBUG - SetConsoleTitleA("IW4x: Worker"); -#else - FreeConsole(); -#endif + auto handler = this->functions.find(function.name()); + if (handler != this->functions.end()) + { + printf("Handing function %s\n", function.name().data()); - Utils::Hook(0x6BABA1, Worker::EntryPoint, HOOK_CALL).install()->quick(); + auto params = function.params(); + handler->second(endpoint, std::vector(params.begin(), params.end())); + } + else + { + printf("No handler for function %s\n", function.name().data()); + } } } - void Uninitialize() + void Friends::addFunction(std::string function, Friends::Callback callback) { - Steam::Proxy::Uninititalize(); + this->functions[function] = callback; } - bool ParseWorkerFlag() + Friends::Friends() { - char* command = "-parent "; - char* parentProc = strstr(GetCommandLineA(), command); - - if (parentProc) + this->addFunction("getFriends", [&](Worker::Endpoint endpoint, std::vector params) { - parentProc += strlen(command); - Worker::ProcessId = atoi(parentProc); + if (params.size() >= 1 && Steam::Proxy::SteamFriends) + { + int flag = atoi(params[0].data()); + int count = Steam::Proxy::SteamFriends->GetFriendCount(flag); - return true; - } + Proto::IPC::Function response; + response.set_name("friendsResponse"); - return false; - } + for (int i = 0; i < count; ++i) + { + std::string* param = response.add_params(); + SteamID id = Steam::Proxy::SteamFriends->GetFriendByIndex(i, flag); - bool IsWorker() - { - static Utils::Value flag; + param->clear(); + param->append(Utils::String::VA("%llX", id.Bits)); + } - if (!flag.isValid()) - { - flag.set(Worker::ParseWorkerFlag()); - } - - return flag.get(); + endpoint.send(this->getCommand(), response.SerializeAsString()); + } + }); } } diff --git a/src/Worker/Handlers/Friends.hpp b/src/Worker/Handlers/Friends.hpp index 4f700b23..357e5667 100644 --- a/src/Worker/Handlers/Friends.hpp +++ b/src/Worker/Handlers/Friends.hpp @@ -1,11 +1,19 @@ #pragma once -namespace Worker +namespace Handlers { - void Initialize(); - void Uninitialize(); + class Friends : public Worker::Runner::Handler + { + public: + typedef std::function)> Callback; - bool IsWorker(); + Friends(); + + std::string getCommand() override { return "friends"; }; + void handle(Worker::Endpoint endpoint, std::string data) override; + + private: + void addFunction(std::string function, Callback callback); + std::unordered_map functions; + }; } - -#include "Runner.hpp" diff --git a/src/Worker/Runner.cpp b/src/Worker/Runner.cpp index a2b0c38a..5d2b9e11 100644 --- a/src/Worker/Runner.cpp +++ b/src/Worker/Runner.cpp @@ -37,9 +37,9 @@ namespace Worker } } - void Runner::attachHandler(std::shared_ptr handler) + void Runner::attachHandler(Runner::Handler* handler) { - this->handlers[handler->getCommand()] = handler; + this->handlers[handler->getCommand()] = std::shared_ptr(handler); } void Runner::worker() @@ -57,15 +57,15 @@ namespace Worker Proto::IPC::Command command; if(command.ParseFromString(buffer)) { - auto handler = this->handlers.find(command.command()); + auto handler = this->handlers.find(command.name()); if (handler != this->handlers.end()) { - printf("Handling command %s\n", command.command().data()); + printf("Dispathcing command %s to handler\n", command.name().data()); handler->second->handle(&channel, command.data()); } else { - printf("No handler found for command %s\n", command.command().data()); + printf("No handler found for command %s\n", command.name().data()); } } } diff --git a/src/Worker/Runner.hpp b/src/Worker/Runner.hpp index f882acb2..328aa529 100644 --- a/src/Worker/Runner.hpp +++ b/src/Worker/Runner.hpp @@ -2,6 +2,29 @@ namespace Worker { + class Endpoint + { + public: + Endpoint() : Endpoint(nullptr) {} + Endpoint(Utils::IPC::BidirectionalChannel* _channel) : channel(_channel) {} + Endpoint(const Endpoint& obj) : Endpoint(obj.channel) {} + + void send(std::string message, std::string data) + { + if (this->channel) + { + Proto::IPC::Command command; + command.set_name(message); + command.set_data(data); + + this->channel->send(command.SerializeAsString()); + } + } + + private: + Utils::IPC::BidirectionalChannel* channel; + }; + class Runner { public: @@ -10,7 +33,7 @@ namespace Worker public: virtual ~Handler() {}; virtual std::string getCommand() = 0; - virtual void handle(Utils::IPC::BidirectionalChannel* channel, std::string data) = 0; + virtual void handle(Endpoint endpoint, std::string data) = 0; }; Runner(int pid); @@ -18,13 +41,15 @@ namespace Worker void run(); - void attachHandler(std::shared_ptr handler); + void attachHandler(Runner::Handler* handler); private: void worker(); int processId; bool terminate; - std::map> handlers; + std::unordered_map> handlers; }; } + +#include "Handlers/Friends.hpp" diff --git a/src/Worker/Worker.cpp b/src/Worker/Worker.cpp index 8fe8d635..fbf9792e 100644 --- a/src/Worker/Worker.cpp +++ b/src/Worker/Worker.cpp @@ -7,6 +7,7 @@ namespace Worker int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, char* /*lpCmdLine*/, int /*nCmdShow*/) { Runner runner(Worker::ProcessId); + runner.attachHandler(new Handlers::Friends()); runner.run(); return 0; } From 20139d4a5288f9d95f82b8e15433329c845ccf5d Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 28 Jan 2017 19:03:44 +0100 Subject: [PATCH 04/16] [Friends] Query friends from the worker --- src/Components/Modules/Friends.cpp | 130 ++++++++++++++------------ src/Components/Modules/Friends.hpp | 3 + src/Components/Modules/IPCHandler.cpp | 19 ++++ src/Components/Modules/IPCHandler.hpp | 32 +++++++ src/STDInclude.hpp | 1 + src/Utils/IPC.cpp | 48 +++++++++- src/Utils/IPC.hpp | 9 +- src/Worker/Handlers/Friends.cpp | 116 ++++++++++++++++++----- src/Worker/Handlers/Friends.hpp | 9 +- src/Worker/Runner.cpp | 2 +- 10 files changed, 282 insertions(+), 87 deletions(-) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index 5b02a8ac..46e1dc20 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -55,33 +55,11 @@ namespace Components void Friends::UpdateFriends() { - if (!Steam::Proxy::SteamFriends) return; - std::lock_guard _(Friends::Mutex); + Proto::IPC::Function function; + function.set_name("getFriends"); + *function.add_params() = Utils::String::VA("%d", 4); - auto listCopy = Friends::FriendsList; - Friends::FriendsList.clear(); - - int count = Steam::Proxy::SteamFriends->GetFriendCount(4); - Friends::FriendsList.reserve(count); - - for(int i = 0; i < count; ++i) - { - SteamID friendId = Steam::Proxy::SteamFriends->GetFriendByIndex(i, 4); - //if(!Steam::Proxy::SteamFriends->GetFriendPersonaState(friendId)) continue; // Offline - - auto entry = std::find_if(listCopy.begin(), listCopy.end(), [friendId](Friends::Friend entry) - { - return (entry.userId.Bits == friendId.Bits); - }); - - if (entry != listCopy.end()) - { - Friends::FriendsList.push_back(*entry); - } - - Friends::UpdateUserInfo(friendId); - Steam::Proxy::SteamFriends->RequestFriendRichPresence(friendId); - } + IPCHandler::SendWorker("friends", function.SerializeAsString()); } unsigned int Friends::GetFriendCount() @@ -145,6 +123,71 @@ namespace Components Friends::CurrentFriend = index; } + void Friends::NameResponse(std::vector params) + { + if (params.size() >= 2) + { + std::lock_guard _(Friends::Mutex); + + SteamID id; + id.Bits = strtoull(params[0].data(), nullptr, 16); + + for(auto& entry : Friends::FriendsList) + { + if(entry.userId.Bits == id.Bits) + { + entry.name = params[1]; + break; + } + } + } + } + + void Friends::FriendsResponse(std::vector params) + { + std::lock_guard _(Friends::Mutex); + + auto oldFriends = Friends::FriendsList; + Friends::FriendsList.clear(); + + for (auto param : params) + { + SteamID id; + id.Bits = strtoull(param.data(), nullptr, 16); + + Friends::Friend entry; + entry.userId = id; + entry.online = false; + entry.prestige = 0; + entry.experience = 0; + + auto oldEntry = std::find_if(oldFriends.begin(), oldFriends.end(), [id](Friends::Friend entry) + { + return (entry.userId.Bits == id.Bits); + }); + + if (oldEntry != oldFriends.end()) entry = *oldEntry; + + Friends::FriendsList.push_back(entry); + + Proto::IPC::Function function; + function.set_name("getName"); + *function.add_params() = Utils::String::VA("%llx", id.Bits); + IPCHandler::SendWorker("friends", function.SerializeAsString()); + + function.Clear(); + function.set_name("getPresence"); + *function.add_params() = Utils::String::VA("%llx", id.Bits); + *function.add_params() = "iw4x_status"; + IPCHandler::SendWorker("friends", function.SerializeAsString()); + + function.Clear(); + function.set_name("requestPresence"); + *function.add_params() = Utils::String::VA("%llx", id.Bits); + IPCHandler::SendWorker("friends", function.SerializeAsString()); + } + } + Friends::Friends() { // Callback to update user information @@ -168,33 +211,9 @@ namespace Components UIFeeder::Add(6.0f, Friends::GetFriendCount, Friends::GetFriendText, Friends::SelectFriend); - IPCHandler::OnWorker("friends", [](std::string data) - { - Proto::IPC::Function function; - if(function.ParseFromString(data)) - { - if(function.name() == "friendsResponse") - { - auto params = function.params(); - - Logger::Print("Received friendslist: %d\n", params.size()); - - for(auto param : params) - { - Logger::Print("%s\n", param.data()); - } - } - } - }); - - Command::Add("getFriends", [](Command::Params*) - { - Proto::IPC::Function function; - function.set_name("getFriends"); - function.add_params()->append(Utils::String::VA("%d", 4)); - - IPCHandler::SendWorker("friends", function.SerializeAsString()); - }); + auto fInterface = IPCHandler::NewInterface("friends"); + fInterface->map("friendsResponse", Friends::FriendsResponse); + fInterface->map("nameResponse", Friends::NameResponse); } Friends::~Friends() @@ -202,11 +221,6 @@ namespace Components Steam::Proxy::UnregisterCallback(304); Steam::Proxy::UnregisterCallback(336); - if (Steam::Proxy::SteamFriends) - { - Steam::Proxy::SteamFriends->SetRichPresence("iw4x_status", nullptr); - } - { std::lock_guard _(Friends::Mutex); Friends::FriendsList.clear(); diff --git a/src/Components/Modules/Friends.hpp b/src/Components/Modules/Friends.hpp index b4abf082..cd08cad6 100644 --- a/src/Components/Modules/Friends.hpp +++ b/src/Components/Modules/Friends.hpp @@ -48,5 +48,8 @@ namespace Components static unsigned int GetFriendCount(); static const char* GetFriendText(unsigned int index, int column); static void SelectFriend(unsigned int index); + + static void FriendsResponse(std::vector params); + static void NameResponse(std::vector params); }; } diff --git a/src/Components/Modules/IPCHandler.cpp b/src/Components/Modules/IPCHandler.cpp index f0f519fe..31f2d338 100644 --- a/src/Components/Modules/IPCHandler.cpp +++ b/src/Components/Modules/IPCHandler.cpp @@ -8,6 +8,15 @@ namespace Components std::unique_ptr IPCHandler::WorkerChannel; std::unique_ptr IPCHandler::ClientChannel; + std::unordered_map> IPCHandler::FunctionInterfaces; + + std::shared_ptr IPCHandler::NewInterface(std::string command) + { + std::shared_ptr fInterface(new IPCHandler::FunctionInterface()); + IPCHandler::FunctionInterfaces[command] = fInterface; + return fInterface; + } + void IPCHandler::SendWorker(std::string message, std::string data) { IPCHandler::InitChannels(); @@ -98,10 +107,15 @@ namespace Components if (command.ParseFromString(packet)) { auto callback = IPCHandler::WorkerCallbacks.find(command.name()); + auto fInterface = IPCHandler::FunctionInterfaces.find(command.name()); if (callback != IPCHandler::WorkerCallbacks.end()) { callback->second(command.data()); } + else if(fInterface != IPCHandler::FunctionInterfaces.end()) + { + fInterface->second->handle(command.data()); + } } } } @@ -122,7 +136,12 @@ namespace Components IPCHandler::~IPCHandler() { + IPCHandler::FunctionInterfaces.clear(); + IPCHandler::WorkerCallbacks.clear(); IPCHandler::ClientCallbacks.clear(); + + IPCHandler::WorkerChannel.release(); + IPCHandler::ClientChannel.release(); } } diff --git a/src/Components/Modules/IPCHandler.hpp b/src/Components/Modules/IPCHandler.hpp index d63ac912..407ba255 100644 --- a/src/Components/Modules/IPCHandler.hpp +++ b/src/Components/Modules/IPCHandler.hpp @@ -5,6 +5,34 @@ namespace Components class IPCHandler : public Component { public: + class FunctionInterface + { + public: + typedef std::function)> Callback; + + void map(std::string name, Callback function) + { + this->functions[name] = function; + } + + void handle(std::string data) + { + Proto::IPC::Function function; + if (function.ParseFromString(data)) + { + auto handler = this->functions.find(function.name()); + if (handler != this->functions.end()) + { + auto params = function.params(); + handler->second(std::vector(params.begin(), params.end())); + } + } + } + + private: + std::unordered_map functions; + }; + typedef Utils::Slot Callback; IPCHandler(); @@ -20,12 +48,16 @@ namespace Components static void OnWorker(std::string message, Callback callback); static void OnClient(std::string message, Callback callback); + static std::shared_ptr NewInterface(std::string command); + private: static std::unique_ptr WorkerChannel; static std::unique_ptr ClientChannel; static std::unordered_map WorkerCallbacks; static std::unordered_map ClientCallbacks; + + static std::unordered_map> FunctionInterfaces; static void InitChannels(); static void StartWorker(); diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index 971715fd..4764310e 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -31,6 +31,7 @@ #include #include #include +#include // Experimental C++17 features #include diff --git a/src/Utils/IPC.cpp b/src/Utils/IPC.cpp index 3e133b9e..aa667db5 100644 --- a/src/Utils/IPC.cpp +++ b/src/Utils/IPC.cpp @@ -4,14 +4,24 @@ namespace Utils { namespace IPC { - Channel::Channel(std::string _name, int _queueSize, int _bufferSize, bool _remove) : name(_name), remove(_remove) + Channel::Channel(std::string _name, int _queueSize, int _bufferSize, bool _remove) : terminateQueue(false), remove(_remove), name(_name) { if(this->remove) 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))); + + this->queueThread = std::thread(&Channel::queueWorker, this); } Channel::~Channel() { + this->terminateQueue = true; + this->queueEvent.notify_all(); + + if(this->queueThread.joinable()) + { + this->queueThread.join(); + } + if (this->remove) boost::interprocess::message_queue::remove(this->name.data()); } @@ -84,19 +94,47 @@ namespace Utils std::string buffer; buffer.append(reinterpret_cast(&header), sizeof(Channel::Header)); buffer.append(data.data() + sentSize, header.fragmentSize); - Channel::sendMessage(buffer); + Channel::enqueueMessage(buffer); sentSize += header.fragmentSize; } } - void Channel::sendMessage(std::string data) + void Channel::enqueueMessage(std::string data) { if (data.size() <= this->queue->get_max_msg_size()) { - while (!this->queue->try_send(data.data(), data.size(), 0)) + std::lock_guard _(this->queueMutex); + this->internalQueue.push(data); + this->queueEvent.notify_all(); + } + } + + void Channel::queueWorker() + { + while (!this->terminateQueue) + { + std::unique_lock lock(this->queueMutex); + + while(!this->terminateQueue && this->internalQueue.empty()) { - std::this_thread::sleep_for(100us); + this->queueEvent.wait(lock); + } + + while(!this->terminateQueue && !this->internalQueue.empty()) + { + std::string data = this->internalQueue.front(); + this->internalQueue.pop(); + + if (data.size() <= this->queue->get_max_msg_size()) + { + while (!this->terminateQueue && !this->queue->try_send(data.data(), data.size(), 0)) + { + lock.unlock(); + std::this_thread::sleep_for(1000us); + lock.lock(); + } + } } } } diff --git a/src/Utils/IPC.hpp b/src/Utils/IPC.hpp index 01fc080c..c12728db 100644 --- a/src/Utils/IPC.hpp +++ b/src/Utils/IPC.hpp @@ -45,7 +45,14 @@ namespace Utils unsigned int fragmentPart; }; - void sendMessage(std::string data); + void enqueueMessage(std::string data); + void queueWorker(); + + bool terminateQueue; + std::condition_variable queueEvent; + std::mutex queueMutex; + std::thread queueThread; + std::queue internalQueue; bool remove; std::unique_ptr queue; diff --git a/src/Worker/Handlers/Friends.cpp b/src/Worker/Handlers/Friends.cpp index c659839e..96728fdb 100644 --- a/src/Worker/Handlers/Friends.cpp +++ b/src/Worker/Handlers/Friends.cpp @@ -27,29 +27,103 @@ namespace Handlers this->functions[function] = callback; } + void Friends::getFriends(Worker::Endpoint endpoint, std::vector params) + { + if (params.size() >= 1 && Steam::Proxy::SteamFriends) + { + int flag = atoi(params[0].data()); + int count = Steam::Proxy::SteamFriends->GetFriendCount(flag); + + Proto::IPC::Function response; + response.set_name("friendsResponse"); + + for (int i = 0; i < count; ++i) + { + SteamID id = Steam::Proxy::SteamFriends->GetFriendByIndex(i, flag); + *response.add_params() = Utils::String::VA("%llX", id.Bits); + } + + endpoint.send(this->getCommand(), response.SerializeAsString()); + } + } + + void Friends::getName(Worker::Endpoint endpoint, std::vector params) + { + if(Steam::Proxy::SteamFriends) + { + std::string name; + SteamID id; + + if(params.size() >= 1) + { + id.Bits = strtoull(params[0].data(), nullptr, 16); + name = Steam::Proxy::SteamFriends->GetFriendPersonaName(id); + } + else + { + id.Bits = 0; + name = Steam::Proxy::SteamFriends->GetPersonaName(); + } + + Proto::IPC::Function response; + response.set_name("nameResponse"); + *response.add_params() = Utils::String::VA("%llX", id.Bits); + *response.add_params() = name; + + endpoint.send(this->getCommand(), response.SerializeAsString()); + } + } + + void Friends::setPresence(Worker::Endpoint /*endpoint*/, std::vector params) + { + if (params.size() >= 2 && Steam::Proxy::SteamFriends) + { + Steam::Proxy::SteamFriends->SetRichPresence(params[0].data(), params[1].data()); + } + } + + void Friends::getPresence(Worker::Endpoint endpoint, std::vector params) + { + if (params.size() >= 2 && Steam::Proxy::SteamFriends) + { + SteamID id; + id.Bits = strtoull(params[0].data(), nullptr, 16); + + Proto::IPC::Function response; + response.set_name("presenceResponse"); + *response.add_params() = Utils::String::VA("%llX", id.Bits); + *response.add_params() = Steam::Proxy::SteamFriends->GetFriendRichPresence(id, params[1].data()); + + endpoint.send(this->getCommand(), response.SerializeAsString()); + } + } + + void Friends::requestPresence(Worker::Endpoint /*endpoint*/, std::vector params) + { + if (params.size() >= 1 && Steam::Proxy::SteamFriends) + { + SteamID id; + id.Bits = strtoull(params[0].data(), nullptr, 16); + + Steam::Proxy::SteamFriends->RequestFriendRichPresence(id); + } + } + Friends::Friends() { - this->addFunction("getFriends", [&](Worker::Endpoint endpoint, std::vector params) + using namespace std::placeholders; + this->addFunction("getFriends", std::bind(&Friends::getFriends, this, _1, _2)); + this->addFunction("getName", std::bind(&Friends::getName, this, _1, _2)); + this->addFunction("setPresence", std::bind(&Friends::setPresence, this, _1, _2)); + this->addFunction("getPresence", std::bind(&Friends::getPresence, this, _1, _2)); + this->addFunction("requestPresence", std::bind(&Friends::requestPresence, this, _1, _2)); + } + + Friends::~Friends() + { + if(Steam::Proxy::SteamFriends) { - if (params.size() >= 1 && Steam::Proxy::SteamFriends) - { - int flag = atoi(params[0].data()); - int count = Steam::Proxy::SteamFriends->GetFriendCount(flag); - - Proto::IPC::Function response; - response.set_name("friendsResponse"); - - for (int i = 0; i < count; ++i) - { - std::string* param = response.add_params(); - SteamID id = Steam::Proxy::SteamFriends->GetFriendByIndex(i, flag); - - param->clear(); - param->append(Utils::String::VA("%llX", id.Bits)); - } - - endpoint.send(this->getCommand(), response.SerializeAsString()); - } - }); + Steam::Proxy::SteamFriends->ClearRichPresence(); + } } } diff --git a/src/Worker/Handlers/Friends.hpp b/src/Worker/Handlers/Friends.hpp index 357e5667..3a6e0435 100644 --- a/src/Worker/Handlers/Friends.hpp +++ b/src/Worker/Handlers/Friends.hpp @@ -8,12 +8,19 @@ namespace Handlers typedef std::function)> Callback; Friends(); + ~Friends(); std::string getCommand() override { return "friends"; }; void handle(Worker::Endpoint endpoint, std::string data) override; private: - void addFunction(std::string function, Callback callback); std::unordered_map functions; + void addFunction(std::string function, Callback callback); + + void getFriends(Worker::Endpoint endpoint, std::vector params); + void getName(Worker::Endpoint endpoint, std::vector params); + void setPresence(Worker::Endpoint endpoint, std::vector params); + void getPresence(Worker::Endpoint endpoint, std::vector params); + void requestPresence(Worker::Endpoint endpoint, std::vector params); }; } diff --git a/src/Worker/Runner.cpp b/src/Worker/Runner.cpp index 5d2b9e11..0b06b5b5 100644 --- a/src/Worker/Runner.cpp +++ b/src/Worker/Runner.cpp @@ -60,7 +60,7 @@ namespace Worker auto handler = this->handlers.find(command.name()); if (handler != this->handlers.end()) { - printf("Dispathcing command %s to handler\n", command.name().data()); + printf("Dispatching command %s to handler\n", command.name().data()); handler->second->handle(&channel, command.data()); } else From f81f820b470b0c319a241174a976921436e9c299 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 28 Jan 2017 19:21:55 +0100 Subject: [PATCH 05/16] [IPC] Experimental second handler --- src/Utils/IPC.cpp | 28 +++++++++++++++++++++++++--- src/Utils/IPC.hpp | 1 + 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Utils/IPC.cpp b/src/Utils/IPC.cpp index aa667db5..d1ad2fbe 100644 --- a/src/Utils/IPC.cpp +++ b/src/Utils/IPC.cpp @@ -9,13 +9,16 @@ namespace Utils if(this->remove) 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))); - this->queueThread = std::thread(&Channel::queueWorker, this); + this->queueThread = std::thread(&Channel::queueWorker2, this); } Channel::~Channel() { - this->terminateQueue = true; - this->queueEvent.notify_all(); + { + std::lock_guard _(this->queueMutex); + this->terminateQueue = true; + this->queueEvent.notify_all(); + } if(this->queueThread.joinable()) { @@ -110,6 +113,25 @@ namespace Utils } } + void Channel::queueWorker2() + { + while (!this->terminateQueue) + { + if(!this->internalQueue.empty()) + { + std::lock_guard lock(this->queueMutex); + + std::string data = this->internalQueue.front(); + if(this->queue->try_send(data.data(), data.size(), 0)) + { + this->internalQueue.pop(); + } + } + + std::this_thread::sleep_for(1000us); + } + } + void Channel::queueWorker() { while (!this->terminateQueue) diff --git a/src/Utils/IPC.hpp b/src/Utils/IPC.hpp index c12728db..ade2cc85 100644 --- a/src/Utils/IPC.hpp +++ b/src/Utils/IPC.hpp @@ -47,6 +47,7 @@ namespace Utils void enqueueMessage(std::string data); void queueWorker(); + void queueWorker2(); bool terminateQueue; std::condition_variable queueEvent; From cd15ad88cd8cf1a15d25ba4ef8371dfbb10c1e92 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 28 Jan 2017 19:22:39 +0100 Subject: [PATCH 06/16] [IPC] Use first worker --- src/Utils/IPC.cpp | 21 +-------------------- src/Utils/IPC.hpp | 1 - 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/Utils/IPC.cpp b/src/Utils/IPC.cpp index d1ad2fbe..5dd7d98d 100644 --- a/src/Utils/IPC.cpp +++ b/src/Utils/IPC.cpp @@ -9,7 +9,7 @@ namespace Utils if(this->remove) 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))); - this->queueThread = std::thread(&Channel::queueWorker2, this); + this->queueThread = std::thread(&Channel::queueWorker, this); } Channel::~Channel() @@ -113,25 +113,6 @@ namespace Utils } } - void Channel::queueWorker2() - { - while (!this->terminateQueue) - { - if(!this->internalQueue.empty()) - { - std::lock_guard lock(this->queueMutex); - - std::string data = this->internalQueue.front(); - if(this->queue->try_send(data.data(), data.size(), 0)) - { - this->internalQueue.pop(); - } - } - - std::this_thread::sleep_for(1000us); - } - } - void Channel::queueWorker() { while (!this->terminateQueue) diff --git a/src/Utils/IPC.hpp b/src/Utils/IPC.hpp index ade2cc85..c12728db 100644 --- a/src/Utils/IPC.hpp +++ b/src/Utils/IPC.hpp @@ -47,7 +47,6 @@ namespace Utils void enqueueMessage(std::string data); void queueWorker(); - void queueWorker2(); bool terminateQueue; std::condition_variable queueEvent; From 07469816bbbc4cea858c8e83ef20bbc979c573c6 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 28 Jan 2017 20:36:48 +0100 Subject: [PATCH 07/16] [Friends] Update friends when the game starts --- src/Components/Modules/Friends.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index 46e1dc20..69907d59 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -190,6 +190,8 @@ namespace Components Friends::Friends() { + Friends::UpdateFriends(); + // Callback to update user information Steam::Proxy::RegisterCallback(336, [](void* data) { From 4271e3b10859d7d15c0c548d3dab17667138c45e Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 28 Jan 2017 22:01:49 +0100 Subject: [PATCH 08/16] [SteamCallbacks] Transmit callbacks from the worker to the client --- src/Components/Modules/Friends.cpp | 29 +++++++++----- src/Steam/Proxy.cpp | 11 ++++-- src/Steam/Proxy.hpp | 2 +- src/Worker/Handlers/Friends.cpp | 1 + src/Worker/Handlers/SteamCallbacks.cpp | 55 ++++++++++++++++++++++++++ src/Worker/Handlers/SteamCallbacks.hpp | 22 +++++++++++ src/Worker/Runner.cpp | 8 +++- src/Worker/Runner.hpp | 3 ++ 8 files changed, 115 insertions(+), 16 deletions(-) create mode 100644 src/Worker/Handlers/SteamCallbacks.cpp create mode 100644 src/Worker/Handlers/SteamCallbacks.hpp diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index 69907d59..d4845512 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -192,18 +192,30 @@ namespace Components { Friends::UpdateFriends(); + auto fInterface = IPCHandler::NewInterface("steamCallbacks"); + // Callback to update user information - Steam::Proxy::RegisterCallback(336, [](void* data) + fInterface->map("304", [](std::vector params) { - Friends::FriendRichPresenceUpdate* update = static_cast(data); - Friends::UpdateUserInfo(update->m_steamIDFriend); + if (params.size() >= 1 && params[0].size() == sizeof(Friends::FriendRichPresenceUpdate)) + { + const Friends::FriendRichPresenceUpdate* update = reinterpret_cast(params[0].data()); + Friends::UpdateUserInfo(update->m_steamIDFriend); + } }); // Persona state has changed - Steam::Proxy::RegisterCallback(304, [](void* data) + fInterface->map("304", [](std::vector params) { - Friends::PersonaStateChange* state = static_cast(data); - if(Steam::Proxy::SteamFriends) Steam::Proxy::SteamFriends->RequestFriendRichPresence(state->m_ulSteamID); + if(params.size() >= 1 && params[0].size() == sizeof(Friends::PersonaStateChange)) + { + const Friends::PersonaStateChange* state = reinterpret_cast(params[0].data()); + + Proto::IPC::Function function; + function.set_name("requestPresence"); + *function.add_params() = Utils::String::VA("%llx", state->m_ulSteamID.Bits); + IPCHandler::SendWorker("friends", function.SerializeAsString()); + } }); UIScript::Add("LoadFriends", [](UIScript::Token) @@ -213,16 +225,13 @@ namespace Components UIFeeder::Add(6.0f, Friends::GetFriendCount, Friends::GetFriendText, Friends::SelectFriend); - auto fInterface = IPCHandler::NewInterface("friends"); + fInterface = IPCHandler::NewInterface("friends"); fInterface->map("friendsResponse", Friends::FriendsResponse); fInterface->map("nameResponse", Friends::NameResponse); } Friends::~Friends() { - Steam::Proxy::UnregisterCallback(304); - Steam::Proxy::UnregisterCallback(336); - { std::lock_guard _(Friends::Mutex); Friends::FriendsList.clear(); diff --git a/src/Steam/Proxy.cpp b/src/Steam/Proxy.cpp index e59b00ed..e3b409a8 100644 --- a/src/Steam/Proxy.cpp +++ b/src/Steam/Proxy.cpp @@ -82,7 +82,7 @@ namespace Steam Proxy::Callbacks.erase(callId); } - void Proxy::RunCallback(int32_t callId, void* data) + void Proxy::RunCallback(int32_t callId, void* data, size_t size) { std::lock_guard _(Proxy::CallMutex); @@ -91,6 +91,11 @@ namespace Steam { ::Utils::Hook::Call(callback->second)(data); } + + if(Worker::IsWorker()) + { + Handlers::SteamCallbacks::HandleCallback(callId, data, size); + } } void Proxy::RunFrame() @@ -110,7 +115,7 @@ namespace Steam #endif //Steam::Callbacks::RunCallback(message.m_iCallback, message.m_pubParam); - Proxy::RunCallback(message.m_iCallback, message.m_pubParam); + Proxy::RunCallback(message.m_iCallback, message.m_pubParam, message.m_cubParam); Proxy::SteamFreeLastCallback(Proxy::SteamPipe); } @@ -151,7 +156,7 @@ namespace Steam continue; } - Proxy::RunCallback(call.callId, buffer); + Proxy::RunCallback(call.callId, buffer, call.dataSize); } } } diff --git a/src/Steam/Proxy.hpp b/src/Steam/Proxy.hpp index fe974175..6945f97b 100644 --- a/src/Steam/Proxy.hpp +++ b/src/Steam/Proxy.hpp @@ -396,7 +396,7 @@ namespace Steam static std::function SteamFreeLastCallback; static std::function SteamGetAPICallResult; - static void RunCallback(int32_t callId, void* data); + static void RunCallback(int32_t callId, void* data, size_t size); static void UnregisterCalls(); diff --git a/src/Worker/Handlers/Friends.cpp b/src/Worker/Handlers/Friends.cpp index 96728fdb..fe7ef36e 100644 --- a/src/Worker/Handlers/Friends.cpp +++ b/src/Worker/Handlers/Friends.cpp @@ -92,6 +92,7 @@ namespace Handlers Proto::IPC::Function response; response.set_name("presenceResponse"); *response.add_params() = Utils::String::VA("%llX", id.Bits); + *response.add_params() = params[1].data(); *response.add_params() = Steam::Proxy::SteamFriends->GetFriendRichPresence(id, params[1].data()); endpoint.send(this->getCommand(), response.SerializeAsString()); diff --git a/src/Worker/Handlers/SteamCallbacks.cpp b/src/Worker/Handlers/SteamCallbacks.cpp new file mode 100644 index 00000000..e2ed0ac1 --- /dev/null +++ b/src/Worker/Handlers/SteamCallbacks.cpp @@ -0,0 +1,55 @@ +#include "STDInclude.hpp" + +namespace Handlers +{ + void SteamCallbacks::handle(Worker::Endpoint endpoint, std::string data) + { + Proto::IPC::Function function; + if (function.ParseFromString(data)) + { + auto handler = this->functions.find(function.name()); + if (handler != this->functions.end()) + { + printf("Handing function %s\n", function.name().data()); + + auto params = function.params(); + handler->second(endpoint, std::vector(params.begin(), params.end())); + } + else + { + printf("No handler for function %s\n", function.name().data()); + } + } + } + + void SteamCallbacks::addFunction(std::string function, Friends::Callback callback) + { + this->functions[function] = callback; + } + + void SteamCallbacks::HandleCallback(int32_t callId, void* data, size_t size) + { + if(Worker::Runner::Channel) + { + Proto::IPC::Function response; + response.set_name(Utils::String::VA("%d", callId)); + response.add_params()->append(static_cast(data), size); + + Proto::IPC::Command command; + command.set_name(SteamCallbacks().getCommand()); + command.set_data(response.SerializeAsString()); + + Worker::Runner::Channel->send(command.SerializeAsString()); + } + } + + SteamCallbacks::SteamCallbacks() + { + + } + + SteamCallbacks::~SteamCallbacks() + { + + } +} diff --git a/src/Worker/Handlers/SteamCallbacks.hpp b/src/Worker/Handlers/SteamCallbacks.hpp new file mode 100644 index 00000000..968510ea --- /dev/null +++ b/src/Worker/Handlers/SteamCallbacks.hpp @@ -0,0 +1,22 @@ +#pragma once + +namespace Handlers +{ + class SteamCallbacks : public Worker::Runner::Handler + { + public: + typedef std::function)> Callback; + + SteamCallbacks(); + ~SteamCallbacks(); + + std::string getCommand() override { return "steamCallbacks"; }; + void handle(Worker::Endpoint endpoint, std::string data) override; + + static void HandleCallback(int32_t callId, void* data, size_t size); + + private: + std::unordered_map functions; + void addFunction(std::string function, Callback callback); + }; +} diff --git a/src/Worker/Runner.cpp b/src/Worker/Runner.cpp index 0b06b5b5..afd27a15 100644 --- a/src/Worker/Runner.cpp +++ b/src/Worker/Runner.cpp @@ -2,14 +2,16 @@ namespace Worker { + Utils::IPC::BidirectionalChannel* Runner::Channel; + Runner::Runner(int pid) : processId(pid), terminate(false) { - + Runner::Channel = nullptr; } Runner::~Runner() { - + Runner::Channel = nullptr; } void Runner::run() @@ -46,6 +48,7 @@ namespace Worker { printf("Worker started\n"); Utils::IPC::BidirectionalChannel channel("IW4x-Worker-Channel", !Worker::IsWorker()); + Runner::Channel = &channel; while (!this->terminate) { @@ -74,5 +77,6 @@ namespace Worker } printf("Terminating worker\n"); + Runner::Channel = nullptr; } } diff --git a/src/Worker/Runner.hpp b/src/Worker/Runner.hpp index 328aa529..31ad6731 100644 --- a/src/Worker/Runner.hpp +++ b/src/Worker/Runner.hpp @@ -43,6 +43,8 @@ namespace Worker void attachHandler(Runner::Handler* handler); + static Utils::IPC::BidirectionalChannel* Channel; + private: void worker(); @@ -53,3 +55,4 @@ namespace Worker } #include "Handlers/Friends.hpp" +#include "Handlers/SteamCallbacks.hpp" From 4e587e91a041ecc2d4bef2cf7ce19531c1547b18 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 29 Jan 2017 00:31:11 +0100 Subject: [PATCH 09/16] [Friends] Correctly transmit presence data --- src/Components/Modules/Friends.cpp | 77 ++++++++++++++++++++++-------- src/Components/Modules/Friends.hpp | 3 ++ src/Worker/Handlers/Friends.cpp | 13 ++++- src/Worker/Worker.cpp | 1 + 4 files changed, 72 insertions(+), 22 deletions(-) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index d4845512..e6094454 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -8,11 +8,8 @@ namespace Components void Friends::UpdateUserInfo(SteamID user) { - if (!Steam::Proxy::SteamFriends) return; std::lock_guard _(Friends::Mutex); - Friends::Friend userInfo; - auto i = std::find_if(Friends::FriendsList.begin(), Friends::FriendsList.end(), [user] (Friends::Friend entry) { return (entry.userId.Bits == user.Bits); @@ -20,28 +17,30 @@ namespace Components if(i != Friends::FriendsList.end()) { - userInfo = *i; + Proto::IPC::Function function; + + function.set_name("getPresence"); + *function.add_params() = Utils::String::VA("%llx", user.Bits); + + std::string* key = function.add_params(); + + *key = "iw4x_status"; + IPCHandler::SendWorker("friends", function.SerializeAsString()); + + *key = "iw4x_rank"; + IPCHandler::SendWorker("friends", function.SerializeAsString()); + + *key = "iw4x_server"; + IPCHandler::SendWorker("friends", function.SerializeAsString()); } - userInfo.userId = user; - userInfo.online = Steam::Proxy::SteamFriends->GetFriendPersonaState(user) != 0; + /*userInfo.online = Steam::Proxy::SteamFriends->GetFriendPersonaState(user) != 0; userInfo.name = Steam::Proxy::SteamFriends->GetFriendPersonaName(user); userInfo.statusName = Steam::Proxy::SteamFriends->GetFriendRichPresence(user, "iw4x_status"); userInfo.prestige = Utils::Cryptography::Rand::GenerateInt() % (10 + 1); - userInfo.experience = Utils::Cryptography::Rand::GenerateInt() % (2516000 + 1); + userInfo.experience = Utils::Cryptography::Rand::GenerateInt() % (2516000 + 1);*/ - //if (!userInfo.online) return; - - if (i != Friends::FriendsList.end()) - { - *i = userInfo; - } - else - { - Friends::FriendsList.push_back(userInfo); - } - - qsort(Friends::FriendsList.data(), Friends::FriendsList.size(), sizeof(Friends::Friend), [](const void* first, const void* second) +/* qsort(Friends::FriendsList.data(), Friends::FriendsList.size(), sizeof(Friends::Friend), [](const void* first, const void* second) { const Friends::Friend* friend1 = static_cast(first); const Friends::Friend* friend2 = static_cast(second); @@ -50,7 +49,7 @@ namespace Components std::string name2 = Utils::String::ToLower(Colors::Strip(friend2->name)); return name1.compare(name2); - }); + });*/ } void Friends::UpdateFriends() @@ -142,6 +141,41 @@ namespace Components } } } + + void Friends::PresenceResponse(std::vector params) + { + if (params.size() >= 3) + { + std::lock_guard _(Friends::Mutex); + + SteamID id; + id.Bits = strtoull(params[0].data(), nullptr, 16); + std::string key = params[1]; + std::string value = params[2]; + + auto entry = std::find_if(Friends::FriendsList.begin(), Friends::FriendsList.end(), [id](Friends::Friend entry) + { + return (entry.userId.Bits == id.Bits); + }); + + if (entry == Friends::FriendsList.end()) return; + + if(key == "iw4x_status") + { + entry->statusName = value; + } + else if(key == "iw4x_rank") + { + if(value.size() == 4) + { + int data = *reinterpret_cast(const_cast(value.data())); + + entry->experience = data & 0xFFFFFF; + entry->prestige = (data >> 24) & 0xFF; + } + } + } + } void Friends::FriendsResponse(std::vector params) { @@ -195,7 +229,7 @@ namespace Components auto fInterface = IPCHandler::NewInterface("steamCallbacks"); // Callback to update user information - fInterface->map("304", [](std::vector params) + fInterface->map("336", [](std::vector params) { if (params.size() >= 1 && params[0].size() == sizeof(Friends::FriendRichPresenceUpdate)) { @@ -228,6 +262,7 @@ namespace Components fInterface = IPCHandler::NewInterface("friends"); fInterface->map("friendsResponse", Friends::FriendsResponse); fInterface->map("nameResponse", Friends::NameResponse); + fInterface->map("presenceResponse", Friends::PresenceResponse); } Friends::~Friends() diff --git a/src/Components/Modules/Friends.hpp b/src/Components/Modules/Friends.hpp index cd08cad6..3e122300 100644 --- a/src/Components/Modules/Friends.hpp +++ b/src/Components/Modules/Friends.hpp @@ -15,11 +15,13 @@ namespace Components static void UpdateFriends(); private: +#pragma pack(push, 4) struct FriendRichPresenceUpdate { SteamID m_steamIDFriend; // friend who's rich presence has changed int32_t m_nAppID; // the appID of the game (should always be the current game) }; +#pragma pack(pop) struct PersonaStateChange { @@ -51,5 +53,6 @@ namespace Components static void FriendsResponse(std::vector params); static void NameResponse(std::vector params); + static void PresenceResponse(std::vector params); }; } diff --git a/src/Worker/Handlers/Friends.cpp b/src/Worker/Handlers/Friends.cpp index fe7ef36e..f6020fdd 100644 --- a/src/Worker/Handlers/Friends.cpp +++ b/src/Worker/Handlers/Friends.cpp @@ -93,7 +93,18 @@ namespace Handlers response.set_name("presenceResponse"); *response.add_params() = Utils::String::VA("%llX", id.Bits); *response.add_params() = params[1].data(); - *response.add_params() = Steam::Proxy::SteamFriends->GetFriendRichPresence(id, params[1].data()); + + std::string* value = response.add_params(); + *value = Steam::Proxy::SteamFriends->GetFriendRichPresence(id, params[1].data()); + + if (params[1] == "iw4x_rank") + { + int experience = Utils::Cryptography::Rand::GenerateInt() % (2516000 + 1); + int prestige = Utils::Cryptography::Rand::GenerateInt() % (10 + 1); + + int data = (experience & 0xFFFFFF) | ((prestige << 24) & 0xFF); + *value = std::string(reinterpret_cast(&data), 4); + } endpoint.send(this->getCommand(), response.SerializeAsString()); } diff --git a/src/Worker/Worker.cpp b/src/Worker/Worker.cpp index fbf9792e..3d85042e 100644 --- a/src/Worker/Worker.cpp +++ b/src/Worker/Worker.cpp @@ -8,6 +8,7 @@ namespace Worker { Runner runner(Worker::ProcessId); runner.attachHandler(new Handlers::Friends()); + runner.attachHandler(new Handlers::SteamCallbacks()); runner.run(); return 0; } From 7a9bda58c235fc0666b1384a879c30e509b3b017 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 29 Jan 2017 11:02:10 +0100 Subject: [PATCH 10/16] [RCon] Remove console spam --- src/Components/Modules/RCon.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Components/Modules/RCon.cpp b/src/Components/Modules/RCon.cpp index 8d76aee2..4d734553 100644 --- a/src/Components/Modules/RCon.cpp +++ b/src/Components/Modules/RCon.cpp @@ -106,7 +106,9 @@ namespace Components static std::string outputBuffer; outputBuffer.clear(); +#ifdef DEBUG Logger::Print("Executing RCon request from %s: %s\n", address.getCString(), command.data()); +#endif Logger::PipeOutput([] (std::string output) { From adedc4088f020ad7c6c7fe120afca5aefac8b685 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 29 Jan 2017 11:02:29 +0100 Subject: [PATCH 11/16] [IPCHandler] Only run worker for the first instance --- src/Components/Modules/IPCHandler.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Components/Modules/IPCHandler.cpp b/src/Components/Modules/IPCHandler.cpp index 31f2d338..1177fe11 100644 --- a/src/Components/Modules/IPCHandler.cpp +++ b/src/Components/Modules/IPCHandler.cpp @@ -20,6 +20,7 @@ namespace Components void IPCHandler::SendWorker(std::string message, std::string data) { IPCHandler::InitChannels(); + if (!Singleton::IsFirstInstance()) return; Proto::IPC::Command command; command.set_name(message); @@ -51,9 +52,12 @@ namespace Components void IPCHandler::InitChannels() { - if (!IPCHandler::WorkerChannel) + if (Singleton::IsFirstInstance()) { - IPCHandler::WorkerChannel.reset(new Utils::IPC::BidirectionalChannel("IW4x-Worker-Channel", !Worker::IsWorker())); + if (!IPCHandler::WorkerChannel) + { + IPCHandler::WorkerChannel.reset(new Utils::IPC::BidirectionalChannel("IW4x-Worker-Channel", !Worker::IsWorker())); + } } if (!IPCHandler::ClientChannel) @@ -64,6 +68,8 @@ namespace Components void IPCHandler::StartWorker() { + if (!Singleton::IsFirstInstance()) return; + STARTUPINFOA sInfo; PROCESS_INFORMATION pInfo; @@ -99,6 +105,7 @@ namespace Components void IPCHandler::HandleWorker() { IPCHandler::InitChannels(); + if (!Singleton::IsFirstInstance()) return; std::string packet; if (IPCHandler::WorkerChannel->receive(&packet)) From 0139465337b0987f998ad164917e5050f10148dc Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 29 Jan 2017 11:40:20 +0100 Subject: [PATCH 12/16] [Friends] Faster update --- src/Components/Modules/Friends.cpp | 112 ++++++++++++++++------------- src/Components/Modules/Friends.hpp | 1 + src/Worker/Handlers/Friends.cpp | 56 +++++++++++---- src/Worker/Handlers/Friends.hpp | 1 + 4 files changed, 108 insertions(+), 62 deletions(-) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index e6094454..a6d2aedf 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -8,48 +8,19 @@ namespace Components void Friends::UpdateUserInfo(SteamID user) { - std::lock_guard _(Friends::Mutex); - - auto i = std::find_if(Friends::FriendsList.begin(), Friends::FriendsList.end(), [user] (Friends::Friend entry) - { - return (entry.userId.Bits == user.Bits); - }); - - if(i != Friends::FriendsList.end()) - { Proto::IPC::Function function; - function.set_name("getPresence"); + function.set_name("getInfo"); *function.add_params() = Utils::String::VA("%llx", user.Bits); - std::string* key = function.add_params(); + *function.add_params() = "name"; + *function.add_params() = "state"; + *function.add_params() = "iw4x_name"; + *function.add_params() = "iw4x_status"; + *function.add_params() = "iw4x_rank"; + *function.add_params() = "iw4x_server"; - *key = "iw4x_status"; IPCHandler::SendWorker("friends", function.SerializeAsString()); - - *key = "iw4x_rank"; - IPCHandler::SendWorker("friends", function.SerializeAsString()); - - *key = "iw4x_server"; - IPCHandler::SendWorker("friends", function.SerializeAsString()); - } - - /*userInfo.online = Steam::Proxy::SteamFriends->GetFriendPersonaState(user) != 0; - userInfo.name = Steam::Proxy::SteamFriends->GetFriendPersonaName(user); - userInfo.statusName = Steam::Proxy::SteamFriends->GetFriendRichPresence(user, "iw4x_status"); - userInfo.prestige = Utils::Cryptography::Rand::GenerateInt() % (10 + 1); - userInfo.experience = Utils::Cryptography::Rand::GenerateInt() % (2516000 + 1);*/ - -/* qsort(Friends::FriendsList.data(), Friends::FriendsList.size(), sizeof(Friends::Friend), [](const void* first, const void* second) - { - const Friends::Friend* friend1 = static_cast(first); - const Friends::Friend* friend2 = static_cast(second); - - std::string name1 = Utils::String::ToLower(Colors::Strip(friend1->name)); - std::string name2 = Utils::String::ToLower(Colors::Strip(friend2->name)); - - return name1.compare(name2); - });*/ } void Friends::UpdateFriends() @@ -160,13 +131,24 @@ namespace Components if (entry == Friends::FriendsList.end()) return; - if(key == "iw4x_status") + if (key == "iw4x_status") { entry->statusName = value; } - else if(key == "iw4x_rank") + else if (key == "iw4x_server") { - if(value.size() == 4) + entry->server = value; + + // TODO: Query server here? + if (entry->server.getType() != Game::NA_BAD) + { + Node::AddNode(entry->server); + Network::SendCommand(entry->server, "getinfo", Utils::Cryptography::Rand::GenerateChallenge()); + } + } + else if (key == "iw4x_rank") + { + if (value.size() == 4) { int data = *reinterpret_cast(const_cast(value.data())); @@ -177,6 +159,44 @@ namespace Components } } + void Friends::InfoResponse(std::vector params) + { + if (params.size() >= 1) + { + std::lock_guard _(Friends::Mutex); + + SteamID id; + id.Bits = strtoull(params[0].data(), nullptr, 16); + + auto entry = std::find_if(Friends::FriendsList.begin(), Friends::FriendsList.end(), [id](Friends::Friend entry) + { + return (entry.userId.Bits == id.Bits); + }); + + if (entry == Friends::FriendsList.end()) return; + + for(unsigned int i = 1; i < params.size(); i += 2) + { + if ((i + 1) >= params.size()) break; + std::string key = params[i]; + std::string value = params[i + 1]; + + if(key == "name") + { + entry->name = value; + } + else if(key == "state") + { + entry->online = atoi(value.data()) != 0; + } + else + { + Friends::PresenceResponse({ Utils::String::VA("%llx", id.Bits), key, value }); + } + } + } + } + void Friends::FriendsResponse(std::vector params) { std::lock_guard _(Friends::Mutex); @@ -204,18 +224,9 @@ namespace Components Friends::FriendsList.push_back(entry); + Friends::UpdateUserInfo(id); + Proto::IPC::Function function; - function.set_name("getName"); - *function.add_params() = Utils::String::VA("%llx", id.Bits); - IPCHandler::SendWorker("friends", function.SerializeAsString()); - - function.Clear(); - function.set_name("getPresence"); - *function.add_params() = Utils::String::VA("%llx", id.Bits); - *function.add_params() = "iw4x_status"; - IPCHandler::SendWorker("friends", function.SerializeAsString()); - - function.Clear(); function.set_name("requestPresence"); *function.add_params() = Utils::String::VA("%llx", id.Bits); IPCHandler::SendWorker("friends", function.SerializeAsString()); @@ -263,6 +274,7 @@ namespace Components fInterface->map("friendsResponse", Friends::FriendsResponse); fInterface->map("nameResponse", Friends::NameResponse); fInterface->map("presenceResponse", Friends::PresenceResponse); + fInterface->map("infoResponse", Friends::InfoResponse); } Friends::~Friends() diff --git a/src/Components/Modules/Friends.hpp b/src/Components/Modules/Friends.hpp index 3e122300..ca377471 100644 --- a/src/Components/Modules/Friends.hpp +++ b/src/Components/Modules/Friends.hpp @@ -54,5 +54,6 @@ namespace Components static void FriendsResponse(std::vector params); static void NameResponse(std::vector params); static void PresenceResponse(std::vector params); + static void InfoResponse(std::vector params); }; } diff --git a/src/Worker/Handlers/Friends.cpp b/src/Worker/Handlers/Friends.cpp index f6020fdd..7c63bc10 100644 --- a/src/Worker/Handlers/Friends.cpp +++ b/src/Worker/Handlers/Friends.cpp @@ -93,18 +93,7 @@ namespace Handlers response.set_name("presenceResponse"); *response.add_params() = Utils::String::VA("%llX", id.Bits); *response.add_params() = params[1].data(); - - std::string* value = response.add_params(); - *value = Steam::Proxy::SteamFriends->GetFriendRichPresence(id, params[1].data()); - - if (params[1] == "iw4x_rank") - { - int experience = Utils::Cryptography::Rand::GenerateInt() % (2516000 + 1); - int prestige = Utils::Cryptography::Rand::GenerateInt() % (10 + 1); - - int data = (experience & 0xFFFFFF) | ((prestige << 24) & 0xFF); - *value = std::string(reinterpret_cast(&data), 4); - } + *response.add_params() = Steam::Proxy::SteamFriends->GetFriendRichPresence(id, params[1].data()); endpoint.send(this->getCommand(), response.SerializeAsString()); } @@ -121,6 +110,48 @@ namespace Handlers } } + void Friends::getInfo(Worker::Endpoint endpoint, std::vector params) + { + if (params.size() >= 1 && Steam::Proxy::SteamFriends) + { + SteamID id; + id.Bits = strtoull(params[0].data(), nullptr, 16); + + Proto::IPC::Function response; + response.set_name("infoResponse"); + *response.add_params() = Utils::String::VA("%llX", id.Bits); + + for(unsigned int i = 1; i < params.size(); ++i) + { + std::string key = params[i]; + *response.add_params() = key; + + if(key == "name") + { + *response.add_params() = Steam::Proxy::SteamFriends->GetFriendPersonaName(id); + } + else if(key == "state") + { + *response.add_params() = Utils::String::VA("%d", Steam::Proxy::SteamFriends->GetFriendPersonaState(id)); + } + else if (key == "iw4x_rank") // This is just a test + { + int experience = Utils::Cryptography::Rand::GenerateInt() % (2516000 + 1); + int prestige = Utils::Cryptography::Rand::GenerateInt() % (10 + 1); + + int data = (experience & 0xFFFFFF) | ((prestige << 24) & 0xFF); + *response.add_params() = std::string(reinterpret_cast(&data), 4); + } + else + { + *response.add_params() = Steam::Proxy::SteamFriends->GetFriendRichPresence(id, key.data()); + } + } + + endpoint.send(this->getCommand(), response.SerializeAsString()); + } + } + Friends::Friends() { using namespace std::placeholders; @@ -129,6 +160,7 @@ namespace Handlers this->addFunction("setPresence", std::bind(&Friends::setPresence, this, _1, _2)); this->addFunction("getPresence", std::bind(&Friends::getPresence, this, _1, _2)); this->addFunction("requestPresence", std::bind(&Friends::requestPresence, this, _1, _2)); + this->addFunction("getInfo", std::bind(&Friends::getInfo, this, _1, _2)); } Friends::~Friends() diff --git a/src/Worker/Handlers/Friends.hpp b/src/Worker/Handlers/Friends.hpp index 3a6e0435..0e15cdd9 100644 --- a/src/Worker/Handlers/Friends.hpp +++ b/src/Worker/Handlers/Friends.hpp @@ -22,5 +22,6 @@ namespace Handlers void setPresence(Worker::Endpoint endpoint, std::vector params); void getPresence(Worker::Endpoint endpoint, std::vector params); void requestPresence(Worker::Endpoint endpoint, std::vector params); + void getInfo(Worker::Endpoint endpoint, std::vector params); }; } From 67d963a0c8ec59bd507fbb0f152547b7d792c690 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 29 Jan 2017 11:48:42 +0100 Subject: [PATCH 13/16] [IPC] Better worker cleanup --- src/Utils/IPC.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Utils/IPC.cpp b/src/Utils/IPC.cpp index 5dd7d98d..3822db53 100644 --- a/src/Utils/IPC.cpp +++ b/src/Utils/IPC.cpp @@ -14,13 +14,12 @@ namespace Utils Channel::~Channel() { - { - std::lock_guard _(this->queueMutex); - this->terminateQueue = true; - this->queueEvent.notify_all(); - } + std::unique_lock lock(this->queueMutex); + this->terminateQueue = true; + this->queueEvent.notify_all(); + lock.unlock(); - if(this->queueThread.joinable()) + if (this->queueThread.joinable()) { this->queueThread.join(); } From 7d24aa8fe0d6ed1562a6e88626bbecbd80fe9e84 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 29 Jan 2017 15:10:54 +0100 Subject: [PATCH 14/16] [Friends] Transmit rank --- src/Components/Modules/Friends.cpp | 40 +++++++++++++++++++++++++-- src/Components/Modules/Friends.hpp | 2 ++ src/Game/Functions.cpp | 20 ++++++++++++++ src/Game/Functions.hpp | 7 +++++ src/Steam/Interfaces/SteamFriends.hpp | 10 +++++++ src/Steam/Proxy.cpp | 4 +++ src/Steam/Proxy.hpp | 1 + src/Worker/Handlers/Friends.cpp | 26 ++++++++++++++--- src/Worker/Handlers/Friends.hpp | 3 ++ 9 files changed, 107 insertions(+), 6 deletions(-) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index a6d2aedf..9bf3e800 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -23,6 +23,34 @@ namespace Components IPCHandler::SendWorker("friends", function.SerializeAsString()); } + void Friends::UpdateState() + { + Proto::IPC::Function function; + function.set_name("notifyChange"); + IPCHandler::SendWorker("friends", function.SerializeAsString()); + } + + void Friends::UpdateRank() + { + static Utils::Value levelVal; + + int experience = Game::Live_GetXp(0); + int prestige = Game::Live_GetPrestige(0); + int level = (experience & 0xFFFFFF) | ((prestige & 0xFF) << 24); + + if(!levelVal.isValid() || levelVal.get() != level) + { + levelVal.set(level); + + Proto::IPC::Function function; + function.set_name("setPresence"); + *function.add_params() = "iw4x_rank"; + *function.add_params() = std::string(reinterpret_cast(&level), 4); + + IPCHandler::SendWorker("friends", function.SerializeAsString()); + } + } + void Friends::UpdateFriends() { Proto::IPC::Function function; @@ -55,6 +83,7 @@ namespace Components Game::Material* rankIcon = nullptr; int rank = Game::CL_GetRankForXP(user.experience); Game::CL_GetRankIcon(rank, user.prestige, &rankIcon); + if (!rankIcon) rankIcon = Game::DB_FindXAssetDefaultHeaderInternal(Game::XAssetType::ASSET_TYPE_MATERIAL).material; buffer[0] = '^'; buffer[1] = 2; @@ -68,7 +97,7 @@ namespace Components buffer[4] = static_cast(strlen(rankIcon->name)); strcat_s(buffer, rankIcon->name); - strcat_s(buffer, Utils::String::VA(" %i", rank)); + strcat_s(buffer, Utils::String::VA(" %i", (rank + 1))); return buffer; } @@ -139,7 +168,6 @@ namespace Components { entry->server = value; - // TODO: Query server here? if (entry->server.getType() != Game::NA_BAD) { Node::AddNode(entry->server); @@ -268,6 +296,14 @@ namespace Components Friends::UpdateFriends(); }); + QuickPatch::OnFrame([]() + { + if(*reinterpret_cast(0x1AD5690)) // LiveStorage_DoWeHaveStats + { + Friends::UpdateRank(); + } + }); + UIFeeder::Add(6.0f, Friends::GetFriendCount, Friends::GetFriendText, Friends::SelectFriend); fInterface = IPCHandler::NewInterface("friends"); diff --git a/src/Components/Modules/Friends.hpp b/src/Components/Modules/Friends.hpp index ca377471..6c3ba192 100644 --- a/src/Components/Modules/Friends.hpp +++ b/src/Components/Modules/Friends.hpp @@ -13,6 +13,7 @@ namespace Components #endif static void UpdateFriends(); + static void UpdateRank(); private: #pragma pack(push, 4) @@ -46,6 +47,7 @@ namespace Components static std::vector FriendsList; static void UpdateUserInfo(SteamID user); + static void UpdateState(); static unsigned int GetFriendCount(); static const char* GetFriendText(unsigned int index, int column); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index fbc74237..40f6a8e4 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -164,6 +164,8 @@ namespace Game Live_MPAcceptInvite_t Live_MPAcceptInvite = Live_MPAcceptInvite_t(0x420A6D); Live_GetMapIndex_t Live_GetMapIndex = Live_GetMapIndex_t(0x4F6440); + Live_GetPrestige_t Live_GetPrestige = Live_GetPrestige_t(0x430F90); + Live_GetXp_t Live_GetXp = Live_GetXp_t(0x404C60); LoadModdableRawfile_t LoadModdableRawfile = LoadModdableRawfile_t(0x61ABC0); @@ -461,6 +463,24 @@ namespace Game return false; } + XAssetHeader DB_FindXAssetDefaultHeaderInternal(XAssetType _type) + { + // ReSharper disable once CppEntityNeverUsed + static int func = 0x5BB210; + XAssetHeader result; + + __asm + { + push edi + mov edi, _type + call func + pop edi + mov result, eax + } + + return result; + } + void FS_AddLocalizedGameDirectory(const char *path, const char *dir) { __asm diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index ae0c44f5..f67eeb41 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -412,6 +412,12 @@ namespace Game typedef int(__cdecl * Live_GetMapIndex_t)(const char* mapname); extern Live_GetMapIndex_t Live_GetMapIndex; + typedef int(__cdecl * Live_GetPrestige_t)(int controllerIndex); + extern Live_GetPrestige_t Live_GetPrestige; + + typedef int(__cdecl * Live_GetXp_t)(int controllerIndex); + extern Live_GetXp_t Live_GetXp; + typedef char* (__cdecl * LoadModdableRawfile_t)(int a1, const char* filename); extern LoadModdableRawfile_t LoadModdableRawfile; @@ -697,6 +703,7 @@ namespace Game const char *DB_GetXAssetName(XAsset *asset); XAssetType DB_GetXAssetNameType(const char* name); bool DB_IsZoneLoaded(const char* zone); + XAssetHeader DB_FindXAssetDefaultHeaderInternal(XAssetType type); void FS_AddLocalizedGameDirectory(const char *path, const char *dir); diff --git a/src/Steam/Interfaces/SteamFriends.hpp b/src/Steam/Interfaces/SteamFriends.hpp index cc831ef6..15d41ac8 100644 --- a/src/Steam/Interfaces/SteamFriends.hpp +++ b/src/Steam/Interfaces/SteamFriends.hpp @@ -105,4 +105,14 @@ namespace Steam virtual uint64_t IsFollowing(SteamID steamID) = 0; virtual uint64_t EnumerateFollowingList(uint32_t unStartIndex) = 0; }; + + class Friends2 + { + public: + virtual const char *GetPersonaName() = 0; + virtual void SetPersonaName(const char *pchPersonaName) = 0; + virtual int GetPersonaState() = 0; + virtual void SetPersonaState(int ePersonaState) = 0; + // [...] + }; } diff --git a/src/Steam/Proxy.cpp b/src/Steam/Proxy.cpp index e3b409a8..31ab2b9e 100644 --- a/src/Steam/Proxy.cpp +++ b/src/Steam/Proxy.cpp @@ -13,6 +13,7 @@ namespace Steam void* Proxy::SteamUser = nullptr; Friends15* Proxy::SteamFriends = nullptr; + Friends2* Proxy::SteamLegacyFriends = nullptr; Utils* Proxy::SteamUtils = nullptr; uint32_t Proxy::AppId = 0; @@ -208,6 +209,9 @@ namespace Steam Proxy::SteamFriends = reinterpret_cast(Proxy::SteamClient->GetISteamFriends(Proxy::SteamUser, Proxy::SteamPipe, "SteamFriends015")); if (!Proxy::SteamFriends) return false; + Proxy::SteamLegacyFriends = reinterpret_cast(Proxy::SteamClient->GetISteamFriends(Proxy::SteamUser, Proxy::SteamPipe, "SteamFriends002")); + if (!Proxy::SteamLegacyFriends) return false; + Proxy::SteamUtils = reinterpret_cast(Proxy::SteamClient->GetISteamFriends(Proxy::SteamUser, Proxy::SteamPipe, "SteamUtils005")); if (!Proxy::SteamUtils) return false; diff --git a/src/Steam/Proxy.hpp b/src/Steam/Proxy.hpp index 6945f97b..35d11f23 100644 --- a/src/Steam/Proxy.hpp +++ b/src/Steam/Proxy.hpp @@ -353,6 +353,7 @@ namespace Steam static void UnregisterCallback(int32_t callId); static Friends15* SteamFriends; + static Friends2* SteamLegacyFriends; static Utils* SteamUtils; private: diff --git a/src/Worker/Handlers/Friends.cpp b/src/Worker/Handlers/Friends.cpp index 7c63bc10..df8a252a 100644 --- a/src/Worker/Handlers/Friends.cpp +++ b/src/Worker/Handlers/Friends.cpp @@ -110,6 +110,13 @@ namespace Handlers } } + void Friends::notifyChange(Worker::Endpoint /*endpoint*/, std::vector params) + { + // Ugly, but for now it works + int state = Steam::Proxy::SteamLegacyFriends->GetPersonaState(); + Steam::Proxy::SteamLegacyFriends->SetPersonaState((state == 1 ? 2 : 1)); + } + void Friends::getInfo(Worker::Endpoint endpoint, std::vector params) { if (params.size() >= 1 && Steam::Proxy::SteamFriends) @@ -134,14 +141,14 @@ namespace Handlers { *response.add_params() = Utils::String::VA("%d", Steam::Proxy::SteamFriends->GetFriendPersonaState(id)); } - else if (key == "iw4x_rank") // This is just a test + /*else if (key == "iw4x_rank") // This is just a test { int experience = Utils::Cryptography::Rand::GenerateInt() % (2516000 + 1); int prestige = Utils::Cryptography::Rand::GenerateInt() % (10 + 1); - int data = (experience & 0xFFFFFF) | ((prestige << 24) & 0xFF); + int data = (experience & 0xFFFFFF) | ((prestige & 0xFF) << 24); *response.add_params() = std::string(reinterpret_cast(&data), 4); - } + }*/ else { *response.add_params() = Steam::Proxy::SteamFriends->GetFriendRichPresence(id, key.data()); @@ -152,7 +159,7 @@ namespace Handlers } } - Friends::Friends() + Friends::Friends() : personaState(1) { using namespace std::placeholders; this->addFunction("getFriends", std::bind(&Friends::getFriends, this, _1, _2)); @@ -161,6 +168,12 @@ namespace Handlers this->addFunction("getPresence", std::bind(&Friends::getPresence, this, _1, _2)); this->addFunction("requestPresence", std::bind(&Friends::requestPresence, this, _1, _2)); this->addFunction("getInfo", std::bind(&Friends::getInfo, this, _1, _2)); + this->addFunction("notifyChange", std::bind(&Friends::notifyChange, this, _1, _2)); + + if (Steam::Proxy::SteamLegacyFriends) + { + this->personaState = Steam::Proxy::SteamLegacyFriends->GetPersonaState(); + } } Friends::~Friends() @@ -169,5 +182,10 @@ namespace Handlers { Steam::Proxy::SteamFriends->ClearRichPresence(); } + + if(Steam::Proxy::SteamLegacyFriends) + { + Steam::Proxy::SteamLegacyFriends->SetPersonaState(this->personaState); + } } } diff --git a/src/Worker/Handlers/Friends.hpp b/src/Worker/Handlers/Friends.hpp index 0e15cdd9..df32d676 100644 --- a/src/Worker/Handlers/Friends.hpp +++ b/src/Worker/Handlers/Friends.hpp @@ -14,6 +14,8 @@ namespace Handlers void handle(Worker::Endpoint endpoint, std::string data) override; private: + int personaState; + std::unordered_map functions; void addFunction(std::string function, Callback callback); @@ -23,5 +25,6 @@ namespace Handlers void getPresence(Worker::Endpoint endpoint, std::vector params); void requestPresence(Worker::Endpoint endpoint, std::vector params); void getInfo(Worker::Endpoint endpoint, std::vector params); + void notifyChange(Worker::Endpoint /*endpoint*/, std::vector params); }; } From f20fa5763c8307a3c961eb26cf144ef23ca20668 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 29 Jan 2017 15:26:46 +0100 Subject: [PATCH 15/16] [Friends] Trigger state change when updating stats --- src/Components/Modules/Friends.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index 9bf3e800..07f63326 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -48,6 +48,8 @@ namespace Components *function.add_params() = std::string(reinterpret_cast(&level), 4); IPCHandler::SendWorker("friends", function.SerializeAsString()); + + Friends::UpdateState(); } } From 22b91c7a773eca7be5dd94074705553ab3a43a04 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 29 Jan 2017 17:16:09 +0100 Subject: [PATCH 16/16] [Friends] First working version of the friend system --- src/Components/Modules/Friends.cpp | 181 +++++++++++++++++++++++++++-- src/Components/Modules/Friends.hpp | 13 ++- src/Components/Modules/Party.cpp | 1 + src/Worker/Handlers/Friends.cpp | 14 +-- 4 files changed, 188 insertions(+), 21 deletions(-) diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index 07f63326..47beffb1 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -6,6 +6,59 @@ namespace Components std::recursive_mutex Friends::Mutex; std::vector Friends::FriendsList; + void Friends::SortIndividualList(std::vector* list) + { + qsort(list->data(), list->size(), sizeof(Friends::Friend), [](const void* first, const void* second) + { + const Friends::Friend* friend1 = static_cast(first); + const Friends::Friend* friend2 = static_cast(second); + + std::string name1 = Utils::String::ToLower(Colors::Strip(friend1->name)); + std::string name2 = Utils::String::ToLower(Colors::Strip(friend2->name)); + + return name1.compare(name2); + }); + } + + void Friends::SortList() + { + std::lock_guard _(Friends::Mutex); + + std::vector playingList; + std::vector onlineList; + std::vector offlineList; + + // Split up the list + for(auto entry : Friends::FriendsList) + { + if(entry.online) + { + if(entry.server.getType() == Game::NA_BAD) + { + onlineList.push_back(entry); + } + else + { + playingList.push_back(entry); + } + } + else + { + offlineList.push_back(entry); + } + } + + Friends::SortIndividualList(&playingList); + Friends::SortIndividualList(&onlineList); + Friends::SortIndividualList(&offlineList); + + Friends::FriendsList.clear(); + + Utils::Merge(&Friends::FriendsList, playingList); + Utils::Merge(&Friends::FriendsList, onlineList); + Utils::Merge(&Friends::FriendsList, offlineList); + } + void Friends::UpdateUserInfo(SteamID user) { Proto::IPC::Function function; @@ -30,6 +83,42 @@ namespace Components IPCHandler::SendWorker("friends", function.SerializeAsString()); } + void Friends::UpdateHostname(Network::Address server, std::string hostname) + { + std::lock_guard _(Friends::Mutex); + + for(auto& entry : Friends::FriendsList) + { + if(entry.server == server) + { + entry.serverName = hostname; + } + } + } + + void Friends::SetServer() + { + Proto::IPC::Function function; + function.set_name("setPresence"); + *function.add_params() = "iw4x_server"; + *function.add_params() = Network::Address(*Game::connectedHost).getString();//reinterpret_cast(0x7ED3F8); + + IPCHandler::SendWorker("friends", function.SerializeAsString()); + + Friends::UpdateState(); + } + + void Friends::ClearServer() + { + Proto::IPC::Function function; + function.set_name("setPresence"); + *function.add_params() = "iw4x_server"; + + IPCHandler::SendWorker("friends", function.SerializeAsString()); + + Friends::UpdateState(); + } + void Friends::UpdateRank() { static Utils::Value levelVal; @@ -64,7 +153,6 @@ namespace Components unsigned int Friends::GetFriendCount() { - std::lock_guard _(Friends::Mutex); return Friends::FriendsList.size(); } @@ -107,7 +195,27 @@ namespace Components return Utils::String::VA("%s", user.name.data()); case 2: - return "Trickshot Isnipe server"; + { + if(user.online && user.server.getType() != Game::NA_BAD) + { + if(user.serverName.empty()) + { + return Utils::String::VA("Playing on %s", user.server.getCString()); + } + else + { + return Utils::String::VA("Playing on %s", user.serverName.data()); + } + } + else if(user.online) + { + return "Online"; + } + else + { + return "Offline"; + } + } default: break; @@ -143,8 +251,8 @@ namespace Components } } } - - void Friends::PresenceResponse(std::vector params) + + void Friends::ParsePresence(std::vector params, bool sort) { if (params.size() >= 3) { @@ -162,15 +270,26 @@ namespace Components if (entry == Friends::FriendsList.end()) return; - if (key == "iw4x_status") + if (key == "iw4x_name") { - entry->statusName = value; + entry->playerName = value; } else if (key == "iw4x_server") { - entry->server = value; + Network::Address oldAddress = entry->server; - if (entry->server.getType() != Game::NA_BAD) + if (value.empty()) + { + entry->server.setType(Game::NA_BAD); + entry->serverName.clear(); + } + else if (entry->server != value) + { + entry->server = value; + entry->serverName.clear(); + } + + if (entry->server.getType() != Game::NA_BAD && entry->server != oldAddress) { Node::AddNode(entry->server); Network::SendCommand(entry->server, "getinfo", Utils::Cryptography::Rand::GenerateChallenge()); @@ -186,8 +305,15 @@ namespace Components entry->prestige = (data >> 24) & 0xFF; } } + + if (sort) Friends::SortList(); } } + + void Friends::PresenceResponse(std::vector params) + { + Friends::ParsePresence(params, true); + } void Friends::InfoResponse(std::vector params) { @@ -221,9 +347,11 @@ namespace Components } else { - Friends::PresenceResponse({ Utils::String::VA("%llx", id.Bits), key, value }); + Friends::ParsePresence({ Utils::String::VA("%llx", id.Bits), key, value }, false); } } + + Friends::SortList(); } } @@ -244,6 +372,7 @@ namespace Components entry.online = false; entry.prestige = 0; entry.experience = 0; + entry.server.setType(Game::NA_BAD); auto oldEntry = std::find_if(oldFriends.begin(), oldFriends.end(), [id](Friends::Friend entry) { @@ -263,10 +392,27 @@ namespace Components } } + __declspec(naked) void Friends::DisconnectStub() + { + __asm + { + pushad + call Friends::ClearServer + popad + + push 467CC0h + retn + } + } + Friends::Friends() { Friends::UpdateFriends(); + // Update state when connecting/disconnecting + Utils::Hook(0x403582, Friends::DisconnectStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x4CD023, Friends::SetServer, HOOK_JUMP).install()->quick(); + auto fInterface = IPCHandler::NewInterface("steamCallbacks"); // Callback to update user information @@ -298,6 +444,23 @@ namespace Components Friends::UpdateFriends(); }); + UIScript::Add("JoinFriend", [](UIScript::Token) + { + std::lock_guard _(Friends::Mutex); + if (Friends::CurrentFriend >= Friends::FriendsList.size()) return; + + auto& user = Friends::FriendsList[Friends::CurrentFriend]; + + if(user.online && user.server.getType() != Game::NA_BAD) + { + Party::Connect(user.server); + } + else + { + Command::Execute("snd_playLocal exit_prestige", false); + } + }); + QuickPatch::OnFrame([]() { if(*reinterpret_cast(0x1AD5690)) // LiveStorage_DoWeHaveStats diff --git a/src/Components/Modules/Friends.hpp b/src/Components/Modules/Friends.hpp index 6c3ba192..fd7f88d6 100644 --- a/src/Components/Modules/Friends.hpp +++ b/src/Components/Modules/Friends.hpp @@ -14,6 +14,7 @@ namespace Components static void UpdateFriends(); static void UpdateRank(); + static void UpdateHostname(Network::Address server, std::string hostname); private: #pragma pack(push, 4) @@ -35,8 +36,9 @@ namespace Components public: SteamID userId; std::string name; + std::string playerName; Network::Address server; - std::string statusName; + std::string serverName; bool online; int experience; int prestige; @@ -46,13 +48,22 @@ namespace Components static std::recursive_mutex Mutex; static std::vector FriendsList; + static void DisconnectStub(); + static void ClearServer(); + static void SetServer(); + static void UpdateUserInfo(SteamID user); static void UpdateState(); + static void SortList(); + static void SortIndividualList(std::vector* list); + static unsigned int GetFriendCount(); static const char* GetFriendText(unsigned int index, int column); static void SelectFriend(unsigned int index); + static void ParsePresence(std::vector params, bool sort); + static void FriendsResponse(std::vector params); static void NameResponse(std::vector params); static void PresenceResponse(std::vector params); diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index 10c831ed..01ad3888 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -451,6 +451,7 @@ namespace Components } ServerList::Insert(address, info); + Friends::UpdateHostname(address, info.get("hostname")); }); } diff --git a/src/Worker/Handlers/Friends.cpp b/src/Worker/Handlers/Friends.cpp index df8a252a..29b7cadb 100644 --- a/src/Worker/Handlers/Friends.cpp +++ b/src/Worker/Handlers/Friends.cpp @@ -76,9 +76,9 @@ namespace Handlers void Friends::setPresence(Worker::Endpoint /*endpoint*/, std::vector params) { - if (params.size() >= 2 && Steam::Proxy::SteamFriends) + if (params.size() >= 1 && Steam::Proxy::SteamFriends) { - Steam::Proxy::SteamFriends->SetRichPresence(params[0].data(), params[1].data()); + Steam::Proxy::SteamFriends->SetRichPresence(params[0].data(), (params.size() >= 2 ? params[1].data() : nullptr)); } } @@ -141,14 +141,6 @@ namespace Handlers { *response.add_params() = Utils::String::VA("%d", Steam::Proxy::SteamFriends->GetFriendPersonaState(id)); } - /*else if (key == "iw4x_rank") // This is just a test - { - int experience = Utils::Cryptography::Rand::GenerateInt() % (2516000 + 1); - int prestige = Utils::Cryptography::Rand::GenerateInt() % (10 + 1); - - int data = (experience & 0xFFFFFF) | ((prestige & 0xFF) << 24); - *response.add_params() = std::string(reinterpret_cast(&data), 4); - }*/ else { *response.add_params() = Steam::Proxy::SteamFriends->GetFriendRichPresence(id, key.data()); @@ -180,7 +172,7 @@ namespace Handlers { if(Steam::Proxy::SteamFriends) { - Steam::Proxy::SteamFriends->ClearRichPresence(); + Steam::Proxy::SteamFriends->SetRichPresence("iw4x_server", nullptr); } if(Steam::Proxy::SteamLegacyFriends)