mirror of
https://github.com/geode-sdk/geode.git
synced 2025-04-16 06:55:04 -04:00
Launch Arguments (#444)
* Loader launch args * Implement launch args on loader/mod * Add to test mod * Documentation * Rename methods and better docs * Expand API * Fix loader impls * Expand tests * Add an extra hyphen to the launch arg prefix * Update comments with extra hyphen
This commit is contained in:
parent
79d9184344
commit
a2b164af29
13 changed files with 245 additions and 2 deletions
loader
include/Geode/loader
src
loader
platform
test/main
|
@ -83,6 +83,29 @@ namespace geode {
|
|||
std::vector<Mod*> getAllMods();
|
||||
std::vector<LoadProblem> getProblems() const;
|
||||
|
||||
/**
|
||||
* Returns the available launch argument names.
|
||||
*/
|
||||
std::vector<std::string> getLaunchArgumentNames() const;
|
||||
/**
|
||||
* Returns whether the specified launch argument was passed in via the command line.
|
||||
* @param name The argument name
|
||||
*/
|
||||
bool hasLaunchArgument(std::string_view const name) const;
|
||||
/**
|
||||
* Get a launch argument. These are passed into the game as command-line arguments
|
||||
* with the format `--geode:argName=value`.
|
||||
* @param name The argument name
|
||||
* @return The value, if present
|
||||
*/
|
||||
std::optional<std::string> getLaunchArgument(std::string_view const name) const;
|
||||
/**
|
||||
* Get a boolean launch argument. Returns whether the argument is present and its
|
||||
* value is exactly `true`.
|
||||
* @param name The argument name
|
||||
*/
|
||||
bool getLaunchBool(std::string_view const name) const;
|
||||
|
||||
void queueInMainThread(ScheduledFunction func);
|
||||
|
||||
friend class LoaderImpl;
|
||||
|
|
|
@ -150,6 +150,31 @@ namespace geode {
|
|||
this->registerCustomSetting(key, std::make_unique<T>(std::string(key), this->getID(), value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the available mod-specific launch arguments.
|
||||
*/
|
||||
std::vector<std::string> getLaunchArgumentNames() const;
|
||||
/**
|
||||
* Equivalent to a prefixed `Loader::hasLaunchArgument` call. See `Mod::getLaunchArgument`
|
||||
* for details about mod-specific launch arguments.
|
||||
* @param name The argument name
|
||||
*/
|
||||
bool hasLaunchArgument(std::string_view const name) const;
|
||||
/**
|
||||
* Get a mod-specific launch argument. This is equivalent to `Loader::getLaunchArgument`
|
||||
* with the argument name prefixed by the mod ID. For example, a launch argument named
|
||||
* `modArg` for the mod `author.test` would be specified with `--geode:author.test.modArg=value`.
|
||||
* @param name The argument name
|
||||
* @return The value, if present
|
||||
*/
|
||||
std::optional<std::string> getLaunchArgument(std::string_view const name) const;
|
||||
/**
|
||||
* Equivalent to a prefixed `Loader::getLaunchBool` call. See `Mod::getLaunchArgument`
|
||||
* for details about mod-specific launch arguments.
|
||||
* @param name The argument name
|
||||
*/
|
||||
bool getLaunchBool(std::string_view const name) const;
|
||||
|
||||
matjson::Value& getSaveContainer();
|
||||
|
||||
template <class T>
|
||||
|
|
|
@ -76,3 +76,19 @@ void Loader::queueInMainThread(ScheduledFunction func) {
|
|||
Mod* Loader::takeNextMod() {
|
||||
return m_impl->takeNextMod();
|
||||
}
|
||||
|
||||
std::vector<std::string> Loader::getLaunchArgumentNames() const {
|
||||
return m_impl->getLaunchArgumentNames();
|
||||
}
|
||||
|
||||
bool Loader::hasLaunchArgument(std::string_view const name) const {
|
||||
return m_impl->hasLaunchArgument(name);
|
||||
}
|
||||
|
||||
std::optional<std::string> Loader::getLaunchArgument(std::string_view const name) const {
|
||||
return m_impl->getLaunchArgument(name);
|
||||
}
|
||||
|
||||
bool Loader::getLaunchBool(std::string_view const name) const {
|
||||
return m_impl->getLaunchBool(name);
|
||||
}
|
||||
|
|
|
@ -22,8 +22,11 @@
|
|||
#include <fmt/format.h>
|
||||
#include <hash.hpp>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <resources.hpp>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
@ -68,6 +71,13 @@ Result<> Loader::Impl::setup() {
|
|||
return Ok();
|
||||
}
|
||||
|
||||
if (this->supportsLaunchArguments()) {
|
||||
log::debug("Loading launch arguments");
|
||||
log::pushNest();
|
||||
this->initLaunchArguments();
|
||||
log::popNest();
|
||||
}
|
||||
|
||||
log::debug("Setting up crash handler");
|
||||
log::pushNest();
|
||||
if (!crashlog::setupPlatformHandler()) {
|
||||
|
@ -726,6 +736,52 @@ void Loader::Impl::releaseNextMod() {
|
|||
m_nextModLock.unlock();
|
||||
}
|
||||
|
||||
// TODO: Support for quoted launch args w/ spaces (this will be backwards compatible)
|
||||
// e.g. "--geode:arg=My spaced value"
|
||||
void Loader::Impl::initLaunchArguments() {
|
||||
auto launchStr = this->getLaunchCommand();
|
||||
log::debug("Found launch string: {}", launchStr);
|
||||
auto args = string::split(launchStr, " ");
|
||||
for (const auto& arg : args) {
|
||||
if (!arg.starts_with(LAUNCH_ARG_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
auto pair = arg.substr(LAUNCH_ARG_PREFIX.size());
|
||||
auto sep = pair.find('=');
|
||||
if (sep == std::string::npos) {
|
||||
m_launchArgs.insert({ pair, "true" });
|
||||
continue;
|
||||
}
|
||||
auto key = pair.substr(0, sep);
|
||||
auto value = pair.substr(sep + 1);
|
||||
m_launchArgs.insert({ key, value });
|
||||
}
|
||||
for (const auto& pair : m_launchArgs) {
|
||||
log::debug("Loaded '{}' as '{}'", pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> Loader::Impl::getLaunchArgumentNames() const {
|
||||
return map::keys(m_launchArgs);
|
||||
}
|
||||
|
||||
bool Loader::Impl::hasLaunchArgument(std::string_view const name) const {
|
||||
return m_launchArgs.find(std::string(name)) != m_launchArgs.end();
|
||||
}
|
||||
|
||||
std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view const name) const {
|
||||
auto value = m_launchArgs.find(std::string(name));
|
||||
if (value == m_launchArgs.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::optional(value->second);
|
||||
}
|
||||
|
||||
bool Loader::Impl::getLaunchBool(std::string_view const name) const {
|
||||
auto arg = this->getLaunchArgument(name);
|
||||
return arg.has_value() && arg.value() == "true";
|
||||
}
|
||||
|
||||
Result<tulip::hook::HandlerHandle> Loader::Impl::getHandler(void* address) {
|
||||
if (!m_handlerHandles.count(address)) {
|
||||
return Err("Handler does not exist at address");
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
// TODO: Find a file convention for impl headers
|
||||
namespace geode {
|
||||
constexpr std::string LAUNCH_ARG_PREFIX = "--geode:";
|
||||
|
||||
class Loader::Impl {
|
||||
public:
|
||||
mutable std::mutex m_mutex;
|
||||
|
@ -58,6 +60,8 @@ namespace geode {
|
|||
int m_refreshedModCount = 0;
|
||||
int m_lateRefreshedModCount = 0;
|
||||
|
||||
std::unordered_map<std::string, std::string> m_launchArgs;
|
||||
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_timerBegin;
|
||||
|
||||
std::string getGameVersion();
|
||||
|
@ -109,6 +113,14 @@ namespace geode {
|
|||
std::vector<Mod*> getAllMods();
|
||||
std::vector<LoadProblem> getProblems() const;
|
||||
|
||||
bool supportsLaunchArguments() const;
|
||||
std::string getLaunchCommand() const;
|
||||
void initLaunchArguments();
|
||||
std::vector<std::string> getLaunchArgumentNames() const;
|
||||
bool hasLaunchArgument(std::string_view const name) const;
|
||||
std::optional<std::string> getLaunchArgument(std::string_view const name) const;
|
||||
bool getLaunchBool(std::string_view const name) const;
|
||||
|
||||
void updateResources(bool forceReload);
|
||||
|
||||
void queueInMainThread(const ScheduledFunction& func);
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include "ModImpl.hpp"
|
||||
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
Mod::Mod(ModMetadata const& metadata) : m_impl(std::make_unique<Impl>(this, metadata)) {}
|
||||
|
@ -72,6 +75,7 @@ ghc::filesystem::path Mod::getResourcesDir() const {
|
|||
void Mod::setMetadata(ModMetadata const& metadata) {
|
||||
m_impl->setMetadata(metadata);
|
||||
}
|
||||
|
||||
std::vector<Mod*> Mod::getDependants() const {
|
||||
return m_impl->getDependants();
|
||||
}
|
||||
|
@ -117,6 +121,22 @@ void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr<Sett
|
|||
return m_impl->registerCustomSetting(key, std::move(value));
|
||||
}
|
||||
|
||||
std::vector<std::string> Mod::getLaunchArgumentNames() const {
|
||||
return m_impl->getLaunchArgumentNames();
|
||||
}
|
||||
|
||||
bool Mod::hasLaunchArgument(std::string_view const name) const {
|
||||
return m_impl->hasLaunchArgument(name);
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::getLaunchArgument(std::string_view const name) const {
|
||||
return m_impl->getLaunchArgument(name);
|
||||
}
|
||||
|
||||
bool Mod::getLaunchBool(std::string_view const name) const {
|
||||
return m_impl->getLaunchBool(name);
|
||||
}
|
||||
|
||||
Result<Hook*> Mod::claimHook(std::shared_ptr<Hook> hook) {
|
||||
return m_impl->claimHook(hook);
|
||||
}
|
||||
|
|
|
@ -327,6 +327,37 @@ bool Mod::Impl::hasSetting(std::string_view const key) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string Mod::Impl::getLaunchArgPrefix() const {
|
||||
return m_metadata.getID() + ".";
|
||||
}
|
||||
|
||||
std::string Mod::Impl::getLaunchArgName(std::string_view const name) const {
|
||||
return this->getLaunchArgPrefix() + std::string(name);
|
||||
}
|
||||
|
||||
std::vector<std::string> Mod::Impl::getLaunchArgumentNames() const {
|
||||
auto prefix = getLaunchArgPrefix();
|
||||
std::vector<std::string> names;
|
||||
for (const auto& name : Loader::get()->getLaunchArgumentNames()) {
|
||||
if (name.starts_with(prefix)) {
|
||||
names.push_back(name.substr(prefix.size()));
|
||||
}
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
bool Mod::Impl::hasLaunchArgument(std::string_view const name) const {
|
||||
return Loader::get()->hasLaunchArgument(this->getLaunchArgName(name));
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::Impl::getLaunchArgument(std::string_view const name) const {
|
||||
return Loader::get()->getLaunchArgument(this->getLaunchArgName(name));
|
||||
}
|
||||
|
||||
bool Mod::Impl::getLaunchBool(std::string_view const name) const {
|
||||
return Loader::get()->getLaunchBool(this->getLaunchArgName(name));
|
||||
}
|
||||
|
||||
// Loading, Toggling, Installing
|
||||
|
||||
Result<> Mod::Impl::loadBinary() {
|
||||
|
|
|
@ -113,6 +113,13 @@ namespace geode {
|
|||
SettingValue* getSetting(std::string_view const key) const;
|
||||
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
|
||||
|
||||
std::string getLaunchArgPrefix() const;
|
||||
std::string getLaunchArgName(std::string_view const name) const;
|
||||
std::vector<std::string> getLaunchArgumentNames() const;
|
||||
bool hasLaunchArgument(std::string_view const name) const;
|
||||
std::optional<std::string> getLaunchArgument(std::string_view const name) const;
|
||||
bool getLaunchBool(std::string_view const name) const;
|
||||
|
||||
Result<Hook*> claimHook(std::shared_ptr<Hook> hook);
|
||||
Result<> disownHook(Hook* hook);
|
||||
[[nodiscard]] std::vector<Hook*> getHooks() const;
|
||||
|
|
|
@ -36,3 +36,11 @@ bool Loader::Impl::userTriedToLoadDLLs() const {
|
|||
void Loader::Impl::addNativeBinariesPath(ghc::filesystem::path const& path) {
|
||||
log::warn("LoaderImpl::addNativeBinariesPath not implement on this platform, not adding path {}", path.string());
|
||||
}
|
||||
|
||||
bool Loader::Impl::supportsLaunchArguments() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Loader::Impl::getLaunchCommand() const {
|
||||
return std::string(); // Empty
|
||||
}
|
||||
|
|
|
@ -39,3 +39,11 @@ void Loader::Impl::setupIPC() {
|
|||
bool Loader::Impl::userTriedToLoadDLLs() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Loader::Impl::supportsLaunchArguments() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Loader::Impl::getLaunchCommand() const {
|
||||
return std::string(); // Empty
|
||||
}
|
||||
|
|
|
@ -145,3 +145,12 @@ void Loader::Impl::addNativeBinariesPath(ghc::filesystem::path const& path) {
|
|||
std::string Loader::Impl::getGameVersion() {
|
||||
return GEODE_STR(GEODE_GD_VERSION); // TODO implement
|
||||
}
|
||||
|
||||
// TODO
|
||||
bool Loader::Impl::supportsLaunchArguments() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Loader::Impl::getLaunchCommand() const {
|
||||
return std::string(); // Empty
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <loader/ModImpl.hpp>
|
||||
#include <loader/LoaderImpl.hpp>
|
||||
#include <Geode/utils/string.hpp>
|
||||
#include <processenv.h>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
@ -83,3 +84,11 @@ void Loader::Impl::addNativeBinariesPath(ghc::filesystem::path const& path) {
|
|||
}();
|
||||
AddDllDirectory(path.wstring().c_str());
|
||||
}
|
||||
|
||||
bool Loader::Impl::supportsLaunchArguments() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Loader::Impl::getLaunchCommand() const {
|
||||
return GetCommandLineA();
|
||||
}
|
||||
|
|
|
@ -44,6 +44,25 @@ struct $modify(MenuLayer) {
|
|||
node->release();
|
||||
log::info("ref: {}", ref.lock().data());
|
||||
|
||||
// Launch arguments
|
||||
log::info("Testing launch args...");
|
||||
log::pushNest();
|
||||
log::info("For global context:");
|
||||
log::pushNest();
|
||||
for (const auto& arg : Loader::get()->getLaunchArgumentNames()) {
|
||||
log::info(arg);
|
||||
}
|
||||
log::popNest();
|
||||
log::info("For this mod:");
|
||||
log::pushNest();
|
||||
for (const auto& arg : Mod::get()->getLaunchArgumentNames()) {
|
||||
log::info(arg);
|
||||
}
|
||||
log::popNest();
|
||||
log::info("Mod has launch arg 'modArg': {}", Mod::get()->hasLaunchArgument("modArg"));
|
||||
log::info("Loader bool arg 'boolArg': {}", Loader::get()->getLaunchBool("boolArg"));
|
||||
log::popNest();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue