mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-22 23:48:08 -05:00
Refactor some stuff in Loader (#420)
* move console stuff from loader * compile platform sources only per platform * move ipc from loader * move updater from loader * remove Loader::didLastLaunchCrash * remove platformdata from loader * move updaters events and filters too
This commit is contained in:
parent
05064eb4d5
commit
be7ee3ef18
49 changed files with 710 additions and 811 deletions
|
@ -60,9 +60,6 @@ file(GLOB SOURCES CONFIGURE_DEPENDS
|
||||||
src/hooks/*.cpp
|
src/hooks/*.cpp
|
||||||
src/ids/*.cpp
|
src/ids/*.cpp
|
||||||
src/internal/*.cpp
|
src/internal/*.cpp
|
||||||
src/platform/mac/*.cpp
|
|
||||||
src/platform/ios/*.cpp
|
|
||||||
src/platform/android/*.cpp
|
|
||||||
src/loader/*.cpp
|
src/loader/*.cpp
|
||||||
src/load.cpp
|
src/load.cpp
|
||||||
src/utils/*.cpp
|
src/utils/*.cpp
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
#include "Loader.hpp"
|
#include "Loader.hpp"
|
||||||
#include <matjson.hpp>
|
#include <matjson.hpp>
|
||||||
|
|
||||||
namespace geode {
|
namespace geode::ipc {
|
||||||
#ifdef GEODE_IS_WINDOWS
|
#ifdef GEODE_IS_WINDOWS
|
||||||
constexpr char const* IPC_PIPE_NAME = "\\\\.\\pipe\\GeodeIPCPipe";
|
constexpr char const* IPC_PIPE_NAME = R"(\\.\pipe\GeodeIPCPipe)";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef GEODE_IS_MACOS
|
#ifdef GEODE_IS_MACOS
|
||||||
|
@ -15,20 +15,20 @@ namespace geode {
|
||||||
|
|
||||||
class IPCFilter;
|
class IPCFilter;
|
||||||
|
|
||||||
// IPC (Inter-Process Communication) provides a way for Geode mods to talk
|
// IPC (Inter-Process Communication) provides a way for Geode mods to talk
|
||||||
// to other programs on the user's computer. If you have, for example, a
|
// to other programs on the user's computer. If you have, for example, a
|
||||||
// debugger, or an external modding UI, that application can open up the
|
// debugger, or an external modding UI, that application can open up the
|
||||||
// platform-specific pipe and start sending messages to mods. Mods can
|
// platform-specific pipe and start sending messages to mods. Mods can
|
||||||
// listen for messages using the listenForIPC function, and reply to
|
// listen for messages using the listenForIPC function, and reply to
|
||||||
// messages the get by using the reply method on the event provided. For
|
// messages the get by using the reply method on the event provided. For
|
||||||
// example, an external application can query what mods are loaded in Geode
|
// example, an external application can query what mods are loaded in Geode
|
||||||
// by sending the `list-mods` message to `geode.loader`.
|
// by sending the `list-mods` message to `geode.loader`.
|
||||||
|
|
||||||
class GEODE_DLL IPCEvent : public Event {
|
class GEODE_DLL IPCEvent : public Event {
|
||||||
protected:
|
protected:
|
||||||
void* m_rawPipeHandle;
|
void* m_rawPipeHandle;
|
||||||
bool m_replied = false;
|
bool m_replied = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::string targetModID;
|
std::string targetModID;
|
||||||
std::string messageID;
|
std::string messageID;
|
||||||
|
@ -57,12 +57,12 @@ namespace geode {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, IPCEvent* event);
|
ListenerResult handle(utils::MiniFunction<Callback> fn, IPCEvent* event);
|
||||||
IPCFilter(
|
IPCFilter(
|
||||||
std::string const& modID,
|
std::string const& modID,
|
||||||
std::string const& messageID
|
std::string const& messageID
|
||||||
);
|
);
|
||||||
IPCFilter(IPCFilter const&) = default;
|
IPCFilter(IPCFilter const&) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::monostate listenForIPC(std::string const& messageID, matjson::Value(*callback)(IPCEvent*));
|
std::monostate listen(std::string const& messageID, matjson::Value(*callback)(IPCEvent*));
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,8 +85,6 @@ namespace geode {
|
||||||
|
|
||||||
void queueInMainThread(ScheduledFunction func);
|
void queueInMainThread(ScheduledFunction func);
|
||||||
|
|
||||||
bool didLastLaunchCrash() const;
|
|
||||||
|
|
||||||
friend class LoaderImpl;
|
friend class LoaderImpl;
|
||||||
|
|
||||||
friend Mod* takeNextLoaderMod();
|
friend Mod* takeNextLoaderMod();
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
|
||||||
#include <Geode/modify/LoadingLayer.hpp>
|
#include <Geode/modify/LoadingLayer.hpp>
|
||||||
#include <Geode/modify/CCLayer.hpp>
|
#include <Geode/modify/CCLayer.hpp>
|
||||||
#include <Geode/utils/cocos.hpp>
|
#include <Geode/utils/cocos.hpp>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <loader/LoaderImpl.hpp>
|
#include <loader/LoaderImpl.hpp>
|
||||||
|
#include <loader/console.hpp>
|
||||||
|
#include <loader/updater.hpp>
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
@ -77,23 +78,23 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
||||||
this->setSmallText("Verifying Loader Resources");
|
this->setSmallText("Verifying Loader Resources");
|
||||||
// verify loader resources
|
// verify loader resources
|
||||||
Loader::get()->queueInMainThread([&]() {
|
Loader::get()->queueInMainThread([&]() {
|
||||||
if (!LoaderImpl::get()->verifyLoaderResources()) {
|
if (!updater::verifyLoaderResources()) {
|
||||||
log::debug("Downloading Loader Resources");
|
log::debug("Downloading Loader Resources");
|
||||||
this->setSmallText("Downloading Loader Resources");
|
this->setSmallText("Downloading Loader Resources");
|
||||||
this->addChild(EventListenerNode<ResourceDownloadFilter>::create(
|
this->addChild(EventListenerNode<updater::ResourceDownloadFilter>::create(
|
||||||
this, &CustomLoadingLayer::updateResourcesProgress
|
this, &CustomLoadingLayer::updateResourcesProgress
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log::debug("Loading Loader Resources");
|
log::debug("Loading Loader Resources");
|
||||||
this->setSmallText("Loading Loader Resources");
|
this->setSmallText("Loading Loader Resources");
|
||||||
LoaderImpl::get()->updateSpecialFiles();
|
updater::updateSpecialFiles();
|
||||||
this->continueLoadAssets();
|
this->continueLoadAssets();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateResourcesProgress(ResourceDownloadEvent* event) {
|
void updateResourcesProgress(updater::ResourceDownloadEvent* event) {
|
||||||
std::visit(makeVisitor {
|
std::visit(makeVisitor {
|
||||||
[&](UpdateProgress const& progress) {
|
[&](UpdateProgress const& progress) {
|
||||||
this->setSmallText(fmt::format(
|
this->setSmallText(fmt::format(
|
||||||
|
@ -107,7 +108,7 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
||||||
},
|
},
|
||||||
[&](UpdateFailed const& error) {
|
[&](UpdateFailed const& error) {
|
||||||
log::debug("Failed Loader Resources");
|
log::debug("Failed Loader Resources");
|
||||||
LoaderImpl::get()->platformMessageBox(
|
console::messageBox(
|
||||||
"Error updating resources",
|
"Error updating resources",
|
||||||
error + ".\n"
|
error + ".\n"
|
||||||
"You will have to install resources manually by downloading resources.zip "
|
"You will have to install resources manually by downloading resources.zip "
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
#include "../ui/internal/list/ModListLayer.hpp"
|
#include "../ui/internal/list/ModListLayer.hpp"
|
||||||
|
|
||||||
#include <Geode/loader/Index.hpp>
|
#include <Geode/loader/Index.hpp>
|
||||||
|
@ -7,13 +6,13 @@
|
||||||
#include <Geode/modify/IDManager.hpp>
|
#include <Geode/modify/IDManager.hpp>
|
||||||
#include <Geode/utils/NodeIDs.hpp>
|
#include <Geode/utils/NodeIDs.hpp>
|
||||||
#include <Geode/ui/BasedButtonSprite.hpp>
|
#include <Geode/ui/BasedButtonSprite.hpp>
|
||||||
#include <Geode/ui/GeodeUI.hpp>
|
|
||||||
#include <Geode/ui/Notification.hpp>
|
#include <Geode/ui/Notification.hpp>
|
||||||
#include <Geode/ui/Popup.hpp>
|
#include <Geode/ui/Popup.hpp>
|
||||||
#include <Geode/ui/MDPopup.hpp>
|
#include <Geode/ui/MDPopup.hpp>
|
||||||
#include <Geode/utils/cocos.hpp>
|
#include <Geode/utils/cocos.hpp>
|
||||||
#include <loader/ModImpl.hpp>
|
#include <loader/ModImpl.hpp>
|
||||||
#include <loader/LoaderImpl.hpp>
|
#include <loader/LoaderImpl.hpp>
|
||||||
|
#include <loader/updater.hpp>
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
@ -129,7 +128,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
||||||
|
|
||||||
// show auto update message
|
// show auto update message
|
||||||
static bool shownUpdateInfo = false;
|
static bool shownUpdateInfo = false;
|
||||||
if (LoaderImpl::get()->isNewUpdateDownloaded() && !shownUpdateInfo) {
|
if (updater::isNewUpdateDownloaded() && !shownUpdateInfo) {
|
||||||
shownUpdateInfo = true;
|
shownUpdateInfo = true;
|
||||||
auto popup = FLAlertLayer::create(
|
auto popup = FLAlertLayer::create(
|
||||||
"Update downloaded",
|
"Update downloaded",
|
||||||
|
@ -147,7 +146,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
||||||
// show crash info
|
// show crash info
|
||||||
static bool shownLastCrash = false;
|
static bool shownLastCrash = false;
|
||||||
if (
|
if (
|
||||||
Loader::get()->didLastLaunchCrash() &&
|
crashlog::didLastLaunchCrash() &&
|
||||||
!shownLastCrash &&
|
!shownLastCrash &&
|
||||||
!Mod::get()->template getSettingValue<bool>("disable-last-crashed-popup")
|
!Mod::get()->template getSettingValue<bool>("disable-last-crashed-popup")
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
#include <loader/LoaderImpl.hpp>
|
#include <loader/LoaderImpl.hpp>
|
||||||
|
#include <loader/console.hpp>
|
||||||
|
#include <loader/IPC.hpp>
|
||||||
|
#include <loader/updater.hpp>
|
||||||
|
|
||||||
#include <Geode/loader/IPC.hpp>
|
#include <Geode/loader/IPC.hpp>
|
||||||
#include <Geode/loader/Loader.hpp>
|
#include <Geode/loader/Loader.hpp>
|
||||||
|
@ -10,7 +13,7 @@
|
||||||
#include <loader/LogImpl.hpp>
|
#include <loader/LogImpl.hpp>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
#include "load.hpp"
|
#include "load.hpp"
|
||||||
|
@ -18,22 +21,22 @@ using namespace geode::prelude;
|
||||||
$execute {
|
$execute {
|
||||||
listenForSettingChanges("show-platform-console", +[](bool value) {
|
listenForSettingChanges("show-platform-console", +[](bool value) {
|
||||||
if (value) {
|
if (value) {
|
||||||
LoaderImpl::get()->openPlatformConsole();
|
console::open();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LoaderImpl::get()->closePlatformConsole();
|
console::close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
listenForIPC("ipc-test", [](IPCEvent* event) -> matjson::Value {
|
ipc::listen("ipc-test", [](ipc::IPCEvent* event) -> matjson::Value {
|
||||||
return "Hello from Geode!";
|
return "Hello from Geode!";
|
||||||
});
|
});
|
||||||
|
|
||||||
listenForIPC("loader-info", [](IPCEvent* event) -> matjson::Value {
|
ipc::listen("loader-info", [](ipc::IPCEvent* event) -> matjson::Value {
|
||||||
return Mod::get()->getMetadata();
|
return Mod::get()->getMetadata();
|
||||||
});
|
});
|
||||||
|
|
||||||
listenForIPC("list-mods", [](IPCEvent* event) -> matjson::Value {
|
ipc::listen("list-mods", [](ipc::IPCEvent* event) -> matjson::Value {
|
||||||
std::vector<matjson::Value> res;
|
std::vector<matjson::Value> res;
|
||||||
|
|
||||||
auto args = *event->messageData;
|
auto args = *event->messageData;
|
||||||
|
@ -76,7 +79,7 @@ void tryShowForwardCompat() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO: change text later
|
// TODO: change text later
|
||||||
LoaderImpl::get()->platformMessageBox(
|
console::messageBox(
|
||||||
"Forward Compatibility Warning",
|
"Forward Compatibility Warning",
|
||||||
"Geode is running in a newer version of GD than Geode targets.\n"
|
"Geode is running in a newer version of GD than Geode targets.\n"
|
||||||
"UI is going to be disabled, platform console is forced on and crashes can be more common.\n"
|
"UI is going to be disabled, platform console is forced on and crashes can be more common.\n"
|
||||||
|
@ -116,7 +119,7 @@ int geodeEntry(void* platformData) {
|
||||||
auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
|
auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
|
||||||
log::popNest();
|
log::popNest();
|
||||||
if (!internalSetupRes) {
|
if (!internalSetupRes) {
|
||||||
LoaderImpl::get()->platformMessageBox(
|
console::messageBox(
|
||||||
"Unable to Load Geode!",
|
"Unable to Load Geode!",
|
||||||
"There was a fatal error setting up "
|
"There was a fatal error setting up "
|
||||||
"the internal mod and Geode can not be loaded: " + internalSetupRes.unwrapErr()
|
"the internal mod and Geode can not be loaded: " + internalSetupRes.unwrapErr()
|
||||||
|
@ -131,7 +134,7 @@ int geodeEntry(void* platformData) {
|
||||||
if (LoaderImpl::get()->isForwardCompatMode() ||
|
if (LoaderImpl::get()->isForwardCompatMode() ||
|
||||||
Mod::get()->getSettingValue<bool>("show-platform-console")) {
|
Mod::get()->getSettingValue<bool>("show-platform-console")) {
|
||||||
log::debug("Opening console");
|
log::debug("Opening console");
|
||||||
LoaderImpl::get()->openPlatformConsole();
|
console::open();
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up loader, load mods, etc.
|
// set up loader, load mods, etc.
|
||||||
|
@ -140,7 +143,7 @@ int geodeEntry(void* platformData) {
|
||||||
auto setupRes = LoaderImpl::get()->setup();
|
auto setupRes = LoaderImpl::get()->setup();
|
||||||
log::popNest();
|
log::popNest();
|
||||||
if (!setupRes) {
|
if (!setupRes) {
|
||||||
LoaderImpl::get()->platformMessageBox(
|
console::messageBox(
|
||||||
"Unable to Load Geode!",
|
"Unable to Load Geode!",
|
||||||
"There was an unknown fatal error setting up "
|
"There was an unknown fatal error setting up "
|
||||||
"the loader and Geode can not be loaded. "
|
"the loader and Geode can not be loaded. "
|
||||||
|
@ -152,12 +155,15 @@ int geodeEntry(void* platformData) {
|
||||||
|
|
||||||
crashlog::setupPlatformHandlerPost();
|
crashlog::setupPlatformHandlerPost();
|
||||||
|
|
||||||
log::info("Set up loader");
|
log::debug("Setting up IPC");
|
||||||
|
log::pushNest();
|
||||||
|
ipc::setup();
|
||||||
|
log::popNest();
|
||||||
|
|
||||||
// download and install new loader update in the background
|
// download and install new loader update in the background
|
||||||
if (Mod::get()->getSettingValue<bool>("auto-check-updates")) {
|
if (Mod::get()->getSettingValue<bool>("auto-check-updates")) {
|
||||||
log::info("Starting loader update check");
|
log::info("Starting loader update check");
|
||||||
LoaderImpl::get()->checkForLoaderUpdates();
|
updater::checkForLoaderUpdates();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log::info("Skipped loader update check");
|
log::info("Skipped loader update check");
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
#include <Geode/loader/IPC.hpp>
|
#include <Geode/loader/IPC.hpp>
|
||||||
|
#include "IPC.hpp"
|
||||||
#include <matjson.hpp>
|
#include <matjson.hpp>
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
std::monostate geode::listenForIPC(std::string const& messageID, matjson::Value(*callback)(IPCEvent*)) {
|
std::monostate ipc::listen(std::string const& messageID, matjson::Value(*callback)(IPCEvent*)) {
|
||||||
(void) new EventListener(
|
(void) new EventListener(
|
||||||
callback, IPCFilter(getMod()->getID(), messageID)
|
callback, IPCFilter(getMod()->getID(), messageID)
|
||||||
);
|
);
|
||||||
return std::monostate();
|
return std::monostate();
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCEvent::IPCEvent(
|
ipc::IPCEvent::IPCEvent(
|
||||||
void* rawPipeHandle,
|
void* rawPipeHandle,
|
||||||
std::string const& targetModID,
|
std::string const& targetModID,
|
||||||
std::string const& messageID,
|
std::string const& messageID,
|
||||||
|
@ -22,9 +23,9 @@ IPCEvent::IPCEvent(
|
||||||
replyData(replyData),
|
replyData(replyData),
|
||||||
messageData(std::make_unique<matjson::Value>(messageData)) {}
|
messageData(std::make_unique<matjson::Value>(messageData)) {}
|
||||||
|
|
||||||
IPCEvent::~IPCEvent() {}
|
ipc::IPCEvent::~IPCEvent() {}
|
||||||
|
|
||||||
ListenerResult IPCFilter::handle(utils::MiniFunction<Callback> fn, IPCEvent* event) {
|
ListenerResult ipc::IPCFilter::handle(utils::MiniFunction<Callback> fn, IPCEvent* event) {
|
||||||
if (event->targetModID == m_modID && event->messageID == m_messageID) {
|
if (event->targetModID == m_modID && event->messageID == m_messageID) {
|
||||||
event->replyData = fn(event);
|
event->replyData = fn(event);
|
||||||
return ListenerResult::Stop;
|
return ListenerResult::Stop;
|
||||||
|
@ -32,5 +33,34 @@ ListenerResult IPCFilter::handle(utils::MiniFunction<Callback> fn, IPCEvent* eve
|
||||||
return ListenerResult::Propagate;
|
return ListenerResult::Propagate;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPCFilter::IPCFilter(std::string const& modID, std::string const& messageID) :
|
ipc::IPCFilter::IPCFilter(std::string const& modID, std::string const& messageID) :
|
||||||
m_modID(modID), m_messageID(messageID) {}
|
m_modID(modID), m_messageID(messageID) {}
|
||||||
|
|
||||||
|
matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) {
|
||||||
|
matjson::Value reply;
|
||||||
|
|
||||||
|
matjson::Value json;
|
||||||
|
try {
|
||||||
|
json = matjson::parse(buffer);
|
||||||
|
} catch (...) {
|
||||||
|
log::warn("Received IPC message that isn't valid JSON");
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!json.contains("mod") || !json["mod"].is_string()) {
|
||||||
|
log::warn("Received IPC message without 'mod' field");
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
if (!json.contains("message") || !json["message"].is_string()) {
|
||||||
|
log::warn("Received IPC message without 'message' field");
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
matjson::Value data;
|
||||||
|
if (json.contains("data")) {
|
||||||
|
data = json["data"];
|
||||||
|
}
|
||||||
|
// log::debug("Posting IPC event");
|
||||||
|
// ! warning: if the event system is ever made asynchronous this will break!
|
||||||
|
IPCEvent(rawHandle, json["mod"].as_string(), json["message"].as_string(), data, reply).post();
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
9
loader/src/loader/IPC.hpp
Normal file
9
loader/src/loader/IPC.hpp
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <matjson.hpp>
|
||||||
|
|
||||||
|
namespace geode::ipc {
|
||||||
|
void setup();
|
||||||
|
matjson::Value processRaw(void* rawHandle, std::string const& buffer);
|
||||||
|
}
|
|
@ -73,10 +73,6 @@ void Loader::queueInMainThread(ScheduledFunction func) {
|
||||||
return m_impl->queueInMainThread(std::move(func));
|
return m_impl->queueInMainThread(std::move(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Loader::didLastLaunchCrash() const {
|
|
||||||
return m_impl->didLastLaunchCrash();
|
|
||||||
}
|
|
||||||
|
|
||||||
Mod* Loader::takeNextMod() {
|
Mod* Loader::takeNextMod() {
|
||||||
return m_impl->takeNextMod();
|
return m_impl->takeNextMod();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "ModImpl.hpp"
|
#include "ModImpl.hpp"
|
||||||
#include "ModMetadataImpl.hpp"
|
#include "ModMetadataImpl.hpp"
|
||||||
#include "LogImpl.hpp"
|
#include "LogImpl.hpp"
|
||||||
|
#include "console.hpp"
|
||||||
|
|
||||||
#include <Geode/loader/Dirs.hpp>
|
#include <Geode/loader/Dirs.hpp>
|
||||||
#include <Geode/loader/IPC.hpp>
|
#include <Geode/loader/IPC.hpp>
|
||||||
|
@ -82,11 +83,6 @@ Result<> Loader::Impl::setup() {
|
||||||
}
|
}
|
||||||
log::popNest();
|
log::popNest();
|
||||||
|
|
||||||
log::debug("Setting up IPC");
|
|
||||||
log::pushNest();
|
|
||||||
this->setupIPC();
|
|
||||||
log::popNest();
|
|
||||||
|
|
||||||
log::debug("Setting up directories");
|
log::debug("Setting up directories");
|
||||||
log::pushNest();
|
log::pushNest();
|
||||||
this->createDirectories();
|
this->createDirectories();
|
||||||
|
@ -663,12 +659,8 @@ std::vector<LoadProblem> Loader::Impl::getProblems() const {
|
||||||
return m_problems;
|
return m_problems;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Loader::Impl::didLastLaunchCrash() const {
|
|
||||||
return crashlog::didLastLaunchCrash();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::forceReset() {
|
void Loader::Impl::forceReset() {
|
||||||
this->closePlatformConsole();
|
console::close();
|
||||||
for (auto& [_, mod] : m_mods) {
|
for (auto& [_, mod] : m_mods) {
|
||||||
delete mod;
|
delete mod;
|
||||||
}
|
}
|
||||||
|
@ -719,356 +711,6 @@ void Loader::Impl::executeGDThreadQueue() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::logConsoleMessage(std::string const& msg) {
|
|
||||||
if (m_platformConsoleOpen) {
|
|
||||||
// TODO: make flushing optional
|
|
||||||
std::cout << msg << '\n' << std::flush;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Loader::Impl::platformConsoleOpen() const {
|
|
||||||
return m_platformConsoleOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::fetchLatestGithubRelease(
|
|
||||||
utils::MiniFunction<void(matjson::Value const&)> then,
|
|
||||||
utils::MiniFunction<void(std::string const&)> expect
|
|
||||||
) {
|
|
||||||
if (m_latestGithubRelease) {
|
|
||||||
return then(m_latestGithubRelease.value());
|
|
||||||
}
|
|
||||||
// TODO: add header to not get rate limited
|
|
||||||
web::AsyncWebRequest()
|
|
||||||
.join("loader-auto-update-check")
|
|
||||||
.userAgent("github_api/1.0")
|
|
||||||
.fetch("https://api.github.com/repos/geode-sdk/geode/releases/latest")
|
|
||||||
.json()
|
|
||||||
.then([this, then](matjson::Value const& json) {
|
|
||||||
m_latestGithubRelease = json;
|
|
||||||
then(json);
|
|
||||||
})
|
|
||||||
.expect(expect);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::tryDownloadLoaderResources(
|
|
||||||
std::string const& url,
|
|
||||||
bool tryLatestOnError
|
|
||||||
) {
|
|
||||||
auto tempResourcesZip = dirs::getTempDir() / "new.zip";
|
|
||||||
auto resourcesDir = dirs::getGeodeResourcesDir() / Mod::get()->getID();
|
|
||||||
|
|
||||||
web::AsyncWebRequest()
|
|
||||||
// use the url as a join handle
|
|
||||||
.join(url)
|
|
||||||
.fetch(url)
|
|
||||||
.into(tempResourcesZip)
|
|
||||||
.then([tempResourcesZip, resourcesDir, this](auto) {
|
|
||||||
// unzip resources zip
|
|
||||||
auto unzip = file::Unzip::intoDir(tempResourcesZip, resourcesDir, true);
|
|
||||||
if (!unzip) {
|
|
||||||
ResourceDownloadEvent(
|
|
||||||
UpdateFailed("Unable to unzip new resources: " + unzip.unwrapErr())
|
|
||||||
).post();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->updateSpecialFiles();
|
|
||||||
|
|
||||||
ResourceDownloadEvent(UpdateFinished()).post();
|
|
||||||
})
|
|
||||||
.expect([this, tryLatestOnError](std::string const& info, int code) {
|
|
||||||
// if the url was not found, try downloading latest release instead
|
|
||||||
// (for development versions)
|
|
||||||
if (code == 404) {
|
|
||||||
log::warn("Unable to download resources: {}", info);
|
|
||||||
}
|
|
||||||
ResourceDownloadEvent(
|
|
||||||
UpdateFailed("Unable to download resources: " + info)
|
|
||||||
).post();
|
|
||||||
})
|
|
||||||
.progress([](auto&, double now, double total) {
|
|
||||||
ResourceDownloadEvent(
|
|
||||||
UpdateProgress(
|
|
||||||
static_cast<uint8_t>(now / total * 100.0),
|
|
||||||
"Downloading resources"
|
|
||||||
)
|
|
||||||
).post();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::updateSpecialFiles() {
|
|
||||||
auto resourcesDir = dirs::getGeodeResourcesDir() / Mod::get()->getID();
|
|
||||||
auto res = ModMetadataImpl::getImpl(ModImpl::get()->m_metadata).addSpecialFiles(resourcesDir);
|
|
||||||
if (res.isErr()) {
|
|
||||||
log::warn("Unable to add special files: {}", res.unwrapErr());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::downloadLoaderResources(bool useLatestRelease) {
|
|
||||||
web::AsyncWebRequest()
|
|
||||||
.join("loader-tag-exists-check")
|
|
||||||
.userAgent("github_api/1.0")
|
|
||||||
.fetch(fmt::format(
|
|
||||||
"https://api.github.com/repos/geode-sdk/geode/git/ref/tags/{}",
|
|
||||||
this->getVersion().toString()
|
|
||||||
))
|
|
||||||
.json()
|
|
||||||
.then([this](matjson::Value const& json) {
|
|
||||||
this->tryDownloadLoaderResources(fmt::format(
|
|
||||||
"https://github.com/geode-sdk/geode/releases/download/{}/resources.zip",
|
|
||||||
this->getVersion().toString()
|
|
||||||
), true);
|
|
||||||
})
|
|
||||||
.expect([=, this](std::string const& info, int code) {
|
|
||||||
if (code == 404) {
|
|
||||||
if (useLatestRelease) {
|
|
||||||
log::debug("Loader version {} does not exist on Github, downloading latest resources", this->getVersion().toString());
|
|
||||||
fetchLatestGithubRelease(
|
|
||||||
[this](matjson::Value const& raw) {
|
|
||||||
auto json = raw;
|
|
||||||
JsonChecker checker(json);
|
|
||||||
auto root = checker.root("[]").obj();
|
|
||||||
|
|
||||||
// find release asset
|
|
||||||
for (auto asset : root.needs("assets").iterate()) {
|
|
||||||
auto obj = asset.obj();
|
|
||||||
if (obj.needs("name").template get<std::string>() == "resources.zip") {
|
|
||||||
this->tryDownloadLoaderResources(
|
|
||||||
obj.needs("browser_download_url").template get<std::string>(),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceDownloadEvent(
|
|
||||||
UpdateFailed("Unable to find resources in latest GitHub release")
|
|
||||||
).post();
|
|
||||||
},
|
|
||||||
[this](std::string const& info) {
|
|
||||||
ResourceDownloadEvent(
|
|
||||||
UpdateFailed("Unable to download resources: " + info)
|
|
||||||
).post();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log::debug("Loader version {} does not exist on Github, not downloading the resources", this->getVersion().toString());
|
|
||||||
}
|
|
||||||
ResourceDownloadEvent(
|
|
||||||
UpdateFinished()
|
|
||||||
).post();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ResourceDownloadEvent(
|
|
||||||
UpdateFailed("Unable to check if tag exists: " + info)
|
|
||||||
).post();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Loader::Impl::verifyLoaderResources() {
|
|
||||||
static std::optional<bool> CACHED = std::nullopt;
|
|
||||||
if (CACHED.has_value()) {
|
|
||||||
return CACHED.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
// geode/resources/geode.loader
|
|
||||||
auto resourcesDir = dirs::getGeodeResourcesDir() / Mod::get()->getID();
|
|
||||||
|
|
||||||
// if the resources dir doesn't exist, then it's probably incorrect
|
|
||||||
if (!(
|
|
||||||
ghc::filesystem::exists(resourcesDir) &&
|
|
||||||
ghc::filesystem::is_directory(resourcesDir)
|
|
||||||
)) {
|
|
||||||
log::debug("Resources directory does not exist");
|
|
||||||
this->downloadLoaderResources(true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: actually have a proper way to disable checking resources
|
|
||||||
// for development builds
|
|
||||||
if (ghc::filesystem::exists(resourcesDir / "dont-update.txt")) {
|
|
||||||
// this is kind of a hack, but it's the easiest way to prevent
|
|
||||||
// auto update while developing
|
|
||||||
log::debug("Not updating resources since dont-update.txt exists");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure every file was covered
|
|
||||||
size_t coverage = 0;
|
|
||||||
|
|
||||||
// verify hashes
|
|
||||||
for (auto& file : ghc::filesystem::directory_iterator(resourcesDir)) {
|
|
||||||
auto name = file.path().filename().string();
|
|
||||||
// skip unknown files
|
|
||||||
if (!LOADER_RESOURCE_HASHES.count(name)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// verify hash
|
|
||||||
auto hash = calculateSHA256(file.path());
|
|
||||||
auto expected = LOADER_RESOURCE_HASHES.at(name);
|
|
||||||
if (hash != expected) {
|
|
||||||
log::debug("Resource hash mismatch: {} ({}, {})", name, hash.substr(0, 7), expected.substr(0, 7));
|
|
||||||
this->downloadLoaderResources();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
coverage += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure every file was found
|
|
||||||
if (coverage != LOADER_RESOURCE_HASHES.size()) {
|
|
||||||
log::debug("Resource coverage mismatch");
|
|
||||||
this->downloadLoaderResources();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::downloadLoaderUpdate(std::string const& url) {
|
|
||||||
auto updateZip = dirs::getTempDir() / "loader-update.zip";
|
|
||||||
auto targetDir = dirs::getGeodeDir() / "update";
|
|
||||||
|
|
||||||
web::AsyncWebRequest()
|
|
||||||
.join("loader-update-download")
|
|
||||||
.fetch(url)
|
|
||||||
.into(updateZip)
|
|
||||||
.then([this, updateZip, targetDir](auto) {
|
|
||||||
// unzip resources zip
|
|
||||||
auto unzip = file::Unzip::intoDir(updateZip, targetDir, true);
|
|
||||||
if (!unzip) {
|
|
||||||
LoaderUpdateEvent(
|
|
||||||
UpdateFailed("Unable to unzip update: " + unzip.unwrapErr())
|
|
||||||
).post();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_isNewUpdateDownloaded = true;
|
|
||||||
LoaderUpdateEvent(UpdateFinished()).post();
|
|
||||||
})
|
|
||||||
.expect([](std::string const& info) {
|
|
||||||
LoaderUpdateEvent(
|
|
||||||
UpdateFailed("Unable to download update: " + info)
|
|
||||||
).post();
|
|
||||||
})
|
|
||||||
.progress([](auto&, double now, double total) {
|
|
||||||
LoaderUpdateEvent(
|
|
||||||
UpdateProgress(
|
|
||||||
static_cast<uint8_t>(now / total * 100.0),
|
|
||||||
"Downloading update"
|
|
||||||
)
|
|
||||||
).post();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::checkForLoaderUpdates() {
|
|
||||||
// Check for updates in the background
|
|
||||||
fetchLatestGithubRelease(
|
|
||||||
[this](matjson::Value const& raw) {
|
|
||||||
auto json = raw;
|
|
||||||
JsonChecker checker(json);
|
|
||||||
auto root = checker.root("[]").obj();
|
|
||||||
|
|
||||||
VersionInfo ver { 0, 0, 0 };
|
|
||||||
root.needs("tag_name").into(ver);
|
|
||||||
|
|
||||||
// make sure release is newer
|
|
||||||
if (ver <= this->getVersion()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't auto-update major versions
|
|
||||||
if (ver.getMajor() > this->getVersion().getMajor()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find release asset
|
|
||||||
for (auto asset : root.needs("assets").iterate()) {
|
|
||||||
auto obj = asset.obj();
|
|
||||||
if (string::endsWith(
|
|
||||||
obj.needs("name").template get<std::string>(),
|
|
||||||
GEODE_PLATFORM_SHORT_IDENTIFIER ".zip"
|
|
||||||
)) {
|
|
||||||
this->downloadLoaderUpdate(
|
|
||||||
obj.needs("browser_download_url").template get<std::string>()
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LoaderUpdateEvent(
|
|
||||||
UpdateFailed("Unable to find release asset for " GEODE_PLATFORM_NAME)
|
|
||||||
).post();
|
|
||||||
},
|
|
||||||
[](std::string const& info) {
|
|
||||||
LoaderUpdateEvent(
|
|
||||||
UpdateFailed("Unable to check for updates: " + info)
|
|
||||||
).post();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Loader::Impl::isNewUpdateDownloaded() const {
|
|
||||||
return m_isNewUpdateDownloaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
matjson::Value Loader::Impl::processRawIPC(void* rawHandle, std::string const& buffer) {
|
|
||||||
matjson::Value reply;
|
|
||||||
|
|
||||||
matjson::Value json;
|
|
||||||
try {
|
|
||||||
json = matjson::parse(buffer);
|
|
||||||
} catch (...) {
|
|
||||||
log::warn("Received IPC message that isn't valid JSON");
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!json.contains("mod") || !json["mod"].is_string()) {
|
|
||||||
log::warn("Received IPC message without 'mod' field");
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
if (!json.contains("message") || !json["message"].is_string()) {
|
|
||||||
log::warn("Received IPC message without 'message' field");
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
matjson::Value data;
|
|
||||||
if (json.contains("data")) {
|
|
||||||
data = json["data"];
|
|
||||||
}
|
|
||||||
// log::debug("Posting IPC event");
|
|
||||||
// ! warning: if the event system is ever made asynchronous this will break!
|
|
||||||
IPCEvent(rawHandle, json["mod"].as_string(), json["message"].as_string(), data, reply).post();
|
|
||||||
return reply;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceDownloadEvent::ResourceDownloadEvent(
|
|
||||||
UpdateStatus const& status
|
|
||||||
) : status(status) {}
|
|
||||||
|
|
||||||
ListenerResult ResourceDownloadFilter::handle(
|
|
||||||
utils::MiniFunction<Callback> fn,
|
|
||||||
ResourceDownloadEvent* event
|
|
||||||
) {
|
|
||||||
fn(event);
|
|
||||||
return ListenerResult::Propagate;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResourceDownloadFilter::ResourceDownloadFilter() {}
|
|
||||||
|
|
||||||
LoaderUpdateEvent::LoaderUpdateEvent(
|
|
||||||
UpdateStatus const& status
|
|
||||||
) : status(status) {}
|
|
||||||
|
|
||||||
ListenerResult LoaderUpdateFilter::handle(
|
|
||||||
utils::MiniFunction<Callback> fn,
|
|
||||||
LoaderUpdateEvent* event
|
|
||||||
) {
|
|
||||||
fn(event);
|
|
||||||
return ListenerResult::Propagate;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoaderUpdateFilter::LoaderUpdateFilter() {}
|
|
||||||
|
|
||||||
void Loader::Impl::provideNextMod(Mod* mod) {
|
void Loader::Impl::provideNextMod(Mod* mod) {
|
||||||
m_nextModLock.lock();
|
m_nextModLock.lock();
|
||||||
if (mod) {
|
if (mod) {
|
||||||
|
|
|
@ -25,32 +25,6 @@
|
||||||
|
|
||||||
// TODO: Find a file convention for impl headers
|
// TODO: Find a file convention for impl headers
|
||||||
namespace geode {
|
namespace geode {
|
||||||
struct ResourceDownloadEvent : public Event {
|
|
||||||
const UpdateStatus status;
|
|
||||||
ResourceDownloadEvent(UpdateStatus const& status);
|
|
||||||
};
|
|
||||||
|
|
||||||
class ResourceDownloadFilter : public EventFilter<ResourceDownloadEvent> {
|
|
||||||
public:
|
|
||||||
using Callback = void(ResourceDownloadEvent*);
|
|
||||||
|
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, ResourceDownloadEvent* event);
|
|
||||||
ResourceDownloadFilter();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LoaderUpdateEvent : public Event {
|
|
||||||
const UpdateStatus status;
|
|
||||||
LoaderUpdateEvent(UpdateStatus const& status);
|
|
||||||
};
|
|
||||||
|
|
||||||
class LoaderUpdateFilter : public EventFilter<LoaderUpdateEvent> {
|
|
||||||
public:
|
|
||||||
using Callback = void(LoaderUpdateEvent*);
|
|
||||||
|
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, LoaderUpdateEvent* event);
|
|
||||||
LoaderUpdateFilter();
|
|
||||||
};
|
|
||||||
|
|
||||||
class Loader::Impl {
|
class Loader::Impl {
|
||||||
public:
|
public:
|
||||||
mutable std::mutex m_mutex;
|
mutable std::mutex m_mutex;
|
||||||
|
@ -66,11 +40,6 @@ namespace geode {
|
||||||
std::vector<ghc::filesystem::path> m_texturePaths;
|
std::vector<ghc::filesystem::path> m_texturePaths;
|
||||||
bool m_isSetup = false;
|
bool m_isSetup = false;
|
||||||
|
|
||||||
// cache for the json of the latest github release to avoid hitting
|
|
||||||
// the github api too much
|
|
||||||
std::optional<matjson::Value> m_latestGithubRelease;
|
|
||||||
bool m_isNewUpdateDownloaded = false;
|
|
||||||
|
|
||||||
LoadingState m_loadingState;
|
LoadingState m_loadingState;
|
||||||
|
|
||||||
std::vector<utils::MiniFunction<void(void)>> m_mainThreadQueue;
|
std::vector<utils::MiniFunction<void(void)>> m_mainThreadQueue;
|
||||||
|
@ -78,9 +47,6 @@ namespace geode {
|
||||||
std::vector<std::pair<Hook*, Mod*>> m_uninitializedHooks;
|
std::vector<std::pair<Hook*, Mod*>> m_uninitializedHooks;
|
||||||
bool m_readyToHook = false;
|
bool m_readyToHook = false;
|
||||||
|
|
||||||
bool m_platformConsoleOpen = false;
|
|
||||||
void* m_platformData = nullptr;
|
|
||||||
|
|
||||||
std::mutex m_nextModMutex;
|
std::mutex m_nextModMutex;
|
||||||
std::unique_lock<std::mutex> m_nextModLock = std::unique_lock<std::mutex>(m_nextModMutex, std::defer_lock);
|
std::unique_lock<std::mutex> m_nextModLock = std::unique_lock<std::mutex>(m_nextModMutex, std::defer_lock);
|
||||||
std::condition_variable m_nextModCV;
|
std::condition_variable m_nextModCV;
|
||||||
|
@ -109,17 +75,7 @@ namespace geode {
|
||||||
Result<tulip::hook::HandlerHandle> getHandler(void* address);
|
Result<tulip::hook::HandlerHandle> getHandler(void* address);
|
||||||
Result<> removeHandler(void* address);
|
Result<> removeHandler(void* address);
|
||||||
|
|
||||||
void updateSpecialFiles();
|
|
||||||
void tryDownloadLoaderResources(std::string const& url, bool tryLatestOnError = true);
|
|
||||||
void downloadLoaderResources(bool useLatestRelease = false);
|
|
||||||
void downloadLoaderUpdate(std::string const& url);
|
|
||||||
void fetchLatestGithubRelease(
|
|
||||||
utils::MiniFunction<void(matjson::Value const&)> then,
|
|
||||||
utils::MiniFunction<void(std::string const&)> expect
|
|
||||||
);
|
|
||||||
|
|
||||||
bool loadHooks();
|
bool loadHooks();
|
||||||
void setupIPC();
|
|
||||||
|
|
||||||
Impl();
|
Impl();
|
||||||
~Impl();
|
~Impl();
|
||||||
|
@ -157,25 +113,9 @@ namespace geode {
|
||||||
|
|
||||||
void updateResources(bool forceReload);
|
void updateResources(bool forceReload);
|
||||||
|
|
||||||
bool didLastLaunchCrash() const;
|
|
||||||
|
|
||||||
matjson::Value processRawIPC(void* rawHandle, std::string const& buffer);
|
|
||||||
|
|
||||||
void queueInMainThread(ScheduledFunction func);
|
void queueInMainThread(ScheduledFunction func);
|
||||||
void executeGDThreadQueue();
|
void executeGDThreadQueue();
|
||||||
|
|
||||||
void logConsoleMessage(std::string const& msg);
|
|
||||||
void logConsoleMessageWithSeverity(std::string const& msg, Severity severity);
|
|
||||||
|
|
||||||
bool platformConsoleOpen() const;
|
|
||||||
void openPlatformConsole();
|
|
||||||
void closePlatformConsole();
|
|
||||||
void platformMessageBox(char const* title, std::string const& info, Severity severity = Severity::Error);
|
|
||||||
|
|
||||||
bool verifyLoaderResources();
|
|
||||||
void checkForLoaderUpdates();
|
|
||||||
bool isNewUpdateDownloaded() const;
|
|
||||||
|
|
||||||
bool isReadyToHook() const;
|
bool isReadyToHook() const;
|
||||||
void addUninitializedHook(Hook* hook, Mod* mod);
|
void addUninitializedHook(Hook* hook, Mod* mod);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "LoaderImpl.hpp"
|
#include "console.hpp"
|
||||||
#include "LogImpl.hpp"
|
#include "LogImpl.hpp"
|
||||||
|
|
||||||
#include <Geode/loader/Dirs.hpp>
|
#include <Geode/loader/Dirs.hpp>
|
||||||
|
@ -198,7 +198,7 @@ void Logger::push(Severity sev, Mod* mod, std::string&& content) {
|
||||||
auto& log = m_logs.emplace_back(sev, mod, std::move(content));
|
auto& log = m_logs.emplace_back(sev, mod, std::move(content));
|
||||||
auto const logStr = log.toString(true, m_nestLevel);
|
auto const logStr = log.toString(true, m_nestLevel);
|
||||||
|
|
||||||
LoaderImpl::get()->logConsoleMessageWithSeverity(logStr, log.getSeverity());
|
console::log(logStr, log.getSeverity());
|
||||||
m_logStream << logStr << std::endl;
|
m_logStream << logStr << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,4 +233,4 @@ void log::pushNest() {
|
||||||
|
|
||||||
void log::popNest() {
|
void log::popNest() {
|
||||||
Logger::get()->popNest();
|
Logger::get()->popNest();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "LoaderImpl.hpp"
|
#include "LoaderImpl.hpp"
|
||||||
#include "ModMetadataImpl.hpp"
|
#include "ModMetadataImpl.hpp"
|
||||||
#include "about.hpp"
|
#include "about.hpp"
|
||||||
|
#include "console.hpp"
|
||||||
|
|
||||||
#include <hash/hash.hpp>
|
#include <hash/hash.hpp>
|
||||||
#include <Geode/loader/Dirs.hpp>
|
#include <Geode/loader/Dirs.hpp>
|
||||||
|
@ -686,7 +687,7 @@ Mod* Loader::Impl::getInternalMod() {
|
||||||
}
|
}
|
||||||
auto infoRes = getModImplInfo();
|
auto infoRes = getModImplInfo();
|
||||||
if (!infoRes) {
|
if (!infoRes) {
|
||||||
LoaderImpl::get()->platformMessageBox(
|
console::messageBox(
|
||||||
"Fatal Internal Error",
|
"Fatal Internal Error",
|
||||||
"Unable to create internal mod info: \"" + infoRes.unwrapErr() +
|
"Unable to create internal mod info: \"" + infoRes.unwrapErr() +
|
||||||
"\"\n"
|
"\"\n"
|
||||||
|
|
10
loader/src/loader/console.hpp
Normal file
10
loader/src/loader/console.hpp
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace geode::console {
|
||||||
|
void open();
|
||||||
|
void close();
|
||||||
|
void log(std::string const& msg, Severity severity);
|
||||||
|
void messageBox(char const* title, std::string const& info, Severity severity = Severity::Error);
|
||||||
|
}
|
325
loader/src/loader/updater.cpp
Normal file
325
loader/src/loader/updater.cpp
Normal file
|
@ -0,0 +1,325 @@
|
||||||
|
#include "updater.hpp"
|
||||||
|
#include <Geode/utils/web.hpp>
|
||||||
|
#include <Geode/loader/Index.hpp>
|
||||||
|
#include <resources.hpp>
|
||||||
|
#include <hash.hpp>
|
||||||
|
#include <utility>
|
||||||
|
#include "LoaderImpl.hpp"
|
||||||
|
#include "ModMetadataImpl.hpp"
|
||||||
|
|
||||||
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
updater::ResourceDownloadEvent::ResourceDownloadEvent(
|
||||||
|
UpdateStatus status
|
||||||
|
) : status(std::move(status)) {}
|
||||||
|
|
||||||
|
ListenerResult updater::ResourceDownloadFilter::handle(
|
||||||
|
const utils::MiniFunction<Callback>& fn,
|
||||||
|
ResourceDownloadEvent* event
|
||||||
|
) {
|
||||||
|
fn(event);
|
||||||
|
return ListenerResult::Propagate;
|
||||||
|
}
|
||||||
|
|
||||||
|
updater::ResourceDownloadFilter::ResourceDownloadFilter() = default;
|
||||||
|
|
||||||
|
updater::LoaderUpdateEvent::LoaderUpdateEvent(
|
||||||
|
UpdateStatus status
|
||||||
|
) : status(std::move(status)) {}
|
||||||
|
|
||||||
|
ListenerResult updater::LoaderUpdateFilter::handle(
|
||||||
|
const utils::MiniFunction<Callback>& fn,
|
||||||
|
LoaderUpdateEvent* event
|
||||||
|
) {
|
||||||
|
fn(event);
|
||||||
|
return ListenerResult::Propagate;
|
||||||
|
}
|
||||||
|
|
||||||
|
updater::LoaderUpdateFilter::LoaderUpdateFilter() = default;
|
||||||
|
|
||||||
|
// cache for the json of the latest github release to avoid hitting
|
||||||
|
// the github api too much
|
||||||
|
std::optional<matjson::Value> s_latestGithubRelease;
|
||||||
|
bool s_isNewUpdateDownloaded = false;
|
||||||
|
|
||||||
|
void updater::fetchLatestGithubRelease(
|
||||||
|
const utils::MiniFunction<void(matjson::Value const&)>& then,
|
||||||
|
utils::MiniFunction<void(std::string const&)> expect
|
||||||
|
) {
|
||||||
|
if (s_latestGithubRelease) {
|
||||||
|
return then(s_latestGithubRelease.value());
|
||||||
|
}
|
||||||
|
// TODO: add header to not get rate limited
|
||||||
|
web::AsyncWebRequest()
|
||||||
|
.join("loader-auto-update-check")
|
||||||
|
.userAgent("github_api/1.0")
|
||||||
|
.fetch("https://api.github.com/repos/geode-sdk/geode/releases/latest")
|
||||||
|
.json()
|
||||||
|
.then([then](matjson::Value const& json) {
|
||||||
|
s_latestGithubRelease = json;
|
||||||
|
then(json);
|
||||||
|
})
|
||||||
|
.expect(std::move(expect));
|
||||||
|
}
|
||||||
|
|
||||||
|
void updater::tryDownloadLoaderResources(
|
||||||
|
std::string const& url,
|
||||||
|
bool tryLatestOnError
|
||||||
|
) {
|
||||||
|
auto tempResourcesZip = dirs::getTempDir() / "new.zip";
|
||||||
|
auto resourcesDir = dirs::getGeodeResourcesDir() / Mod::get()->getID();
|
||||||
|
|
||||||
|
web::AsyncWebRequest()
|
||||||
|
// use the url as a join handle
|
||||||
|
.join(url)
|
||||||
|
.fetch(url)
|
||||||
|
.into(tempResourcesZip)
|
||||||
|
.then([tempResourcesZip, resourcesDir](auto) {
|
||||||
|
// unzip resources zip
|
||||||
|
auto unzip = file::Unzip::intoDir(tempResourcesZip, resourcesDir, true);
|
||||||
|
if (!unzip) {
|
||||||
|
ResourceDownloadEvent(
|
||||||
|
UpdateFailed("Unable to unzip new resources: " + unzip.unwrapErr())
|
||||||
|
).post();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updater::updateSpecialFiles();
|
||||||
|
|
||||||
|
ResourceDownloadEvent(UpdateFinished()).post();
|
||||||
|
})
|
||||||
|
.expect([tryLatestOnError](std::string const& info, int code) {
|
||||||
|
// if the url was not found, try downloading latest release instead
|
||||||
|
// (for development versions)
|
||||||
|
if (code == 404) {
|
||||||
|
log::warn("Unable to download resources: {}", info);
|
||||||
|
}
|
||||||
|
ResourceDownloadEvent(
|
||||||
|
UpdateFailed("Unable to download resources: " + info)
|
||||||
|
).post();
|
||||||
|
})
|
||||||
|
.progress([](auto&, double now, double total) {
|
||||||
|
ResourceDownloadEvent(
|
||||||
|
UpdateProgress(
|
||||||
|
static_cast<uint8_t>(now / total * 100.0),
|
||||||
|
"Downloading resources"
|
||||||
|
)
|
||||||
|
).post();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updater::updateSpecialFiles() {
|
||||||
|
auto resourcesDir = dirs::getGeodeResourcesDir() / Mod::get()->getID();
|
||||||
|
auto res = ModMetadataImpl::getImpl(ModImpl::get()->m_metadata).addSpecialFiles(resourcesDir);
|
||||||
|
if (res.isErr()) {
|
||||||
|
log::warn("Unable to add special files: {}", res.unwrapErr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updater::downloadLoaderResources(bool useLatestRelease) {
|
||||||
|
web::AsyncWebRequest()
|
||||||
|
.join("loader-tag-exists-check")
|
||||||
|
.userAgent("github_api/1.0")
|
||||||
|
.fetch(fmt::format(
|
||||||
|
"https://api.github.com/repos/geode-sdk/geode/git/ref/tags/{}",
|
||||||
|
Loader::get()->getVersion().toString()
|
||||||
|
))
|
||||||
|
.json()
|
||||||
|
.then([](matjson::Value const& json) {
|
||||||
|
updater::tryDownloadLoaderResources(fmt::format(
|
||||||
|
"https://github.com/geode-sdk/geode/releases/download/{}/resources.zip",
|
||||||
|
Loader::get()->getVersion().toString()
|
||||||
|
), true);
|
||||||
|
})
|
||||||
|
.expect([=](std::string const& info, int code) {
|
||||||
|
if (code == 404) {
|
||||||
|
if (useLatestRelease) {
|
||||||
|
log::debug("Loader version {} does not exist on Github, downloading latest resources", Loader::get()->getVersion().toString());
|
||||||
|
fetchLatestGithubRelease(
|
||||||
|
[](matjson::Value const& raw) {
|
||||||
|
auto json = raw;
|
||||||
|
JsonChecker checker(json);
|
||||||
|
auto root = checker.root("[]").obj();
|
||||||
|
|
||||||
|
// find release asset
|
||||||
|
for (auto asset : root.needs("assets").iterate()) {
|
||||||
|
auto obj = asset.obj();
|
||||||
|
if (obj.needs("name").template get<std::string>() == "resources.zip") {
|
||||||
|
updater::tryDownloadLoaderResources(
|
||||||
|
obj.needs("browser_download_url").template get<std::string>(),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResourceDownloadEvent(
|
||||||
|
UpdateFailed("Unable to find resources in latest GitHub release")
|
||||||
|
).post();
|
||||||
|
},
|
||||||
|
[](std::string const& info) {
|
||||||
|
ResourceDownloadEvent(
|
||||||
|
UpdateFailed("Unable to download resources: " + info)
|
||||||
|
).post();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log::debug("Loader version {} does not exist on GitHub, not downloading the resources", Loader::get()->getVersion().toString());
|
||||||
|
}
|
||||||
|
ResourceDownloadEvent(
|
||||||
|
UpdateFinished()
|
||||||
|
).post();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ResourceDownloadEvent(
|
||||||
|
UpdateFailed("Unable to check if tag exists: " + info)
|
||||||
|
).post();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updater::verifyLoaderResources() {
|
||||||
|
static std::optional<bool> CACHED = std::nullopt;
|
||||||
|
if (CACHED.has_value()) {
|
||||||
|
return CACHED.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// geode/resources/geode.loader
|
||||||
|
auto resourcesDir = dirs::getGeodeResourcesDir() / Mod::get()->getID();
|
||||||
|
|
||||||
|
// if the resources dir doesn't exist, then it's probably incorrect
|
||||||
|
if (!(
|
||||||
|
ghc::filesystem::exists(resourcesDir) &&
|
||||||
|
ghc::filesystem::is_directory(resourcesDir)
|
||||||
|
)) {
|
||||||
|
log::debug("Resources directory does not exist");
|
||||||
|
updater::downloadLoaderResources(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: actually have a proper way to disable checking resources
|
||||||
|
// for development builds
|
||||||
|
if (ghc::filesystem::exists(resourcesDir / "dont-update.txt")) {
|
||||||
|
// this is kind of a hack, but it's the easiest way to prevent
|
||||||
|
// auto update while developing
|
||||||
|
log::debug("Not updating resources since dont-update.txt exists");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure every file was covered
|
||||||
|
size_t coverage = 0;
|
||||||
|
|
||||||
|
// verify hashes
|
||||||
|
for (auto& file : ghc::filesystem::directory_iterator(resourcesDir)) {
|
||||||
|
auto name = file.path().filename().string();
|
||||||
|
// skip unknown files
|
||||||
|
if (!LOADER_RESOURCE_HASHES.count(name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// verify hash
|
||||||
|
auto hash = calculateSHA256(file.path());
|
||||||
|
const auto& expected = LOADER_RESOURCE_HASHES.at(name);
|
||||||
|
if (hash != expected) {
|
||||||
|
log::debug("Resource hash mismatch: {} ({}, {})", name, hash.substr(0, 7), expected.substr(0, 7));
|
||||||
|
updater::downloadLoaderResources();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
coverage += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure every file was found
|
||||||
|
if (coverage != LOADER_RESOURCE_HASHES.size()) {
|
||||||
|
log::debug("Resource coverage mismatch");
|
||||||
|
updater::downloadLoaderResources();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updater::downloadLoaderUpdate(std::string const& url) {
|
||||||
|
auto updateZip = dirs::getTempDir() / "loader-update.zip";
|
||||||
|
auto targetDir = dirs::getGeodeDir() / "update";
|
||||||
|
|
||||||
|
web::AsyncWebRequest()
|
||||||
|
.join("loader-update-download")
|
||||||
|
.fetch(url)
|
||||||
|
.into(updateZip)
|
||||||
|
.then([updateZip, targetDir](auto) {
|
||||||
|
// unzip resources zip
|
||||||
|
auto unzip = file::Unzip::intoDir(updateZip, targetDir, true);
|
||||||
|
if (!unzip) {
|
||||||
|
LoaderUpdateEvent(
|
||||||
|
UpdateFailed("Unable to unzip update: " + unzip.unwrapErr())
|
||||||
|
).post();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s_isNewUpdateDownloaded = true;
|
||||||
|
LoaderUpdateEvent(UpdateFinished()).post();
|
||||||
|
})
|
||||||
|
.expect([](std::string const& info) {
|
||||||
|
LoaderUpdateEvent(
|
||||||
|
UpdateFailed("Unable to download update: " + info)
|
||||||
|
).post();
|
||||||
|
})
|
||||||
|
.progress([](auto&, double now, double total) {
|
||||||
|
LoaderUpdateEvent(
|
||||||
|
UpdateProgress(
|
||||||
|
static_cast<uint8_t>(now / total * 100.0),
|
||||||
|
"Downloading update"
|
||||||
|
)
|
||||||
|
).post();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void updater::checkForLoaderUpdates() {
|
||||||
|
// Check for updates in the background
|
||||||
|
fetchLatestGithubRelease(
|
||||||
|
[](matjson::Value const& raw) {
|
||||||
|
auto json = raw;
|
||||||
|
JsonChecker checker(json);
|
||||||
|
auto root = checker.root("[]").obj();
|
||||||
|
|
||||||
|
VersionInfo ver { 0, 0, 0 };
|
||||||
|
root.needs("tag_name").into(ver);
|
||||||
|
|
||||||
|
// make sure release is newer
|
||||||
|
if (ver <= Loader::get()->getVersion()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't auto-update major versions
|
||||||
|
if (ver.getMajor() > Loader::get()->getVersion().getMajor()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find release asset
|
||||||
|
for (auto asset : root.needs("assets").iterate()) {
|
||||||
|
auto obj = asset.obj();
|
||||||
|
if (string::endsWith(
|
||||||
|
obj.needs("name").template get<std::string>(),
|
||||||
|
GEODE_PLATFORM_SHORT_IDENTIFIER ".zip"
|
||||||
|
)) {
|
||||||
|
updater::downloadLoaderUpdate(
|
||||||
|
obj.needs("browser_download_url").template get<std::string>()
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LoaderUpdateEvent(
|
||||||
|
UpdateFailed("Unable to find release asset for " GEODE_PLATFORM_NAME)
|
||||||
|
).post();
|
||||||
|
},
|
||||||
|
[](std::string const& info) {
|
||||||
|
LoaderUpdateEvent(
|
||||||
|
UpdateFailed("Unable to check for updates: " + info)
|
||||||
|
).post();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updater::isNewUpdateDownloaded() {
|
||||||
|
return s_isNewUpdateDownloaded;
|
||||||
|
}
|
46
loader/src/loader/updater.hpp
Normal file
46
loader/src/loader/updater.hpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <Geode/utils/MiniFunction.hpp>
|
||||||
|
#include <Geode/loader/Index.hpp>
|
||||||
|
|
||||||
|
namespace geode::updater {
|
||||||
|
struct ResourceDownloadEvent : public Event {
|
||||||
|
const UpdateStatus status;
|
||||||
|
explicit ResourceDownloadEvent(UpdateStatus status);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ResourceDownloadFilter : public EventFilter<ResourceDownloadEvent> {
|
||||||
|
public:
|
||||||
|
using Callback = void(ResourceDownloadEvent*);
|
||||||
|
|
||||||
|
static ListenerResult handle(const utils::MiniFunction<Callback>& fn, ResourceDownloadEvent* event);
|
||||||
|
ResourceDownloadFilter();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LoaderUpdateEvent : public Event {
|
||||||
|
const UpdateStatus status;
|
||||||
|
explicit LoaderUpdateEvent(UpdateStatus status);
|
||||||
|
};
|
||||||
|
|
||||||
|
class LoaderUpdateFilter : public EventFilter<LoaderUpdateEvent> {
|
||||||
|
public:
|
||||||
|
using Callback = void(LoaderUpdateEvent*);
|
||||||
|
|
||||||
|
static ListenerResult handle(const utils::MiniFunction<Callback>& fn, LoaderUpdateEvent* event);
|
||||||
|
LoaderUpdateFilter();
|
||||||
|
};
|
||||||
|
|
||||||
|
void updateSpecialFiles();
|
||||||
|
void tryDownloadLoaderResources(std::string const& url, bool tryLatestOnError = true);
|
||||||
|
void downloadLoaderResources(bool useLatestRelease = false);
|
||||||
|
void downloadLoaderUpdate(std::string const& url);
|
||||||
|
void fetchLatestGithubRelease(
|
||||||
|
const utils::MiniFunction<void(matjson::Value const&)>& then,
|
||||||
|
utils::MiniFunction<void(std::string const&)> expect
|
||||||
|
);
|
||||||
|
|
||||||
|
bool verifyLoaderResources();
|
||||||
|
void checkForLoaderUpdates();
|
||||||
|
bool isNewUpdateDownloaded();
|
||||||
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
#include <FileWatcher.hpp>
|
#include <FileWatcher.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_ANDROID
|
|
||||||
|
|
||||||
FileWatcher::FileWatcher(
|
FileWatcher::FileWatcher(
|
||||||
ghc::filesystem::path const& file, FileWatchCallback callback, ErrorCallback error
|
ghc::filesystem::path const& file, FileWatchCallback callback, ErrorCallback error
|
||||||
) {
|
) {
|
||||||
|
@ -23,5 +21,3 @@ void FileWatcher::watch() {
|
||||||
bool FileWatcher::watching() const {
|
bool FileWatcher::watching() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
7
loader/src/platform/android/IPC.cpp
Normal file
7
loader/src/platform/android/IPC.cpp
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
#include <loader/IPC.hpp>
|
||||||
|
|
||||||
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
void ipc::setup() {
|
||||||
|
log::debug("IPC is not supported on this platform!");
|
||||||
|
}
|
|
@ -1,29 +1,9 @@
|
||||||
#include <Geode/loader/IPC.hpp>
|
#include <Geode/loader/IPC.hpp>
|
||||||
#include <Geode/loader/Log.hpp>
|
|
||||||
#include <loader/ModImpl.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <loader/LoaderImpl.hpp>
|
#include <loader/LoaderImpl.hpp>
|
||||||
#include <Geode/utils/string.hpp>
|
#include <Geode/cocos/platform/android/jni/JniHelper.h>
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
#ifdef GEODE_IS_ANDROID
|
|
||||||
|
|
||||||
#include <Geode/cocos/platform/android/jni/JniHelper.h>
|
|
||||||
#include <android/log.h>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
android_LogPriority getLogSeverityForSeverity(Severity severity) {
|
|
||||||
switch (severity) {
|
|
||||||
case Severity::Debug: return ANDROID_LOG_DEBUG;
|
|
||||||
case Severity::Info: return ANDROID_LOG_INFO;
|
|
||||||
case Severity::Warning: return ANDROID_LOG_WARN;
|
|
||||||
case Severity::Error: return ANDROID_LOG_ERROR;
|
|
||||||
default: return ANDROID_LOG_DEFAULT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Loader::Impl::getGameVersion() {
|
std::string Loader::Impl::getGameVersion() {
|
||||||
if (m_gdVersion.empty()) {
|
if (m_gdVersion.empty()) {
|
||||||
/*
|
/*
|
||||||
|
@ -49,33 +29,6 @@ std::string Loader::Impl::getGameVersion() {
|
||||||
return m_gdVersion;
|
return m_gdVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::platformMessageBox(char const* title, std::string const& info, Severity severity) {
|
|
||||||
cocos2d::CCMessageBox(info.c_str(), title);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::logConsoleMessageWithSeverity(std::string const& msg, Severity severity) {
|
|
||||||
__android_log_print(
|
|
||||||
getLogSeverityForSeverity(severity),
|
|
||||||
"Geode",
|
|
||||||
"%s",
|
|
||||||
msg.c_str()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::openPlatformConsole() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::closePlatformConsole() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::setupIPC() {
|
|
||||||
log::warn("IPC is not supported on this platform!");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Loader::Impl::userTriedToLoadDLLs() const {
|
bool Loader::Impl::userTriedToLoadDLLs() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_ANDROID
|
|
||||||
|
|
||||||
#include <Geode/loader/Mod.hpp>
|
#include <Geode/loader/Mod.hpp>
|
||||||
#include <loader/ModImpl.hpp>
|
#include <loader/ModImpl.hpp>
|
||||||
|
|
||||||
|
@ -34,5 +32,3 @@ Result<> Mod::Impl::unloadPlatformBinary() {
|
||||||
return Err("Unable to free library");
|
return Err("Unable to free library");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
39
loader/src/platform/android/console.cpp
Normal file
39
loader/src/platform/android/console.cpp
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#include <loader/console.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <Geode/loader/Log.hpp>
|
||||||
|
#include <android/log.h>
|
||||||
|
|
||||||
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
android_LogPriority getLogSeverityForSeverity(Severity severity) {
|
||||||
|
switch (severity) {
|
||||||
|
case Severity::Debug: return ANDROID_LOG_DEBUG;
|
||||||
|
case Severity::Info: return ANDROID_LOG_INFO;
|
||||||
|
case Severity::Warning: return ANDROID_LOG_WARN;
|
||||||
|
case Severity::Error: return ANDROID_LOG_ERROR;
|
||||||
|
default: return ANDROID_LOG_DEFAULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::open() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::close() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::log(std::string const& msg, Severity severity) {
|
||||||
|
__android_log_print(
|
||||||
|
getLogSeverityForSeverity(severity),
|
||||||
|
"Geode",
|
||||||
|
"%s",
|
||||||
|
msg.c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::messageBox(char const* title, std::string const& info, Severity severity) {
|
||||||
|
cocos2d::CCMessageBox(info.c_str(), title);
|
||||||
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
#include <crashlog.hpp>
|
#include <crashlog.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_ANDROID
|
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
#include <Geode/utils/string.hpp>
|
#include <Geode/utils/string.hpp>
|
||||||
|
@ -302,5 +300,3 @@ void crashlog::setupPlatformHandlerPost() {
|
||||||
bool crashlog::didLastLaunchCrash() {
|
bool crashlog::didLastLaunchCrash() {
|
||||||
return s_lastLaunchCrashed;
|
return s_lastLaunchCrashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#include <Geode/c++stl/gdstdlib.hpp>
|
#include <Geode/c++stl/gdstdlib.hpp>
|
||||||
#include "../../c++stl/string-impl.hpp"
|
#include "../../c++stl/string-impl.hpp"
|
||||||
|
|
||||||
#ifdef GEODE_IS_ANDROID
|
|
||||||
|
|
||||||
#if defined(GEODE_IS_ANDROID32)
|
#if defined(GEODE_IS_ANDROID32)
|
||||||
|
|
||||||
static auto constexpr NEW_SYM = "_Znwj";
|
static auto constexpr NEW_SYM = "_Znwj";
|
||||||
|
@ -119,5 +117,3 @@ namespace geode::stl {
|
||||||
// TODO: implement this, remember its copy-on-write...
|
// TODO: implement this, remember its copy-on-write...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#if defined(GEODE_IS_ANDROID)
|
|
||||||
|
|
||||||
#include "../load.hpp"
|
#include "../load.hpp"
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
|
|
||||||
|
@ -30,5 +28,3 @@ extern "C" [[gnu::visibility("default")]] jint JNI_OnLoad(JavaVM* vm, void* rese
|
||||||
extern "C" [[gnu::visibility("default")]] void emptyFunction(void*) {
|
extern "C" [[gnu::visibility("default")]] void emptyFunction(void*) {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_ANDROID
|
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
#include <Geode/utils/cocos.hpp>
|
#include <Geode/utils/cocos.hpp>
|
||||||
|
@ -279,5 +277,3 @@ void geode::utils::game::restart() {
|
||||||
nullptr
|
nullptr
|
||||||
), CCDirector::get()->getRunningScene(), false);
|
), CCDirector::get()->getRunningScene(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <FileWatcher.hpp>
|
#include <FileWatcher.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_IOS
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -52,5 +50,3 @@ void FileWatcher::watch() {
|
||||||
bool FileWatcher::watching() const {
|
bool FileWatcher::watching() const {
|
||||||
return m_platformHandle != NULL;
|
return m_platformHandle != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
#include <loader/LoaderImpl.hpp>
|
#include <loader/LoaderImpl.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_IOS
|
#include <Geode/loader/Dirs.hpp>
|
||||||
|
#include <Geode/loader/Loader.hpp>
|
||||||
#include <Geode/loader/Dirs.hpp>
|
#include <Geode/loader/Log.hpp>
|
||||||
#include <Geode/loader/Loader.hpp>
|
#include <loader/ModImpl.hpp>
|
||||||
#include <Geode/loader/Log.hpp>
|
#include <iostream>
|
||||||
#include <loader/ModImpl.hpp>
|
#include <pwd.h>
|
||||||
#include <iostream>
|
#include <sys/types.h>
|
||||||
#include <pwd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
void Loader::Impl::platformMessageBox(char const* title, std::string const& info) {
|
void Loader::Impl::platformMessageBox(char const* title, std::string const& info) {
|
||||||
std::cout << title << ": " << info << std::endl;
|
std::cout << title << ": " << info << std::endl;
|
||||||
|
@ -41,5 +39,3 @@ void Loader::Impl::setupIPC() {
|
||||||
bool Loader::Impl::userTriedToLoadDLLs() const {
|
bool Loader::Impl::userTriedToLoadDLLs() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_IOS
|
#include <Geode/loader/Mod.hpp>
|
||||||
|
#include <loader/ModImpl.hpp>
|
||||||
#include <Geode/loader/Mod.hpp>
|
#include <dlfcn.h>
|
||||||
#include <loader/ModImpl.hpp>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
@ -43,5 +41,3 @@ Result<> Mod::Impl::unloadPlatformBinary() {
|
||||||
return Err("Unable to free library");
|
return Err("Unable to free library");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <crashlog.hpp>
|
#include <crashlog.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_IOS
|
|
||||||
|
|
||||||
bool crashlog::setupPlatformHandler() {
|
bool crashlog::setupPlatformHandler() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -13,5 +11,3 @@ bool crashlog::didLastLaunchCrash() {
|
||||||
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#if defined(GEODE_IS_IOS)
|
|
||||||
|
|
||||||
#include "../load.hpp"
|
#include "../load.hpp"
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
|
@ -43,5 +41,3 @@ extern "C" __attribute__((visibility("default"))) void dynamicTrigger() {
|
||||||
|
|
||||||
// remove when we can figure out how to not remove it
|
// remove when we can figure out how to not remove it
|
||||||
auto dynamicTriggerRef = &dynamicTrigger;
|
auto dynamicTriggerRef = &dynamicTrigger;
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,9 +1,7 @@
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_IOS
|
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
#include <Geode/loader/Dirs.hpp>
|
#include <Geode/loader/Dirs.hpp>
|
||||||
#include <UIKit/UIKit.h>
|
#include <UIKit/UIKit.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
@ -36,5 +34,3 @@ ghc::filesystem::path dirs::getGameDir() {
|
||||||
ghc::filesystem::path dirs::getSaveDir() {
|
ghc::filesystem::path dirs::getSaveDir() {
|
||||||
return weaklyCanonical(CCFileUtils::sharedFileUtils()->getWritablePath().c_str());
|
return weaklyCanonical(CCFileUtils::sharedFileUtils()->getWritablePath().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_MACOS
|
|
||||||
|
|
||||||
#include <cocos2d.h>
|
#include <cocos2d.h>
|
||||||
using namespace cocos2d;
|
using namespace cocos2d;
|
||||||
|
|
||||||
|
@ -815,5 +813,3 @@ void CCArray::acceptVisitor(CCDataVisitor &visitor)
|
||||||
{
|
{
|
||||||
visitor.visit(this);
|
visitor.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
#include <FileWatcher.hpp>
|
#include <FileWatcher.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_MACOS
|
#import <Cocoa/Cocoa.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#import <Cocoa/Cocoa.h>
|
#include <iostream>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
// static constexpr const auto notifyAttributes = FILE_NOTIFY_CHANGE_LAST_WRITE |
|
// static constexpr const auto notifyAttributes = FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||||
// FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE;
|
// FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE;
|
||||||
|
@ -52,5 +50,3 @@ void FileWatcher::watch() {
|
||||||
bool FileWatcher::watching() const {
|
bool FileWatcher::watching() const {
|
||||||
return m_platformHandle != NULL;
|
return m_platformHandle != NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_MACOS
|
|
||||||
|
|
||||||
#include <Geode/loader/Mod.hpp>
|
#include <Geode/loader/Mod.hpp>
|
||||||
#include <loader/ModImpl.hpp>
|
#include <loader/ModImpl.hpp>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
@ -43,5 +41,3 @@ Result<> Mod::Impl::unloadPlatformBinary() {
|
||||||
return Err("Unable to free library");
|
return Err("Unable to free library");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <crashlog.hpp>
|
#include <crashlog.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_MACOS
|
|
||||||
|
|
||||||
#include <Geode/utils/string.hpp>
|
#include <Geode/utils/string.hpp>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
@ -391,6 +389,3 @@ bool crashlog::didLastLaunchCrash() {
|
||||||
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||||
return dirs::getGeodeDir() / "crashlogs";
|
return dirs::getGeodeDir() / "crashlogs";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <Geode/c++stl/gdstdlib.hpp>
|
#include <Geode/c++stl/gdstdlib.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_MACOS
|
|
||||||
|
|
||||||
namespace gd {
|
namespace gd {
|
||||||
namespace {
|
namespace {
|
||||||
static inline auto emptyInternalString() {
|
static inline auto emptyInternalString() {
|
||||||
|
@ -57,5 +55,3 @@ namespace gd {
|
||||||
return std::string(*this) == std::string(other);
|
return std::string(*this) == std::string(other);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#if defined(GEODE_IS_MACOS)
|
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
#include "../load.hpp"
|
#include "../load.hpp"
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
@ -151,5 +149,3 @@ __attribute__((constructor)) void _entry() {
|
||||||
if (!loadGeode())
|
if (!loadGeode())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,8 +1,5 @@
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_MACOS
|
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
#include <Geode/loader/Dirs.hpp>
|
#include <Geode/loader/Dirs.hpp>
|
||||||
|
@ -304,5 +301,3 @@ Result<void*> geode::hook::getObjcMethodImp(std::string const& className, std::s
|
||||||
|
|
||||||
return Ok((void*)method_getImplementation(method));
|
return Ok((void*)method_getImplementation(method));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#ifdef GEODE_IS_WINDOWS
|
|
||||||
|
|
||||||
static constexpr auto const notifyAttributes =
|
static constexpr auto const notifyAttributes =
|
||||||
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE;
|
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE;
|
||||||
|
|
||||||
|
@ -100,5 +98,3 @@ bool FileWatcher::watching() const {
|
||||||
HANDLE handle = (HANDLE)m_platformHandle;
|
HANDLE handle = (HANDLE)m_platformHandle;
|
||||||
return handle != INVALID_HANDLE_VALUE && handle != nullptr;
|
return handle != INVALID_HANDLE_VALUE && handle != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
75
loader/src/platform/windows/IPC.cpp
Normal file
75
loader/src/platform/windows/IPC.cpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#include <Geode/loader/IPC.hpp>
|
||||||
|
#include <loader/IPC.hpp>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
static constexpr auto IPC_BUFFER_SIZE = 512;
|
||||||
|
|
||||||
|
void ipcPipeThread(HANDLE pipe) {
|
||||||
|
char buffer[IPC_BUFFER_SIZE * sizeof(TCHAR)];
|
||||||
|
DWORD read;
|
||||||
|
|
||||||
|
std::optional<std::string> replyID;
|
||||||
|
|
||||||
|
// log::debug("Waiting for I/O");
|
||||||
|
if (ReadFile(pipe, buffer, sizeof(buffer) - 1, &read, nullptr)) {
|
||||||
|
buffer[read] = '\0';
|
||||||
|
|
||||||
|
std::string reply = ipc::processRaw((void*)pipe, buffer).dump();
|
||||||
|
|
||||||
|
DWORD written;
|
||||||
|
WriteFile(pipe, reply.c_str(), reply.size(), &written, nullptr);
|
||||||
|
}
|
||||||
|
// log::debug("Connection done");
|
||||||
|
|
||||||
|
FlushFileBuffers(pipe);
|
||||||
|
DisconnectNamedPipe(pipe);
|
||||||
|
CloseHandle(pipe);
|
||||||
|
|
||||||
|
// log::debug("Disconnected pipe");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipc::setup() {
|
||||||
|
std::thread ipcThread([]() {
|
||||||
|
while (true) {
|
||||||
|
auto pipe = CreateNamedPipeA(
|
||||||
|
ipc::IPC_PIPE_NAME,
|
||||||
|
PIPE_ACCESS_DUPLEX,
|
||||||
|
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
||||||
|
PIPE_UNLIMITED_INSTANCES,
|
||||||
|
IPC_BUFFER_SIZE,
|
||||||
|
IPC_BUFFER_SIZE,
|
||||||
|
NMPWAIT_USE_DEFAULT_WAIT,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
if (pipe == INVALID_HANDLE_VALUE) {
|
||||||
|
// todo: Rn this quits IPC, but we might wanna change that later
|
||||||
|
// to just continue trying. however, I'm assuming that if
|
||||||
|
// CreateNamedPipeA fails, then it will probably fail again if
|
||||||
|
// you try right after, so changing the break; to continue; might
|
||||||
|
// just result in the console getting filled with error messages
|
||||||
|
log::warn("Unable to create pipe, quitting IPC");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// log::debug("Waiting for pipe connections");
|
||||||
|
if (ConnectNamedPipe(pipe, nullptr)) {
|
||||||
|
// log::debug("Got connection, creating thread");
|
||||||
|
std::thread pipeThread(&ipcPipeThread, pipe);
|
||||||
|
// SetThreadDescription(pipeThread.native_handle(), L"Geode IPC Pipe");
|
||||||
|
pipeThread.detach();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// log::debug("No connection, cleaning pipe");
|
||||||
|
CloseHandle(pipe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// SetThreadDescription(ipcThread.native_handle(), L"Geode Main IPC");
|
||||||
|
ipcThread.detach();
|
||||||
|
|
||||||
|
log::debug("IPC set up");
|
||||||
|
}
|
|
@ -1,19 +1,13 @@
|
||||||
#include <Geode/loader/IPC.hpp>
|
#include <Geode/loader/IPC.hpp>
|
||||||
#include <Geode/loader/Log.hpp>
|
#include <Geode/loader/Log.hpp>
|
||||||
#include <loader/ModImpl.hpp>
|
#include <loader/ModImpl.hpp>
|
||||||
#include <iostream>
|
|
||||||
#include <loader/LoaderImpl.hpp>
|
#include <loader/LoaderImpl.hpp>
|
||||||
#include <Geode/utils/string.hpp>
|
#include <Geode/utils/string.hpp>
|
||||||
#include <loader/LogImpl.hpp>
|
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
#ifdef GEODE_IS_WINDOWS
|
|
||||||
|
|
||||||
#include <Psapi.h>
|
#include <Psapi.h>
|
||||||
|
|
||||||
static constexpr auto IPC_BUFFER_SIZE = 512;
|
|
||||||
|
|
||||||
#include "gdTimestampMap.hpp"
|
#include "gdTimestampMap.hpp"
|
||||||
std::string Loader::Impl::getGameVersion() {
|
std::string Loader::Impl::getGameVersion() {
|
||||||
if (m_gdVersion.empty()) {
|
if (m_gdVersion.empty()) {
|
||||||
|
@ -25,161 +19,6 @@ std::string Loader::Impl::getGameVersion() {
|
||||||
return m_gdVersion;
|
return m_gdVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::platformMessageBox(char const* title, std::string const& info, Severity severity) {
|
|
||||||
unsigned int icon;
|
|
||||||
switch (severity) {
|
|
||||||
case Severity::Debug:
|
|
||||||
case Severity::Info:
|
|
||||||
case Severity::Notice:
|
|
||||||
icon = MB_ICONINFORMATION;
|
|
||||||
break;
|
|
||||||
case Severity::Warning:
|
|
||||||
icon = MB_ICONWARNING;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
icon = MB_ICONERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
MessageBoxA(nullptr, info.c_str(), title, icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasAnsiColorSupport = false;
|
|
||||||
|
|
||||||
void Loader::Impl::logConsoleMessageWithSeverity(std::string const& msg, Severity severity) {
|
|
||||||
if (!m_platformConsoleOpen)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!hasAnsiColorSupport) {
|
|
||||||
std::cout << msg << "\n" << std::flush;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int color = 0;
|
|
||||||
switch (severity) {
|
|
||||||
case Severity::Debug:
|
|
||||||
color = 243;
|
|
||||||
break;
|
|
||||||
case Severity::Info:
|
|
||||||
color = 33;
|
|
||||||
break;
|
|
||||||
case Severity::Warning:
|
|
||||||
color = 229;
|
|
||||||
break;
|
|
||||||
case Severity::Error:
|
|
||||||
color = 9;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
color = 7;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto const colorStr = fmt::format("\x1b[38;5;{}m", color);
|
|
||||||
auto const newMsg = fmt::format("{}{}\x1b[0m{}", colorStr, msg.substr(0, 12),
|
|
||||||
msg.substr(12));
|
|
||||||
|
|
||||||
std::cout << newMsg << "\n" << std::flush;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::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);
|
|
||||||
|
|
||||||
// Set output mode to handle ansi color sequences
|
|
||||||
auto handleStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
|
|
||||||
DWORD consoleMode = 0;
|
|
||||||
if (GetConsoleMode(handleStdout, &consoleMode)) {
|
|
||||||
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
|
||||||
if (SetConsoleMode(handleStdout, consoleMode)) {
|
|
||||||
hasAnsiColorSupport = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_platformConsoleOpen = true;
|
|
||||||
|
|
||||||
for (auto const& log : log::Logger::get()->list()) {
|
|
||||||
this->logConsoleMessageWithSeverity(log.toString(true), log.getSeverity());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::closePlatformConsole() {
|
|
||||||
if (!m_platformConsoleOpen) return;
|
|
||||||
|
|
||||||
fclose(stdin);
|
|
||||||
fclose(stdout);
|
|
||||||
FreeConsole();
|
|
||||||
|
|
||||||
m_platformConsoleOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ipcPipeThread(HANDLE pipe) {
|
|
||||||
char buffer[IPC_BUFFER_SIZE * sizeof(TCHAR)];
|
|
||||||
DWORD read;
|
|
||||||
|
|
||||||
std::optional<std::string> replyID = std::nullopt;
|
|
||||||
|
|
||||||
// log::debug("Waiting for I/O");
|
|
||||||
if (ReadFile(pipe, buffer, sizeof(buffer) - 1, &read, nullptr)) {
|
|
||||||
buffer[read] = '\0';
|
|
||||||
|
|
||||||
std::string reply = LoaderImpl::get()->processRawIPC((void*)pipe, buffer).dump();
|
|
||||||
|
|
||||||
DWORD written;
|
|
||||||
WriteFile(pipe, reply.c_str(), reply.size(), &written, nullptr);
|
|
||||||
}
|
|
||||||
// log::debug("Connection done");
|
|
||||||
|
|
||||||
FlushFileBuffers(pipe);
|
|
||||||
DisconnectNamedPipe(pipe);
|
|
||||||
CloseHandle(pipe);
|
|
||||||
|
|
||||||
// log::debug("Disconnected pipe");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Loader::Impl::setupIPC() {
|
|
||||||
std::thread ipcThread([]() {
|
|
||||||
while (true) {
|
|
||||||
auto pipe = CreateNamedPipeA(
|
|
||||||
IPC_PIPE_NAME,
|
|
||||||
PIPE_ACCESS_DUPLEX,
|
|
||||||
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
|
|
||||||
PIPE_UNLIMITED_INSTANCES,
|
|
||||||
IPC_BUFFER_SIZE,
|
|
||||||
IPC_BUFFER_SIZE,
|
|
||||||
NMPWAIT_USE_DEFAULT_WAIT,
|
|
||||||
nullptr
|
|
||||||
);
|
|
||||||
if (pipe == INVALID_HANDLE_VALUE) {
|
|
||||||
// todo: Rn this quits IPC, but we might wanna change that later
|
|
||||||
// to just continue trying. however, I'm assuming that if
|
|
||||||
// CreateNamedPipeA fails, then it will probably fail again if
|
|
||||||
// you try right after, so changing the break; to continue; might
|
|
||||||
// just result in the console getting filled with error messages
|
|
||||||
log::warn("Unable to create pipe, quitting IPC");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// log::debug("Waiting for pipe connections");
|
|
||||||
if (ConnectNamedPipe(pipe, nullptr)) {
|
|
||||||
// log::debug("Got connection, creating thread");
|
|
||||||
std::thread pipeThread(&ipcPipeThread, pipe);
|
|
||||||
// SetThreadDescription(pipeThread.native_handle(), L"Geode IPC Pipe");
|
|
||||||
pipeThread.detach();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// log::debug("No connection, cleaning pipe");
|
|
||||||
CloseHandle(pipe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// SetThreadDescription(ipcThread.native_handle(), L"Geode Main IPC");
|
|
||||||
ipcThread.detach();
|
|
||||||
|
|
||||||
log::debug("IPC set up");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Loader::Impl::userTriedToLoadDLLs() const {
|
bool Loader::Impl::userTriedToLoadDLLs() const {
|
||||||
static std::unordered_set<std::string> KNOWN_MOD_DLLS {
|
static std::unordered_set<std::string> KNOWN_MOD_DLLS {
|
||||||
"betteredit-v4.0.5.dll",
|
"betteredit-v4.0.5.dll",
|
||||||
|
@ -235,5 +74,3 @@ bool Loader::Impl::userTriedToLoadDLLs() const {
|
||||||
|
|
||||||
return triedToLoadDLLs;
|
return triedToLoadDLLs;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_WINDOWS
|
|
||||||
|
|
||||||
#include <Geode/loader/Mod.hpp>
|
#include <Geode/loader/Mod.hpp>
|
||||||
#include <loader/ModImpl.hpp>
|
#include <loader/ModImpl.hpp>
|
||||||
|
|
||||||
|
@ -81,5 +79,3 @@ Result<> Mod::Impl::unloadPlatformBinary() {
|
||||||
return Err("Unable to free the DLL: " + getLastWinError());
|
return Err("Unable to free the DLL: " + getLastWinError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
96
loader/src/platform/windows/console.cpp
Normal file
96
loader/src/platform/windows/console.cpp
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
#include <loader/console.hpp>
|
||||||
|
#include <loader/LogImpl.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
bool s_isOpen = false;
|
||||||
|
bool s_hasAnsiColorSupport = false;
|
||||||
|
|
||||||
|
void console::open() {
|
||||||
|
if (s_isOpen) 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);
|
||||||
|
|
||||||
|
// Set output mode to handle ansi color sequences
|
||||||
|
auto handleStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
|
||||||
|
DWORD consoleMode = 0;
|
||||||
|
if (GetConsoleMode(handleStdout, &consoleMode)) {
|
||||||
|
consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||||
|
if (SetConsoleMode(handleStdout, consoleMode)) {
|
||||||
|
s_hasAnsiColorSupport = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s_isOpen = true;
|
||||||
|
|
||||||
|
for (auto const& log : log::Logger::get()->list()) {
|
||||||
|
console::log(log.toString(true), log.getSeverity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::close() {
|
||||||
|
if (!s_isOpen) return;
|
||||||
|
|
||||||
|
fclose(stdin);
|
||||||
|
fclose(stdout);
|
||||||
|
FreeConsole();
|
||||||
|
|
||||||
|
s_isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::log(std::string const& msg, Severity severity) {
|
||||||
|
if (!s_isOpen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!s_hasAnsiColorSupport) {
|
||||||
|
std::cout << msg << "\n" << std::flush;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int color = 0;
|
||||||
|
switch (severity) {
|
||||||
|
case Severity::Debug:
|
||||||
|
color = 243;
|
||||||
|
break;
|
||||||
|
case Severity::Info:
|
||||||
|
color = 33;
|
||||||
|
break;
|
||||||
|
case Severity::Warning:
|
||||||
|
color = 229;
|
||||||
|
break;
|
||||||
|
case Severity::Error:
|
||||||
|
color = 9;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
color = 7;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto const colorStr = fmt::format("\x1b[38;5;{}m", color);
|
||||||
|
auto const newMsg = fmt::format("{}{}\x1b[0m{}", colorStr, msg.substr(0, 12),
|
||||||
|
msg.substr(12));
|
||||||
|
|
||||||
|
std::cout << newMsg << "\n" << std::flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
void console::messageBox(char const* title, std::string const& info, Severity severity) {
|
||||||
|
unsigned int icon;
|
||||||
|
switch (severity) {
|
||||||
|
case Severity::Debug:
|
||||||
|
case Severity::Info:
|
||||||
|
case Severity::Notice:
|
||||||
|
icon = MB_ICONINFORMATION;
|
||||||
|
break;
|
||||||
|
case Severity::Warning:
|
||||||
|
icon = MB_ICONWARNING;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
icon = MB_ICONERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
MessageBoxA(nullptr, info.c_str(), title, icon);
|
||||||
|
}
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_WINDOWS
|
|
||||||
|
|
||||||
#include <crashlog.hpp>
|
#include <crashlog.hpp>
|
||||||
#include <Geode/loader/Dirs.hpp>
|
#include <Geode/loader/Dirs.hpp>
|
||||||
#include <Geode/loader/Loader.hpp>
|
#include <Geode/loader/Loader.hpp>
|
||||||
|
@ -301,5 +299,3 @@ void crashlog::setupPlatformHandlerPost() {}
|
||||||
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||||
return dirs::getGeodeDir() / "crashlogs";
|
return dirs::getGeodeDir() / "crashlogs";
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "../../c++stl/string-impl.hpp"
|
#include "../../c++stl/string-impl.hpp"
|
||||||
|
|
||||||
#ifdef GEODE_IS_WINDOWS
|
|
||||||
|
|
||||||
namespace geode::stl {
|
namespace geode::stl {
|
||||||
void StringImpl::setEmpty() {
|
void StringImpl::setEmpty() {
|
||||||
data.m_size = 0;
|
data.m_size = 0;
|
||||||
|
@ -43,5 +41,3 @@ namespace geode::stl {
|
||||||
data.m_capacity = cap;
|
data.m_capacity = cap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,11 +1,11 @@
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#if defined(GEODE_IS_WINDOWS)
|
|
||||||
|
|
||||||
#include "../load.hpp"
|
#include "../load.hpp"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
#include "loader/LoaderImpl.hpp"
|
#include "loader/LoaderImpl.hpp"
|
||||||
|
#include "loader/console.hpp"
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
void updateGeode() {
|
void updateGeode() {
|
||||||
|
@ -38,7 +38,7 @@ int WINAPI gdMainHook(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmd
|
||||||
updateGeode();
|
updateGeode();
|
||||||
|
|
||||||
if (versionToTimestamp(GEODE_STR(GEODE_GD_VERSION)) > gdTimestamp) {
|
if (versionToTimestamp(GEODE_STR(GEODE_GD_VERSION)) > gdTimestamp) {
|
||||||
LoaderImpl::get()->platformMessageBox(
|
console::messageBox(
|
||||||
"Unable to Load Geode!",
|
"Unable to Load Geode!",
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"This version of Geode is made for Geometry Dash {} "
|
"This version of Geode is made for Geometry Dash {} "
|
||||||
|
@ -142,7 +142,7 @@ void earlyError(std::string message) {
|
||||||
std::ofstream fout("_geode_early_error.txt");
|
std::ofstream fout("_geode_early_error.txt");
|
||||||
fout << message;
|
fout << message;
|
||||||
fout.close();
|
fout.close();
|
||||||
LoaderImpl::get()->platformMessageBox("Unable to Load Geode!", message);
|
console::messageBox("Unable to Load Geode!", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID) {
|
BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID) {
|
||||||
|
@ -176,5 +176,3 @@ BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID) {
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
#include "nfdwin.hpp"
|
#include "nfdwin.hpp"
|
||||||
#include <Geode/utils/string.hpp>
|
#include <Geode/utils/string.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_WINDOWS
|
|
||||||
|
|
||||||
using Path = ghc::filesystem::path;
|
using Path = ghc::filesystem::path;
|
||||||
using Paths = std::vector<ghc::filesystem::path>;
|
using Paths = std::vector<ghc::filesystem::path>;
|
||||||
|
|
||||||
|
@ -296,5 +294,3 @@ Result<> nfdPick(
|
||||||
|
|
||||||
return Err("Unknown error");
|
return Err("Unknown error");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_WINDOWS
|
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
#ifdef __MINGW32__
|
||||||
// Explicitly setting NTDDI version, this is necessary for the MinGW compiler
|
// Explicitly setting NTDDI version, this is necessary for the MinGW compiler
|
||||||
#define NTDDI_VERSION NTDDI_VISTA
|
#define NTDDI_VERSION NTDDI_VISTA
|
||||||
|
@ -58,5 +56,3 @@ Result<> nfdPick(
|
||||||
file::FilePickOptions const& options,
|
file::FilePickOptions const& options,
|
||||||
void* result
|
void* result
|
||||||
);
|
);
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
|
|
||||||
#ifdef GEODE_IS_WINDOWS
|
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
#include <Geode/loader/Dirs.hpp>
|
#include <Geode/loader/Dirs.hpp>
|
||||||
#include "nfdwin.hpp"
|
#include "nfdwin.hpp"
|
||||||
#include <ghc/fs_fwd.hpp>
|
#include <ghc/fs_fwd.hpp>
|
||||||
|
@ -265,5 +263,3 @@ Result<> geode::hook::addObjcMethod(std::string const& className, std::string co
|
||||||
Result<void*> geode::hook::getObjcMethodImp(std::string const& className, std::string const& selectorName) {
|
Result<void*> geode::hook::getObjcMethodImp(std::string const& className, std::string const& selectorName) {
|
||||||
return Err("Wrong platform");
|
return Err("Wrong platform");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
Loading…
Reference in a new issue