mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-09-13 03:44:58 -04:00
Merge branch 'main' of https://github.com/TwilitRealm/dusk into randomizer
This commit is contained in:
+69
-7
@@ -1,9 +1,11 @@
|
||||
#ifndef DUSK_CONFIG_HPP
|
||||
#define DUSK_CONFIG_HPP
|
||||
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <stdexcept>
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include "config_var.hpp"
|
||||
|
||||
namespace dusk::config {
|
||||
@@ -40,7 +42,7 @@ public:
|
||||
[[nodiscard]] virtual nlohmann::json dumpToJson(const ConfigVarBase& cVar) const = 0;
|
||||
};
|
||||
|
||||
template<ConfigValue T>
|
||||
template <ConfigValue T>
|
||||
class ConfigImpl : public ConfigImplBase {
|
||||
// Just downcasting the references...
|
||||
void loadFromJson(ConfigVarBase& cVar, const nlohmann::json& jsonValue) const final {
|
||||
@@ -90,20 +92,28 @@ public:
|
||||
void Register(ConfigVarBase& configVar);
|
||||
|
||||
/**
|
||||
* \brief Indicate that all registrations have happened and everything should lock in.
|
||||
* \brief Unregister a CVar, detaching it from the config system.
|
||||
*
|
||||
* If the CVar carries a user-set value (Value or Speedrun layer), it is stashed as an
|
||||
* unregistered key: Save() keeps writing it, and a later Register() of the same name restores
|
||||
* it through the normal back-fill path. The CVar may be destroyed after this returns.
|
||||
*/
|
||||
void FinishRegistration();
|
||||
void unregister(ConfigVarBase& configVar);
|
||||
|
||||
/**
|
||||
* \brief Load config from the standard user preferences location.
|
||||
*/
|
||||
void LoadFromUserPreferences();
|
||||
void LoadFromFileName(const char* path);
|
||||
void load_from_user_preferences();
|
||||
void load_from_file_name(const char* path);
|
||||
|
||||
void load_arg_override(std::string_view name, std::string_view value);
|
||||
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* \brief Save the config to file.
|
||||
*/
|
||||
void Save();
|
||||
void save();
|
||||
|
||||
/**
|
||||
* \brief Get a registered CVar by name.
|
||||
@@ -124,6 +134,58 @@ void ClearAllActionBindings(int port);
|
||||
*/
|
||||
void EnumerateRegistered(std::function<void(ConfigVarBase&)> callback);
|
||||
|
||||
/**
|
||||
* \brief Type-erased change callback. previousValue points at the value before the mutation
|
||||
* (a `const T*` for a `ConfigVar<T>`) and is valid only for the duration of the call.
|
||||
*/
|
||||
using ChangeCallback = std::function<void(ConfigVarBase& cVar, const void* previousValue)>;
|
||||
|
||||
/**
|
||||
* \brief Token identifying a change subscription. 0 is never a valid token.
|
||||
*/
|
||||
using Subscription = u64;
|
||||
|
||||
/**
|
||||
* \brief Subscribe to changes of the named CVar (registered or not yet registered).
|
||||
*
|
||||
* Fired synchronously on the mutating thread (in practice the game thread) whenever the CVar's
|
||||
* effective value changes at runtime: setValue, override/speedrun setters and clears. Values
|
||||
* applied by config load or launch arguments do *not* notify: loads happen during startup
|
||||
* before the subsystems callbacks push values into are initialized, and each subsystem reads
|
||||
* its initial value itself at its own init. Callbacks may mutate other CVars; a nested
|
||||
* mutation of the same CVar applies but does not re-notify.
|
||||
*/
|
||||
Subscription subscribe(std::string_view name, ChangeCallback callback);
|
||||
|
||||
/**
|
||||
* \brief Typed convenience overload: the callback receives the current and previous values.
|
||||
*/
|
||||
template <ConfigValue T, typename Callback>
|
||||
requires std::invocable<Callback, const T&, const T&> Subscription subscribe(
|
||||
ConfigVar<T>& cVar, Callback&& callback) {
|
||||
return subscribe(cVar.getName(),
|
||||
[&cVar, cb = std::forward<Callback>(callback)](ConfigVarBase&, const void* previousValue) {
|
||||
cb(cVar.getValue(), *static_cast<const T*>(previousValue));
|
||||
});
|
||||
}
|
||||
|
||||
void unsubscribe(Subscription token);
|
||||
|
||||
/**
|
||||
* \brief Register a CVar and attach a change callback in one step.
|
||||
*
|
||||
* Useful for pushing settings into external systems (e.g. aurora) from one place instead of
|
||||
* every UI setter. The callback fires only for runtime changes (see subscribe); not when
|
||||
* loaded from config or launch arguments.
|
||||
*/
|
||||
template <ConfigValue T, typename Callback>
|
||||
requires std::invocable<Callback, const T&, const T&> Subscription Register(
|
||||
ConfigVar<T>& cVar, Callback&& onChange) {
|
||||
auto subscription = subscribe(cVar, std::forward<Callback>(onChange));
|
||||
Register(static_cast<ConfigVarBase&>(cVar));
|
||||
return subscription;
|
||||
}
|
||||
|
||||
template <ConfigValue T>
|
||||
const ConfigImplBase* GetConfigImpl() {
|
||||
static ConfigImpl<T> config;
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
#define DUSK_CONFIG_VAR_HPP
|
||||
|
||||
#include "dolphin/types.h"
|
||||
#include <type_traits>
|
||||
#include <concepts>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
/**
|
||||
* The configuration system.
|
||||
@@ -69,7 +71,7 @@ protected:
|
||||
/**
|
||||
* The name of this CVar, used in the configuration file.
|
||||
*/
|
||||
const char* name;
|
||||
std::string name;
|
||||
|
||||
/**
|
||||
* Whether this CVar has been registered with the global managing logic.
|
||||
@@ -87,8 +89,8 @@ protected:
|
||||
*/
|
||||
const ConfigImplBase* impl;
|
||||
|
||||
ConfigVarBase(const char* name, const ConfigImplBase* impl);
|
||||
virtual ~ConfigVarBase() = default;
|
||||
ConfigVarBase(const ConfigVarBase&) = delete;
|
||||
ConfigVarBase(std::string name, const ConfigImplBase* impl);
|
||||
|
||||
/**
|
||||
* Check that the CVar is registered, aborting if this is not the case.
|
||||
@@ -98,7 +100,22 @@ protected:
|
||||
abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether any change subscriber (see config::subscribe) is attached to this CVar's name.
|
||||
*/
|
||||
[[nodiscard]] bool has_subscribers() const;
|
||||
|
||||
/**
|
||||
* Notify change subscribers (see config::subscribe) that the effective value of this CVar
|
||||
* changed. Called by mutators after the change has been applied; previousValue points at
|
||||
* the old value (a `const T*` for a `ConfigVar<T>`), valid only for the duration of the
|
||||
* call.
|
||||
*/
|
||||
void notify_changed(const void* previousValue);
|
||||
|
||||
public:
|
||||
virtual ~ConfigVarBase();
|
||||
|
||||
/**
|
||||
* Get the name of this CVar, used in the configuration file.
|
||||
*/
|
||||
@@ -121,6 +138,7 @@ public:
|
||||
* This is necessary to make it legal to access.
|
||||
*/
|
||||
void markRegistered();
|
||||
void unmarkRegistered();
|
||||
|
||||
/**
|
||||
* Clear a speedrun-mode override if one is active on this CVar.
|
||||
@@ -155,6 +173,7 @@ template <typename T>
|
||||
concept ConfigValue =
|
||||
!std::is_const_v<T>
|
||||
&& !std::is_volatile_v<T>
|
||||
&& std::equality_comparable<T>
|
||||
&& (std::is_same_v<T, bool>
|
||||
|| ConfigValueInteger<T>
|
||||
|| std::is_same_v<T, f32>
|
||||
@@ -166,6 +185,9 @@ concept ConfigValue =
|
||||
template <ConfigValue T>
|
||||
const ConfigImplBase* GetConfigImpl();
|
||||
|
||||
template <ConfigValue T>
|
||||
class ConfigImpl;
|
||||
|
||||
template <typename T>
|
||||
struct ConfigEnumRange {
|
||||
static constexpr auto min = std::numeric_limits<std::underlying_type_t<T>>::min();
|
||||
@@ -192,10 +214,12 @@ public:
|
||||
* @param arg Arguments to forward to construct the default value.
|
||||
*/
|
||||
template <typename... Args>
|
||||
ConfigVar(const char* name, Args&&... arg)
|
||||
: ConfigVarBase(name, GetConfigImpl<T>()), defaultValue(std::forward<Args>(arg)...),
|
||||
ConfigVar(std::string name, Args&&... arg)
|
||||
: ConfigVarBase(std::move(name), GetConfigImpl<T>()), defaultValue(std::forward<Args>(arg)...),
|
||||
value(), overrideValue() {}
|
||||
|
||||
ConfigVar(ConfigVar const&) = delete;
|
||||
|
||||
/**
|
||||
* \brief Get the current value of the CVar.
|
||||
*
|
||||
@@ -234,6 +258,7 @@ public:
|
||||
*/
|
||||
void setValue(T newValue, bool replaceOverride = true) {
|
||||
checkRegistered();
|
||||
const auto previous = previous_for_notify();
|
||||
value = std::move(newValue);
|
||||
|
||||
if (replaceOverride) {
|
||||
@@ -242,6 +267,7 @@ public:
|
||||
} else if (layer != ConfigVarLayer::Override) {
|
||||
layer = ConfigVarLayer::Value;
|
||||
}
|
||||
notify_if_changed(previous);
|
||||
}
|
||||
|
||||
operator const T&() {
|
||||
@@ -258,8 +284,10 @@ public:
|
||||
*/
|
||||
void setOverrideValue(T newValue) {
|
||||
checkRegistered();
|
||||
const auto previous = previous_for_notify();
|
||||
overrideValue = std::move(newValue);
|
||||
layer = ConfigVarLayer::Override;
|
||||
notify_if_changed(previous);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,25 +301,31 @@ public:
|
||||
void setSpeedrunValue(T newValue) {
|
||||
checkRegistered();
|
||||
if (layer != ConfigVarLayer::Override) {
|
||||
const auto previous = previous_for_notify();
|
||||
priorLayer = layer;
|
||||
overrideValue = std::move(newValue);
|
||||
layer = ConfigVarLayer::Speedrun;
|
||||
notify_if_changed(previous);
|
||||
}
|
||||
}
|
||||
|
||||
void clearOverride() {
|
||||
checkRegistered();
|
||||
if (layer == ConfigVarLayer::Override) {
|
||||
const auto previous = previous_for_notify();
|
||||
overrideValue = {};
|
||||
layer = ConfigVarLayer::Value;
|
||||
notify_if_changed(previous);
|
||||
}
|
||||
}
|
||||
|
||||
void clearSpeedrunOverride() override {
|
||||
checkRegistered();
|
||||
if (layer == ConfigVarLayer::Speedrun) {
|
||||
const auto previous = previous_for_notify();
|
||||
overrideValue = {};
|
||||
layer = priorLayer;
|
||||
notify_if_changed(previous);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +339,48 @@ public:
|
||||
const ConfigVarLayer effectiveLayer = (layer == ConfigVarLayer::Speedrun) ? priorLayer : layer;
|
||||
return effectiveLayer == ConfigVarLayer::Default ? defaultValue : value;
|
||||
}
|
||||
|
||||
private:
|
||||
// The config loader applies values through the silent load_* methods below.
|
||||
friend class ConfigImpl<T>;
|
||||
|
||||
/**
|
||||
* Copy of the effective value before a mutation, taken only when someone is subscribed.
|
||||
*/
|
||||
[[nodiscard]] std::optional<T> previous_for_notify() const {
|
||||
return has_subscribers() ? std::optional<T>{getValue()} : std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify subscribers if the effective value actually changed across a mutation.
|
||||
*/
|
||||
void notify_if_changed(const std::optional<T>& previous) {
|
||||
if (previous.has_value() && !(getValue() == *previous)) {
|
||||
notify_changed(&*previous);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setValue(newValue, false) without notifying change subscribers. Used when loading config:
|
||||
* loads happen during startup before the subsystems change callbacks push values into are
|
||||
* initialized, and each subsystem applies the loaded value itself at its own init.
|
||||
*/
|
||||
void load_value(T newValue) {
|
||||
checkRegistered();
|
||||
value = std::move(newValue);
|
||||
if (layer != ConfigVarLayer::Override) {
|
||||
layer = ConfigVarLayer::Value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* setOverrideValue without notifying change subscribers (see load_value).
|
||||
*/
|
||||
void load_override_value(T newValue) {
|
||||
checkRegistered();
|
||||
overrideValue = std::move(newValue);
|
||||
layer = ConfigVarLayer::Override;
|
||||
}
|
||||
};
|
||||
|
||||
using ActionBindConfigVar = ConfigVar<int>;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "mods/svc/gfx.h"
|
||||
|
||||
namespace dusk::mods {
|
||||
|
||||
void gfx_run_stage(GfxStage stage, const view_class* gameView = nullptr,
|
||||
const view_port_class* gameViewport = nullptr);
|
||||
|
||||
} // namespace dusk::mods
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
#include <dolphin/gx/GXAurora.h>
|
||||
#include <dolphin/gx/GXExtra.h>
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
#include "profiling.hpp"
|
||||
|
||||
#if DUSK_GFX_DEBUG_GROUPS
|
||||
#define GX_DEBUG_GROUP(name, ...) \
|
||||
|
||||
@@ -12,6 +12,18 @@ extern bool RestartRequested;
|
||||
extern std::filesystem::path ConfigPath;
|
||||
extern std::filesystem::path CachePath;
|
||||
|
||||
extern uint8_t SaveRequested;
|
||||
struct StageRequest {
|
||||
std::string stage;
|
||||
bool set;
|
||||
s8 room;
|
||||
s16 point;
|
||||
s8 layer;
|
||||
};
|
||||
extern StageRequest StageRequested;
|
||||
|
||||
|
||||
|
||||
#if defined(__ANDROID__) || (defined(TARGET_OS_IOS) && TARGET_OS_IOS) || \
|
||||
(defined(TARGET_OS_TV) && TARGET_OS_TV)
|
||||
inline constexpr bool SupportsProcessRestart = false;
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/config_var.hpp"
|
||||
#include "mods/api.h"
|
||||
|
||||
namespace dusk::mods {
|
||||
struct LoadedMod;
|
||||
class ModBundle;
|
||||
} // namespace dusk::mods
|
||||
|
||||
struct ModContext {
|
||||
dusk::mods::LoadedMod* mod = nullptr;
|
||||
};
|
||||
|
||||
namespace dusk::mods::loader {
|
||||
class NativeModule;
|
||||
} // namespace dusk::mods::loader
|
||||
|
||||
namespace dusk::mods {
|
||||
|
||||
struct ModDependencyEdge {
|
||||
LoadedMod* mod = nullptr;
|
||||
bool required = false;
|
||||
};
|
||||
|
||||
struct ModManifestInfo {
|
||||
struct Import {
|
||||
std::string id;
|
||||
uint16_t major = 0;
|
||||
bool required = false;
|
||||
bool operator==(const Import&) const = default;
|
||||
};
|
||||
struct Export {
|
||||
std::string id;
|
||||
uint16_t major = 0;
|
||||
bool operator==(const Export&) const = default;
|
||||
};
|
||||
std::vector<Import> imports;
|
||||
std::vector<Export> exports;
|
||||
bool operator==(const ModManifestInfo&) const = default;
|
||||
};
|
||||
|
||||
struct ModMetadata {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string version;
|
||||
std::string author;
|
||||
std::string description;
|
||||
std::string iconPath;
|
||||
std::string bannerPath;
|
||||
};
|
||||
|
||||
struct ModSearchDir {
|
||||
std::filesystem::path path;
|
||||
// Directory bundles dlopen their native lib in place instead of extracting it to the cache.
|
||||
// Required where extracted code cannot run (iOS), desirable for signed/read-only installs.
|
||||
bool inPlaceNative = false;
|
||||
// Native library location for platforms that restrict placement (e.g. iOS/tvOS Frameworks/)
|
||||
std::filesystem::path nativeLibDir;
|
||||
};
|
||||
|
||||
struct NativeMod {
|
||||
std::unique_ptr<loader::NativeModule> handle;
|
||||
const ModManifest* manifest = nullptr;
|
||||
ModContext** contextSymbol = nullptr;
|
||||
|
||||
ModInitializeFn fn_initialize = nullptr;
|
||||
ModUpdateFn fn_update = nullptr;
|
||||
ModShutdownFn fn_shutdown = nullptr;
|
||||
};
|
||||
|
||||
enum class NativeModStatus : u8 {
|
||||
/**
|
||||
* Mod does not have native code included.
|
||||
*/
|
||||
None,
|
||||
|
||||
/**
|
||||
* Native code mod loaded successfully.
|
||||
*
|
||||
* Note that this only indicates load status of the native library. If the native lib throws in
|
||||
* its init function, it will still be disabled!
|
||||
*/
|
||||
Loaded,
|
||||
|
||||
/**
|
||||
* This build was compiled without native mod support!
|
||||
*/
|
||||
BuildDisabled,
|
||||
|
||||
/**
|
||||
* Mod ships native libraries, but none matches this build's platform and architecture.
|
||||
*/
|
||||
ModMissingPlatform,
|
||||
|
||||
/**
|
||||
* Mod is built for a different ABI version than this build of the game.
|
||||
*/
|
||||
ApiVersionMismatch,
|
||||
|
||||
/**
|
||||
* Mod is missing a required native API export.
|
||||
*/
|
||||
MissingExport,
|
||||
|
||||
/**
|
||||
* Unknown error loading the native mod.
|
||||
*/
|
||||
Unknown,
|
||||
};
|
||||
|
||||
struct LoadedMod {
|
||||
ModMetadata metadata;
|
||||
std::string modPath;
|
||||
std::string dir;
|
||||
|
||||
uint32_t searchDirIndex = 0;
|
||||
// Native lib is dlopen'd in place and stays resident for the session. Reload is unsupported.
|
||||
bool inPlace = false;
|
||||
|
||||
std::unique_ptr<ConfigVar<bool> > cvarIsEnabled;
|
||||
config::Subscription enabledSubscription = 0;
|
||||
|
||||
bool active = false;
|
||||
bool loadFailed = false;
|
||||
std::string failureReason;
|
||||
|
||||
// mod_initialize succeeded; a mod_shutdown is owed on deactivation.
|
||||
bool initialized = false;
|
||||
// Static service exports are currently present in the registry.
|
||||
bool servicesRegistered = false;
|
||||
// Lifecycle state last applied by the loader; diffed against cvarIsEnabled to pick up
|
||||
// runtime enable/disable requests.
|
||||
bool enabledApplied = false;
|
||||
// Deactivated because a provider it imports from was disabled, not by its own cvar.
|
||||
bool suspendedByProvider = false;
|
||||
// Bumped per native lib extraction so every dlopen sees a fresh path (and thus a fresh
|
||||
// image with fresh statics; a previous dlclose may not fully unmap). Also bumped by
|
||||
// asset-only reloads, so it doubles as a generation for anything caching per-mod content.
|
||||
uint32_t cacheGeneration = 0;
|
||||
// Currently extracted native library, empty if none.
|
||||
std::string nativePath;
|
||||
|
||||
NativeModStatus nativeStatus = NativeModStatus::None;
|
||||
std::unique_ptr<NativeMod> native;
|
||||
std::unique_ptr<ModContext> context;
|
||||
|
||||
// Shared with overlay file registrations so in-flight DVD reads survive disable/reload.
|
||||
std::shared_ptr<ModBundle> bundle;
|
||||
|
||||
ModManifestInfo manifestInfo;
|
||||
|
||||
// Mods this mod imports services from, and mods importing services from this mod.
|
||||
std::vector<ModDependencyEdge> dependencies;
|
||||
std::vector<ModDependencyEdge> dependents;
|
||||
};
|
||||
|
||||
class ModLoader {
|
||||
public:
|
||||
static ModLoader& instance();
|
||||
|
||||
void set_search_dirs(std::vector<ModSearchDir> dirs) { m_searchDirs = std::move(dirs); }
|
||||
void set_cache_dir(std::filesystem::path dir) { m_cacheDir = std::move(dir); }
|
||||
void init();
|
||||
void tick();
|
||||
void shutdown();
|
||||
|
||||
void request_enable(std::string_view id);
|
||||
void request_disable(std::string_view id);
|
||||
void request_reload(std::string_view id);
|
||||
void notify_mod_failure(LoadedMod& mod, bool firstFailure);
|
||||
|
||||
[[nodiscard]] auto mods() const {
|
||||
return m_mods | std::views::transform([](const auto& m) -> LoadedMod& { return *m; });
|
||||
}
|
||||
|
||||
[[nodiscard]] auto active_mods() const {
|
||||
return mods() | std::views::filter([](const auto& m) { return m.active; });
|
||||
}
|
||||
|
||||
private:
|
||||
enum class RequestKind : u8 { Enable, Disable, Reload };
|
||||
struct Request {
|
||||
std::string modId;
|
||||
RequestKind kind;
|
||||
};
|
||||
// ModLoader::tick runs inside fapGm_Execute, so code from an unloading mod can still be
|
||||
// live on the stack (its frame unwinds after the tick). dlclose is therefore deferred to
|
||||
// the next tick, by which point every per-frame entry into the mod should have returned.
|
||||
struct RetiredNative {
|
||||
std::unique_ptr<NativeMod> native;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
std::vector<std::unique_ptr<LoadedMod> > m_mods;
|
||||
std::vector<ModSearchDir> m_searchDirs;
|
||||
std::filesystem::path m_cacheDir;
|
||||
std::vector<Request> m_pendingRequests;
|
||||
std::vector<std::string> m_pendingFailures;
|
||||
std::vector<RetiredNative> m_retiredNatives;
|
||||
bool m_initialized = false;
|
||||
bool m_startupComplete = false;
|
||||
|
||||
void try_load_mod(const std::filesystem::path& modPath, bool fromDir, uint32_t searchDirIndex);
|
||||
void load_native(LoadedMod& mod, const std::string& dllEntry);
|
||||
// Resolved <nativeLibDir>/<mod id><ext> if it exists on disk, empty otherwise.
|
||||
[[nodiscard]] std::filesystem::path external_native_lib_path(const LoadedMod& mod) const;
|
||||
void unload_native(LoadedMod& mod);
|
||||
// Registers exports (if needed), resolves imports and runs mod_initialize.
|
||||
// Returns whether the mod ended up active; failures go through fail_mod.
|
||||
bool activate_mod(LoadedMod& mod);
|
||||
// Runs mod_shutdown (if needed), detaches the mod from every service, and unloads the
|
||||
// native lib. Must only run with no mod code on the stack (startup, shutdown, or top of tick).
|
||||
void deactivate_mod(LoadedMod& mod);
|
||||
void init_services();
|
||||
bool register_static_service_exports(LoadedMod& mod);
|
||||
bool resolve_service_imports(LoadedMod& mod);
|
||||
[[nodiscard]] std::string describe_missing_import(
|
||||
const char* serviceId, uint16_t majorVersion, uint16_t minMinorVersion) const;
|
||||
|
||||
LoadedMod* find_mod(std::string_view id) const;
|
||||
void drain_retired_natives();
|
||||
void apply_pending_requests();
|
||||
void flush_toasts();
|
||||
void on_enabled_changed(LoadedMod& mod);
|
||||
// Deactivates `target` (if needed) and its transitive dependents, optionally re-reads the
|
||||
// bundle from disk, then reactivates whatever the current cvar/provider state allows.
|
||||
void apply_lifecycle_change(LoadedMod& target, bool reload);
|
||||
// `target` plus transitive active/suspended dependents, in m_mods (init) order.
|
||||
std::vector<LoadedMod*> collect_lifecycle_set(LoadedMod& target);
|
||||
bool reload_bundle(LoadedMod& mod);
|
||||
bool ensure_native_loaded(LoadedMod& mod);
|
||||
};
|
||||
|
||||
using ModIndex = std::ranges::range_difference_t<decltype(std::declval<ModLoader>().mods())>;
|
||||
|
||||
} // namespace dusk::mods
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<tracy/Tracy.hpp>)
|
||||
#include <tracy/Tracy.hpp>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef ZoneScoped
|
||||
#define ZoneScoped
|
||||
#define ZoneScopedN(name)
|
||||
#endif
|
||||
@@ -7,7 +7,8 @@
|
||||
|
||||
namespace dusk {
|
||||
|
||||
using namespace config;
|
||||
using config::ConfigVar;
|
||||
using config::ActionBindConfigVar;
|
||||
|
||||
enum class BloomMode : int {
|
||||
Off = 0,
|
||||
|
||||
@@ -37,5 +37,6 @@ struct SpeedrunInfo {
|
||||
extern SpeedrunInfo m_speedrunInfo;
|
||||
|
||||
void resetForSpeedrunMode();
|
||||
void restoreFromSpeedrunMode();
|
||||
|
||||
} // namespace dusk
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#ifndef DUSK_TEXTURE_REPLACEMENTS_HPP
|
||||
#define DUSK_TEXTURE_REPLACEMENTS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace dusk::texture_replacements {
|
||||
|
||||
// Mod replacements are prioritized *over* user replacements (<data folder>/texture_replacements/)
|
||||
inline constexpr int32_t kUserTextureReplacementPriority = -1'000'000;
|
||||
|
||||
void reload();
|
||||
void set_enabled(bool enabled);
|
||||
void shutdown();
|
||||
|
||||
Reference in New Issue
Block a user