mirror of
https://github.com/geode-sdk/geode.git
synced 2025-03-22 02:45:49 -04:00
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:
parent
d701563534
commit
f5b5a64557
12 changed files with 361 additions and 86 deletions
loader
include/Geode
src
|
@ -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;
|
||||
|
|
67
loader/include/Geode/loader/IPC.hpp
Normal file
67
loader/include/Geode/loader/IPC.hpp
Normal 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();
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
);
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "AddIDs.hpp"
|
||||
#include <Geode/binding/LevelInfoLayer.hpp>
|
||||
#include <Geode/modify/LevelInfoLayer.hpp>
|
||||
|
||||
$register_ids(LevelInfoLayer) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
38
loader/src/internal/ios/InternalLoader.cpp
Normal file
38
loader/src/internal/ios/InternalLoader.cpp
Normal 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
|
42
loader/src/internal/mac/InternalLoader.cpp
Normal file
42
loader/src/internal/mac/InternalLoader.cpp
Normal 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
|
110
loader/src/internal/windows/InternalLoader.cpp
Normal file
110
loader/src/internal/windows/InternalLoader.cpp
Normal 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
55
loader/src/load/IPC.cpp
Normal 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) {}
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue