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/ids/*.cpp
|
||||
src/internal/*.cpp
|
||||
src/platform/mac/*.cpp
|
||||
src/platform/ios/*.cpp
|
||||
src/platform/android/*.cpp
|
||||
src/loader/*.cpp
|
||||
src/load.cpp
|
||||
src/utils/*.cpp
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
#include "Loader.hpp"
|
||||
#include <matjson.hpp>
|
||||
|
||||
namespace geode {
|
||||
namespace geode::ipc {
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
constexpr char const* IPC_PIPE_NAME = "\\\\.\\pipe\\GeodeIPCPipe";
|
||||
constexpr char const* IPC_PIPE_NAME = R"(\\.\pipe\GeodeIPCPipe)";
|
||||
#endif
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
|
@ -15,20 +15,20 @@ namespace geode {
|
|||
|
||||
class IPCFilter;
|
||||
|
||||
// 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
|
||||
// debugger, or an external modding UI, that application can open up the
|
||||
// platform-specific pipe and start sending messages to mods. Mods can
|
||||
// listen for messages using the listenForIPC function, and reply to
|
||||
// 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
|
||||
// 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
|
||||
// debugger, or an external modding UI, that application can open up the
|
||||
// platform-specific pipe and start sending messages to mods. Mods can
|
||||
// listen for messages using the listenForIPC function, and reply to
|
||||
// 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
|
||||
// by sending the `list-mods` message to `geode.loader`.
|
||||
|
||||
class GEODE_DLL IPCEvent : public Event {
|
||||
protected:
|
||||
void* m_rawPipeHandle;
|
||||
bool m_replied = false;
|
||||
|
||||
|
||||
public:
|
||||
std::string targetModID;
|
||||
std::string messageID;
|
||||
|
@ -57,12 +57,12 @@ namespace geode {
|
|||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, IPCEvent* event);
|
||||
IPCFilter(
|
||||
IPCFilter(
|
||||
std::string const& modID,
|
||||
std::string const& messageID
|
||||
);
|
||||
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);
|
||||
|
||||
bool didLastLaunchCrash() const;
|
||||
|
||||
friend class LoaderImpl;
|
||||
|
||||
friend Mod* takeNextLoaderMod();
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
|
||||
#include <Geode/modify/LoadingLayer.hpp>
|
||||
#include <Geode/modify/CCLayer.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <array>
|
||||
#include <fmt/format.h>
|
||||
#include <loader/LoaderImpl.hpp>
|
||||
#include <loader/console.hpp>
|
||||
#include <loader/updater.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
@ -77,23 +78,23 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
|||
this->setSmallText("Verifying Loader Resources");
|
||||
// verify loader resources
|
||||
Loader::get()->queueInMainThread([&]() {
|
||||
if (!LoaderImpl::get()->verifyLoaderResources()) {
|
||||
if (!updater::verifyLoaderResources()) {
|
||||
log::debug("Downloading Loader Resources");
|
||||
this->setSmallText("Downloading Loader Resources");
|
||||
this->addChild(EventListenerNode<ResourceDownloadFilter>::create(
|
||||
this->addChild(EventListenerNode<updater::ResourceDownloadFilter>::create(
|
||||
this, &CustomLoadingLayer::updateResourcesProgress
|
||||
));
|
||||
}
|
||||
else {
|
||||
log::debug("Loading Loader Resources");
|
||||
this->setSmallText("Loading Loader Resources");
|
||||
LoaderImpl::get()->updateSpecialFiles();
|
||||
updater::updateSpecialFiles();
|
||||
this->continueLoadAssets();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void updateResourcesProgress(ResourceDownloadEvent* event) {
|
||||
void updateResourcesProgress(updater::ResourceDownloadEvent* event) {
|
||||
std::visit(makeVisitor {
|
||||
[&](UpdateProgress const& progress) {
|
||||
this->setSmallText(fmt::format(
|
||||
|
@ -107,7 +108,7 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
|
|||
},
|
||||
[&](UpdateFailed const& error) {
|
||||
log::debug("Failed Loader Resources");
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
console::messageBox(
|
||||
"Error updating resources",
|
||||
error + ".\n"
|
||||
"You will have to install resources manually by downloading resources.zip "
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
#include "../ui/internal/list/ModListLayer.hpp"
|
||||
|
||||
#include <Geode/loader/Index.hpp>
|
||||
|
@ -7,13 +6,13 @@
|
|||
#include <Geode/modify/IDManager.hpp>
|
||||
#include <Geode/utils/NodeIDs.hpp>
|
||||
#include <Geode/ui/BasedButtonSprite.hpp>
|
||||
#include <Geode/ui/GeodeUI.hpp>
|
||||
#include <Geode/ui/Notification.hpp>
|
||||
#include <Geode/ui/Popup.hpp>
|
||||
#include <Geode/ui/MDPopup.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
#include <loader/LoaderImpl.hpp>
|
||||
#include <loader/updater.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
@ -129,7 +128,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
|||
|
||||
// show auto update message
|
||||
static bool shownUpdateInfo = false;
|
||||
if (LoaderImpl::get()->isNewUpdateDownloaded() && !shownUpdateInfo) {
|
||||
if (updater::isNewUpdateDownloaded() && !shownUpdateInfo) {
|
||||
shownUpdateInfo = true;
|
||||
auto popup = FLAlertLayer::create(
|
||||
"Update downloaded",
|
||||
|
@ -147,7 +146,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
|||
// show crash info
|
||||
static bool shownLastCrash = false;
|
||||
if (
|
||||
Loader::get()->didLastLaunchCrash() &&
|
||||
crashlog::didLastLaunchCrash() &&
|
||||
!shownLastCrash &&
|
||||
!Mod::get()->template getSettingValue<bool>("disable-last-crashed-popup")
|
||||
) {
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
#include <loader/LoaderImpl.hpp>
|
||||
#include <loader/console.hpp>
|
||||
#include <loader/IPC.hpp>
|
||||
#include <loader/updater.hpp>
|
||||
|
||||
#include <Geode/loader/IPC.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
@ -10,7 +13,7 @@
|
|||
#include <loader/LogImpl.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
#include "load.hpp"
|
||||
|
@ -18,22 +21,22 @@ using namespace geode::prelude;
|
|||
$execute {
|
||||
listenForSettingChanges("show-platform-console", +[](bool value) {
|
||||
if (value) {
|
||||
LoaderImpl::get()->openPlatformConsole();
|
||||
console::open();
|
||||
}
|
||||
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!";
|
||||
});
|
||||
|
||||
listenForIPC("loader-info", [](IPCEvent* event) -> matjson::Value {
|
||||
ipc::listen("loader-info", [](ipc::IPCEvent* event) -> matjson::Value {
|
||||
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;
|
||||
|
||||
auto args = *event->messageData;
|
||||
|
@ -76,7 +79,7 @@ void tryShowForwardCompat() {
|
|||
return;
|
||||
|
||||
// TODO: change text later
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
console::messageBox(
|
||||
"Forward Compatibility Warning",
|
||||
"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"
|
||||
|
@ -116,7 +119,7 @@ int geodeEntry(void* platformData) {
|
|||
auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
|
||||
log::popNest();
|
||||
if (!internalSetupRes) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
console::messageBox(
|
||||
"Unable to Load Geode!",
|
||||
"There was a fatal error setting up "
|
||||
"the internal mod and Geode can not be loaded: " + internalSetupRes.unwrapErr()
|
||||
|
@ -131,7 +134,7 @@ int geodeEntry(void* platformData) {
|
|||
if (LoaderImpl::get()->isForwardCompatMode() ||
|
||||
Mod::get()->getSettingValue<bool>("show-platform-console")) {
|
||||
log::debug("Opening console");
|
||||
LoaderImpl::get()->openPlatformConsole();
|
||||
console::open();
|
||||
}
|
||||
|
||||
// set up loader, load mods, etc.
|
||||
|
@ -140,7 +143,7 @@ int geodeEntry(void* platformData) {
|
|||
auto setupRes = LoaderImpl::get()->setup();
|
||||
log::popNest();
|
||||
if (!setupRes) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
console::messageBox(
|
||||
"Unable to Load Geode!",
|
||||
"There was an unknown fatal error setting up "
|
||||
"the loader and Geode can not be loaded. "
|
||||
|
@ -152,12 +155,15 @@ int geodeEntry(void* platformData) {
|
|||
|
||||
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
|
||||
if (Mod::get()->getSettingValue<bool>("auto-check-updates")) {
|
||||
log::info("Starting loader update check");
|
||||
LoaderImpl::get()->checkForLoaderUpdates();
|
||||
updater::checkForLoaderUpdates();
|
||||
}
|
||||
else {
|
||||
log::info("Skipped loader update check");
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
#include <Geode/loader/IPC.hpp>
|
||||
#include "IPC.hpp"
|
||||
#include <matjson.hpp>
|
||||
|
||||
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(
|
||||
callback, IPCFilter(getMod()->getID(), messageID)
|
||||
);
|
||||
return std::monostate();
|
||||
}
|
||||
|
||||
IPCEvent::IPCEvent(
|
||||
ipc::IPCEvent::IPCEvent(
|
||||
void* rawPipeHandle,
|
||||
std::string const& targetModID,
|
||||
std::string const& messageID,
|
||||
|
@ -22,9 +23,9 @@ IPCEvent::IPCEvent(
|
|||
replyData(replyData),
|
||||
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) {
|
||||
event->replyData = fn(event);
|
||||
return ListenerResult::Stop;
|
||||
|
@ -32,5 +33,34 @@ ListenerResult IPCFilter::handle(utils::MiniFunction<Callback> fn, IPCEvent* eve
|
|||
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) {}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
bool Loader::didLastLaunchCrash() const {
|
||||
return m_impl->didLastLaunchCrash();
|
||||
}
|
||||
|
||||
Mod* Loader::takeNextMod() {
|
||||
return m_impl->takeNextMod();
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "ModImpl.hpp"
|
||||
#include "ModMetadataImpl.hpp"
|
||||
#include "LogImpl.hpp"
|
||||
#include "console.hpp"
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/IPC.hpp>
|
||||
|
@ -82,11 +83,6 @@ Result<> Loader::Impl::setup() {
|
|||
}
|
||||
log::popNest();
|
||||
|
||||
log::debug("Setting up IPC");
|
||||
log::pushNest();
|
||||
this->setupIPC();
|
||||
log::popNest();
|
||||
|
||||
log::debug("Setting up directories");
|
||||
log::pushNest();
|
||||
this->createDirectories();
|
||||
|
@ -663,12 +659,8 @@ std::vector<LoadProblem> Loader::Impl::getProblems() const {
|
|||
return m_problems;
|
||||
}
|
||||
|
||||
bool Loader::Impl::didLastLaunchCrash() const {
|
||||
return crashlog::didLastLaunchCrash();
|
||||
}
|
||||
|
||||
void Loader::Impl::forceReset() {
|
||||
this->closePlatformConsole();
|
||||
console::close();
|
||||
for (auto& [_, mod] : m_mods) {
|
||||
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) {
|
||||
m_nextModLock.lock();
|
||||
if (mod) {
|
||||
|
|
|
@ -25,32 +25,6 @@
|
|||
|
||||
// TODO: Find a file convention for impl headers
|
||||
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 {
|
||||
public:
|
||||
mutable std::mutex m_mutex;
|
||||
|
@ -66,11 +40,6 @@ namespace geode {
|
|||
std::vector<ghc::filesystem::path> m_texturePaths;
|
||||
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;
|
||||
|
||||
std::vector<utils::MiniFunction<void(void)>> m_mainThreadQueue;
|
||||
|
@ -78,9 +47,6 @@ namespace geode {
|
|||
std::vector<std::pair<Hook*, Mod*>> m_uninitializedHooks;
|
||||
bool m_readyToHook = false;
|
||||
|
||||
bool m_platformConsoleOpen = false;
|
||||
void* m_platformData = nullptr;
|
||||
|
||||
std::mutex m_nextModMutex;
|
||||
std::unique_lock<std::mutex> m_nextModLock = std::unique_lock<std::mutex>(m_nextModMutex, std::defer_lock);
|
||||
std::condition_variable m_nextModCV;
|
||||
|
@ -109,17 +75,7 @@ namespace geode {
|
|||
Result<tulip::hook::HandlerHandle> getHandler(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();
|
||||
void setupIPC();
|
||||
|
||||
Impl();
|
||||
~Impl();
|
||||
|
@ -157,25 +113,9 @@ namespace geode {
|
|||
|
||||
void updateResources(bool forceReload);
|
||||
|
||||
bool didLastLaunchCrash() const;
|
||||
|
||||
matjson::Value processRawIPC(void* rawHandle, std::string const& buffer);
|
||||
|
||||
void queueInMainThread(ScheduledFunction func);
|
||||
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;
|
||||
void addUninitializedHook(Hook* hook, Mod* mod);
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "LoaderImpl.hpp"
|
||||
#include "console.hpp"
|
||||
#include "LogImpl.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 const logStr = log.toString(true, m_nestLevel);
|
||||
|
||||
LoaderImpl::get()->logConsoleMessageWithSeverity(logStr, log.getSeverity());
|
||||
console::log(logStr, log.getSeverity());
|
||||
m_logStream << logStr << std::endl;
|
||||
}
|
||||
}
|
||||
|
@ -233,4 +233,4 @@ void log::pushNest() {
|
|||
|
||||
void log::popNest() {
|
||||
Logger::get()->popNest();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "LoaderImpl.hpp"
|
||||
#include "ModMetadataImpl.hpp"
|
||||
#include "about.hpp"
|
||||
#include "console.hpp"
|
||||
|
||||
#include <hash/hash.hpp>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
|
@ -686,7 +687,7 @@ Mod* Loader::Impl::getInternalMod() {
|
|||
}
|
||||
auto infoRes = getModImplInfo();
|
||||
if (!infoRes) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
console::messageBox(
|
||||
"Fatal Internal Error",
|
||||
"Unable to create internal mod info: \"" + infoRes.unwrapErr() +
|
||||
"\"\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>
|
||||
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
|
||||
FileWatcher::FileWatcher(
|
||||
ghc::filesystem::path const& file, FileWatchCallback callback, ErrorCallback error
|
||||
) {
|
||||
|
@ -23,5 +21,3 @@ void FileWatcher::watch() {
|
|||
bool FileWatcher::watching() const {
|
||||
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/Log.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
#include <iostream>
|
||||
#include <loader/LoaderImpl.hpp>
|
||||
#include <Geode/utils/string.hpp>
|
||||
#include <Geode/cocos/platform/android/jni/JniHelper.h>
|
||||
|
||||
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() {
|
||||
if (m_gdVersion.empty()) {
|
||||
/*
|
||||
|
@ -49,33 +29,6 @@ std::string Loader::Impl::getGameVersion() {
|
|||
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 {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
|
||||
|
@ -34,5 +32,3 @@ Result<> Mod::Impl::unloadPlatformBinary() {
|
|||
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>
|
||||
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
#include <Geode/utils/string.hpp>
|
||||
|
@ -302,5 +300,3 @@ void crashlog::setupPlatformHandlerPost() {
|
|||
bool crashlog::didLastLaunchCrash() {
|
||||
return s_lastLaunchCrashed;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include <Geode/c++stl/gdstdlib.hpp>
|
||||
#include "../../c++stl/string-impl.hpp"
|
||||
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
|
||||
#if defined(GEODE_IS_ANDROID32)
|
||||
|
||||
static auto constexpr NEW_SYM = "_Znwj";
|
||||
|
@ -119,5 +117,3 @@ namespace geode::stl {
|
|||
// TODO: implement this, remember its copy-on-write...
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,7 +1,5 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#if defined(GEODE_IS_ANDROID)
|
||||
|
||||
#include "../load.hpp"
|
||||
#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*) {
|
||||
// empty
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,7 +1,5 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
|
@ -279,5 +277,3 @@ void geode::utils::game::restart() {
|
|||
nullptr
|
||||
), CCDirector::get()->getRunningScene(), false);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <FileWatcher.hpp>
|
||||
|
||||
#ifdef GEODE_IS_IOS
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
|
@ -52,5 +50,3 @@ void FileWatcher::watch() {
|
|||
bool FileWatcher::watching() const {
|
||||
return m_platformHandle != NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
#include <loader/LoaderImpl.hpp>
|
||||
|
||||
#ifdef GEODE_IS_IOS
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
#include <iostream>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
#include <iostream>
|
||||
#include <pwd.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void Loader::Impl::platformMessageBox(char const* title, std::string const& info) {
|
||||
std::cout << title << ": " << info << std::endl;
|
||||
|
@ -41,5 +39,3 @@ void Loader::Impl::setupIPC() {
|
|||
bool Loader::Impl::userTriedToLoadDLLs() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_IOS
|
||||
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
#include <dlfcn.h>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
#include <dlfcn.h>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
@ -43,5 +41,3 @@ Result<> Mod::Impl::unloadPlatformBinary() {
|
|||
return Err("Unable to free library");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <crashlog.hpp>
|
||||
|
||||
#ifdef GEODE_IS_IOS
|
||||
|
||||
bool crashlog::setupPlatformHandler() {
|
||||
return false;
|
||||
}
|
||||
|
@ -13,5 +11,3 @@ bool crashlog::didLastLaunchCrash() {
|
|||
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||
return "";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#if defined(GEODE_IS_IOS)
|
||||
|
||||
#include "../load.hpp"
|
||||
#include <dlfcn.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
|
||||
auto dynamicTriggerRef = &dynamicTrigger;
|
||||
|
||||
#endif
|
|
@ -1,9 +1,7 @@
|
|||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_IOS
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <UIKit/UIKit.h>
|
||||
#include <iostream>
|
||||
|
@ -36,5 +34,3 @@ ghc::filesystem::path dirs::getGameDir() {
|
|||
ghc::filesystem::path dirs::getSaveDir() {
|
||||
return weaklyCanonical(CCFileUtils::sharedFileUtils()->getWritablePath().c_str());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
|
||||
#include <cocos2d.h>
|
||||
using namespace cocos2d;
|
||||
|
||||
|
@ -815,5 +813,3 @@ void CCArray::acceptVisitor(CCDataVisitor &visitor)
|
|||
{
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
#include <FileWatcher.hpp>
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
|
||||
// static constexpr const auto notifyAttributes = FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||
// FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE;
|
||||
|
@ -52,5 +50,3 @@ void FileWatcher::watch() {
|
|||
bool FileWatcher::watching() const {
|
||||
return m_platformHandle != NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
#include <dlfcn.h>
|
||||
|
@ -43,5 +41,3 @@ Result<> Mod::Impl::unloadPlatformBinary() {
|
|||
return Err("Unable to free library");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <crashlog.hpp>
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
|
||||
#include <Geode/utils/string.hpp>
|
||||
#include <array>
|
||||
#include <thread>
|
||||
|
@ -391,6 +389,3 @@ bool crashlog::didLastLaunchCrash() {
|
|||
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||
return dirs::getGeodeDir() / "crashlogs";
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <Geode/c++stl/gdstdlib.hpp>
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
|
||||
namespace gd {
|
||||
namespace {
|
||||
static inline auto emptyInternalString() {
|
||||
|
@ -57,5 +55,3 @@ namespace gd {
|
|||
return std::string(*this) == std::string(other);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,7 +1,5 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#if defined(GEODE_IS_MACOS)
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#include "../load.hpp"
|
||||
#include <dlfcn.h>
|
||||
|
@ -151,5 +149,3 @@ __attribute__((constructor)) void _entry() {
|
|||
if (!loadGeode())
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_MACOS
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
#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));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
static constexpr auto const notifyAttributes =
|
||||
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;
|
||||
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/Log.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
#include <iostream>
|
||||
#include <loader/LoaderImpl.hpp>
|
||||
#include <Geode/utils/string.hpp>
|
||||
#include <loader/LogImpl.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
#include <Psapi.h>
|
||||
|
||||
static constexpr auto IPC_BUFFER_SIZE = 512;
|
||||
|
||||
#include "gdTimestampMap.hpp"
|
||||
std::string Loader::Impl::getGameVersion() {
|
||||
if (m_gdVersion.empty()) {
|
||||
|
@ -25,161 +19,6 @@ std::string Loader::Impl::getGameVersion() {
|
|||
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 {
|
||||
static std::unordered_set<std::string> KNOWN_MOD_DLLS {
|
||||
"betteredit-v4.0.5.dll",
|
||||
|
@ -235,5 +74,3 @@ bool Loader::Impl::userTriedToLoadDLLs() const {
|
|||
|
||||
return triedToLoadDLLs;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <loader/ModImpl.hpp>
|
||||
|
||||
|
@ -81,5 +79,3 @@ Result<> Mod::Impl::unloadPlatformBinary() {
|
|||
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>
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
#include <crashlog.hpp>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
@ -301,5 +299,3 @@ void crashlog::setupPlatformHandlerPost() {}
|
|||
ghc::filesystem::path crashlog::getCrashLogDirectory() {
|
||||
return dirs::getGeodeDir() / "crashlogs";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#include "../../c++stl/string-impl.hpp"
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
namespace geode::stl {
|
||||
void StringImpl::setEmpty() {
|
||||
data.m_size = 0;
|
||||
|
@ -43,5 +41,3 @@ namespace geode::stl {
|
|||
data.m_capacity = cap;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,11 +1,11 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#if defined(GEODE_IS_WINDOWS)
|
||||
|
||||
#include "../load.hpp"
|
||||
#include <Windows.h>
|
||||
|
||||
#include "loader/LoaderImpl.hpp"
|
||||
#include "loader/console.hpp"
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
void updateGeode() {
|
||||
|
@ -38,7 +38,7 @@ int WINAPI gdMainHook(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmd
|
|||
updateGeode();
|
||||
|
||||
if (versionToTimestamp(GEODE_STR(GEODE_GD_VERSION)) > gdTimestamp) {
|
||||
LoaderImpl::get()->platformMessageBox(
|
||||
console::messageBox(
|
||||
"Unable to Load Geode!",
|
||||
fmt::format(
|
||||
"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");
|
||||
fout << message;
|
||||
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) {
|
||||
|
@ -176,5 +176,3 @@ BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID) {
|
|||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
#include "nfdwin.hpp"
|
||||
#include <Geode/utils/string.hpp>
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
using Path = ghc::filesystem::path;
|
||||
using Paths = std::vector<ghc::filesystem::path>;
|
||||
|
||||
|
@ -296,5 +294,3 @@ Result<> nfdPick(
|
|||
|
||||
return Err("Unknown error");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
#ifdef __MINGW32__
|
||||
// Explicitly setting NTDDI version, this is necessary for the MinGW compiler
|
||||
#define NTDDI_VERSION NTDDI_VISTA
|
||||
|
@ -58,5 +56,3 @@ Result<> nfdPick(
|
|||
file::FilePickOptions const& options,
|
||||
void* result
|
||||
);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include "nfdwin.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) {
|
||||
return Err("Wrong platform");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue