new events stuff

- add preliminary IPC stuff
 - preliminary IPC implementation on Windows (will evolve still)
 - more ids
 - move InternalLoader platform definitions to their own files
 - fix ranges::map not resizing the output
This commit is contained in:
HJfod 2022-11-22 17:21:00 +02:00
parent d701563534
commit f5b5a64557
12 changed files with 361 additions and 86 deletions

View file

@ -65,12 +65,20 @@ namespace geode {
return ListenerResult::Propagate;
}
EventListener(T filter = T()) {}
EventListener(std::function<Callback> fn, T filter = T()) : m_callback(fn), m_filter(filter) {}
EventListener(Callback* fnptr, T filter = T()) : m_callback(fnptr), m_filter(filter) {}
EventListener(T filter = T()) {
this->enable();
}
EventListener(std::function<Callback> fn, T filter = T()) : m_callback(fn), m_filter(filter) {
this->enable();
}
EventListener(Callback* fnptr, T filter = T()) : m_callback(fnptr), m_filter(filter) {
this->enable();
}
template <class C>
EventListener(C* cls, MemberFn<C> fn, T filter = T()) : EventListener(std::bind(fn, cls, std::placeholders::_1), filter) {}
EventListener(C* cls, MemberFn<C> fn, T filter = T()) : EventListener(std::bind(fn, cls, std::placeholders::_1), filter) {
this->enable();
}
void bind(std::function<Callback> fn) {
m_callback = fn;

View file

@ -0,0 +1,67 @@
#pragma once
#include "Event.hpp"
#include "Loader.hpp"
namespace geode {
#ifdef GEODE_IS_WINDOWS
constexpr const char* IPC_PIPE_NAME = "\\\\.\\pipe\\GeodeIPCPipe";
#endif
// message ID may be anything that doesn't contain /, but to be safe, you
// should only define message IDs that match the following: [a-z\-_\.]+
class GEODE_DLL IPCEvent : public Event {
protected:
void* m_rawPipeHandle;
std::string m_targetModID;
std::string m_senderID;
std::string m_messageID;
std::string m_messageData;
public:
IPCEvent(
void* rawPipeHandle,
std::string targetModID,
std::string senderID,
std::string messageID,
std::string messageData
);
std::string getSenderID() const;
std::string getTargetModID() const;
std::string getMessageID() const;
std::string getMessageData() const;
/**
* Reply to the message. You can only reply once
*/
void reply(std::string const& data);
};
class GEODE_DLL IPCFilter : public EventFilter<IPCEvent> {
public:
using Callback = void(IPCEvent*);
protected:
std::string m_modID;
std::string m_messageID;
public:
ListenerResult handle(std::function<Callback> fn, IPCEvent* event);
IPCFilter(
std::string const& modID,
std::string const& messageID
);
};
template<class = void>
std::monostate listenForIPC(std::string const& messageID, void(*callback)(IPCEvent*)) {
Loader::get()->scheduleOnModLoad(getMod(), [=]() {
new EventListener(
callback, IPCFilter(getMod()->getID(), messageID)
);
});
return std::monostate();
}
}

View file

@ -57,7 +57,7 @@ namespace geode {
std::string const& settingID, void (*callback)(std::shared_ptr<T>)
) {
Loader::get()->scheduleOnModLoad(getMod(), [=]() {
static auto _ = EventListener(
new EventListener(
callback, SettingChangedFilter<T>(getMod()->getID(), settingID)
);
});
@ -66,7 +66,7 @@ namespace geode {
static std::monostate listenForAllSettingChanges(void (*callback)(std::shared_ptr<Setting>)) {
Loader::get()->scheduleOnModLoad(getMod(), [=]() {
static auto _ = EventListener(
new EventListener(
callback, SettingChangedFilter(getMod()->getID())
);
});

View file

@ -201,11 +201,11 @@ namespace geode::utils::ranges {
}
template <
ValidConstContainer From, ValidContainer Into,
ValidContainer Into, ValidConstContainer From,
ValidIntoConverter<typename From::value_type, typename Into::value_type> Mapper>
Into map(From const& from, Mapper mapper) {
auto res = Into();
std::transform(from.begin(), from.end(), res.end(), mapper);
std::transform(from.begin(), from.end(), std::back_inserter(res), mapper);
return res;
}

View file

@ -1,4 +1,5 @@
#include "AddIDs.hpp"
#include <Geode/binding/LevelInfoLayer.hpp>
#include <Geode/modify/LevelInfoLayer.hpp>
$register_ids(LevelInfoLayer) {

View file

@ -39,6 +39,9 @@ bool InternalLoader::setup() {
log::log(Severity::Debug, InternalMod::get(), "Loaded hooks");
log::log(Severity::Debug, InternalMod::get(), "Setting up IPC...");
this->setupIPC();
return true;
}
@ -177,79 +180,3 @@ bool InternalLoader::verifyLoaderResources(IndexUpdateCallback callback) {
return true;
}
#if defined(GEODE_IS_WINDOWS)
void InternalLoader::platformMessageBox(char const* title, std::string const& info) {
MessageBoxA(nullptr, info.c_str(), title, MB_ICONERROR);
}
void InternalLoader::openPlatformConsole() {
if (m_platformConsoleOpen) return;
if (AllocConsole() == 0) return;
SetConsoleCP(CP_UTF8);
// redirect console output
freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);
freopen_s(reinterpret_cast<FILE**>(stdin), "CONIN$", "r", stdin);
m_platformConsoleOpen = true;
for (auto const& log : Loader::get()->getLogs()) {
std::cout << log->toString(true) << "\n";
}
}
void InternalLoader::closePlatformConsole() {
if (!m_platformConsoleOpen) return;
fclose(stdin);
fclose(stdout);
FreeConsole();
m_platformConsoleOpen = false;
}
#elif defined(GEODE_IS_MACOS)
#include <CoreFoundation/CoreFoundation.h>
void InternalLoader::platformMessageBox(char const* title, std::string const& info) {
CFStringRef cfTitle = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
CFStringRef cfMessage = CFStringCreateWithCString(NULL, info.c_str(), kCFStringEncodingUTF8);
CFUserNotificationDisplayNotice(
0, kCFUserNotificationNoteAlertLevel, NULL, NULL, NULL, cfTitle, cfMessage, NULL
);
}
void InternalLoader::openPlatformConsole() {
m_platformConsoleOpen = true;
for (auto const& log : Loader::get()->getLogs()) {
std::cout << log->toString(true) << "\n";
}
}
void InternalLoader::closePlatformConsole() {
m_platformConsoleOpen = false;
}
#elif defined(GEODE_IS_IOS)
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
void InternalLoader::platformMessageBox(char const* title, std::string const& info) {
std::cout << title << ": " << info << std::endl;
}
void InternalLoader::openPlatformConsole() {
ghc::filesystem::path(getpwuid(getuid())->pw_dir);
freopen(
ghc::filesystem::path(utils::file::geodeRoot() / "geode_log.txt").string().c_str(), "w",
stdout
);
InternalLoader::m_platformConsoleOpen = true;
}
void InternalLoader::closePlatformConsole() {}
#endif

View file

@ -31,6 +31,9 @@ protected:
void downloadLoaderResources(IndexUpdateCallback callback);
bool loadHooks();
void setupIPC();
InternalLoader();
~InternalLoader();
@ -41,7 +44,11 @@ public:
bool setup();
bool loadHooks();
void postIPCMessage(
void* rawPipeHandle,
std::string const& senderID,
std::string const& data
);
/**
* Check if a one-time event has been shown to the user,

View file

@ -0,0 +1,38 @@
#include "../InternalLoader.hpp"
#include <Geode/loader/Log.hpp>
#include <iostream>
#include "../InternalMod.hpp"
#ifdef GEODE_IS_IOS
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
void InternalLoader::platformMessageBox(char const* title, std::string const& info) {
std::cout << title << ": " << info << std::endl;
}
void InternalLoader::openPlatformConsole() {
ghc::filesystem::path(getpwuid(getuid())->pw_dir);
freopen(
ghc::filesystem::path(utils::file::geodeRoot() / "geode_log.txt").string().c_str(), "w",
stdout
);
InternalLoader::m_platformConsoleOpen = true;
}
void InternalLoader::closePlatformConsole() {}
void InternalLoader::postIPCMessage(
void* rawPipeHandle,
std::string const& senderID,
std::string const& data
) {}
void InternalLoader::setupIPC() {
#warning "Set up pipes or smth for this platform"
log::log(Severity::Warning, InternalMod::get(), "IPC is not supported on this platform");
}
#endif

View file

@ -0,0 +1,42 @@
#include "../InternalLoader.hpp"
#include <Geode/loader/Log.hpp>
#include <iostream>
#include "../InternalMod.hpp"
#ifdef GEODE_IS_MACOS
#include <CoreFoundation/CoreFoundation.h>
void InternalLoader::platformMessageBox(char const* title, std::string const& info) {
CFStringRef cfTitle = CFStringCreateWithCString(NULL, title, kCFStringEncodingUTF8);
CFStringRef cfMessage = CFStringCreateWithCString(NULL, info.c_str(), kCFStringEncodingUTF8);
CFUserNotificationDisplayNotice(
0, kCFUserNotificationNoteAlertLevel, NULL, NULL, NULL, cfTitle, cfMessage, NULL
);
}
void InternalLoader::openPlatformConsole() {
m_platformConsoleOpen = true;
for (auto const& log : Loader::get()->getLogs()) {
std::cout << log->toString(true) << "\n";
}
}
void InternalLoader::closePlatformConsole() {
m_platformConsoleOpen = false;
}
void InternalLoader::postIPCMessage(
void* rawPipeHandle,
std::string const& senderID,
std::string const& data
) {}
void InternalLoader::setupIPC() {
#warning "Set up pipes or smth for this platform"
log::log(Severity::Warning, InternalMod::get(), "IPC is not supported on this platform");
}
#endif

View file

@ -0,0 +1,110 @@
#include "../InternalLoader.hpp"
#include <Geode/loader/Log.hpp>
#include <Geode/loader/IPC.hpp>
#include <iostream>
#include "../InternalMod.hpp"
USE_GEODE_NAMESPACE();
#ifdef GEODE_IS_WINDOWS
void InternalLoader::platformMessageBox(char const* title, std::string const& info) {
MessageBoxA(nullptr, info.c_str(), title, MB_ICONERROR);
}
void InternalLoader::openPlatformConsole() {
if (m_platformConsoleOpen) return;
if (AllocConsole() == 0) return;
SetConsoleCP(CP_UTF8);
// redirect console output
freopen_s(reinterpret_cast<FILE**>(stdout), "CONOUT$", "w", stdout);
freopen_s(reinterpret_cast<FILE**>(stdin), "CONIN$", "r", stdin);
m_platformConsoleOpen = true;
for (auto const& log : Loader::get()->getLogs()) {
std::cout << log->toString(true) << "\n";
}
}
void InternalLoader::closePlatformConsole() {
if (!m_platformConsoleOpen) return;
fclose(stdin);
fclose(stdout);
FreeConsole();
m_platformConsoleOpen = false;
}
void InternalLoader::postIPCMessage(
void* rawPipeHandle,
std::string const& senderID,
std::string const& data
) {
std::string msg = senderID + "/" + data;
log::debug("Replying msg: {}", msg);
DWORD written;
WriteFile(rawPipeHandle, msg.c_str(), msg.size(), &written, nullptr);
}
void InternalLoader::setupIPC() {
auto pipe = CreateNamedPipeA(
IPC_PIPE_NAME,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1,
1024 * 16,
1024 * 16,
NMPWAIT_USE_DEFAULT_WAIT,
nullptr
);
std::thread([pipe]() {
while (pipe != INVALID_HANDLE_VALUE) {
log::debug("Waiting for connection");
if (ConnectNamedPipe(pipe, nullptr)) {
log::debug("Got connection");
char buffer[1024];
DWORD read;
while (ReadFile(pipe, buffer, sizeof(buffer) - 1, &read, nullptr)) {
buffer[read] = '\0';
// format of the message should be modID/senderID/msgID/data
std::string modID;
std::string senderID;
std::string msgID;
std::string data;
size_t collectPart = 0;
for (size_t i = 0; i < read; i++) {
if (buffer[i] == '/' && collectPart < 3) {
collectPart++;
} else {
switch (collectPart) {
case 0: modID += buffer[i]; break;
case 1: senderID += buffer[i]; break;
case 2: msgID += buffer[i]; break;
default: data += buffer[i]; break;
}
}
}
if (modID.size() && senderID.size() && msgID.size()) {
IPCEvent(pipe, modID, senderID, msgID, data).post();
} else {
log::warn("Received invalid IPC message: '{}'", buffer);
}
}
log::debug("Connection done");
}
DisconnectNamedPipe(pipe);
log::debug("Disconnected pipe");
}
log::debug("IPC ended");
}).detach();
if (pipe != INVALID_HANDLE_VALUE) {
log::log(Severity::Debug, InternalMod::get(), "IPC set up");
} else {
log::log(Severity::Error, InternalMod::get(), "Unable to set up IPC");
}
}
#endif

55
loader/src/load/IPC.cpp Normal file
View file

@ -0,0 +1,55 @@
#include <Geode/loader/IPC.hpp>
#include <InternalLoader.hpp>
USE_GEODE_NAMESPACE();
IPCEvent::IPCEvent(
void* rawPipeHandle,
std::string targetModID,
std::string senderID,
std::string messageID,
std::string messageData
) : m_rawPipeHandle(rawPipeHandle),
m_targetModID(targetModID),
m_senderID(senderID),
m_messageID(messageID),
m_messageData(messageData) {}
std::string IPCEvent::getSenderID() const {
return m_senderID;
}
std::string IPCEvent::getTargetModID() const {
return m_targetModID;
}
std::string IPCEvent::getMessageID() const {
return m_messageID;
}
std::string IPCEvent::getMessageData() const {
return m_messageData;
}
void IPCEvent::reply(std::string const& data) {
if (m_rawPipeHandle) {
InternalLoader::get()->postIPCMessage(m_rawPipeHandle, m_senderID, data);
}
}
ListenerResult IPCFilter::handle(std::function<Callback> fn, IPCEvent* event) {
if (
event->getTargetModID() == m_modID &&
event->getMessageID() == m_messageID
) {
fn(event);
return ListenerResult::Stop;
}
return ListenerResult::Propagate;
}
IPCFilter::IPCFilter(
std::string const& modID,
std::string const& messageID
) : m_modID(modID),
m_messageID(messageID) {}

View file

@ -4,6 +4,7 @@
#include <Geode/loader/Log.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/loader/IPC.hpp>
#include <InternalLoader.hpp>
#include <InternalMod.hpp>
#include <array>
@ -96,7 +97,9 @@ BOOL WINAPI DllMain(HINSTANCE lib, DWORD reason, LPVOID) {
}
#endif
static auto _ = listenForSettingChanges(
#define $_ GEODE_CONCAT(unnamedVar_, __LINE__)
static auto $_ = listenForSettingChanges(
"show-platform-console",
+[](std::shared_ptr<BoolSetting> setting) {
if (setting->getValue()) {
@ -108,6 +111,23 @@ static auto _ = listenForSettingChanges(
}
);
static auto $_ = listenForIPC("ipc-test", +[](IPCEvent* event) {
event->reply("Hello from Geode!");
});
static auto $_ = listenForIPC("list-mods", +[](IPCEvent* event) {
event->reply(
"[ " + ranges::join(
ranges::map<std::vector<std::string>>(
Loader::get()->getAllMods(),
[](Mod* mod) {
return "\"" + mod->getID() + "\"";
}
), ", "
) + " ]"
);
});
int geodeEntry(void* platformData) {
// setup internals