mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-21 10:38:27 -05:00
commit
47f0b555f3
153 changed files with 2103 additions and 11006 deletions
1
.github/workflows/build.yml
vendored
1
.github/workflows/build.yml
vendored
|
@ -137,6 +137,7 @@ jobs:
|
|||
build-mac:
|
||||
name: Build macOS
|
||||
runs-on: macos-latest
|
||||
if: false
|
||||
|
||||
env:
|
||||
SCCACHE_CACHE_MULTIARCH: 1
|
||||
|
|
|
@ -189,8 +189,8 @@ include(cmake/Platform.cmake)
|
|||
include(cmake/GeodeFile.cmake)
|
||||
|
||||
if (NOT DEFINED GEODE_GD_VERSION)
|
||||
set(GEODE_GD_VERSION 2.206)
|
||||
set(GEODE_COMP_GD_VERSION 22060)
|
||||
set(GEODE_GD_VERSION 2.2074)
|
||||
set(GEODE_COMP_GD_VERSION 22074)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(
|
||||
|
@ -236,8 +236,9 @@ if (ANDROID)
|
|||
endif()
|
||||
|
||||
set(MAT_JSON_AS_INTERFACE ON)
|
||||
CPMAddPackage("gh:geode-sdk/json#cda9807")
|
||||
CPMAddPackage("gh:fmtlib/fmt#10.2.1")
|
||||
CPMAddPackage("gh:geode-sdk/result@1.2.1")
|
||||
CPMAddPackage("gh:geode-sdk/json@3.0.3")
|
||||
CPMAddPackage("gh:fmtlib/fmt#11.0.2")
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE MAT_JSON_DYNAMIC=1)
|
||||
|
||||
|
@ -266,7 +267,7 @@ if (DEFINED GEODE_TULIPHOOK_REPO_PATH)
|
|||
message(STATUS "Using ${GEODE_TULIPHOOK_REPO_PATH} for TulipHook")
|
||||
add_subdirectory(${GEODE_TULIPHOOK_REPO_PATH} ${GEODE_TULIPHOOK_REPO_PATH}/build)
|
||||
else()
|
||||
CPMAddPackage("gh:geode-sdk/TulipHook#d1d9559")
|
||||
CPMAddPackage("gh:geode-sdk/TulipHook@2.4.1")
|
||||
endif()
|
||||
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)
|
||||
|
||||
|
@ -296,7 +297,7 @@ target_include_directories(GeodeBindings PUBLIC
|
|||
${GEODE_LOADER_PATH}/include/Geode/fmod
|
||||
)
|
||||
target_link_directories(GeodeBindings PUBLIC ${GEODE_LOADER_PATH}/include/link)
|
||||
target_link_libraries(GeodeBindings PUBLIC fmt TulipHookInclude mat-json)
|
||||
target_link_libraries(GeodeBindings PUBLIC fmt TulipHookInclude mat-json GeodeResult)
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE GeodeBindings)
|
||||
|
||||
if (NOT EXISTS ${GEODE_BIN_PATH})
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
3.9.0
|
||||
4.0.0-alpha.1
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
|
||||
|
||||
set(CPM_DOWNLOAD_VERSION 0.39.0)
|
||||
set(CPM_HASH_SUM "66639bcac9dd2907b2918de466783554c1334446b9874e90d38e3778d404c2ef")
|
||||
set(CPM_DOWNLOAD_VERSION 0.40.2)
|
||||
set(CPM_HASH_SUM "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d")
|
||||
|
||||
if(CPM_SOURCE_CACHE)
|
||||
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
|
|
|
@ -5,21 +5,10 @@
|
|||
#include <Geode/platform/platform.hpp>
|
||||
#include <variant>
|
||||
|
||||
#define GEODE_STATIC_PTR(type, name) \
|
||||
static type* s_##name; \
|
||||
inline type* name() { \
|
||||
if (!s_##name) s_##name = new type(); \
|
||||
return s_##name; \
|
||||
}
|
||||
|
||||
#define GEODE_STATIC_VAR(type, name) \
|
||||
inline type& name() { \
|
||||
static type s_##name; \
|
||||
return s_##name; \
|
||||
}
|
||||
|
||||
#define GEODE_WRAPPER_CONCAT(x, y) x##y
|
||||
#define GEODE_CONCAT(x, y) GEODE_WRAPPER_CONCAT(x, y)
|
||||
#if !defined(GEODE_CONCAT)
|
||||
#define GEODE_WRAPPER_CONCAT(x, y) x##y
|
||||
#define GEODE_CONCAT(x, y) GEODE_WRAPPER_CONCAT(x, y)
|
||||
#endif
|
||||
|
||||
#define GEODE_WRAPPER_STR(...) #__VA_ARGS__
|
||||
#define GEODE_STR(...) GEODE_WRAPPER_STR(__VA_ARGS__)
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
#include "ui/EnterLayerEvent.hpp"
|
||||
#include "ui/BasedButtonSprite.hpp"
|
||||
#include "ui/IconButtonSprite.hpp"
|
||||
#include "ui/InputNode.hpp"
|
||||
#include "ui/Layout.hpp"
|
||||
#include "ui/SpacerNode.hpp"
|
||||
#include "ui/General.hpp"
|
||||
#include "ui/ListView.hpp"
|
||||
#include "ui/MDPopup.hpp"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "DefaultInclude.hpp"
|
||||
#include "utils/Result.hpp"
|
||||
#include "utils/VersionInfo.hpp"
|
||||
#include "utils/ranges.hpp"
|
||||
#include "utils/casts.hpp"
|
||||
|
@ -12,5 +11,4 @@
|
|||
#include "utils/permission.hpp"
|
||||
#include "utils/general.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
#include "utils/MiniFunction.hpp"
|
||||
#include "utils/ObjcHook.hpp"
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace gd {
|
|||
bool empty() const;
|
||||
|
||||
bool operator==(string const& other) const;
|
||||
bool operator==(std::string_view const other) const;
|
||||
bool operator==(std::string_view other) const;
|
||||
bool operator==(char const* other) const {
|
||||
return *this == std::string_view(other);
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ namespace gd {
|
|||
return *this == std::string_view(other);
|
||||
}
|
||||
std::strong_ordering operator<=>(string const& other) const;
|
||||
std::strong_ordering operator<=>(std::string_view const other) const;
|
||||
std::strong_ordering operator<=>(std::string_view other) const;
|
||||
std::strong_ordering operator<=>(char const* other) const {
|
||||
return *this <=> std::string_view(other);
|
||||
}
|
||||
|
|
63
loader/include/Geode/cocos/base_nodes/CCNode.h
vendored
63
loader/include/Geode/cocos/base_nodes/CCNode.h
vendored
|
@ -37,7 +37,6 @@
|
|||
#include "../kazmath/include/kazmath/kazmath.h"
|
||||
#include "../script_support/CCScriptSupport.h"
|
||||
#include "../include/CCProtocols.h"
|
||||
#include "Layout.hpp"
|
||||
#include "../../loader/Event.hpp"
|
||||
#include <Geode/utils/casts.hpp>
|
||||
|
||||
|
@ -45,6 +44,12 @@
|
|||
#include <matjson.hpp>
|
||||
#endif
|
||||
|
||||
namespace geode {
|
||||
class Layout;
|
||||
class LayoutOptions;
|
||||
enum class Anchor;
|
||||
}
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
class CCCamera;
|
||||
|
@ -871,31 +876,19 @@ public:
|
|||
private:
|
||||
friend class geode::modifier::FieldContainer;
|
||||
|
||||
[[deprecated("Will be removed, it's an ABI break")]]
|
||||
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer();
|
||||
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer(char const* forClass);
|
||||
GEODE_DLL void addEventListenerInternal(
|
||||
std::string const& id,
|
||||
geode::EventListenerProtocol* protocol
|
||||
);
|
||||
|
||||
#ifdef GEODE_EXPORTING
|
||||
[[deprecated("Will be removed, it's an ABI break")]]
|
||||
GEODE_DLL std::optional<matjson::Value> getAttributeInternal(std::string const& attribute);
|
||||
#endif
|
||||
|
||||
public:
|
||||
#ifdef GEODE_EXPORTING
|
||||
[[deprecated("Will be removed, it's an ABI break")]]
|
||||
GEODE_DLL void setAttribute(std::string const& attribute, matjson::Value const& value);
|
||||
#endif
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get the string ID of this node
|
||||
* @returns The ID, or an empty string if the node has no ID.
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL std::string getID();
|
||||
GEODE_DLL const std::string& getID();
|
||||
/**
|
||||
* Set the string ID of this node. String IDs are a Geode addition
|
||||
* that are much safer to use to get nodes than absolute indexes
|
||||
|
@ -906,13 +899,23 @@ public:
|
|||
*/
|
||||
GEODE_DLL void setID(std::string const& id);
|
||||
|
||||
/**
|
||||
* Set the string ID of this node. String IDs are a Geode addition
|
||||
* that are much safer to use to get nodes than absolute indexes
|
||||
* @param id The ID of the node, recommended to be in kebab case
|
||||
* without any spaces or uppercase letters. If the node is added
|
||||
* by a mod, use the _spr literal to append the mod ID to it
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void setID(std::string&& id);
|
||||
|
||||
/**
|
||||
* Get a child by its string ID
|
||||
* @param id ID of the child
|
||||
* @returns The child, or nullptr if none was found
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL CCNode* getChildByID(std::string const& id);
|
||||
GEODE_DLL CCNode* getChildByID(std::string_view id);
|
||||
|
||||
/**
|
||||
* Get a child by its string ID. Recursively searches all the children
|
||||
|
@ -920,7 +923,7 @@ public:
|
|||
* @returns The child, or nullptr if none was found
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL CCNode* getChildByIDRecursive(std::string const& id);
|
||||
GEODE_DLL CCNode* getChildByIDRecursive(std::string_view id);
|
||||
|
||||
/**
|
||||
* Get a child based on a query. Searches the child tree for a matching
|
||||
|
@ -936,14 +939,14 @@ public:
|
|||
* ->getChildByID("mod.id/epic-button")`
|
||||
* @returns The first matching node, or nullptr if none was found
|
||||
*/
|
||||
GEODE_DLL CCNode* querySelector(std::string const& query);
|
||||
GEODE_DLL CCNode* querySelector(std::string_view query);
|
||||
|
||||
/**
|
||||
* Removes a child from the container by its ID.
|
||||
* @param id The ID of the node
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void removeChildByID(std::string const& id);
|
||||
GEODE_DLL void removeChildByID(std::string_view id);
|
||||
|
||||
/**
|
||||
* Add a child before a specified existing child
|
||||
|
@ -990,13 +993,13 @@ public:
|
|||
* CCLayers / CCMenus, this will change where the children are located
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void setLayout(Layout* layout, bool apply = true, bool respectAnchor = true);
|
||||
GEODE_DLL void setLayout(geode::Layout* layout, bool apply = true, bool respectAnchor = true);
|
||||
/**
|
||||
* Get the Layout for this node
|
||||
* @returns The current layout, or nullptr if no layout is set
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL Layout* getLayout();
|
||||
GEODE_DLL geode::Layout* getLayout();
|
||||
/**
|
||||
* Update the layout of this node using the current Layout. If no layout is
|
||||
* set, nothing happens
|
||||
|
@ -1011,13 +1014,13 @@ public:
|
|||
* @param apply Whether to update the layout of the parent node
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void setLayoutOptions(LayoutOptions* options, bool apply = true);
|
||||
GEODE_DLL void setLayoutOptions(geode::LayoutOptions* options, bool apply = true);
|
||||
/**
|
||||
* Get the layout options for this node
|
||||
* @returns The current layout options, or nullptr if no options are set
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL LayoutOptions* getLayoutOptions();
|
||||
GEODE_DLL geode::LayoutOptions* getLayoutOptions();
|
||||
/**
|
||||
* Adds a child at an anchored position with an offset. The node is placed
|
||||
* in its parent where the anchor specifies, and then the offset is used to
|
||||
|
@ -1029,7 +1032,7 @@ public:
|
|||
* if no other layout is already specified
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void addChildAtPosition(CCNode* child, Anchor anchor, CCPoint const& offset = CCPointZero, bool useAnchorLayout = true);
|
||||
GEODE_DLL void addChildAtPosition(CCNode* child, geode::Anchor anchor, CCPoint const& offset = CCPointZero, bool useAnchorLayout = true);
|
||||
/**
|
||||
* Adds a child at an anchored position with an offset. The node is placed
|
||||
* in its parent where the anchor specifies, and then the offset is used to
|
||||
|
@ -1044,7 +1047,7 @@ public:
|
|||
*/
|
||||
GEODE_DLL void addChildAtPosition(
|
||||
CCNode* child,
|
||||
Anchor anchor,
|
||||
geode::Anchor anchor,
|
||||
CCPoint const& offset,
|
||||
CCPoint const& nodeAnchor,
|
||||
bool useAnchorLayout = true
|
||||
|
@ -1057,7 +1060,7 @@ public:
|
|||
* @param offset Where to place the child relative to the anchor
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void updateAnchoredPosition(Anchor anchor, CCPoint const& offset = CCPointZero);
|
||||
GEODE_DLL void updateAnchoredPosition(geode::Anchor anchor, CCPoint const& offset = CCPointZero);
|
||||
/**
|
||||
* Updates the anchored position of a child. Requires the child to already
|
||||
* have a parent; if the child already has AnchorLayoutOptions set, those
|
||||
|
@ -1068,7 +1071,7 @@ public:
|
|||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void updateAnchoredPosition(
|
||||
Anchor anchor,
|
||||
geode::Anchor anchor,
|
||||
CCPoint const& offset,
|
||||
CCPoint const& nodeAnchor
|
||||
);
|
||||
|
@ -1102,7 +1105,7 @@ public:
|
|||
template <class Filter, class... Args>
|
||||
geode::EventListenerProtocol* addEventListener(
|
||||
std::string const& id,
|
||||
geode::utils::MiniFunction<typename Filter::Callback> callback,
|
||||
std::function<typename Filter::Callback> callback,
|
||||
Args&&... args
|
||||
) {
|
||||
auto listener = new geode::EventListener<Filter>(
|
||||
|
@ -1113,7 +1116,7 @@ public:
|
|||
}
|
||||
template <class Filter, class... Args>
|
||||
geode::EventListenerProtocol* addEventListener(
|
||||
geode::utils::MiniFunction<typename Filter::Callback> callback,
|
||||
std::function<typename Filter::Callback> callback,
|
||||
Args&&... args
|
||||
) {
|
||||
return this->addEventListener<Filter, Args...>(
|
||||
|
@ -1923,7 +1926,7 @@ namespace geode {
|
|||
std::string m_targetID;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, UserObjectSetEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, UserObjectSetEvent* event);
|
||||
|
||||
AttributeSetFilter(std::string const& id);
|
||||
};
|
||||
|
|
1
loader/include/Geode/cocos/include/cocos2d.h
vendored
1
loader/include/Geode/cocos/include/cocos2d.h
vendored
|
@ -59,7 +59,6 @@ THE SOFTWARE.
|
|||
// base_nodes
|
||||
#include "../base_nodes/CCNode.h"
|
||||
#include "../base_nodes/CCAtlasNode.h"
|
||||
#include "../base_nodes/SpacerNode.hpp"
|
||||
|
||||
// cocoa
|
||||
#include "../cocoa/CCAffineTransform.h"
|
||||
|
|
|
@ -142,7 +142,7 @@ public:
|
|||
/**
|
||||
* @note RobTop addition
|
||||
*/
|
||||
void toggleFullScreen(bool fullscreen, bool borderless);
|
||||
void toggleFullScreen(bool fullscreen, bool borderless, bool fix);
|
||||
|
||||
/**
|
||||
* @note RobTop addition
|
||||
|
@ -185,6 +185,8 @@ public:
|
|||
|
||||
protected:
|
||||
static CCEGLView* s_pEglView;
|
||||
// @note unknown members here
|
||||
uint8_t m_unkPad[8];
|
||||
bool m_bCaptured;
|
||||
// Robtop Removal
|
||||
// HWND m_hWnd;
|
||||
|
@ -203,6 +205,8 @@ protected:
|
|||
int m_nRetinaFactor;
|
||||
// @note RobTop Addition
|
||||
bool m_bCursorHidden;
|
||||
// @note may be before m_bCursorHidden
|
||||
int m_unkSize4;
|
||||
// Robtop Removal
|
||||
// LPCWSTR m_menu;
|
||||
// Robtop Removal
|
||||
|
@ -225,6 +229,8 @@ public:
|
|||
// @note RobTop Addition
|
||||
bool m_bIsBorderless;
|
||||
// @note RobTop Addition
|
||||
bool m_bIsFix;
|
||||
// @note RobTop Addition
|
||||
bool m_bShouldHideCursor;
|
||||
// @note RobTop Addition
|
||||
bool m_bCursorLocked;
|
||||
|
|
5994
loader/include/Geode/external/result/result.hpp
vendored
5994
loader/include/Geode/external/result/result.hpp
vendored
File diff suppressed because it is too large
Load diff
|
@ -53,7 +53,7 @@ namespace geode {
|
|||
return dispatchPools()[m_id];
|
||||
}
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, Ev* event) {
|
||||
ListenerResult handle(std::function<Callback> fn, Ev* event) {
|
||||
if (event->getID() == m_id) {
|
||||
return std::apply(fn, event->getArgs());
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../utils/casts.hpp"
|
||||
#include "../utils/MiniFunction.hpp"
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <type_traits>
|
||||
|
@ -52,6 +51,7 @@ namespace geode {
|
|||
|
||||
private:
|
||||
static DefaultEventListenerPool* create();
|
||||
DefaultEventListenerPool();
|
||||
|
||||
public:
|
||||
bool add(EventListenerProtocol* listener) override;
|
||||
|
@ -64,10 +64,7 @@ namespace geode {
|
|||
friend class DispatchEvent;
|
||||
|
||||
template <class... Args>
|
||||
friend class DispatchFilter;
|
||||
|
||||
// todo: make this private in Geode 4.0.0
|
||||
DefaultEventListenerPool();
|
||||
friend class DispatchFilter;
|
||||
};
|
||||
|
||||
class GEODE_DLL EventListenerProtocol {
|
||||
|
@ -103,7 +100,7 @@ namespace geode {
|
|||
using Callback = ListenerResult(T*);
|
||||
using Event = T;
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, T* e) {
|
||||
ListenerResult handle(std::function<Callback> fn, T* e) {
|
||||
return fn(e);
|
||||
}
|
||||
|
||||
|
@ -156,7 +153,7 @@ namespace geode {
|
|||
this->enable();
|
||||
}
|
||||
|
||||
EventListener(utils::MiniFunction<Callback> fn, T filter = T())
|
||||
EventListener(std::function<Callback> fn, T filter = T())
|
||||
: m_callback(fn), m_filter(filter)
|
||||
{
|
||||
m_filter.setListener(this);
|
||||
|
@ -193,10 +190,7 @@ namespace geode {
|
|||
this->enable();
|
||||
}
|
||||
|
||||
void bind(utils::MiniFunction<Callback> const& fn) {
|
||||
m_callback = fn;
|
||||
}
|
||||
void bind(utils::MiniFunction<Callback>&& fn) {
|
||||
void bind(std::function<Callback> fn) {
|
||||
m_callback = fn;
|
||||
}
|
||||
|
||||
|
@ -218,12 +212,12 @@ namespace geode {
|
|||
return m_filter;
|
||||
}
|
||||
|
||||
utils::MiniFunction<Callback>& getCallback() {
|
||||
std::function<Callback>& getCallback() {
|
||||
return m_callback;
|
||||
}
|
||||
|
||||
protected:
|
||||
utils::MiniFunction<Callback> m_callback = nullptr;
|
||||
std::function<Callback> m_callback = nullptr;
|
||||
T m_filter;
|
||||
};
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace geode::ipc {
|
|||
std::string m_messageID;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, IPCEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, IPCEvent* event);
|
||||
IPCFilter(
|
||||
std::string const& modID,
|
||||
std::string const& messageID
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include "../utils/Result.hpp"
|
||||
#include "../utils/MiniFunction.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "Log.hpp"
|
||||
#include "ModEvent.hpp"
|
||||
#include "ModMetadata.hpp"
|
||||
|
@ -15,7 +14,7 @@
|
|||
#include <string_view>
|
||||
|
||||
namespace geode {
|
||||
using ScheduledFunction = utils::MiniFunction<void()>;
|
||||
using ScheduledFunction = std::function<void()>;
|
||||
|
||||
struct InvalidGeodeFile {
|
||||
std::filesystem::path path;
|
||||
|
@ -47,6 +46,21 @@ namespace geode {
|
|||
Type type;
|
||||
std::variant<std::filesystem::path, ModMetadata, Mod*> cause;
|
||||
std::string message;
|
||||
|
||||
bool isSuggestion() const {
|
||||
return
|
||||
type == LoadProblem::Type::Recommendation ||
|
||||
type == LoadProblem::Type::Suggestion;
|
||||
}
|
||||
bool isOutdated() const {
|
||||
return
|
||||
type == LoadProblem::Type::UnsupportedVersion ||
|
||||
type == LoadProblem::Type::NeedsNewerGeodeVersion ||
|
||||
type == LoadProblem::Type::UnsupportedGeodeVersion;
|
||||
}
|
||||
bool isProblem() const {
|
||||
return !isSuggestion() && !isOutdated();
|
||||
}
|
||||
};
|
||||
|
||||
class LoaderImpl;
|
||||
|
@ -92,7 +106,8 @@ namespace geode {
|
|||
Mod* getLoadedMod(std::string const& id) const;
|
||||
std::vector<Mod*> getAllMods();
|
||||
std::vector<LoadProblem> getAllProblems() const;
|
||||
std::vector<LoadProblem> getProblems() const;
|
||||
std::vector<LoadProblem> getLoadProblems() const;
|
||||
std::vector<LoadProblem> getOutdated() const;
|
||||
std::vector<LoadProblem> getRecommendations() const;
|
||||
|
||||
/**
|
||||
|
@ -103,41 +118,36 @@ namespace geode {
|
|||
* 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;
|
||||
bool hasLaunchArgument(std::string_view name) const;
|
||||
/**
|
||||
* Get a launch argument. These are passed into the game as command-line arguments
|
||||
* with the format `--geode:arg-name=value`.
|
||||
* @param name The argument name
|
||||
* @return The value, if present
|
||||
*/
|
||||
std::optional<std::string> getLaunchArgument(std::string_view const name) const;
|
||||
std::optional<std::string> getLaunchArgument(std::string_view name) const;
|
||||
/**
|
||||
* Get a launch argument flag. Returns whether the argument is present and its
|
||||
* value is exactly `true`.
|
||||
* @param name The argument name
|
||||
*/
|
||||
bool getLaunchFlag(std::string_view const name) const;
|
||||
bool getLaunchFlag(std::string_view name) const;
|
||||
/**
|
||||
* Get and parse a launch argument value using the setting value system.
|
||||
* @param name The argument name
|
||||
*/
|
||||
template <class T>
|
||||
std::optional<T> parseLaunchArgument(std::string_view const name) const {
|
||||
Result<T> parseLaunchArgument(std::string_view name) const {
|
||||
auto str = this->getLaunchArgument(name);
|
||||
if (!str.has_value()) {
|
||||
return std::nullopt;
|
||||
return Err(fmt::format("Launch argument '{}' not found", name));
|
||||
}
|
||||
std::string parseError;
|
||||
auto jsonOpt = matjson::parse(str.value(), parseError);
|
||||
if (!jsonOpt.has_value()) {
|
||||
log::debug("Parsing launch argument '{}' failed: {}", name, parseError);
|
||||
return std::nullopt;
|
||||
auto jsonOpt = matjson::Value::parse(str.value());
|
||||
if (jsonOpt.isErr()) {
|
||||
return Err(fmt::format("Parsing launch argument '{}' failed: {}", name, jsonOpt.unwrapErr()));
|
||||
}
|
||||
auto value = jsonOpt.value();
|
||||
if (!value.is<T>()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return value.as<T>();
|
||||
auto value = jsonOpt.unwrap();
|
||||
return value.template as<T>();
|
||||
}
|
||||
|
||||
void queueInMainThread(ScheduledFunction&& func);
|
||||
|
|
|
@ -65,12 +65,6 @@ namespace std::filesystem {
|
|||
}
|
||||
}
|
||||
|
||||
namespace matjson {
|
||||
GEODE_INLINE GEODE_HIDDEN std::string format_as(matjson::Value const& value) {
|
||||
return value.dump(matjson::NO_INDENTATION);
|
||||
}
|
||||
}
|
||||
|
||||
namespace geode {
|
||||
|
||||
class Mod;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include "../cocos/support/zip_support/ZipUtils.h"
|
||||
#include "../utils/Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "../utils/VersionInfo.hpp"
|
||||
#include "../utils/general.hpp"
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
|||
#include "Hook.hpp"
|
||||
#include "ModMetadata.hpp"
|
||||
#include "Setting.hpp"
|
||||
#include "SettingV3.hpp"
|
||||
#include "Types.hpp"
|
||||
#include "Loader.hpp"
|
||||
|
||||
|
@ -23,9 +22,7 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace geode {
|
||||
class SettingV3;
|
||||
|
||||
namespace geode {
|
||||
template <class T>
|
||||
struct HandleToSaved : public T {
|
||||
Mod* m_mod;
|
||||
|
@ -54,22 +51,6 @@ namespace geode {
|
|||
return action == ModRequestedAction::Uninstall || action == ModRequestedAction::UninstallWithSaveData;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static consteval bool typeImplementsIsJSON() {
|
||||
using namespace matjson;
|
||||
if constexpr (requires(const Value& json) { Serialize<std::decay_t<T>>::is_json(json); })
|
||||
return true;
|
||||
if constexpr (std::is_same_v<T, Value>) return true;
|
||||
if constexpr (std::is_same_v<T, Array>) return true;
|
||||
if constexpr (std::is_same_v<T, Object>) return true;
|
||||
if constexpr (std::is_constructible_v<std::string, T>) return true;
|
||||
if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) return true;
|
||||
if constexpr (std::is_same_v<T, bool>) return true;
|
||||
if constexpr (std::is_same_v<T, std::nullptr_t>) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GEODE_HIDDEN Mod* takeNextLoaderMod();
|
||||
|
||||
class ModImpl;
|
||||
|
@ -108,8 +89,6 @@ namespace geode {
|
|||
|
||||
std::string getID() const;
|
||||
std::string getName() const;
|
||||
[[deprecated("Use Mod::getDevelopers instead")]]
|
||||
std::string getDeveloper() const;
|
||||
std::vector<std::string> getDevelopers() const;
|
||||
std::optional<std::string> getDescription() const;
|
||||
std::optional<std::string> getDetails() const;
|
||||
|
@ -137,17 +116,6 @@ namespace geode {
|
|||
std::vector<Mod*> getDependants() const;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Check if this Mod has updates available on the mods index. If
|
||||
* you're using this for automatic update checking, use
|
||||
* `openInfoPopup` or `openIndexPopup` from the `ui/GeodeUI.hpp`
|
||||
* header to open the Mod's page to let the user install the update
|
||||
* @returns The latest available version on the index if there are
|
||||
* updates for this mod
|
||||
*/
|
||||
[[deprecated("Use Mod::checkUpdates instead; this function always returns nullopt")]]
|
||||
std::optional<VersionInfo> hasAvailableUpdate() const;
|
||||
|
||||
using CheckUpdatesTask = Task<Result<std::optional<VersionInfo>, std::string>>;
|
||||
/**
|
||||
* Check if this Mod has updates available on the mods index. If
|
||||
|
@ -187,52 +155,15 @@ namespace geode {
|
|||
* declared in `mod.json`)
|
||||
*/
|
||||
std::vector<std::string> getSettingKeys() const;
|
||||
bool hasSetting(std::string_view const key) const;
|
||||
bool hasSetting(std::string_view key) const;
|
||||
|
||||
// todo in v4: remove these
|
||||
[[deprecated("Use Mod::getSettingV3")]]
|
||||
std::optional<Setting> getSettingDefinition(std::string_view const key) const;
|
||||
[[deprecated("Use Mod::getSettingV3")]]
|
||||
SettingValue* getSetting(std::string_view const key) const;
|
||||
|
||||
// todo in v4: possibly rename this to getSetting?
|
||||
/**
|
||||
* Get the definition of a setting, or null if the setting was not found,
|
||||
* or if it's a custom setting that has not yet been registered using
|
||||
* `Mod::registerCustomSettingType`
|
||||
* @param key The key of the setting as defined in `mod.json`
|
||||
*/
|
||||
std::shared_ptr<SettingV3> getSettingV3(std::string_view const key) const;
|
||||
|
||||
/**
|
||||
* Register a custom setting's value class. See Mod::addCustomSetting
|
||||
* for a convenience wrapper that creates the value in-place to avoid
|
||||
* code duplication. Also see
|
||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
|
||||
* more information about custom settings
|
||||
* @param key The setting's key
|
||||
* @param value The SettingValue class that shall handle this setting
|
||||
* @see addCustomSetting
|
||||
*/
|
||||
[[deprecated("Use Mod::registerCustomSettingType")]]
|
||||
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
|
||||
/**
|
||||
* Register a custom setting's value class. The new SettingValue class
|
||||
* will be created in-place using `std::make_unique`. See
|
||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
|
||||
* more information about custom settings
|
||||
* @param key The setting's key
|
||||
* @param value The value of the custom setting
|
||||
* @example
|
||||
* $on_mod(Loaded) {
|
||||
* Mod::get()->addCustomSetting<MySettingValue>("setting-key", DEFAULT_VALUE);
|
||||
* }
|
||||
*/
|
||||
template <class T, class V>
|
||||
[[deprecated("Use Mod::registerCustomSettingType")]]
|
||||
void addCustomSetting(std::string_view const key, V const& value) {
|
||||
this->registerCustomSetting(key, std::make_unique<T>(std::string(key), this->getID(), value));
|
||||
}
|
||||
std::shared_ptr<Setting> getSetting(std::string_view key) const;
|
||||
|
||||
/**
|
||||
* Register a custom setting type. See
|
||||
|
@ -248,7 +179,7 @@ namespace geode {
|
|||
* Returns a prefixed launch argument name. See `Mod::getLaunchArgument`
|
||||
* for details about mod-specific launch arguments.
|
||||
*/
|
||||
std::string getLaunchArgumentName(std::string_view const name) const;
|
||||
std::string getLaunchArgumentName(std::string_view name) const;
|
||||
/**
|
||||
* Returns the names of the available mod-specific launch arguments.
|
||||
*/
|
||||
|
@ -258,7 +189,7 @@ namespace geode {
|
|||
* for details about mod-specific launch arguments.
|
||||
* @param name The argument name
|
||||
*/
|
||||
bool hasLaunchArgument(std::string_view const name) const;
|
||||
bool hasLaunchArgument(std::string_view 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
|
||||
|
@ -266,20 +197,20 @@ namespace geode {
|
|||
* @param name The argument name
|
||||
* @return The value, if present
|
||||
*/
|
||||
std::optional<std::string> getLaunchArgument(std::string_view const name) const;
|
||||
std::optional<std::string> getLaunchArgument(std::string_view name) const;
|
||||
/**
|
||||
* Equivalent to a prefixed `Loader::getLaunchFlag` call. See `Mod::getLaunchArgument`
|
||||
* for details about mod-specific launch arguments.
|
||||
* @param name The argument name
|
||||
*/
|
||||
bool getLaunchFlag(std::string_view const name) const;
|
||||
bool getLaunchFlag(std::string_view name) const;
|
||||
/**
|
||||
* Equivalent to a prefixed `Loader::parseLaunchArgument` call. See `Mod::getLaunchArgument`
|
||||
* for details about mod-specific launch arguments.
|
||||
* @param name The argument name
|
||||
*/
|
||||
template <class T>
|
||||
std::optional<T> parseLaunchArgument(std::string_view const name) const {
|
||||
std::optional<T> parseLaunchArgument(std::string_view name) const {
|
||||
return Loader::get()->parseLaunchArgument<T>(this->getLaunchArgumentName(name));
|
||||
}
|
||||
|
||||
|
@ -293,18 +224,18 @@ namespace geode {
|
|||
* setting type has a `getValue` function which returns the value
|
||||
*/
|
||||
template <class T>
|
||||
T getSettingValue(std::string_view const key) const {
|
||||
T getSettingValue(std::string_view key) const {
|
||||
using S = typename SettingTypeForValueType<T>::SettingType;
|
||||
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSettingV3(key))) {
|
||||
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSetting(key))) {
|
||||
return sett->getValue();
|
||||
}
|
||||
return T();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T setSettingValue(std::string_view const key, T const& value) {
|
||||
T setSettingValue(std::string_view key, T const& value) {
|
||||
using S = typename SettingTypeForValueType<T>::SettingType;
|
||||
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSettingV3(key))) {
|
||||
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSetting(key))) {
|
||||
auto old = sett->getValue();
|
||||
sett->setValue(value);
|
||||
return old;
|
||||
|
@ -312,30 +243,28 @@ namespace geode {
|
|||
return T();
|
||||
}
|
||||
|
||||
bool hasSavedValue(std::string_view const key);
|
||||
bool hasSavedValue(std::string_view key);
|
||||
|
||||
template <class T>
|
||||
T getSavedValue(std::string_view const key) {
|
||||
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
|
||||
T getSavedValue(std::string_view key) {
|
||||
auto& saved = this->getSaveContainer();
|
||||
if (saved.contains(key)) {
|
||||
if (auto value = saved.try_get<T>(key)) {
|
||||
return *value;
|
||||
}
|
||||
if (auto res = saved.get(key).andThen([](auto&& v) {
|
||||
return v.template as<T>();
|
||||
}); res.isOk()) {
|
||||
return res.unwrap();
|
||||
}
|
||||
return T();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T getSavedValue(std::string_view const key, T const& defaultValue) {
|
||||
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
|
||||
T getSavedValue(std::string_view key, T const& defaultValue) {
|
||||
auto& saved = this->getSaveContainer();
|
||||
if (saved.contains(key)) {
|
||||
if (auto value = saved.try_get<T>(key)) {
|
||||
return *value;
|
||||
}
|
||||
if (auto res = saved.get(key).andThen([](auto&& v) {
|
||||
return v.template as<T>();
|
||||
}); res.isOk()) {
|
||||
return res.unwrap();
|
||||
}
|
||||
saved[key] = defaultValue;
|
||||
saved[key] = matjson::Value(defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
@ -347,7 +276,7 @@ namespace geode {
|
|||
* @returns The old value
|
||||
*/
|
||||
template <class T>
|
||||
T setSavedValue(std::string_view const key, T const& value) {
|
||||
T setSavedValue(std::string_view key, T const& value) {
|
||||
auto& saved = this->getSaveContainer();
|
||||
auto old = this->getSavedValue<T>(key);
|
||||
saved[key] = value;
|
||||
|
@ -488,7 +417,7 @@ namespace geode {
|
|||
* Check whether or not this Mod
|
||||
* depends on another mod
|
||||
*/
|
||||
bool depends(std::string_view const id) const;
|
||||
bool depends(std::string_view id) const;
|
||||
|
||||
/**
|
||||
* Check whether all the required
|
||||
|
@ -517,7 +446,17 @@ namespace geode {
|
|||
bool isLoggingEnabled() const;
|
||||
void setLoggingEnabled(bool enabled);
|
||||
|
||||
bool hasProblems() const;
|
||||
/**
|
||||
* If this mod is built for an outdated GD or Geode version, returns the
|
||||
* `LoadProblem` describing the situation. Otherwise `nullopt` if the
|
||||
* mod is made for the correct version of the game and Geode
|
||||
*/
|
||||
std::optional<LoadProblem> targetsOutdatedVersion() const;
|
||||
/**
|
||||
* @note Make sure to also call `targetsOutdatedVersion` if you want to
|
||||
* make sure the mod is actually loadable
|
||||
*/
|
||||
bool hasLoadProblems() const;
|
||||
std::vector<LoadProblem> getAllProblems() const;
|
||||
std::vector<LoadProblem> getProblems() const;
|
||||
std::vector<LoadProblem> getRecommendations() const;
|
||||
|
|
|
@ -10,9 +10,6 @@ namespace geode {
|
|||
|
||||
enum class ModEventType {
|
||||
Loaded,
|
||||
Unloaded,
|
||||
Enabled,
|
||||
Disabled,
|
||||
DataLoaded,
|
||||
DataSaved,
|
||||
};
|
||||
|
@ -43,7 +40,7 @@ namespace geode {
|
|||
Mod* m_mod;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, ModStateEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, ModStateEvent* event);
|
||||
|
||||
/**
|
||||
* Create a mod state listener
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../utils/Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "../utils/VersionInfo.hpp"
|
||||
#include "Setting.hpp"
|
||||
#include "Types.hpp"
|
||||
|
||||
#include <matjson.hpp>
|
||||
|
@ -123,13 +122,6 @@ namespace geode {
|
|||
* character set.
|
||||
*/
|
||||
[[nodiscard]] std::string getName() const;
|
||||
/**
|
||||
* The name of the head developer.
|
||||
* If the mod has multiple * developers, this will return the first
|
||||
* developer in the list.
|
||||
*/
|
||||
[[nodiscard, deprecated("Use ModMetadata::getDevelopers() instead")]]
|
||||
std::string getDeveloper() const;
|
||||
/**
|
||||
* The developers of this mod
|
||||
*/
|
||||
|
@ -155,11 +147,6 @@ namespace geode {
|
|||
* (see MDTextArea for more info)
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::string> getSupportInfo() const;
|
||||
/**
|
||||
* Git Repository of the mod
|
||||
*/
|
||||
[[nodiscard, deprecated("Use ModMetadata::getLinks instead")]]
|
||||
std::optional<std::string> getRepository() const;
|
||||
/**
|
||||
* Get the links (related websites / servers / etc.) for this mod
|
||||
*/
|
||||
|
@ -184,12 +171,7 @@ namespace geode {
|
|||
* Mod settings
|
||||
* @note Not a map because insertion order must be preserved
|
||||
*/
|
||||
[[nodiscard, deprecated("Use getSettingsV3")]] std::vector<std::pair<std::string, Setting>> getSettings() const;
|
||||
/**
|
||||
* Mod settings
|
||||
* @note Not a map because insertion order must be preserved
|
||||
*/
|
||||
[[nodiscard]] std::vector<std::pair<std::string, matjson::Value>> getSettingsV3() const;
|
||||
[[nodiscard]] std::vector<std::pair<std::string, matjson::Value>> getSettings() const;
|
||||
/**
|
||||
* Get the tags for this mod
|
||||
*/
|
||||
|
@ -237,8 +219,6 @@ namespace geode {
|
|||
void setDependencies(std::vector<Dependency> const& value);
|
||||
void setIncompatibilities(std::vector<Incompatibility> const& value);
|
||||
void setSpritesheets(std::vector<std::string> const& value);
|
||||
[[deprecated("This function does NOTHING")]]
|
||||
void setSettings(std::vector<std::pair<std::string, Setting>> const& value);
|
||||
void setSettings(std::vector<std::pair<std::string, matjson::Value>> const& value);
|
||||
void setTags(std::unordered_set<std::string> const& value);
|
||||
void setNeedsEarlyLoad(bool const& value);
|
||||
|
@ -310,7 +290,8 @@ namespace geode {
|
|||
|
||||
template <>
|
||||
struct matjson::Serialize<geode::ModMetadata> {
|
||||
static matjson::Value to_json(geode::ModMetadata const& info) {
|
||||
return info.toJSON();
|
||||
static Value toJson(geode::ModMetadata const& value)
|
||||
{
|
||||
return Value(value.toJSON());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include "SettingV3.hpp"
|
||||
#include "Setting.hpp"
|
||||
|
||||
namespace geode {
|
||||
class Mod;
|
||||
|
@ -57,12 +57,8 @@ namespace geode {
|
|||
matjson::Value& getSaveData();
|
||||
|
||||
Result<> registerCustomSettingType(std::string_view type, SettingGenerator generator);
|
||||
// todo in v4: remove this
|
||||
Result<> registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr);
|
||||
|
||||
std::shared_ptr<SettingV3> get(std::string_view key);
|
||||
std::shared_ptr<SettingValue> getLegacy(std::string_view key);
|
||||
std::optional<Setting> getLegacyDefinition(std::string_view key);
|
||||
std::shared_ptr<Setting> get(std::string_view key);
|
||||
|
||||
/**
|
||||
* Returns true if any setting with the `"restart-required"` attribute
|
||||
|
|
|
@ -1,324 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "Types.hpp"
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include "../utils/Result.hpp"
|
||||
#include "../utils/file.hpp"
|
||||
#include <matjson.hpp>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
#include <cocos2d.h>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4275)
|
||||
|
||||
namespace geode {
|
||||
class SettingNode;
|
||||
class SettingValue;
|
||||
|
||||
struct JsonMaybeObject;
|
||||
struct JsonMaybeValue;
|
||||
|
||||
/**
|
||||
* A Setting for a boolean value. Represented in-game as a simple toggle
|
||||
*/
|
||||
struct GEODE_DLL BoolSetting final {
|
||||
using ValueType = bool;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
bool defaultValue;
|
||||
|
||||
static Result<BoolSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for an integer value. The value can be limited using the min
|
||||
* and max options
|
||||
*/
|
||||
struct GEODE_DLL IntSetting final {
|
||||
using ValueType = int64_t;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
std::optional<ValueType> min;
|
||||
std::optional<ValueType> max;
|
||||
struct {
|
||||
bool arrows = true;
|
||||
bool bigArrows = false;
|
||||
size_t arrowStep = 1;
|
||||
size_t bigArrowStep = 5;
|
||||
bool slider = true;
|
||||
std::optional<ValueType> sliderStep = std::nullopt;
|
||||
bool input = true;
|
||||
} controls;
|
||||
|
||||
static Result<IntSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for a float value. The value can be limited using the min
|
||||
* and max options
|
||||
*/
|
||||
struct GEODE_DLL FloatSetting final {
|
||||
using ValueType = double;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
std::optional<ValueType> min;
|
||||
std::optional<ValueType> max;
|
||||
struct {
|
||||
bool arrows = true;
|
||||
bool bigArrows = false;
|
||||
size_t arrowStep = 1;
|
||||
size_t bigArrowStep = 5;
|
||||
bool slider = true;
|
||||
std::optional<ValueType> sliderStep = std::nullopt;
|
||||
bool input = true;
|
||||
} controls;
|
||||
|
||||
static Result<FloatSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for a string value
|
||||
*/
|
||||
struct GEODE_DLL StringSetting final {
|
||||
using ValueType = std::string;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
struct Data {
|
||||
/**
|
||||
* A regex the string must successfully match against
|
||||
*/
|
||||
std::optional<std::string> match;
|
||||
|
||||
/**
|
||||
* The CCTextInputNode's allowed character filter
|
||||
*/
|
||||
std::optional<std::string> filter;
|
||||
|
||||
/**
|
||||
* A list of options the user can choose from
|
||||
*/
|
||||
std::optional<std::vector<std::string>> options;
|
||||
};
|
||||
std::unique_ptr<Data> controls;
|
||||
|
||||
std::array<uint8_t, sizeof(Data::match) + sizeof(Data::filter) - sizeof(controls)> m_padding;
|
||||
|
||||
StringSetting();
|
||||
StringSetting(StringSetting const& other);
|
||||
StringSetting(StringSetting&& other) noexcept;
|
||||
StringSetting& operator=(StringSetting const& other);
|
||||
StringSetting& operator=(StringSetting&& other) noexcept;
|
||||
~StringSetting();
|
||||
|
||||
|
||||
static Result<StringSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for a file input. Lets the user select a file from their
|
||||
* local device
|
||||
*/
|
||||
struct GEODE_DLL FileSetting final {
|
||||
using ValueType = std::filesystem::path;
|
||||
using Filter = utils::file::FilePickOptions::Filter;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
struct {
|
||||
std::vector<Filter> filters;
|
||||
} controls;
|
||||
|
||||
static Result<FileSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for an RGB color. See ColorAlphaSetting for a setting that
|
||||
* also allows customizing alpha
|
||||
*/
|
||||
struct GEODE_DLL ColorSetting final {
|
||||
using ValueType = cocos2d::ccColor3B;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
|
||||
static Result<ColorSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for an RGBA color. See ColorSetting for a setting that doesn't
|
||||
* have alpha
|
||||
*/
|
||||
struct GEODE_DLL ColorAlphaSetting final {
|
||||
using ValueType = cocos2d::ccColor4B;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
|
||||
static Result<ColorAlphaSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A custom setting, defined by the mod. See
|
||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
|
||||
* information about how to create custom settings
|
||||
*/
|
||||
struct GEODE_DLL CustomSetting final {
|
||||
std::shared_ptr<ModJson> json;
|
||||
};
|
||||
|
||||
using SettingKind = std::variant<
|
||||
BoolSetting,
|
||||
IntSetting,
|
||||
FloatSetting,
|
||||
StringSetting,
|
||||
FileSetting,
|
||||
ColorSetting,
|
||||
ColorAlphaSetting,
|
||||
CustomSetting
|
||||
>;
|
||||
|
||||
/**
|
||||
* Represents a saved value for a mod that can be customized by the user
|
||||
* through an in-game UI. This class is for modeling the setting's
|
||||
* definition - what values are accepted, its name etc.
|
||||
* See [the tutorial page](https://docs.geode-sdk.org/mods/settings)
|
||||
* for more information about how settings work
|
||||
* @see SettingValue
|
||||
* @see SettingNode
|
||||
*/
|
||||
struct GEODE_DLL Setting final {
|
||||
private:
|
||||
std::string m_key;
|
||||
std::string m_modID;
|
||||
SettingKind m_kind;
|
||||
|
||||
Setting() = default;
|
||||
|
||||
public:
|
||||
static Result<Setting> parse(
|
||||
std::string const& key,
|
||||
std::string const& mod,
|
||||
JsonMaybeValue& obj
|
||||
);
|
||||
Setting(
|
||||
std::string const& key,
|
||||
std::string const& mod,
|
||||
SettingKind const& kind
|
||||
);
|
||||
|
||||
template<class T>
|
||||
std::optional<T> get() {
|
||||
if (std::holds_alternative<T>(m_kind)) {
|
||||
return std::get<T>(m_kind);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<SettingValue> createDefaultValue() const;
|
||||
bool isCustom() const;
|
||||
std::string getDisplayName() const;
|
||||
std::optional<std::string> getDescription() const;
|
||||
std::string getModID() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores the actual, current value of a Setting. See
|
||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
|
||||
* information, and how to create custom settings
|
||||
*/
|
||||
class GEODE_DLL SettingValue {
|
||||
protected:
|
||||
std::string m_key;
|
||||
std::string m_modID;
|
||||
|
||||
SettingValue(std::string const& key, std::string const& mod);
|
||||
|
||||
void valueChanged();
|
||||
|
||||
public:
|
||||
virtual ~SettingValue() = default;
|
||||
virtual bool load(matjson::Value const& json) = 0;
|
||||
virtual bool save(matjson::Value& json) const = 0;
|
||||
virtual SettingNode* createNode(float width) = 0;
|
||||
|
||||
std::string getKey() const;
|
||||
std::string getModID() const;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class GeodeSettingValue final : public SettingValue {
|
||||
public:
|
||||
using ValueType = typename T::ValueType;
|
||||
|
||||
protected:
|
||||
ValueType m_value;
|
||||
T m_definition;
|
||||
|
||||
using Valid = std::pair<ValueType, std::optional<std::string>>;
|
||||
|
||||
GEODE_DLL Valid toValid(ValueType const& value) const;
|
||||
|
||||
public:
|
||||
GeodeSettingValue(std::string const& key, std::string const& modID, T const& definition)
|
||||
: SettingValue(key, modID),
|
||||
m_definition(definition),
|
||||
m_value(definition.defaultValue) {}
|
||||
|
||||
bool load(matjson::Value const& json) override;
|
||||
bool save(matjson::Value& json) const;
|
||||
|
||||
GEODE_DLL SettingNode* createNode(float width) override;
|
||||
T castDefinition() const {
|
||||
return m_definition;
|
||||
}
|
||||
Setting getDefinition() const {
|
||||
return Setting(m_key, m_modID, m_definition);
|
||||
}
|
||||
|
||||
GEODE_DLL ValueType getValue() const;
|
||||
GEODE_DLL void setValue(ValueType const& value);
|
||||
GEODE_DLL Result<> validate(ValueType const& value) const;
|
||||
};
|
||||
|
||||
using BoolSettingValue = GeodeSettingValue<BoolSetting>;
|
||||
using IntSettingValue = GeodeSettingValue<IntSetting>;
|
||||
using FloatSettingValue = GeodeSettingValue<FloatSetting>;
|
||||
using StringSettingValue = GeodeSettingValue<StringSetting>;
|
||||
using FileSettingValue = GeodeSettingValue<FileSetting>;
|
||||
using ColorSettingValue = GeodeSettingValue<ColorSetting>;
|
||||
using ColorAlphaSettingValue = GeodeSettingValue<ColorAlphaSetting>;
|
||||
|
||||
// todo: remove in v3
|
||||
|
||||
template<class T>
|
||||
struct [[deprecated("Use SettingTypeForValueType from SettingV3 instead")]] GEODE_DLL SettingValueSetter {
|
||||
static T get(SettingValue* setting);
|
||||
static void set(SettingValue* setting, T const& value);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
bool GeodeSettingValue<T>::load(matjson::Value const& json) {
|
||||
if (!json.is<ValueType>()) return false;
|
||||
m_value = json.as<ValueType>();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool GeodeSettingValue<T>::save(matjson::Value& json) const {
|
||||
json = m_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
#pragma once
|
||||
|
||||
#include "SettingV3.hpp"
|
||||
|
||||
namespace geode {
|
||||
using Setting = SettingV3;
|
||||
using SettingGenerator = SettingGeneratorV3;
|
||||
|
||||
template <class T, class V = T>
|
||||
using SettingBaseValue = SettingBaseValueV3<T, V>;
|
||||
|
||||
using TitleSetting = TitleSettingV3;
|
||||
using BoolSetting = BoolSettingV3;
|
||||
using IntSetting = IntSettingV3;
|
||||
using FloatSetting = FloatSettingV3;
|
||||
using StringSetting = StringSettingV3;
|
||||
using FileSetting = FileSettingV3;
|
||||
using Color3BSetting = Color3BSettingV3;
|
||||
using Color4BSetting = Color4BSettingV3;
|
||||
|
||||
using SettingNode = SettingNodeV3;
|
||||
template <class S>
|
||||
using SettingValueNode = SettingValueNodeV3<S>;
|
||||
|
||||
using SettingChangedEvent = SettingChangedEventV3;
|
||||
using SettingChangedFilter = SettingChangedFilterV3;
|
||||
using SettingNodeSizeChangeEvent = SettingNodeSizeChangeEventV3;
|
||||
using SettingNodeValueChangeEvent = SettingNodeValueChangeEventV3;
|
||||
|
||||
template <class T, class Lambda>
|
||||
EventListener<SettingChangedFilter>* listenForSettingChanges(std::string_view settingKey, Lambda&& callback, Mod* mod = getMod()) {
|
||||
return listenForSettingChangesV3<T>(settingKey, std::forward<Lambda>(callback), mod);
|
||||
}
|
||||
|
||||
template <class Lambda>
|
||||
EventListener<SettingChangedFilter>* listenForSettingChanges(std::string_view settingKey, Lambda&& callback, Mod* mod = getMod()) {
|
||||
return listenForSettingChangesV3(settingKey, std::forward<Lambda>(callback), mod);
|
||||
}
|
||||
|
||||
inline EventListener<SettingChangedFilter>* listenForAllSettingChanges(
|
||||
std::function<void(std::shared_ptr<SettingV3>)> const& callback,
|
||||
Mod* mod = getMod()
|
||||
) {
|
||||
return listenForAllSettingChangesV3(callback, mod);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "Event.hpp"
|
||||
#include "Loader.hpp"
|
||||
#include "Setting.hpp"
|
||||
#include "Mod.hpp"
|
||||
#include "SettingV3.hpp"
|
||||
#include <optional>
|
||||
|
||||
namespace geode {
|
||||
struct GEODE_DLL [[deprecated("Use SettingChangedEventV3 from SettingV3.hpp instead")]] SettingChangedEvent : public Event {
|
||||
Mod* mod;
|
||||
SettingValue* value;
|
||||
|
||||
SettingChangedEvent(Mod* mod, SettingValue* value);
|
||||
};
|
||||
|
||||
class GEODE_DLL [[deprecated("Use SettingChangedEventV3 from SettingV3.hpp instead")]] SettingChangedFilter : public EventFilter<SettingChangedEvent> {
|
||||
protected:
|
||||
std::string m_modID;
|
||||
std::optional<std::string> m_targetKey;
|
||||
|
||||
public:
|
||||
using Callback = void(SettingValue*);
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, SettingChangedEvent* event);
|
||||
/**
|
||||
* Listen to changes on a setting, or all settings
|
||||
* @param modID Mod whose settings to listen to
|
||||
* @param settingID Setting to listen to, or all settings if nullopt
|
||||
*/
|
||||
SettingChangedFilter(
|
||||
std::string const& modID,
|
||||
std::optional<std::string> const& settingKey
|
||||
);
|
||||
SettingChangedFilter(SettingChangedFilter const&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for built-in setting changes
|
||||
*/
|
||||
template<class T>
|
||||
class [[deprecated("Use SettingChangedEventV3 from SettingV3.hpp instead")]] GeodeSettingChangedFilter : public SettingChangedFilter {
|
||||
public:
|
||||
using Callback = void(T);
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, SettingChangedEvent* event) {
|
||||
if (
|
||||
m_modID == event->mod->getID() &&
|
||||
(!m_targetKey || m_targetKey.value() == event->value->getKey())
|
||||
) {
|
||||
fn(SettingValueSetter<T>::get(event->value));
|
||||
}
|
||||
return ListenerResult::Propagate;
|
||||
}
|
||||
|
||||
GeodeSettingChangedFilter(
|
||||
std::string const& modID,
|
||||
std::string const& settingID
|
||||
) : SettingChangedFilter(modID, settingID) {}
|
||||
GeodeSettingChangedFilter(GeodeSettingChangedFilter const&) = default;
|
||||
};
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "Setting.hpp"
|
||||
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
class SettingNode;
|
||||
|
||||
struct SettingNodeDelegate {
|
||||
virtual void settingValueChanged(SettingNode* node) {}
|
||||
virtual void settingValueCommitted(SettingNode* node) {}
|
||||
};
|
||||
|
||||
class GEODE_DLL SettingNode : public cocos2d::CCNode {
|
||||
protected:
|
||||
SettingValue* m_value;
|
||||
SettingNodeDelegate* m_delegate = nullptr;
|
||||
|
||||
bool init(SettingValue* value);
|
||||
void dispatchChanged();
|
||||
void dispatchCommitted();
|
||||
|
||||
public:
|
||||
void setDelegate(SettingNodeDelegate* delegate);
|
||||
|
||||
virtual void commit() = 0;
|
||||
virtual bool hasUncommittedChanges() = 0;
|
||||
virtual bool hasNonDefaultValue() = 0;
|
||||
virtual void resetToDefault() = 0;
|
||||
};
|
||||
}
|
|
@ -3,22 +3,17 @@
|
|||
#include "../DefaultInclude.hpp"
|
||||
#include <optional>
|
||||
#include <cocos2d.h>
|
||||
// todo: remove this header in 4.0.0
|
||||
#include "Setting.hpp"
|
||||
#include "../utils/cocos.hpp"
|
||||
#include "../utils/file.hpp"
|
||||
// this unfortunately has to be included because of C++ templates
|
||||
#include "../utils/JsonValidation.hpp"
|
||||
#include "../utils/function.hpp"
|
||||
|
||||
// todo in v4: this can be removed as well as the friend decl in LegacyCustomSettingV3
|
||||
class LegacyCustomSettingToV3Node;
|
||||
class ModSettingsPopup;
|
||||
|
||||
namespace geode {
|
||||
class ModSettingsManager;
|
||||
class SettingNodeV3;
|
||||
// todo in v4: remove this
|
||||
class SettingValue;
|
||||
|
||||
class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> {
|
||||
private:
|
||||
|
@ -125,8 +120,6 @@ namespace geode {
|
|||
*/
|
||||
void markChanged();
|
||||
|
||||
friend class ::geode::SettingValue;
|
||||
|
||||
public:
|
||||
SettingV3();
|
||||
virtual ~SettingV3();
|
||||
|
@ -183,20 +176,9 @@ namespace geode {
|
|||
* Reset this setting's value back to its original value
|
||||
*/
|
||||
virtual void reset() = 0;
|
||||
|
||||
[[deprecated(
|
||||
"This function will be removed alongside legacy settings in 4.0.0! "
|
||||
"You should NOT be implementing it for your own custom setting classes"
|
||||
)]]
|
||||
virtual std::optional<Setting> convertToLegacy() const;
|
||||
[[deprecated(
|
||||
"This function will be removed alongside legacy settings in 4.0.0! "
|
||||
"You should NOT be implementing it for your own custom setting classes"
|
||||
)]]
|
||||
virtual std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const;
|
||||
};
|
||||
|
||||
using SettingGenerator = std::function<Result<std::shared_ptr<SettingV3>>(
|
||||
using SettingGeneratorV3 = std::function<Result<std::shared_ptr<SettingV3>>(
|
||||
std::string const& key,
|
||||
std::string const& modID,
|
||||
matjson::Value const& json
|
||||
|
@ -324,14 +306,15 @@ namespace geode {
|
|||
}
|
||||
|
||||
bool load(matjson::Value const& json) override {
|
||||
if (json.is<T>()) {
|
||||
m_impl->value = json.as<T>();
|
||||
return true;
|
||||
auto res = json.as<T>();
|
||||
if (res.isErr()) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
m_impl->value = res.unwrap();
|
||||
return true;
|
||||
}
|
||||
bool save(matjson::Value& json) const override {
|
||||
json = m_impl->value;
|
||||
json = matjson::Value(m_impl->value);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -357,37 +340,6 @@ namespace geode {
|
|||
void reset() override;
|
||||
};
|
||||
|
||||
// todo in v4: remove this class completely
|
||||
class GEODE_DLL LegacyCustomSettingV3 final : public SettingV3 {
|
||||
private:
|
||||
class Impl;
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
||||
friend class ::geode::ModSettingsManager;
|
||||
friend class ::LegacyCustomSettingToV3Node;
|
||||
|
||||
private:
|
||||
class PrivateMarker {};
|
||||
friend class SettingV3;
|
||||
|
||||
public:
|
||||
LegacyCustomSettingV3(PrivateMarker);
|
||||
static Result<std::shared_ptr<LegacyCustomSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
|
||||
|
||||
std::shared_ptr<SettingValue> getValue() const;
|
||||
void setValue(std::shared_ptr<SettingValue> value);
|
||||
|
||||
bool load(matjson::Value const& json) override;
|
||||
bool save(matjson::Value& json) const override;
|
||||
SettingNodeV3* createNode(float width) override;
|
||||
|
||||
bool isDefaultValue() const override;
|
||||
void reset() override;
|
||||
|
||||
std::optional<Setting> convertToLegacy() const override;
|
||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
||||
};
|
||||
|
||||
class GEODE_DLL BoolSettingV3 final : public SettingBaseValueV3<bool> {
|
||||
private:
|
||||
class Impl;
|
||||
|
@ -404,9 +356,6 @@ namespace geode {
|
|||
Result<> isValid(bool value) const override;
|
||||
|
||||
SettingNodeV3* createNode(float width) override;
|
||||
|
||||
std::optional<Setting> convertToLegacy() const override;
|
||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
||||
};
|
||||
|
||||
class GEODE_DLL IntSettingV3 final : public SettingBaseValueV3<int64_t> {
|
||||
|
@ -436,9 +385,6 @@ namespace geode {
|
|||
bool isInputEnabled() const;
|
||||
|
||||
SettingNodeV3* createNode(float width) override;
|
||||
|
||||
std::optional<Setting> convertToLegacy() const override;
|
||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
||||
};
|
||||
|
||||
class GEODE_DLL FloatSettingV3 final : public SettingBaseValueV3<double> {
|
||||
|
@ -468,9 +414,6 @@ namespace geode {
|
|||
bool isInputEnabled() const;
|
||||
|
||||
SettingNodeV3* createNode(float width) override;
|
||||
|
||||
std::optional<Setting> convertToLegacy() const override;
|
||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
||||
};
|
||||
|
||||
class GEODE_DLL StringSettingV3 final : public SettingBaseValueV3<std::string, std::string_view> {
|
||||
|
@ -493,9 +436,6 @@ namespace geode {
|
|||
std::optional<std::vector<std::string>> getEnumOptions() const;
|
||||
|
||||
SettingNodeV3* createNode(float width) override;
|
||||
|
||||
std::optional<Setting> convertToLegacy() const override;
|
||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
||||
};
|
||||
|
||||
class GEODE_DLL FileSettingV3 final : public SettingBaseValueV3<std::filesystem::path, std::filesystem::path const&> {
|
||||
|
@ -519,9 +459,6 @@ namespace geode {
|
|||
std::optional<std::vector<utils::file::FilePickOptions::Filter>> getFilters() const;
|
||||
|
||||
SettingNodeV3* createNode(float width) override;
|
||||
|
||||
std::optional<Setting> convertToLegacy() const override;
|
||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
||||
};
|
||||
|
||||
class GEODE_DLL Color3BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor3B> {
|
||||
|
@ -540,9 +477,6 @@ namespace geode {
|
|||
Result<> isValid(cocos2d::ccColor3B value) const override;
|
||||
|
||||
SettingNodeV3* createNode(float width) override;
|
||||
|
||||
std::optional<Setting> convertToLegacy() const override;
|
||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
||||
};
|
||||
|
||||
class GEODE_DLL Color4BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor4B> {
|
||||
|
@ -561,9 +495,6 @@ namespace geode {
|
|||
Result<> isValid(cocos2d::ccColor4B value) const override;
|
||||
|
||||
SettingNodeV3* createNode(float width) override;
|
||||
|
||||
std::optional<Setting> convertToLegacy() const override;
|
||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
||||
};
|
||||
|
||||
class GEODE_DLL SettingNodeV3 : public cocos2d::CCNode {
|
||||
|
@ -724,7 +655,7 @@ namespace geode {
|
|||
public:
|
||||
using Callback = void(std::shared_ptr<SettingV3>);
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, SettingChangedEventV3* event);
|
||||
ListenerResult handle(std::function<Callback> fn, SettingChangedEventV3* event);
|
||||
/**
|
||||
* Listen to changes on a setting, or all settings
|
||||
* @param modID Mod whose settings to listen to
|
||||
|
@ -800,7 +731,7 @@ namespace geode {
|
|||
};
|
||||
|
||||
template <class T>
|
||||
EventListener<SettingChangedFilterV3>* listenForSettingChanges(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
|
||||
EventListener<SettingChangedFilterV3>* listenForSettingChangesV3(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
|
||||
using Ty = typename SettingTypeForValueType<T>::SettingType;
|
||||
return new EventListener(
|
||||
[callback = std::move(callback)](std::shared_ptr<SettingV3> setting) {
|
||||
|
@ -811,11 +742,11 @@ namespace geode {
|
|||
SettingChangedFilterV3(mod, std::string(settingKey))
|
||||
);
|
||||
}
|
||||
EventListener<SettingChangedFilterV3>* listenForSettingChanges(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
|
||||
EventListener<SettingChangedFilterV3>* listenForSettingChangesV3(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
|
||||
using T = std::remove_cvref_t<utils::function::Arg<0, decltype(callback)>>;
|
||||
return listenForSettingChanges<T>(settingKey, std::move(callback), mod);
|
||||
return listenForSettingChangesV3<T>(settingKey, std::move(callback), mod);
|
||||
}
|
||||
GEODE_DLL EventListener<SettingChangedFilterV3>* listenForAllSettingChanges(
|
||||
GEODE_DLL EventListener<SettingChangedFilterV3>* listenForAllSettingChangesV3(
|
||||
std::function<void(std::shared_ptr<SettingV3>)> const& callback,
|
||||
Mod* mod = getMod()
|
||||
);
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
#include <Geode/platform/platform.hpp>
|
||||
#include <tulip/TulipHook.hpp>
|
||||
#include "../Prelude.hpp"
|
||||
|
||||
namespace geode::hook {
|
||||
/**
|
||||
* Create a calling convention wrapper for a function.
|
||||
*/
|
||||
GEODE_DLL tulip::hook::Result<void*> createWrapper(
|
||||
GEODE_DLL Result<void*> createWrapper(
|
||||
void* address,
|
||||
tulip::hook::WrapperMetadata const& metadata
|
||||
) noexcept;
|
||||
|
|
|
@ -89,14 +89,11 @@ namespace geode {
|
|||
constexpr std::string_view GEODE_MOD_EXTENSION = ".geode";
|
||||
|
||||
class Mod;
|
||||
class Setting;
|
||||
class Loader;
|
||||
class Hook;
|
||||
class VersionInfo;
|
||||
|
||||
class Unknown;
|
||||
using unknownmemfn_t = void (Unknown::*)();
|
||||
using unknownfn_t = void (*)();
|
||||
|
||||
namespace modifier {
|
||||
template <class, class>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "../utils/MiniFunction.hpp"
|
||||
#include "Traits.hpp"
|
||||
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
@ -20,7 +19,7 @@ namespace geode::modifier {
|
|||
class FieldContainer {
|
||||
private:
|
||||
std::vector<void*> m_containedFields;
|
||||
std::vector<utils::MiniFunction<void(void*)>> m_destructorFunctions;
|
||||
std::vector<std::function<void(void*)>> m_destructorFunctions;
|
||||
|
||||
public:
|
||||
~FieldContainer() {
|
||||
|
@ -40,9 +39,9 @@ namespace geode::modifier {
|
|||
return m_containedFields.at(index);
|
||||
}
|
||||
|
||||
void* setField(size_t index, size_t size, utils::MiniFunction<void(void*)> destructor) {
|
||||
void* setField(size_t index, size_t size, std::function<void(void*)> destructor) {
|
||||
m_containedFields.at(index) = operator new(size);
|
||||
m_destructorFunctions.at(index) = destructor;
|
||||
m_destructorFunctions.at(index) = std::move(destructor);
|
||||
return m_containedFields.at(index);
|
||||
}
|
||||
|
||||
|
|
|
@ -104,8 +104,36 @@
|
|||
} \
|
||||
} while (0);
|
||||
|
||||
namespace geode::modifier {
|
||||
namespace geode {
|
||||
class Priority {
|
||||
public:
|
||||
static inline constexpr int32_t First = -3000;
|
||||
static inline constexpr int32_t VeryEarly = -2000;
|
||||
static inline constexpr int32_t Early = -1000;
|
||||
static inline constexpr int32_t Normal = 0;
|
||||
static inline constexpr int32_t Late = 1000;
|
||||
static inline constexpr int32_t VeryLate = 2000;
|
||||
static inline constexpr int32_t Last = 3000;
|
||||
|
||||
static inline constexpr int32_t FirstPre = First;
|
||||
static inline constexpr int32_t VeryEarlyPre = VeryEarly;
|
||||
static inline constexpr int32_t EarlyPre = Early;
|
||||
static inline constexpr int32_t NormalPre = Normal;
|
||||
static inline constexpr int32_t LatePre = Late;
|
||||
static inline constexpr int32_t VeryLatePre = VeryLate;
|
||||
static inline constexpr int32_t LastPre = Last;
|
||||
|
||||
static inline constexpr int32_t FirstPost = Last;
|
||||
static inline constexpr int32_t VeryEarlyPost = VeryLate;
|
||||
static inline constexpr int32_t EarlyPost = Late;
|
||||
static inline constexpr int32_t NormalPost = Normal;
|
||||
static inline constexpr int32_t LatePost = Early;
|
||||
static inline constexpr int32_t VeryLatePost = VeryEarly;
|
||||
static inline constexpr int32_t LastPost = First;
|
||||
};
|
||||
}
|
||||
|
||||
namespace geode::modifier {
|
||||
template <class Derived, class Base>
|
||||
class ModifyDerive;
|
||||
|
||||
|
@ -114,6 +142,9 @@ namespace geode::modifier {
|
|||
public:
|
||||
std::map<std::string, std::shared_ptr<Hook>> m_hooks;
|
||||
|
||||
/// @brief Get a hook by name
|
||||
/// @param name The name of the hook to get
|
||||
/// @returns Ok if the hook was found, Err if the hook was not found
|
||||
Result<Hook*> getHook(std::string const& name) {
|
||||
if (m_hooks.find(name) == m_hooks.end()) {
|
||||
return Err("Hook not in this modify");
|
||||
|
@ -121,15 +152,128 @@ namespace geode::modifier {
|
|||
return Ok(m_hooks[name].get());
|
||||
}
|
||||
|
||||
Result<> setHookPriority(std::string const& name, int32_t priority) {
|
||||
auto res = this->getHook(name);
|
||||
if (!res) {
|
||||
return Err(res.unwrapErr());
|
||||
}
|
||||
res.unwrap()->setPriority(priority);
|
||||
/// @brief Set the priority of a hook
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param priority The priority to set the hook to
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriority(std::string const& name, int32_t priority = Priority::Normal) {
|
||||
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
|
||||
hook->setPriority(priority);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param priority The priority to set the hook to
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityPre(std::string const& name, int32_t priority = Priority::Normal) {
|
||||
return this->setHookPriority(name, priority);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param priority The priority to set the hook to
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityPost(std::string const& name, int32_t priority = Priority::Normal) {
|
||||
return this->setHookPriority(name, -priority);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param after The mod ids of the mods to set the priority after
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
|
||||
Result<> setHookPriorityAfter(std::string const& name, C&&... after) {
|
||||
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
|
||||
([&](){
|
||||
auto mod = Loader::get()->getInstalledMod(after);
|
||||
if (!mod) return;
|
||||
auto hooks = mod->getHooks();
|
||||
auto func = [=](){
|
||||
for (auto modHook : hooks) {
|
||||
if (modHook->getAddress() != hook->getAddress()) continue;
|
||||
auto priority = hook->getPriority();
|
||||
if (priority < modHook->getPriority()) {
|
||||
hook->setPriority(modHook->getPriority() + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Loader::get()->isModLoaded(mod)) {
|
||||
func();
|
||||
}
|
||||
else {
|
||||
new EventListener(func, ModStateFilter(mod, ModEventType::Loaded));
|
||||
}
|
||||
} (), ...);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod ids of the mods to set the priority before
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
|
||||
Result<> setHookPriorityBefore(std::string const& name, C&&... before) {
|
||||
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
|
||||
([&](){
|
||||
auto mod = Loader::get()->getInstalledMod(before);
|
||||
if (!mod) return;
|
||||
auto hooks = mod->getHooks();
|
||||
auto func = [=](){
|
||||
for (auto modHook : hooks) {
|
||||
if (modHook->getAddress() != hook->getAddress()) continue;
|
||||
auto priority = hook->getPriority();
|
||||
if (priority > modHook->getPriority()) {
|
||||
hook->setPriority(modHook->getPriority() - 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Loader::get()->isModLoaded(mod)) {
|
||||
func();
|
||||
}
|
||||
else {
|
||||
new EventListener(func, ModStateFilter(mod, ModEventType::Loaded));
|
||||
}
|
||||
} (), ...);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param after The mod ids of the mods to set the priority after
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
|
||||
Result<> setHookPriorityAfterPre(std::string const& name, C&&... after) {
|
||||
return this->setHookPriorityAfter(name, Priority::NormalPre, std::forward<C>(after)...);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod ids of the mods to set the priority before
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
|
||||
Result<> setHookPriorityBeforePre(std::string const& name, C&&... before) {
|
||||
return this->setHookPriorityBefore(name, Priority::NormalPre, std::forward<C>(before)...);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param after The mod ids of the mods to set the priority after
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
|
||||
Result<> setHookPriorityAfterPost(std::string const& name, C&&... after) {
|
||||
return this->setHookPriorityBefore(name, Priority::NormalPost, std::forward<C>(after)...);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod ids of the mods to set the priority before
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
|
||||
Result<> setHookPriorityBeforePost(std::string const& name, C&&... before) {
|
||||
return this->setHookPriorityAfter(name, Priority::NormalPost, std::forward<C>(before)...);
|
||||
}
|
||||
|
||||
// unordered_map<handles> idea
|
||||
ModifyBase() {
|
||||
struct EboCheck : ModifyDerived::Base {
|
||||
|
@ -158,7 +302,7 @@ namespace geode::modifier {
|
|||
for (auto& [uuid, hook] : m_hooks) {
|
||||
auto res = Mod::get()->claimHook(hook);
|
||||
if (!res) {
|
||||
log::error("Failed to claim hook {}: {}", hook->getDisplayName(), res.error());
|
||||
log::error("Failed to claim hook {}: {}", hook->getDisplayName(), res.unwrapErr());
|
||||
}
|
||||
else {
|
||||
added.push_back(uuid);
|
||||
|
|
|
@ -116,14 +116,22 @@ namespace geode {
|
|||
public:
|
||||
// todo in v4: make these flags and add archless Mac and Android as well as Desktop and Mobile and remove Linux
|
||||
enum {
|
||||
Unknown = -1,
|
||||
Windows = 0,
|
||||
MacIntel = 1,
|
||||
MacArm = 2,
|
||||
iOS = 3,
|
||||
Android32 = 4,
|
||||
Android64 = 5,
|
||||
Linux = 6,
|
||||
Unknown = 0b000000,
|
||||
Windows = 0b000001,
|
||||
Android32 = 0b000010,
|
||||
Android64 = 0b000100,
|
||||
MacIntel = 0b001000,
|
||||
MacArm = 0b010000,
|
||||
iOS = 0b100000,
|
||||
Android = Android32 | Android64,
|
||||
Mac = MacIntel | MacArm,
|
||||
Apple = Mac | iOS,
|
||||
X64 = MacIntel | Windows,
|
||||
X86 = Unknown,
|
||||
ArmV7 = Android32,
|
||||
ArmV8 = Android64 | MacArm | iOS,
|
||||
Desktop = Windows | Mac,
|
||||
Mobile = Android | iOS,
|
||||
};
|
||||
|
||||
using Type = decltype(Unknown);
|
||||
|
@ -190,7 +198,6 @@ namespace geode {
|
|||
case iOS: return "iOS";
|
||||
case Android32: return "Android32";
|
||||
case Android64: return "Android64";
|
||||
case Linux: return "Linux";
|
||||
default: break;
|
||||
}
|
||||
return "Undefined";
|
||||
|
@ -206,7 +213,6 @@ namespace geode {
|
|||
case iOS: return "ios";
|
||||
case Android32: return ignoreArch ? "android" : "android32";
|
||||
case Android64: return ignoreArch ? "android" : "android64";
|
||||
case Linux: return "linux";
|
||||
default: break;
|
||||
}
|
||||
return "undefined";
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace geode {
|
|||
std::optional<std::string> m_targetID;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, AEnterLayerEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, AEnterLayerEvent* event);
|
||||
|
||||
AEnterLayerFilter(
|
||||
std::optional<std::string> const& id
|
||||
|
@ -63,7 +63,7 @@ namespace geode {
|
|||
std::optional<std::string> m_targetID;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, EnterLayerEvent<N>* event) {
|
||||
ListenerResult handle(std::function<Callback> fn, EnterLayerEvent<N>* event) {
|
||||
if (m_targetID == event->getID()) {
|
||||
fn(static_cast<T*>(event));
|
||||
}
|
||||
|
|
|
@ -133,11 +133,6 @@ namespace geode {
|
|||
*/
|
||||
GEODE_DLL void openSupportPopup(Mod* mod);
|
||||
GEODE_DLL void openSupportPopup(ModMetadata const& metadata);
|
||||
/**
|
||||
* Open the store page for a mod (if it exists)
|
||||
*/
|
||||
[[deprecated("Will be removed, use openInfoPopup instead")]]
|
||||
GEODE_DLL void openIndexPopup(Mod* mod);
|
||||
/**
|
||||
* Open the settings popup for a mod (if it has any settings)
|
||||
*/
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <Geode/binding/CCTextInputNode.hpp>
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
class GEODE_DLL InputNode : public cocos2d::CCMenuItem {
|
||||
protected:
|
||||
cocos2d::extension::CCScale9Sprite* m_bgSprite;
|
||||
CCTextInputNode* m_input;
|
||||
|
||||
bool init(float, float, char const*, char const*, std::string const&, int);
|
||||
bool init(float, char const*, char const*, std::string const&, int);
|
||||
|
||||
public:
|
||||
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
|
||||
static InputNode* create(
|
||||
float width, char const* placeholder, char const* fontFile, std::string const& filter,
|
||||
int limit
|
||||
);
|
||||
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
|
||||
static InputNode* create(
|
||||
float width, char const* placeholder, std::string const& filter, int limit
|
||||
);
|
||||
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
|
||||
static InputNode* create(float width, char const* placeholder, std::string const& filter);
|
||||
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
|
||||
static InputNode* create(float width, char const* placeholder, char const* fontFile);
|
||||
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
|
||||
static InputNode* create(float width, char const* placeholder);
|
||||
|
||||
CCTextInputNode* getInput() const;
|
||||
cocos2d::extension::CCScale9Sprite* getBG() const;
|
||||
|
||||
void setEnabled(bool enabled) override;
|
||||
|
||||
void setString(std::string const&);
|
||||
std::string getString();
|
||||
};
|
||||
}
|
|
@ -1,448 +1,430 @@
|
|||
#pragma once
|
||||
|
||||
#include "../include/ccMacros.h"
|
||||
#include "../cocoa/CCAffineTransform.h"
|
||||
#include "../cocoa/CCArray.h"
|
||||
#include <Geode/platform/platform.hpp>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
class CCNode;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4275)
|
||||
|
||||
/**
|
||||
* Layouts automatically handle the positioning of nodes. Use CCNode::setLayout
|
||||
* to apply a layout to a node, and then use CCNode::updateLayout to apply
|
||||
* the layout's positioning. Geode comes with a few default layouts like
|
||||
* RowLayout, ColumnLayout, and GridLayout, but if you need a different kind
|
||||
* of layout you can inherit from the Layout class.
|
||||
*/
|
||||
class GEODE_DLL Layout : public CCObject {
|
||||
protected:
|
||||
CCArray* getNodesToPosition(CCNode* forNode) const;
|
||||
|
||||
bool m_ignoreInvisibleChildren = false;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Automatically apply the layout's positioning on a set of nodes
|
||||
* @param on Node to apply the layout on. Position's the node's children
|
||||
* according to the layout. The content size of the node should be
|
||||
* respected as a boundary the layout shouldn't overflow. The node may be
|
||||
* rescaled to better fit its contents
|
||||
*/
|
||||
virtual void apply(CCNode* on) = 0;
|
||||
|
||||
/**
|
||||
* Get how much space this layout would like to take up for a given target
|
||||
*/
|
||||
virtual CCSize getSizeHint(CCNode* on) const = 0;
|
||||
|
||||
void ignoreInvisibleChildren(bool ignore);
|
||||
bool isIgnoreInvisibleChildren() const;
|
||||
|
||||
virtual ~Layout() = default;
|
||||
};
|
||||
|
||||
class GEODE_DLL LayoutOptions : public CCObject {
|
||||
public:
|
||||
virtual ~LayoutOptions() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* The direction of an AxisLayout
|
||||
*/
|
||||
enum class Axis {
|
||||
Row,
|
||||
Column,
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies the alignment of something in an AxisLayout
|
||||
*/
|
||||
enum class AxisAlignment {
|
||||
// Align items to the start
|
||||
// |ooo......|
|
||||
Start,
|
||||
// All items are centered
|
||||
// |...ooo...|
|
||||
Center,
|
||||
// Align items to the end
|
||||
// |......ooo|
|
||||
End,
|
||||
// Each item gets the same portion from the layout (disregards gap)
|
||||
// |.o..o..o.|
|
||||
Even,
|
||||
// Space between each item is the same (disregards gap)
|
||||
// |o...o...o|
|
||||
Between,
|
||||
};
|
||||
|
||||
constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f;
|
||||
constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0;
|
||||
|
||||
/**
|
||||
* Options for controlling the behaviour of individual nodes in an AxisLayout
|
||||
* @example
|
||||
* auto node = CCNode::create();
|
||||
* // this node will have 10 units of spacing between it and the next one
|
||||
* node->setLayoutOptions(
|
||||
* AxisLayoutOptions::create()
|
||||
* ->setNextGap(10.f)
|
||||
* );
|
||||
* someNodeWithALayout->addChild(node);
|
||||
*/
|
||||
class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
|
||||
protected:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
AxisLayoutOptions();
|
||||
|
||||
public:
|
||||
static AxisLayoutOptions* create();
|
||||
|
||||
virtual ~AxisLayoutOptions();
|
||||
|
||||
std::optional<bool> getAutoScale() const;
|
||||
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten
|
||||
float getMaxScale() const;
|
||||
// @note Use hasExplicitMinScale to know if the default scale has been overwritten
|
||||
float getMinScale() const;
|
||||
bool hasExplicitMaxScale() const;
|
||||
bool hasExplicitMinScale() const;
|
||||
float getRelativeScale() const;
|
||||
std::optional<float> getLength() const;
|
||||
std::optional<float> getPrevGap() const;
|
||||
std::optional<float> getNextGap() const;
|
||||
bool getBreakLine() const;
|
||||
bool getSameLine() const;
|
||||
int getScalePriority() const;
|
||||
std::optional<AxisAlignment> getCrossAxisAlignment() const;
|
||||
|
||||
/**
|
||||
* Set the maximum scale this node can be if it's contained in an
|
||||
* auto-scaled layout. Default is 1
|
||||
*/
|
||||
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
|
||||
AxisLayoutOptions* setMaxScale(float scale);
|
||||
|
||||
/**
|
||||
* Set the minimum scale this node can be if it's contained in an
|
||||
* auto-scaled layout. Default is AXISLAYOUT_DEFAULT_MIN_SCALE
|
||||
*/
|
||||
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
|
||||
AxisLayoutOptions* setMinScale(float scale);
|
||||
|
||||
/**
|
||||
* Set the limits to what the node can be scaled to. Passing `std::nullopt`
|
||||
* uses the parent layout's default min / max scales
|
||||
*/
|
||||
AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max);
|
||||
|
||||
/**
|
||||
* Set the relative scale of this node compared to other nodes if it's
|
||||
* contained in an auto-scaled layout. Default is 1
|
||||
*/
|
||||
AxisLayoutOptions* setRelativeScale(float scale);
|
||||
|
||||
/**
|
||||
* Set auto-scaling for this node, overriding the layout's auto-scale
|
||||
* setting. If nullopt, the layout's auto-scale options will be used
|
||||
*/
|
||||
AxisLayoutOptions* setAutoScale(std::optional<bool> enabled);
|
||||
|
||||
/**
|
||||
* Set an absolute length for this node. If nullopt, the length will be
|
||||
* dynamically calculated based on content size
|
||||
*/
|
||||
AxisLayoutOptions* setLength(std::optional<float> length);
|
||||
|
||||
/**
|
||||
* Override the default gap in the layout between this node and the
|
||||
* previous one. If nullopt, the default gap of the layout will be used
|
||||
*/
|
||||
AxisLayoutOptions* setPrevGap(std::optional<float> gap);
|
||||
|
||||
/**
|
||||
* Override the default gap in the layout between this node and the next
|
||||
* one. If nullopt, the default gap of the layout will be used
|
||||
*/
|
||||
AxisLayoutOptions* setNextGap(std::optional<float> gap);
|
||||
|
||||
/**
|
||||
* If enabled, the node will always cause a growable axis layout to break
|
||||
* into a new line even if the current line could've fit the next node
|
||||
*/
|
||||
AxisLayoutOptions* setBreakLine(bool enable);
|
||||
|
||||
/**
|
||||
* If enabled, the node will be forced to be on the same line as the
|
||||
* previous node even if doing this would overflow
|
||||
*/
|
||||
AxisLayoutOptions* setSameLine(bool enable);
|
||||
|
||||
/**
|
||||
* Set the scale priority of this node. Nodes with higher priority will be
|
||||
* scaled down first before nodes with lower priority when an auto-scaled
|
||||
* layout attempts to fit its contents. Default is
|
||||
* AXISLAYOUT_DEFAULT_PRIORITY
|
||||
* @note For optimal performance, the priorities should all be close to
|
||||
* each other with no gaps
|
||||
*/
|
||||
AxisLayoutOptions* setScalePriority(int priority);
|
||||
|
||||
/**
|
||||
* Override the cross axis alignment for this node in the layout
|
||||
*/
|
||||
AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment);
|
||||
};
|
||||
|
||||
/**
|
||||
* A multi-purpose dynamic layout for arranging nodes along an axis. Can be
|
||||
* used to arrange nodes in a single line, a grid, or a flex layout. The
|
||||
* RowLayout and ColumnLayout classes function as simple thin wrappers over
|
||||
* AxisLayout. The positioning of individual nodes in the layout can be
|
||||
* further controlled using AxisLayoutOptions
|
||||
* @warning Calculating layouts can get increasingly expensive for large
|
||||
* amounts of child nodes being fit into a small space - while this should
|
||||
* never prove a real performance concern as most layouts only have a few
|
||||
* hundred children at the very most, be aware that you probably shouldn't
|
||||
* call CCNode::updateLayout every frame for a menu with thousands of children
|
||||
* @example
|
||||
* auto menu = CCMenu::create();
|
||||
* // The menu's children will be arranged horizontally, unless they overflow
|
||||
* // the content size width in which case a new line will be inserted and
|
||||
* // aligned to the left. The menu automatically will automatically grow in
|
||||
* // height to fit all the rows
|
||||
* menu->setLayout(
|
||||
* RowLayout::create()
|
||||
* ->setGap(10.f)
|
||||
* ->setGrowCrossAxis(true)
|
||||
* ->setAxisAlignment(AxisAlignment::Start)
|
||||
* );
|
||||
* menu->setContentSize({ 200.f, 0.f });
|
||||
* menu->addChild(...);
|
||||
* menu->updateLayout();
|
||||
*/
|
||||
class GEODE_DLL AxisLayout : public Layout {
|
||||
protected:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
AxisLayout(Axis);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new AxisLayout. Note that this class is not automatically
|
||||
* managed by default, so you must assign it to a CCNode or manually
|
||||
* manage the memory yourself. See the chainable setters on AxisLayout for
|
||||
* what options you can customize for the layout
|
||||
* @param axis The direction of the layout
|
||||
* @note For convenience, you can use the RowLayout and ColumnLayout
|
||||
* classes, which are just thin wrappers over AxisLayout
|
||||
* @returns Created AxisLayout
|
||||
*/
|
||||
static AxisLayout* create(Axis axis = Axis::Row);
|
||||
|
||||
virtual ~AxisLayout();
|
||||
|
||||
void apply(CCNode* on) override;
|
||||
CCSize getSizeHint(CCNode* on) const override;
|
||||
|
||||
Axis getAxis() const;
|
||||
AxisAlignment getAxisAlignment() const;
|
||||
AxisAlignment getCrossAxisAlignment() const;
|
||||
AxisAlignment getCrossAxisLineAlignment() const;
|
||||
float getGap() const;
|
||||
bool getAxisReverse() const;
|
||||
bool getCrossAxisReverse() const;
|
||||
bool getAutoScale() const;
|
||||
bool getGrowCrossAxis() const;
|
||||
bool getCrossAxisOverflow() const;
|
||||
std::optional<float> getAutoGrowAxis() const;
|
||||
float getDefaultMinScale() const;
|
||||
float getDefaultMaxScale() const;
|
||||
|
||||
AxisLayout* setAxis(Axis axis);
|
||||
/**
|
||||
* Sets where to align the target node's children on the main axis (X-axis
|
||||
* for Row, Y-axis for Column)
|
||||
*/
|
||||
AxisLayout* setAxisAlignment(AxisAlignment align);
|
||||
/**
|
||||
* Sets where to align the target node's children on the cross-axis (Y-axis
|
||||
* for Row, X-axis for Column)
|
||||
*/
|
||||
AxisLayout* setCrossAxisAlignment(AxisAlignment align);
|
||||
/**
|
||||
* Sets where to align the target node's children on the cross-axis for
|
||||
* each row (Y-axis for Row, X-axis for Column)
|
||||
*/
|
||||
AxisLayout* setCrossAxisLineAlignment(AxisAlignment align);
|
||||
/**
|
||||
* The spacing between the children of the node this layout applies to.
|
||||
* Measured as the space between their edges, not centres. Does not apply
|
||||
* on the main / cross axis if their alignment is AxisAlignment::Even
|
||||
*/
|
||||
AxisLayout* setGap(float gap);
|
||||
/**
|
||||
* Whether to reverse the direction of the children in this layout or not
|
||||
*/
|
||||
AxisLayout* setAxisReverse(bool reverse);
|
||||
/**
|
||||
* Whether to reverse the direction of the rows on the cross-axis or not
|
||||
*/
|
||||
AxisLayout* setCrossAxisReverse(bool reverse);
|
||||
/**
|
||||
* If enabled, then the layout may scale the target's children if they are
|
||||
* about to overflow. Assumes that all the childrens' intended scale is 1
|
||||
*/
|
||||
AxisLayout* setAutoScale(bool enable);
|
||||
/**
|
||||
* If true, if the main axis overflows extra nodes will be placed on new
|
||||
* rows/columns on the cross-axis
|
||||
*/
|
||||
AxisLayout* setGrowCrossAxis(bool expand);
|
||||
/**
|
||||
* If true, the cross-axis content size of the target node will be
|
||||
* automatically adjusted to fit the children
|
||||
*/
|
||||
AxisLayout* setCrossAxisOverflow(bool allow);
|
||||
/**
|
||||
* If not `std::nullopt`, then the axis will be automatically extended to
|
||||
* fit all items in a single row whose minimum length is the specified.
|
||||
* Useful for scrollable list layer contents
|
||||
*/
|
||||
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
|
||||
/**
|
||||
* Set the default minimum/maximum scales for nodes in the layout
|
||||
*/
|
||||
AxisLayout* setDefaultScaleLimits(float min, float max);
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple layout for arranging nodes in a row (horizontal line)
|
||||
*/
|
||||
class GEODE_DLL RowLayout : public AxisLayout {
|
||||
protected:
|
||||
RowLayout();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new RowLayout. See the chainable setters on RowLayout for
|
||||
* what options you can customize for the layout
|
||||
* @returns Created RowLayout
|
||||
*/
|
||||
static RowLayout* create();
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple layout for arranging nodes in a column (vertical line)
|
||||
*/
|
||||
class GEODE_DLL ColumnLayout : public AxisLayout {
|
||||
protected:
|
||||
ColumnLayout();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new ColumnLayout. See the chainable setters on RowLayout for
|
||||
* what options you can customize for the layout
|
||||
* @returns Created ColumnLayout
|
||||
*/
|
||||
static ColumnLayout* create();
|
||||
};
|
||||
|
||||
/**
|
||||
* The relative position of a node to its parent in an AnchorLayout
|
||||
*/
|
||||
enum class Anchor {
|
||||
Center,
|
||||
TopLeft,
|
||||
Top,
|
||||
TopRight,
|
||||
Right,
|
||||
BottomRight,
|
||||
Bottom,
|
||||
BottomLeft,
|
||||
Left,
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for customizing a node's position in an AnchorLayout
|
||||
*/
|
||||
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
|
||||
protected:
|
||||
Anchor m_anchor = Anchor::Center;
|
||||
CCPoint m_offset = CCPointZero;
|
||||
|
||||
public:
|
||||
static AnchorLayoutOptions* create();
|
||||
|
||||
Anchor getAnchor() const;
|
||||
CCPoint getOffset() const;
|
||||
|
||||
AnchorLayoutOptions* setAnchor(Anchor anchor);
|
||||
AnchorLayoutOptions* setOffset(CCPoint const& offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* A layout for positioning nodes at specific positions relative to their
|
||||
* parent's content size. See `Anchor` for available anchoring options. Useful
|
||||
* for example for popups, where a popup using `AnchorLayout` can be
|
||||
* automatically resized without needing to manually shuffle nodes around
|
||||
*/
|
||||
class GEODE_DLL AnchorLayout : public Layout {
|
||||
public:
|
||||
static AnchorLayout* create();
|
||||
|
||||
void apply(CCNode* on) override;
|
||||
CCSize getSizeHint(CCNode* on) const override;
|
||||
|
||||
/**
|
||||
* Get a position according to anchoring rules, with the same algorithm as
|
||||
* `AnchorLayout` uses to position its nodes
|
||||
* @param in The node whose content size to use as a reference
|
||||
* @param anchor The anchor position
|
||||
* @param offset Offset from the anchor
|
||||
* @returns A position in `in` for the anchored and offsetted location
|
||||
*/
|
||||
static CCPoint getAnchoredPosition(CCNode* in, Anchor anchor, CCPoint const& offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* A layout for automatically copying the content size of a node to other nodes.
|
||||
* Basically main use case is for FLAlertLayers (setting the size of the
|
||||
* background and `m_buttonMenu` based on `m_mainLayer`)
|
||||
*/
|
||||
class GEODE_DLL CopySizeLayout : public cocos2d::AnchorLayout {
|
||||
protected:
|
||||
cocos2d::CCArray* m_targets;
|
||||
|
||||
public:
|
||||
static CopySizeLayout* create();
|
||||
virtual ~CopySizeLayout();
|
||||
|
||||
/**
|
||||
* Add a target to be automatically resized. Any targets' layouts will
|
||||
* also be updated when this layout is updated
|
||||
*/
|
||||
CopySizeLayout* add(cocos2d::CCNode* target);
|
||||
/**
|
||||
* Remove a target from being automatically resized
|
||||
*/
|
||||
CopySizeLayout* remove(cocos2d::CCNode* target);
|
||||
|
||||
void apply(cocos2d::CCNode* in) override;
|
||||
cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override;
|
||||
};
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
NS_CC_END
|
||||
#pragma once
|
||||
|
||||
#include <cocos2d.h>
|
||||
#include <Geode/platform/platform.hpp>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
namespace geode {
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4275)
|
||||
|
||||
/**
|
||||
* Layouts automatically handle the positioning of nodes. Use CCNode::setLayout
|
||||
* to apply a layout to a node, and then use CCNode::updateLayout to apply
|
||||
* the layout's positioning. Geode comes with a few default layouts like
|
||||
* RowLayout, ColumnLayout, and GridLayout, but if you need a different kind
|
||||
* of layout you can inherit from the Layout class.
|
||||
*/
|
||||
class GEODE_DLL Layout : public cocos2d::CCObject {
|
||||
protected:
|
||||
cocos2d::CCArray* getNodesToPosition(cocos2d::CCNode* forNode) const;
|
||||
|
||||
bool m_ignoreInvisibleChildren = false;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Automatically apply the layout's positioning on a set of nodes
|
||||
* @param on Node to apply the layout on. Position's the node's children
|
||||
* according to the layout. The content size of the node should be
|
||||
* respected as a boundary the layout shouldn't overflow. The node may be
|
||||
* rescaled to better fit its contents
|
||||
*/
|
||||
virtual void apply(cocos2d::CCNode* on) = 0;
|
||||
|
||||
/**
|
||||
* Get how much space this layout would like to take up for a given target
|
||||
*/
|
||||
virtual cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const = 0;
|
||||
|
||||
void ignoreInvisibleChildren(bool ignore);
|
||||
bool isIgnoreInvisibleChildren() const;
|
||||
|
||||
virtual ~Layout() = default;
|
||||
};
|
||||
|
||||
class GEODE_DLL LayoutOptions : public cocos2d::CCObject {
|
||||
public:
|
||||
virtual ~LayoutOptions() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* The direction of an AxisLayout
|
||||
*/
|
||||
enum class Axis {
|
||||
Row,
|
||||
Column,
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies the alignment of something in an AxisLayout
|
||||
*/
|
||||
enum class AxisAlignment {
|
||||
// Align items to the start
|
||||
// |ooo......|
|
||||
Start,
|
||||
// All items are centered
|
||||
// |...ooo...|
|
||||
Center,
|
||||
// Align items to the end
|
||||
// |......ooo|
|
||||
End,
|
||||
// Each item gets the same portion from the layout (disregards gap)
|
||||
// |.o..o..o.|
|
||||
Even,
|
||||
// Space between each item is the same (disregards gap)
|
||||
// |o...o...o|
|
||||
Between,
|
||||
};
|
||||
|
||||
constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f;
|
||||
constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0;
|
||||
|
||||
/**
|
||||
* Options for controlling the behaviour of individual nodes in an AxisLayout
|
||||
* @example
|
||||
* auto node = CCNode::create();
|
||||
* // this node will have 10 units of spacing between it and the next one
|
||||
* node->setLayoutOptions(
|
||||
* AxisLayoutOptions::create()
|
||||
* ->setNextGap(10.f)
|
||||
* );
|
||||
* someNodeWithALayout->addChild(node);
|
||||
*/
|
||||
class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
|
||||
protected:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
AxisLayoutOptions();
|
||||
|
||||
public:
|
||||
static AxisLayoutOptions* create();
|
||||
|
||||
virtual ~AxisLayoutOptions();
|
||||
|
||||
std::optional<bool> getAutoScale() const;
|
||||
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten
|
||||
float getMaxScale() const;
|
||||
// @note Use hasExplicitMinScale to know if the default scale has been overwritten
|
||||
float getMinScale() const;
|
||||
bool hasExplicitMaxScale() const;
|
||||
bool hasExplicitMinScale() const;
|
||||
float getRelativeScale() const;
|
||||
std::optional<float> getLength() const;
|
||||
std::optional<float> getPrevGap() const;
|
||||
std::optional<float> getNextGap() const;
|
||||
bool getBreakLine() const;
|
||||
bool getSameLine() const;
|
||||
int getScalePriority() const;
|
||||
std::optional<AxisAlignment> getCrossAxisAlignment() const;
|
||||
|
||||
/**
|
||||
* Set the limits to what the node can be scaled to. Passing `std::nullopt`
|
||||
* uses the parent layout's default min / max scales
|
||||
*/
|
||||
AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max);
|
||||
|
||||
/**
|
||||
* Set the relative scale of this node compared to other nodes if it's
|
||||
* contained in an auto-scaled layout. Default is 1
|
||||
*/
|
||||
AxisLayoutOptions* setRelativeScale(float scale);
|
||||
|
||||
/**
|
||||
* Set auto-scaling for this node, overriding the layout's auto-scale
|
||||
* setting. If nullopt, the layout's auto-scale options will be used
|
||||
*/
|
||||
AxisLayoutOptions* setAutoScale(std::optional<bool> enabled);
|
||||
|
||||
/**
|
||||
* Set an absolute length for this node. If nullopt, the length will be
|
||||
* dynamically calculated based on content size
|
||||
*/
|
||||
AxisLayoutOptions* setLength(std::optional<float> length);
|
||||
|
||||
/**
|
||||
* Override the default gap in the layout between this node and the
|
||||
* previous one. If nullopt, the default gap of the layout will be used
|
||||
*/
|
||||
AxisLayoutOptions* setPrevGap(std::optional<float> gap);
|
||||
|
||||
/**
|
||||
* Override the default gap in the layout between this node and the next
|
||||
* one. If nullopt, the default gap of the layout will be used
|
||||
*/
|
||||
AxisLayoutOptions* setNextGap(std::optional<float> gap);
|
||||
|
||||
/**
|
||||
* If enabled, the node will always cause a growable axis layout to break
|
||||
* into a new line even if the current line could've fit the next node
|
||||
*/
|
||||
AxisLayoutOptions* setBreakLine(bool enable);
|
||||
|
||||
/**
|
||||
* If enabled, the node will be forced to be on the same line as the
|
||||
* previous node even if doing this would overflow
|
||||
*/
|
||||
AxisLayoutOptions* setSameLine(bool enable);
|
||||
|
||||
/**
|
||||
* Set the scale priority of this node. Nodes with higher priority will be
|
||||
* scaled down first before nodes with lower priority when an auto-scaled
|
||||
* layout attempts to fit its contents. Default is
|
||||
* AXISLAYOUT_DEFAULT_PRIORITY
|
||||
* @note For optimal performance, the priorities should all be close to
|
||||
* each other with no gaps
|
||||
*/
|
||||
AxisLayoutOptions* setScalePriority(int priority);
|
||||
|
||||
/**
|
||||
* Override the cross axis alignment for this node in the layout
|
||||
*/
|
||||
AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment);
|
||||
};
|
||||
|
||||
/**
|
||||
* A multi-purpose dynamic layout for arranging nodes along an axis. Can be
|
||||
* used to arrange nodes in a single line, a grid, or a flex layout. The
|
||||
* RowLayout and ColumnLayout classes function as simple thin wrappers over
|
||||
* AxisLayout. The positioning of individual nodes in the layout can be
|
||||
* further controlled using AxisLayoutOptions
|
||||
* @warning Calculating layouts can get increasingly expensive for large
|
||||
* amounts of child nodes being fit into a small space - while this should
|
||||
* never prove a real performance concern as most layouts only have a few
|
||||
* hundred children at the very most, be aware that you probably shouldn't
|
||||
* call CCNode::updateLayout every frame for a menu with thousands of children
|
||||
* @example
|
||||
* auto menu = CCMenu::create();
|
||||
* // The menu's children will be arranged horizontally, unless they overflow
|
||||
* // the content size width in which case a new line will be inserted and
|
||||
* // aligned to the left. The menu automatically will automatically grow in
|
||||
* // height to fit all the rows
|
||||
* menu->setLayout(
|
||||
* RowLayout::create()
|
||||
* ->setGap(10.f)
|
||||
* ->setGrowCrossAxis(true)
|
||||
* ->setAxisAlignment(AxisAlignment::Start)
|
||||
* );
|
||||
* menu->setContentSize({ 200.f, 0.f });
|
||||
* menu->addChild(...);
|
||||
* menu->updateLayout();
|
||||
*/
|
||||
class GEODE_DLL AxisLayout : public Layout {
|
||||
protected:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
AxisLayout(Axis);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new AxisLayout. Note that this class is not automatically
|
||||
* managed by default, so you must assign it to a CCNode or manually
|
||||
* manage the memory yourself. See the chainable setters on AxisLayout for
|
||||
* what options you can customize for the layout
|
||||
* @param axis The direction of the layout
|
||||
* @note For convenience, you can use the RowLayout and ColumnLayout
|
||||
* classes, which are just thin wrappers over AxisLayout
|
||||
* @returns Created AxisLayout
|
||||
*/
|
||||
static AxisLayout* create(Axis axis = Axis::Row);
|
||||
|
||||
virtual ~AxisLayout();
|
||||
|
||||
void apply(cocos2d::CCNode* on) override;
|
||||
cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
|
||||
|
||||
Axis getAxis() const;
|
||||
AxisAlignment getAxisAlignment() const;
|
||||
AxisAlignment getCrossAxisAlignment() const;
|
||||
AxisAlignment getCrossAxisLineAlignment() const;
|
||||
float getGap() const;
|
||||
bool getAxisReverse() const;
|
||||
bool getCrossAxisReverse() const;
|
||||
bool getAutoScale() const;
|
||||
bool getGrowCrossAxis() const;
|
||||
bool getCrossAxisOverflow() const;
|
||||
std::optional<float> getAutoGrowAxis() const;
|
||||
float getDefaultMinScale() const;
|
||||
float getDefaultMaxScale() const;
|
||||
|
||||
AxisLayout* setAxis(Axis axis);
|
||||
/**
|
||||
* Sets where to align the target node's children on the main axis (X-axis
|
||||
* for Row, Y-axis for Column)
|
||||
*/
|
||||
AxisLayout* setAxisAlignment(AxisAlignment align);
|
||||
/**
|
||||
* Sets where to align the target node's children on the cross-axis (Y-axis
|
||||
* for Row, X-axis for Column)
|
||||
*/
|
||||
AxisLayout* setCrossAxisAlignment(AxisAlignment align);
|
||||
/**
|
||||
* Sets where to align the target node's children on the cross-axis for
|
||||
* each row (Y-axis for Row, X-axis for Column)
|
||||
*/
|
||||
AxisLayout* setCrossAxisLineAlignment(AxisAlignment align);
|
||||
/**
|
||||
* The spacing between the children of the node this layout applies to.
|
||||
* Measured as the space between their edges, not centres. Does not apply
|
||||
* on the main / cross axis if their alignment is AxisAlignment::Even
|
||||
*/
|
||||
AxisLayout* setGap(float gap);
|
||||
/**
|
||||
* Whether to reverse the direction of the children in this layout or not
|
||||
*/
|
||||
AxisLayout* setAxisReverse(bool reverse);
|
||||
/**
|
||||
* Whether to reverse the direction of the rows on the cross-axis or not
|
||||
*/
|
||||
AxisLayout* setCrossAxisReverse(bool reverse);
|
||||
/**
|
||||
* If enabled, then the layout may scale the target's children if they are
|
||||
* about to overflow. Assumes that all the childrens' intended scale is 1
|
||||
*/
|
||||
AxisLayout* setAutoScale(bool enable);
|
||||
/**
|
||||
* If true, if the main axis overflows extra nodes will be placed on new
|
||||
* rows/columns on the cross-axis
|
||||
*/
|
||||
AxisLayout* setGrowCrossAxis(bool expand);
|
||||
/**
|
||||
* If true, the cross-axis content size of the target node will be
|
||||
* automatically adjusted to fit the children
|
||||
*/
|
||||
AxisLayout* setCrossAxisOverflow(bool allow);
|
||||
/**
|
||||
* If not `std::nullopt`, then the axis will be automatically extended to
|
||||
* fit all items in a single row whose minimum length is the specified.
|
||||
* Useful for scrollable list layer contents
|
||||
*/
|
||||
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
|
||||
/**
|
||||
* Set the default minimum/maximum scales for nodes in the layout
|
||||
*/
|
||||
AxisLayout* setDefaultScaleLimits(float min, float max);
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple layout for arranging nodes in a row (horizontal line)
|
||||
*/
|
||||
class GEODE_DLL RowLayout : public AxisLayout {
|
||||
protected:
|
||||
RowLayout();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new RowLayout. See the chainable setters on RowLayout for
|
||||
* what options you can customize for the layout
|
||||
* @returns Created RowLayout
|
||||
*/
|
||||
static RowLayout* create();
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple layout for arranging nodes in a column (vertical line)
|
||||
*/
|
||||
class GEODE_DLL ColumnLayout : public AxisLayout {
|
||||
protected:
|
||||
ColumnLayout();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new ColumnLayout. See the chainable setters on RowLayout for
|
||||
* what options you can customize for the layout
|
||||
* @returns Created ColumnLayout
|
||||
*/
|
||||
static ColumnLayout* create();
|
||||
};
|
||||
|
||||
/**
|
||||
* The relative position of a node to its parent in an AnchorLayout
|
||||
*/
|
||||
enum class Anchor {
|
||||
Center,
|
||||
TopLeft,
|
||||
Top,
|
||||
TopRight,
|
||||
Right,
|
||||
BottomRight,
|
||||
Bottom,
|
||||
BottomLeft,
|
||||
Left,
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for customizing a node's position in an AnchorLayout
|
||||
*/
|
||||
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
|
||||
protected:
|
||||
Anchor m_anchor = Anchor::Center;
|
||||
cocos2d::CCPoint m_offset = cocos2d::CCPointZero;
|
||||
|
||||
public:
|
||||
static AnchorLayoutOptions* create();
|
||||
|
||||
Anchor getAnchor() const;
|
||||
cocos2d::CCPoint getOffset() const;
|
||||
|
||||
AnchorLayoutOptions* setAnchor(Anchor anchor);
|
||||
AnchorLayoutOptions* setOffset(cocos2d::CCPoint const& offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* A layout for positioning nodes at specific positions relative to their
|
||||
* parent's content size. See `Anchor` for available anchoring options. Useful
|
||||
* for example for popups, where a popup using `AnchorLayout` can be
|
||||
* automatically resized without needing to manually shuffle nodes around
|
||||
*/
|
||||
class GEODE_DLL AnchorLayout : public Layout {
|
||||
public:
|
||||
static AnchorLayout* create();
|
||||
|
||||
void apply(cocos2d::CCNode* on) override;
|
||||
cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
|
||||
|
||||
/**
|
||||
* Get a position according to anchoring rules, with the same algorithm as
|
||||
* `AnchorLayout` uses to position its nodes
|
||||
* @param in The node whose content size to use as a reference
|
||||
* @param anchor The anchor position
|
||||
* @param offset Offset from the anchor
|
||||
* @returns A position in `in` for the anchored and offsetted location
|
||||
*/
|
||||
static cocos2d::CCPoint getAnchoredPosition(cocos2d::CCNode* in, Anchor anchor, cocos2d::CCPoint const& offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* A layout for automatically copying the content size of a node to other nodes.
|
||||
* Basically main use case is for FLAlertLayers (setting the size of the
|
||||
* background and `m_buttonMenu` based on `m_mainLayer`)
|
||||
*/
|
||||
class GEODE_DLL CopySizeLayout : public AnchorLayout {
|
||||
protected:
|
||||
cocos2d::CCArray* m_targets;
|
||||
|
||||
public:
|
||||
static CopySizeLayout* create();
|
||||
virtual ~CopySizeLayout();
|
||||
|
||||
/**
|
||||
* Add a target to be automatically resized. Any targets' layouts will
|
||||
* also be updated when this layout is updated
|
||||
*/
|
||||
CopySizeLayout* add(cocos2d::CCNode* target);
|
||||
/**
|
||||
* Remove a target from being automatically resized
|
||||
*/
|
||||
CopySizeLayout* remove(cocos2d::CCNode* target);
|
||||
|
||||
void apply(cocos2d::CCNode* in) override;
|
||||
cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override;
|
||||
};
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
}
|
|
@ -11,13 +11,13 @@ namespace geode {
|
|||
class GEODE_DLL MDPopup :
|
||||
public Popup<
|
||||
std::string const&, std::string const&, char const*, char const*,
|
||||
utils::MiniFunction<void(bool)>> {
|
||||
std::function<void(bool)>> {
|
||||
protected:
|
||||
utils::MiniFunction<void(bool)> m_onClick = nullptr;
|
||||
std::function<void(bool)> m_onClick = nullptr;
|
||||
|
||||
bool setup(
|
||||
std::string const& title, std::string const& info, char const* btn1, char const* btn2,
|
||||
utils::MiniFunction<void(bool)> onClick
|
||||
std::function<void(bool)> onClick
|
||||
) override;
|
||||
|
||||
void onBtn(CCObject*);
|
||||
|
@ -27,7 +27,7 @@ namespace geode {
|
|||
public:
|
||||
static MDPopup* create(
|
||||
std::string const& title, std::string const& content, char const* btn1,
|
||||
char const* btn2 = nullptr, utils::MiniFunction<void(bool)> onClick = nullptr
|
||||
char const* btn2 = nullptr, std::function<void(bool)> onClick = nullptr
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||
#include <Geode/binding/FLAlertLayer.hpp>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/ui/Layout.hpp>
|
||||
|
||||
namespace geode {
|
||||
template <class... InitArgs>
|
||||
|
@ -51,7 +51,7 @@ namespace geode {
|
|||
}
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, CloseEvent* event) {
|
||||
ListenerResult handle(std::function<Callback> fn, CloseEvent* event) {
|
||||
if (event->getPopup() == m_impl->popup) {
|
||||
fn(event);
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ namespace geode {
|
|||
m_mainLayer->setPosition(winSize / 2);
|
||||
m_mainLayer->setContentSize(m_size);
|
||||
m_mainLayer->setLayout(
|
||||
cocos2d::CopySizeLayout::create()
|
||||
geode::CopySizeLayout::create()
|
||||
->add(m_buttonMenu)
|
||||
->add(m_bgSprite)
|
||||
);
|
||||
|
@ -119,7 +119,7 @@ namespace geode {
|
|||
closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose)
|
||||
);
|
||||
if (dynamic) {
|
||||
m_buttonMenu->addChildAtPosition(m_closeBtn, cocos2d::Anchor::TopLeft, { 3.f, -3.f });
|
||||
m_buttonMenu->addChildAtPosition(m_closeBtn, geode::Anchor::TopLeft, { 3.f, -3.f });
|
||||
}
|
||||
else {
|
||||
m_closeBtn->setPosition(-m_size.width / 2 + 3.f, m_size.height / 2 - 3.f);
|
||||
|
@ -137,14 +137,6 @@ namespace geode {
|
|||
}
|
||||
|
||||
protected:
|
||||
[[deprecated("Use Popup::initAnchored instead, as it has more reasonable menu and layer content sizes")]]
|
||||
bool init(
|
||||
float width, float height, InitArgs... args, char const* bg = "GJ_square01.png",
|
||||
cocos2d::CCRect bgRect = { 0, 0, 80, 80 }
|
||||
) {
|
||||
return this->initBase(width, height, std::forward<InitArgs>(args)..., bg, bgRect, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init with AnchorLayout and the content size of `m_buttonMenu` and
|
||||
* `m_bgSprite` being tied to the size of `m_mainLayer` (rather than
|
||||
|
@ -185,7 +177,7 @@ namespace geode {
|
|||
m_title = cocos2d::CCLabelBMFont::create(title.c_str(), font);
|
||||
m_title->setZOrder(2);
|
||||
if (m_dynamic) {
|
||||
m_mainLayer->addChildAtPosition(m_title, cocos2d::Anchor::Top, ccp(0, -offset));
|
||||
m_mainLayer->addChildAtPosition(m_title, geode::Anchor::Top, ccp(0, -offset));
|
||||
}
|
||||
else {
|
||||
auto winSize = cocos2d::CCDirector::get()->getWinSize();
|
||||
|
@ -221,21 +213,21 @@ namespace geode {
|
|||
|
||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
||||
utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow = true
|
||||
std::function<void(FLAlertLayer*, bool)> selected, bool doShow = true
|
||||
);
|
||||
|
||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
||||
float width, utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow = true
|
||||
float width, std::function<void(FLAlertLayer*, bool)> selected, bool doShow = true
|
||||
);
|
||||
|
||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
||||
utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
|
||||
std::function<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
|
||||
);
|
||||
|
||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
||||
float width, utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
|
||||
float width, std::function<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <Geode/utils/cocos.hpp>
|
||||
|
||||
namespace geode {
|
||||
struct SceneSwitch;
|
||||
|
||||
class GEODE_DLL SceneManager final {
|
||||
protected:
|
||||
std::vector<Ref<cocos2d::CCNode>> m_persistedNodes;
|
||||
|
@ -15,6 +17,10 @@ namespace geode {
|
|||
|
||||
virtual ~SceneManager();
|
||||
|
||||
void willSwitchToScene(cocos2d::CCScene* scene);
|
||||
|
||||
friend struct SceneSwitch;
|
||||
|
||||
public:
|
||||
static SceneManager* get();
|
||||
|
||||
|
@ -34,9 +40,5 @@ namespace geode {
|
|||
* Gets a span of the persisted nodes. To add new nodes to the list, use keepAcrossScenes.
|
||||
*/
|
||||
std::span<Ref<cocos2d::CCNode> const> getPersistedNodes();
|
||||
|
||||
// This method should only be called by geode itself
|
||||
// TODO(v4): hide this
|
||||
void willSwitchToScene(cocos2d::CCScene* scene);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -14,13 +14,13 @@ namespace geode {
|
|||
protected:
|
||||
std::vector<T> m_list;
|
||||
size_t m_index = 0;
|
||||
utils::MiniFunction<void(T const&, size_t)> m_onChange;
|
||||
std::function<void(T const&, size_t)> m_onChange;
|
||||
cocos2d::CCLabelBMFont* m_label;
|
||||
CCMenuItemSpriteExtra* m_prevBtn;
|
||||
CCMenuItemSpriteExtra* m_nextBtn;
|
||||
|
||||
bool init(
|
||||
float width, std::vector<T> const& list, utils::MiniFunction<void(T const&, size_t)> onChange
|
||||
float width, std::vector<T> const& list, std::function<void(T const&, size_t)> onChange
|
||||
) {
|
||||
if (!cocos2d::CCMenu::init()) return false;
|
||||
|
||||
|
@ -94,7 +94,7 @@ namespace geode {
|
|||
|
||||
public:
|
||||
static SelectList* create(
|
||||
float width, std::vector<T> const& list, utils::MiniFunction<void(T const&, size_t)> onChange
|
||||
float width, std::vector<T> const& list, std::function<void(T const&, size_t)> onChange
|
||||
) {
|
||||
auto ret = new SelectList();
|
||||
if (ret->init(width, list, onChange)) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "CCNode.h"
|
||||
#include <cocos2d.h>
|
||||
|
||||
NS_CC_BEGIN
|
||||
namespace geode {
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4275)
|
||||
|
@ -22,7 +22,7 @@ NS_CC_BEGIN
|
|||
* @note If you want to specify a minimum width for a SpacerNode, add
|
||||
* AxisLayoutOptions for it and use setLength
|
||||
*/
|
||||
class GEODE_DLL SpacerNode : public CCNode {
|
||||
class GEODE_DLL SpacerNode : public cocos2d::CCNode {
|
||||
protected:
|
||||
size_t m_grow;
|
||||
|
||||
|
@ -61,9 +61,9 @@ public:
|
|||
*/
|
||||
class GEODE_DLL SpacerNodeChild : public SpacerNode {
|
||||
protected:
|
||||
CCNode* m_child = nullptr;
|
||||
cocos2d::CCNode* m_child = nullptr;
|
||||
|
||||
bool init(CCNode* child, size_t grow);
|
||||
bool init(cocos2d::CCNode* child, size_t grow);
|
||||
|
||||
public:
|
||||
/**
|
||||
|
@ -73,11 +73,11 @@ public:
|
|||
* factors (akin to CSS flew grow)
|
||||
* @param grow The grow factor for this node. Default is 1
|
||||
*/
|
||||
static SpacerNodeChild* create(CCNode* child, size_t grow = 1);
|
||||
static SpacerNodeChild* create(cocos2d::CCNode* child, size_t grow = 1);
|
||||
|
||||
void setContentSize(CCSize const& size) override;
|
||||
void setContentSize(cocos2d::CCSize const& size) override;
|
||||
};
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
NS_CC_END
|
||||
}
|
|
@ -24,34 +24,34 @@ namespace geode {
|
|||
*/
|
||||
class GEODE_DLL SimpleTextArea : public cocos2d::CCNode {
|
||||
public:
|
||||
static SimpleTextArea* create(const std::string& text, const std::string& font = "chatFont.fnt", const float scale = 1);
|
||||
static SimpleTextArea* create(const std::string& text, const std::string& font, const float scale, const float width);
|
||||
static SimpleTextArea* create(const std::string& text, const std::string& font = "chatFont.fnt", float scale = 1.0f);
|
||||
static SimpleTextArea* create(const std::string& text, const std::string& font, float scale, float width);
|
||||
|
||||
void setFont(const std::string& font);
|
||||
std::string getFont();
|
||||
void setColor(const cocos2d::ccColor4B& color);
|
||||
cocos2d::ccColor4B getColor();
|
||||
void setAlignment(const cocos2d::CCTextAlignment alignment);
|
||||
void setAlignment(cocos2d::CCTextAlignment alignment);
|
||||
cocos2d::CCTextAlignment getAlignment();
|
||||
void setWrappingMode(const WrappingMode mode);
|
||||
void setWrappingMode(WrappingMode mode);
|
||||
WrappingMode getWrappingMode();
|
||||
void setText(const std::string& text);
|
||||
std::string getText();
|
||||
void setMaxLines(const size_t maxLines);
|
||||
void setMaxLines(size_t maxLines);
|
||||
size_t getMaxLines();
|
||||
void setWidth(const float width);
|
||||
void setWidth(float width);
|
||||
float getWidth();
|
||||
void setScale(const float scale) override;
|
||||
void setScale(float scale) override;
|
||||
float getScale() override;
|
||||
void setLinePadding(const float padding);
|
||||
void setLinePadding(float padding);
|
||||
float getLinePadding();
|
||||
std::vector<cocos2d::CCLabelBMFont*> getLines();
|
||||
float getHeight();
|
||||
float getLineHeight();
|
||||
private:
|
||||
static SimpleTextArea* create(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth);
|
||||
static SimpleTextArea* create(const std::string& font, const std::string& text, float scale, float width, const bool artificialWidth);
|
||||
|
||||
bool init(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth);
|
||||
bool init(const std::string& font, const std::string& text, float scale, float width, const bool artificialWidth);
|
||||
|
||||
bool m_shouldUpdate = false;
|
||||
bool m_artificialWidth = false;
|
||||
|
@ -67,9 +67,9 @@ namespace geode {
|
|||
float m_lineHeight = 0.f;
|
||||
float m_linePadding = 0.f;
|
||||
|
||||
cocos2d::CCLabelBMFont* createLabel(const std::string& text, const float top);
|
||||
cocos2d::CCLabelBMFont* createLabel(const std::string& text, float top);
|
||||
float calculateOffset(cocos2d::CCLabelBMFont* label);
|
||||
void charIteration(const std::function<cocos2d::CCLabelBMFont*(cocos2d::CCLabelBMFont* line, const char c, const float top)>& overflowHandling);
|
||||
void charIteration(const std::function<cocos2d::CCLabelBMFont*(cocos2d::CCLabelBMFont* line, char c, float top)>& overflowHandling);
|
||||
void updateLinesNoWrap();
|
||||
void updateLinesWordWrap(bool spaceWrap);
|
||||
void updateLinesCutoffWrap();
|
||||
|
|
|
@ -122,7 +122,7 @@ namespace geode {
|
|||
* to distinguish between bold, italic and
|
||||
* regular text.
|
||||
*/
|
||||
using Font = utils::MiniFunction<Label(int)>;
|
||||
using Font = std::function<Label(int)>;
|
||||
|
||||
protected:
|
||||
cocos2d::CCPoint m_origin = cocos2d::CCPointZero;
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace geode {
|
|||
std::string m_id;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, ColorProvidedEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, ColorProvidedEvent* event);
|
||||
|
||||
ColorProvidedFilter(std::string const& id);
|
||||
};
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
#include "../loader/Log.hpp"
|
||||
#include <set>
|
||||
#include <variant>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include <Geode/utils/Result.hpp>
|
||||
#include <Geode/Result.hpp>
|
||||
|
||||
namespace geode {
|
||||
struct JsonChecker;
|
||||
|
@ -73,231 +72,11 @@ namespace geode {
|
|||
}
|
||||
|
||||
template <class T>
|
||||
using JsonValueValidator = utils::MiniFunction<bool(T const&)>;
|
||||
using JsonValueValidator = std::function<bool(T const&)>;
|
||||
|
||||
struct JsonMaybeObject;
|
||||
struct JsonMaybeValue;
|
||||
|
||||
struct GEODE_DLL
|
||||
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
|
||||
JsonMaybeSomething {
|
||||
protected:
|
||||
JsonChecker& m_checker;
|
||||
matjson::Value& m_json;
|
||||
std::string m_hierarchy;
|
||||
bool m_hasValue;
|
||||
|
||||
friend struct JsonMaybeObject;
|
||||
friend struct JsonMaybeValue;
|
||||
|
||||
void setError(std::string const& error);
|
||||
|
||||
public:
|
||||
matjson::Value& json();
|
||||
|
||||
JsonMaybeSomething(
|
||||
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
|
||||
);
|
||||
|
||||
bool isError() const;
|
||||
std::string getError() const;
|
||||
|
||||
operator bool() const;
|
||||
};
|
||||
|
||||
struct GEODE_DLL
|
||||
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
|
||||
JsonMaybeValue : public JsonMaybeSomething {
|
||||
bool m_inferType = true;
|
||||
|
||||
JsonMaybeValue(
|
||||
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
|
||||
);
|
||||
|
||||
JsonMaybeSomething& self();
|
||||
|
||||
template <matjson::Type T>
|
||||
JsonMaybeValue& as() {
|
||||
if (this->isError()) return *this;
|
||||
if (!jsonConvertibleTo(self().m_json.type(), T)) {
|
||||
this->setError(
|
||||
self().m_hierarchy + ": Invalid type \"" + jsonValueTypeToString(self().m_json.type()) +
|
||||
"\", expected \"" + jsonValueTypeToString(T) + "\""
|
||||
);
|
||||
}
|
||||
m_inferType = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
JsonMaybeValue& array();
|
||||
|
||||
template <matjson::Type... T>
|
||||
JsonMaybeValue& asOneOf() {
|
||||
if (this->isError()) return *this;
|
||||
bool isOneOf = (... || jsonConvertibleTo(self().m_json.type(), T));
|
||||
if (!isOneOf) {
|
||||
this->setError(
|
||||
self().m_hierarchy + ": Invalid type \"" + jsonValueTypeToString(self().m_json.type()) +
|
||||
"\", expected one of \"" + (jsonValueTypeToString(T), ...) + "\""
|
||||
);
|
||||
}
|
||||
m_inferType = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool is() {
|
||||
if (this->isError()) return false;
|
||||
return self().m_json.is<T>();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
JsonMaybeValue& validate(JsonValueValidator<T> validator) {
|
||||
if (this->isError()) return *this;
|
||||
if (self().m_json.is<T>()) {
|
||||
if (!validator(self().m_json.as<T>())) {
|
||||
this->setError(self().m_hierarchy + ": Invalid value format");
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->setError(
|
||||
self().m_hierarchy + ": Invalid type \"" +
|
||||
std::string(jsonValueTypeToString(self().m_json.type())) + "\""
|
||||
);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
JsonMaybeValue& inferType() {
|
||||
if (this->isError() || !m_inferType) return *this;
|
||||
return this->as<getJsonType<T>()>();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
JsonMaybeValue& intoRaw(T& target) {
|
||||
if (this->isError()) return *this;
|
||||
target = self().m_json;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
JsonMaybeValue& into(T& target) {
|
||||
return this->intoAs<T, T>(target);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
JsonMaybeValue& into(std::optional<T>& target) {
|
||||
return this->intoAs<T, std::optional<T>>(target);
|
||||
}
|
||||
|
||||
template <class A, class T>
|
||||
JsonMaybeValue& intoAs(T& target) {
|
||||
this->inferType<A>();
|
||||
if (this->isError()) return *this;
|
||||
|
||||
if (self().m_json.is<A>()) {
|
||||
try {
|
||||
target = self().m_json.as<A>();
|
||||
}
|
||||
catch(matjson::JsonException const& e) {
|
||||
this->setError(
|
||||
self().m_hierarchy + ": Error parsing JSON: " + std::string(e.what())
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->setError(
|
||||
self().m_hierarchy + ": Invalid type \"" +
|
||||
std::string(jsonValueTypeToString(self().m_json.type())) + "\""
|
||||
);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T get() {
|
||||
this->inferType<T>();
|
||||
if (this->isError()) return T();
|
||||
if (self().m_json.is<T>()) {
|
||||
return self().m_json.as<T>();
|
||||
}
|
||||
return T();
|
||||
}
|
||||
|
||||
JsonMaybeObject obj();
|
||||
|
||||
template <class T>
|
||||
struct Iterator {
|
||||
std::vector<T> m_values;
|
||||
|
||||
using iterator = typename std::vector<T>::iterator;
|
||||
using const_iterator = typename std::vector<T>::const_iterator;
|
||||
|
||||
iterator begin() {
|
||||
return m_values.begin();
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return m_values.end();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return m_values.begin();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return m_values.end();
|
||||
}
|
||||
};
|
||||
|
||||
JsonMaybeValue at(size_t i);
|
||||
|
||||
Iterator<JsonMaybeValue> iterate();
|
||||
|
||||
Iterator<std::pair<std::string, JsonMaybeValue>> items();
|
||||
};
|
||||
|
||||
struct
|
||||
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
|
||||
GEODE_DLL JsonMaybeObject : JsonMaybeSomething {
|
||||
std::set<std::string> m_knownKeys;
|
||||
|
||||
JsonMaybeObject(
|
||||
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
|
||||
);
|
||||
|
||||
JsonMaybeSomething& self();
|
||||
|
||||
void addKnownKey(std::string const& key);
|
||||
|
||||
matjson::Value& json();
|
||||
|
||||
JsonMaybeValue emptyValue();
|
||||
|
||||
JsonMaybeValue has(std::string const& key);
|
||||
|
||||
JsonMaybeValue needs(std::string const& key);
|
||||
|
||||
void checkUnknownKeys();
|
||||
};
|
||||
|
||||
struct
|
||||
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
|
||||
GEODE_DLL JsonChecker {
|
||||
std::variant<std::monostate, std::string> m_result;
|
||||
matjson::Value& m_json;
|
||||
|
||||
JsonChecker(matjson::Value& json);
|
||||
|
||||
bool isError() const;
|
||||
|
||||
std::string getError() const;
|
||||
|
||||
JsonMaybeValue root(std::string const& hierarchy);
|
||||
};
|
||||
|
||||
class GEODE_DLL JsonExpectedValue final {
|
||||
protected:
|
||||
class Impl;
|
||||
|
@ -325,21 +104,14 @@ namespace geode {
|
|||
return this->getJSONRef();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
if (this->getJSONRef().is<T>()) {
|
||||
return this->getJSONRef().as<T>();
|
||||
}
|
||||
else {
|
||||
this->setError(
|
||||
"unexpected type {}",
|
||||
this->matJsonTypeToString(this->getJSONRef().type())
|
||||
);
|
||||
}
|
||||
}
|
||||
// matjson can throw variant exceptions too so you need to do this
|
||||
catch(std::exception const& e) {
|
||||
this->setError("unable to parse json: {}", e);
|
||||
auto res = this->getJSONRef().as<T>();
|
||||
if (res) {
|
||||
return res.unwrap();
|
||||
}
|
||||
this->setError(
|
||||
"unexpected type {}",
|
||||
this->matJsonTypeToString(this->getJSONRef().type())
|
||||
);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -451,6 +223,13 @@ namespace geode {
|
|||
* @returns The key, which is a no-op value if it didn't exist
|
||||
*/
|
||||
JsonExpectedValue has(std::string_view key);
|
||||
/**
|
||||
* Check if this object has an optional key. Asserts that this JSON
|
||||
* value is an object. If the key doesn't exist, or the value is null, returns a
|
||||
* `JsonExpectValue` that does nothing
|
||||
* @returns The key, which is a no-op value if it didn't exist, or was null
|
||||
*/
|
||||
JsonExpectedValue hasNullable(std::string_view key);
|
||||
/**
|
||||
* Check if this object has an optional key. Asserts that this JSON
|
||||
* value is an object. If the key doesn't exist, sets an error and
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <memory>
|
||||
#include <concepts>
|
||||
#include "terminate.hpp"
|
||||
|
||||
namespace geode::utils {
|
||||
|
||||
template <class FunctionType>
|
||||
class MiniFunction;
|
||||
|
||||
template <class Ret, class... Args>
|
||||
class MiniFunctionStateBase {
|
||||
public:
|
||||
virtual ~MiniFunctionStateBase() = default;
|
||||
virtual Ret call(Args... args) const = 0;
|
||||
virtual MiniFunctionStateBase* clone() const = 0;
|
||||
};
|
||||
|
||||
template <class Type, class Ret, class... Args>
|
||||
class MiniFunctionState final : public MiniFunctionStateBase<Ret, Args...> {
|
||||
public:
|
||||
Type m_func;
|
||||
|
||||
explicit MiniFunctionState(Type func) : m_func(func) {}
|
||||
|
||||
Ret call(Args... args) const override {
|
||||
return const_cast<Type&>(m_func)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
MiniFunctionStateBase<Ret, Args...>* clone() const override {
|
||||
return new MiniFunctionState(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Type, class Ret, class... Args>
|
||||
class MiniFunctionStatePointer final : public MiniFunctionStateBase<Ret, Args...> {
|
||||
public:
|
||||
Type m_func;
|
||||
|
||||
explicit MiniFunctionStatePointer(Type func) : m_func(func) {}
|
||||
|
||||
Ret call(Args... args) const override {
|
||||
return const_cast<Type&>(*m_func)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
MiniFunctionStateBase<Ret, Args...>* clone() const override {
|
||||
return new MiniFunctionStatePointer(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Type, class Ret, class Class, class... Args>
|
||||
class MiniFunctionStateMemberPointer final : public MiniFunctionStateBase<Ret, Class, Args...> {
|
||||
public:
|
||||
Type m_func;
|
||||
|
||||
explicit MiniFunctionStateMemberPointer(Type func) : m_func(func) {}
|
||||
|
||||
Ret call(Class self, Args... args) const override {
|
||||
return const_cast<Type&>(self->*m_func)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
MiniFunctionStateBase<Ret, Class, Args...>* clone() const override {
|
||||
return new MiniFunctionStateMemberPointer(*this);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Callable, class Ret, class... Args>
|
||||
concept MiniFunctionCallable = requires(Callable&& func, Args... args) {
|
||||
{ func(std::forward<Args>(args)...) } -> std::same_as<Ret>;
|
||||
};
|
||||
|
||||
template <class Ret, class... Args>
|
||||
class MiniFunction<Ret(Args...)> {
|
||||
public:
|
||||
using FunctionType = Ret(Args...);
|
||||
using StateType = MiniFunctionStateBase<Ret, Args...>;
|
||||
|
||||
private:
|
||||
StateType* m_state;
|
||||
|
||||
public:
|
||||
MiniFunction() : m_state(nullptr) {}
|
||||
|
||||
MiniFunction(std::nullptr_t) : MiniFunction() {}
|
||||
|
||||
MiniFunction(MiniFunction const& other) :
|
||||
m_state(other.m_state ? other.m_state->clone() : nullptr) {}
|
||||
|
||||
MiniFunction(MiniFunction&& other) : m_state(other.m_state) {
|
||||
other.m_state = nullptr;
|
||||
}
|
||||
|
||||
~MiniFunction() {
|
||||
if (m_state) delete m_state;
|
||||
}
|
||||
|
||||
template <class Callable>
|
||||
requires(MiniFunctionCallable<Callable, Ret, Args...> && !std::is_same_v<std::decay_t<Callable>, MiniFunction<FunctionType>>)
|
||||
MiniFunction(Callable&& func) :
|
||||
m_state(new MiniFunctionState<std::decay_t<Callable>, Ret, Args...>(std::forward<Callable>(func))) {}
|
||||
|
||||
template <class FunctionPointer>
|
||||
requires(!MiniFunctionCallable<FunctionPointer, Ret, Args...> && std::is_pointer_v<FunctionPointer> && std::is_function_v<std::remove_pointer_t<FunctionPointer>>)
|
||||
MiniFunction(FunctionPointer func) :
|
||||
m_state(new MiniFunctionStatePointer<FunctionPointer, Ret, Args...>(func)) {}
|
||||
|
||||
template <class MemberFunctionPointer>
|
||||
requires(std::is_member_function_pointer_v<MemberFunctionPointer>)
|
||||
MiniFunction(MemberFunctionPointer func) :
|
||||
m_state(new MiniFunctionStateMemberPointer<MemberFunctionPointer, Ret, Args...>(func)) {}
|
||||
|
||||
MiniFunction& operator=(MiniFunction const& other) {
|
||||
if (m_state) delete m_state;
|
||||
m_state = other.m_state ? other.m_state->clone() : nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MiniFunction& operator=(MiniFunction&& other) {
|
||||
if (m_state) delete m_state;
|
||||
m_state = other.m_state;
|
||||
other.m_state = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Ret operator()(Args... args) const {
|
||||
if (!m_state) {
|
||||
utils::terminate(
|
||||
"Attempted to call a MiniFunction that was never assigned "
|
||||
"any function, or one that has been moved"
|
||||
);
|
||||
}
|
||||
return m_state->call(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return m_state;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
#pragma once
|
||||
#include <Geode/modify/Modify.hpp>
|
||||
#include "cocos.hpp"
|
||||
|
||||
namespace geode::node_ids {
|
||||
using namespace cocos2d;
|
||||
|
||||
static constexpr int32_t GEODE_ID_PRIORITY = 0x100000;
|
||||
static constexpr int32_t GEODE_ID_PRIORITY = Priority::VeryEarlyPost;
|
||||
|
||||
template <class T = CCNode>
|
||||
requires std::is_base_of_v<CCNode, T>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../loader/Hook.hpp"
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
|
||||
namespace geode {
|
||||
namespace hook {
|
||||
|
|
|
@ -1,305 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include "../external/result/result.hpp"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <optional>
|
||||
|
||||
namespace geode {
|
||||
namespace impl {
|
||||
using DefaultValue = std::monostate;
|
||||
using DefaultError = std::string;
|
||||
template <class T>
|
||||
using WrappedResult = std::conditional_t<
|
||||
std::is_lvalue_reference_v<T>, std::reference_wrapper<std::remove_reference_t<T>>,
|
||||
std::remove_const_t<T>>;
|
||||
|
||||
template <class E = impl::DefaultError>
|
||||
class [[nodiscard]] Failure {
|
||||
public:
|
||||
WrappedResult<E> m_error;
|
||||
|
||||
Failure() = default;
|
||||
|
||||
template <class E2>
|
||||
requires(std::is_constructible_v<E, E2 const&>)
|
||||
explicit constexpr Failure(E2 const& e) : m_error(e) {}
|
||||
|
||||
template <class E2>
|
||||
requires(std::is_constructible_v<E, E2 &&>)
|
||||
explicit constexpr Failure(E2&& e) : m_error(std::move(e)) {}
|
||||
|
||||
E& error() & noexcept {
|
||||
return m_error;
|
||||
}
|
||||
|
||||
E const& error() const& noexcept {
|
||||
return m_error;
|
||||
}
|
||||
|
||||
E&& error() && noexcept {
|
||||
return static_cast<E&&>(m_error);
|
||||
}
|
||||
|
||||
E const&& error() const&& noexcept {
|
||||
return static_cast<E&&>(m_error);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T = impl::DefaultValue>
|
||||
class [[nodiscard]] Success {
|
||||
public:
|
||||
WrappedResult<T> m_value;
|
||||
|
||||
Success() = default;
|
||||
|
||||
template <class T2>
|
||||
requires(std::is_constructible_v<T, T2 const&>)
|
||||
explicit constexpr Success(T2 const& v) : m_value(v) {}
|
||||
|
||||
template <class T2>
|
||||
requires(std::is_constructible_v<T, T2 &&>)
|
||||
explicit constexpr Success(T2&& v) : m_value(std::forward<T2>(v)) {}
|
||||
|
||||
T& value() & noexcept {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
T const& value() const& noexcept {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
T&& value() && noexcept {
|
||||
return static_cast<T&&>(m_value);
|
||||
}
|
||||
|
||||
T const&& value() const&& noexcept {
|
||||
return static_cast<T&&>(m_value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <class T = impl::DefaultValue, class E = impl::DefaultError>
|
||||
class [[nodiscard]] Result : public cpp::result<T, E> {
|
||||
public:
|
||||
using Base = cpp::result<T, E>;
|
||||
using ValueType = typename Base::value_type;
|
||||
using ErrorType = typename Base::error_type;
|
||||
|
||||
using Base::result;
|
||||
|
||||
template <class U>
|
||||
requires(cpp::detail::result_is_implicit_value_convertible<T, U>::value)
|
||||
constexpr Result(U&& value) = delete;
|
||||
|
||||
template <class E2>
|
||||
requires(std::is_constructible_v<E, E2 const&>)
|
||||
constexpr Result(impl::Failure<E2> const& e) : Base(cpp::failure<E>(e.error())) {}
|
||||
|
||||
template <class E2>
|
||||
requires(std::is_constructible_v<E, E2 &&>)
|
||||
constexpr Result(impl::Failure<E2>&& e) : Base(cpp::failure<E>(std::move(e.error()))) {}
|
||||
|
||||
template <class T2>
|
||||
requires(std::is_constructible_v<T, T2 const&>)
|
||||
constexpr Result(impl::Success<T2> const& s) : Base(s.value()) {}
|
||||
|
||||
template <class T2>
|
||||
requires(std::is_constructible_v<T, T2 &&>)
|
||||
constexpr Result(impl::Success<T2>&& s) : Base(std::move(s.value())) {}
|
||||
|
||||
[[nodiscard]] constexpr explicit operator bool() const noexcept {
|
||||
return this->Base::operator bool();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isOk() const noexcept {
|
||||
return this->Base::has_value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool isErr() const noexcept {
|
||||
return this->Base::has_error();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr decltype(auto) unwrap() & {
|
||||
return this->Base::value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr decltype(auto) unwrap() const& {
|
||||
return this->Base::value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr decltype(auto) unwrap() && {
|
||||
return this->Base::value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr decltype(auto) unwrap() const&& {
|
||||
return this->Base::value();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr decltype(auto) unwrapErr() & {
|
||||
return this->Base::error();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr decltype(auto) unwrapErr() const& {
|
||||
return this->Base::error();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr decltype(auto) unwrapErr() && {
|
||||
return this->Base::error();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr decltype(auto) unwrapErr() const&& {
|
||||
return this->Base::error();
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
requires(std::is_constructible_v<T, T &&>)
|
||||
[[nodiscard]] Result<T, std::string> expect(std::string const& format, Args&&... args) {
|
||||
if (this->isErr()) {
|
||||
return impl::Failure<std::string>(fmt::format(
|
||||
fmt::runtime(format), std::forward<Args>(args)...,
|
||||
fmt::arg("error", this->unwrapErr())
|
||||
));
|
||||
}
|
||||
else {
|
||||
return std::move(*this);
|
||||
}
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
requires(std::is_constructible_v<T, T const&>)
|
||||
[[nodiscard]] Result<T, std::string> expect(std::string const& format, Args&&... args)
|
||||
const {
|
||||
if (this->isErr()) {
|
||||
return impl::Failure<std::string>(fmt::format(
|
||||
fmt::runtime(format), std::forward<Args>(args)...,
|
||||
fmt::arg("error", this->unwrapErr())
|
||||
));
|
||||
}
|
||||
else {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
template <class U>
|
||||
[[nodiscard]] constexpr decltype(auto) unwrapOr(U&& val) && {
|
||||
return this->Base::value_or(std::forward<U>(val));
|
||||
}
|
||||
|
||||
template <class U>
|
||||
[[nodiscard]] constexpr decltype(auto) unwrapOr(U&& val) const& {
|
||||
return this->Base::value_or(std::forward<U>(val));
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr decltype(auto) unwrapOrDefault() && requires std::is_default_constructible_v<T> {
|
||||
return this->Base::value_or(T());
|
||||
}
|
||||
[[nodiscard]] constexpr decltype(auto) unwrapOrDefault() const& requires std::is_default_constructible_v<T> {
|
||||
return this->Base::value_or(T());
|
||||
}
|
||||
|
||||
template <class U>
|
||||
[[nodiscard]] constexpr decltype(auto) errorOr(U&& val) && {
|
||||
return this->Base::error_or(std::forward<U>(val));
|
||||
}
|
||||
|
||||
template <class U>
|
||||
[[nodiscard]] constexpr decltype(auto) errorOr(U&& val) const& {
|
||||
return this->Base::error_or(std::forward<U>(val));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the result into an optional containing the value if Ok, and
|
||||
* nullopt if Err
|
||||
*/
|
||||
[[nodiscard]] constexpr std::optional<T> ok() const& {
|
||||
if (this->isOk()) {
|
||||
return this->unwrap();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the result into an optional containing the value if Ok, and
|
||||
* nullopt if Err
|
||||
*/
|
||||
[[nodiscard]] constexpr std::optional<T> ok() && {
|
||||
if (this->isOk()) {
|
||||
return this->unwrap();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the result into an optional containing the error if Err, and
|
||||
* nullopt if Ok
|
||||
*/
|
||||
[[nodiscard]] constexpr std::optional<E> err() const& {
|
||||
if (this->isErr()) {
|
||||
return this->unwrapErr();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the result into an optional containing the error if Err, and
|
||||
* nullopt if Ok
|
||||
*/
|
||||
[[nodiscard]] constexpr std::optional<E> err() && {
|
||||
if (this->isErr()) {
|
||||
return this->unwrapErr();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely disregard the result. Only recommended if the result is
|
||||
* inconsequential
|
||||
*/
|
||||
constexpr void disregard() && {}
|
||||
};
|
||||
|
||||
template <class T = impl::DefaultValue>
|
||||
constexpr impl::Success<T> Ok() {
|
||||
return impl::Success<T>();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
constexpr impl::Success<T> Ok(T value) {
|
||||
// DO NOT MAKE THE PARAMETER T&&!!!! THAT WILL CAUSE C++ TO DO UNEXPECTED
|
||||
// IMPLICIT MOVES FOR EXAMPLE WHEN DOING `Ok(unordered_map.at(value))`
|
||||
return impl::Success<T>(std::forward<T>(value));
|
||||
}
|
||||
|
||||
template <class E>
|
||||
constexpr impl::Failure<E> Err(E error) {
|
||||
return impl::Failure<E>(std::forward<E>(error));
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
inline impl::Failure<std::string> Err(std::string const& format, Args&&... args) {
|
||||
return impl::Failure<std::string>(
|
||||
fmt::format(fmt::runtime(format), std::forward<Args>(args)...)
|
||||
);
|
||||
}
|
||||
|
||||
#define GEODE_UNWRAP_INTO(into, ...) \
|
||||
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
|
||||
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
|
||||
return geode::Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
|
||||
} \
|
||||
into = std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrap())
|
||||
|
||||
#define GEODE_UNWRAP(...) \
|
||||
do { \
|
||||
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
|
||||
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
|
||||
return geode::Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
|
||||
} \
|
||||
} while(false)
|
||||
}
|
|
@ -1,13 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "general.hpp"
|
||||
#include "MiniFunction.hpp"
|
||||
#include "../loader/Event.hpp"
|
||||
#include "../loader/Loader.hpp"
|
||||
#include <mutex>
|
||||
#include <string_view>
|
||||
#include <coroutine>
|
||||
|
||||
namespace geode {
|
||||
namespace geode_internal {
|
||||
template <class T, class P>
|
||||
struct TaskPromise;
|
||||
|
||||
template <class T, class P>
|
||||
struct TaskAwaiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tasks represent an asynchronous operation that will be finished at some
|
||||
* unknown point in the future. Tasks can report their progress, and will
|
||||
|
@ -141,7 +149,7 @@ namespace geode {
|
|||
|
||||
class PrivateMarker final {};
|
||||
|
||||
static std::shared_ptr<Handle> create(std::string_view const name) {
|
||||
static std::shared_ptr<Handle> create(std::string_view name) {
|
||||
return std::make_shared<Handle>(PrivateMarker(), name);
|
||||
}
|
||||
|
||||
|
@ -153,8 +161,14 @@ namespace geode {
|
|||
template <std::move_constructible T2, std::move_constructible P2>
|
||||
friend class Task;
|
||||
|
||||
template <class, class>
|
||||
friend struct geode_internal::TaskPromise;
|
||||
|
||||
template <class, class>
|
||||
friend struct geode_internal::TaskAwaiter;
|
||||
|
||||
public:
|
||||
Handle(PrivateMarker, std::string_view const name) : m_name(name) {}
|
||||
Handle(PrivateMarker, std::string_view name) : m_name(name) {}
|
||||
~Handle() {
|
||||
// If this Task was still pending when the Handle was destroyed,
|
||||
// it can no longer be listened to so just cancel and cleanup
|
||||
|
@ -249,11 +263,11 @@ namespace geode {
|
|||
|
||||
using Value = T;
|
||||
using Progress = P;
|
||||
using PostResult = utils::MiniFunction<void(Result)>;
|
||||
using PostProgress = utils::MiniFunction<void(P)>;
|
||||
using HasBeenCancelled = utils::MiniFunction<bool()>;
|
||||
using Run = utils::MiniFunction<Result(PostProgress, HasBeenCancelled)>;
|
||||
using RunWithCallback = utils::MiniFunction<void(PostResult, PostProgress, HasBeenCancelled)>;
|
||||
using PostResult = std::function<void(Result&&)>;
|
||||
using PostProgress = std::function<void(P)>;
|
||||
using HasBeenCancelled = std::function<bool()>;
|
||||
using Run = std::function<Result(PostProgress, HasBeenCancelled)>;
|
||||
using RunWithCallback = std::function<void(PostResult, PostProgress, HasBeenCancelled)>;
|
||||
|
||||
using Callback = void(Event*);
|
||||
|
||||
|
@ -308,6 +322,12 @@ namespace geode {
|
|||
template <std::move_constructible T2, std::move_constructible P2>
|
||||
friend class Task;
|
||||
|
||||
template <class, class>
|
||||
friend struct geode_internal::TaskPromise;
|
||||
|
||||
template <class, class>
|
||||
friend struct geode_internal::TaskAwaiter;
|
||||
|
||||
public:
|
||||
// Allow default-construction
|
||||
Task() : m_handle(nullptr) {}
|
||||
|
@ -394,7 +414,7 @@ namespace geode {
|
|||
* Create a new Task that is immediately cancelled
|
||||
* @param name The name of the Task; used for debugging
|
||||
*/
|
||||
static Task cancelled(std::string_view const name = "<Cancelled Task>") {
|
||||
static Task cancelled(std::string_view name = "<Cancelled Task>") {
|
||||
auto task = Task(Handle::create(name));
|
||||
Task::cancel(task.m_handle);
|
||||
return task;
|
||||
|
@ -405,7 +425,7 @@ namespace geode {
|
|||
* @param value The value the Task shall be finished with
|
||||
* @param name The name of the Task; used for debugging
|
||||
*/
|
||||
static Task immediate(T value, std::string_view const name = "<Immediate Task>") {
|
||||
static Task immediate(T value, std::string_view name = "<Immediate Task>") {
|
||||
auto task = Task(Handle::create(name));
|
||||
Task::finish(task.m_handle, std::move(value));
|
||||
return task;
|
||||
|
@ -417,7 +437,7 @@ namespace geode {
|
|||
* function MUST be synchronous - Task creates the thread for you!
|
||||
* @param name The name of the Task; used for debugging
|
||||
*/
|
||||
static Task run(Run&& body, std::string_view const name = "<Task>") {
|
||||
static Task run(Run&& body, std::string_view name = "<Task>") {
|
||||
auto task = Task(Handle::create(name));
|
||||
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
|
||||
utils::thread::setName(fmt::format("Task '{}'", name));
|
||||
|
@ -450,7 +470,7 @@ namespace geode {
|
|||
* calls will always be ignored
|
||||
* @param name The name of the Task; used for debugging
|
||||
*/
|
||||
static Task runWithCallback(RunWithCallback&& body, std::string_view const name = "<Callback Task>") {
|
||||
static Task runWithCallback(RunWithCallback&& body, std::string_view name = "<Callback Task>") {
|
||||
auto task = Task(Handle::create(name));
|
||||
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
|
||||
utils::thread::setName(fmt::format("Task '{}'", name));
|
||||
|
@ -485,7 +505,7 @@ namespace geode {
|
|||
* were cancelled!
|
||||
*/
|
||||
template <std::move_constructible NP>
|
||||
static Task<std::vector<T*>, std::monostate> all(std::vector<Task<T, NP>>&& tasks, std::string_view const name = "<Multiple Tasks>") {
|
||||
static Task<std::vector<T*>, std::monostate> all(std::vector<Task<T, NP>>&& tasks, std::string_view name = "<Multiple Tasks>") {
|
||||
using AllTask = Task<std::vector<T*>, std::monostate>;
|
||||
|
||||
// If there are no tasks, return an immediate task that does nothing
|
||||
|
@ -582,7 +602,7 @@ namespace geode {
|
|||
* the mapped task is appended to the end
|
||||
*/
|
||||
template <class ResultMapper, class ProgressMapper, class OnCancelled>
|
||||
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, OnCancelled&& onCancelled, std::string_view const name = "<Mapping Task>") const {
|
||||
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, OnCancelled&& onCancelled, std::string_view name = "<Mapping Task>") const {
|
||||
using T2 = decltype(resultMapper(std::declval<T*>()));
|
||||
using P2 = decltype(progressMapper(std::declval<P*>()));
|
||||
|
||||
|
@ -652,7 +672,7 @@ namespace geode {
|
|||
* @param name The name of the Task; used for debugging. The name of
|
||||
* the mapped task is appended to the end
|
||||
*/ template <class ResultMapper, class ProgressMapper>
|
||||
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, std::string_view const name = "<Mapping Task>") const {
|
||||
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, std::string_view name = "<Mapping Task>") const {
|
||||
return this->map(std::move(resultMapper), std::move(progressMapper), +[]() {}, name);
|
||||
}
|
||||
|
||||
|
@ -670,7 +690,7 @@ namespace geode {
|
|||
* the mapped task is appended to the end
|
||||
*/ template <class ResultMapper>
|
||||
requires std::copy_constructible<P>
|
||||
auto map(ResultMapper&& resultMapper, std::string_view const name = "<Mapping Task>") const {
|
||||
auto map(ResultMapper&& resultMapper, std::string_view name = "<Mapping Task>") const {
|
||||
return this->map(std::move(resultMapper), +[](P* p) -> P { return *p; }, name);
|
||||
}
|
||||
|
||||
|
@ -752,7 +772,94 @@ namespace geode {
|
|||
this->listen(std::move(onResult), [](auto const&) {}, [] {});
|
||||
}
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, Event* e) {
|
||||
/**
|
||||
* Create a new Task that listens to this Task and maps the values using
|
||||
* the provided function. The new Task will only start when this Task finishes.
|
||||
* @param mapper Function that makes a new task given the finished value of this task.
|
||||
* The function signature should be `Task<NewType, NewProgress>(T*)`, and it will be executed
|
||||
* on the main thread.
|
||||
* @param name The name of the Task; used for debugging.
|
||||
* @return The new Task that will run when this Task finishes.
|
||||
* @note Progress from this task is not sent through, only progress from the new task is.
|
||||
*/
|
||||
template <std::invocable<T*> Mapper>
|
||||
auto chain(Mapper mapper, std::string_view name = "<Chained Task>") const -> decltype(mapper(std::declval<T*>())) {
|
||||
using NewTask = decltype(mapper(std::declval<T*>()));
|
||||
using NewType = typename NewTask::Value;
|
||||
using NewProgress = typename NewTask::Progress;
|
||||
|
||||
std::unique_lock<std::recursive_mutex> lock(m_handle->m_mutex);
|
||||
|
||||
if (m_handle->m_status == Status::Cancelled) {
|
||||
// if the current task has been cancelled already, make an immediate cancelled task
|
||||
return NewTask::cancelled();
|
||||
}
|
||||
else if (m_handle->m_status == Status::Finished) {
|
||||
// if the current task is already done, we can just call the mapper directly
|
||||
return mapper(&*m_handle->m_resultValue);
|
||||
}
|
||||
|
||||
// otherwise, make a wrapper task that waits for the current task to finish,
|
||||
// and then runs the mapper on the result. this new task will also wait for the task
|
||||
// created by the mapper to finish, and will just forward the values through.
|
||||
// do this because we cant really change the handle of the task we already returned
|
||||
|
||||
NewTask task = NewTask::Handle::create(fmt::format("{} <- {}", name, m_handle->m_name));
|
||||
|
||||
task.m_handle->m_extraData = std::make_unique<typename NewTask::Handle::ExtraData>(
|
||||
// make the first event listener that waits for the current task
|
||||
static_cast<void*>(new EventListener<Task>(
|
||||
[handle = std::weak_ptr(task.m_handle), mapper = std::move(mapper)](Event* event) mutable {
|
||||
if (auto v = event->getValue()) {
|
||||
auto newInnerTask = mapper(v);
|
||||
// this is scary.. but it doesn't seem to crash lol
|
||||
handle.lock()->m_extraData = std::make_unique<typename NewTask::Handle::ExtraData>(
|
||||
// make the second event listener that waits for the mapper's task
|
||||
// and just forwards everything through
|
||||
static_cast<void*>(new EventListener<NewTask>(
|
||||
[handle](Event* event) mutable {
|
||||
if (auto v = event->getValue()) {
|
||||
NewTask::finish(handle.lock(), std::move(*v));
|
||||
}
|
||||
else if (auto p = event->getProgress()) {
|
||||
NewTask::progress(handle.lock(), std::move(*p));
|
||||
}
|
||||
else if (event->isCancelled()) {
|
||||
NewTask::cancel(handle.lock());
|
||||
}
|
||||
},
|
||||
std::move(newInnerTask)
|
||||
)),
|
||||
+[](void* ptr) {
|
||||
delete static_cast<EventListener<NewTask>*>(ptr);
|
||||
},
|
||||
+[](void* ptr) {
|
||||
static_cast<EventListener<NewTask>*>(ptr)->getFilter().cancel();
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (auto p = event->getProgress()) {
|
||||
// no guarantee P and NewProgress are compatible
|
||||
// nor does it seem like the intended behavior?
|
||||
// TODO: maybe add a mapper for progress?
|
||||
}
|
||||
else if (event->isCancelled()) {
|
||||
NewTask::cancel(handle.lock());
|
||||
}
|
||||
},
|
||||
*this
|
||||
)),
|
||||
+[](void* ptr) {
|
||||
delete static_cast<EventListener<Task>*>(ptr);
|
||||
},
|
||||
+[](void* ptr) {
|
||||
static_cast<EventListener<Task>*>(ptr)->getFilter().cancel();
|
||||
}
|
||||
);
|
||||
return task;
|
||||
}
|
||||
|
||||
ListenerResult handle(std::function<Callback> fn, Event* e) {
|
||||
if (e->m_handle == m_handle && (!e->m_for || e->m_for == m_listener)) {
|
||||
fn(e);
|
||||
}
|
||||
|
@ -797,3 +904,132 @@ namespace geode {
|
|||
|
||||
static_assert(is_filter<Task<int>>, "The Task class must be a valid event filter!");
|
||||
}
|
||||
|
||||
// - C++20 coroutine support for Task - //
|
||||
|
||||
// Example usage (function must return a Task):
|
||||
// ```
|
||||
// Task<int> someTask() {
|
||||
// auto response = co_await web::WebRequest().get("https://example.com");
|
||||
// co_return response.code();
|
||||
// }
|
||||
// ```
|
||||
// This will create a Task that will finish with the response code of the
|
||||
// web request.
|
||||
//
|
||||
// Note: If the Task the coroutine is waiting on is cancelled, the coroutine
|
||||
// will be destroyed and the Task will be cancelled as well. If the Task returned
|
||||
// by the coroutine is cancelled, the coroutine will be destroyed as well and execution
|
||||
// stops as soon as possible.
|
||||
//
|
||||
// The body of the coroutine is ran in whatever thread it got called in.
|
||||
// TODO: maybe guarantee main thread?
|
||||
//
|
||||
// The coroutine can also yield progress values using `co_yield`:
|
||||
// ```
|
||||
// Task<std::string, int> someTask() {
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// co_yield i;
|
||||
// }
|
||||
// co_return "done!";
|
||||
// }
|
||||
// ```
|
||||
|
||||
namespace geode {
|
||||
namespace geode_internal {
|
||||
template <class T, class P>
|
||||
struct TaskPromise {
|
||||
using MyTask = Task<T, P>;
|
||||
std::weak_ptr<typename MyTask::Handle> m_handle;
|
||||
|
||||
~TaskPromise() {
|
||||
// does nothing if its not pending
|
||||
MyTask::cancel(m_handle.lock());
|
||||
}
|
||||
|
||||
std::suspend_never initial_suspend() noexcept { return {}; }
|
||||
std::suspend_never final_suspend() noexcept { return {}; }
|
||||
// TODO: do something here?
|
||||
void unhandled_exception() {}
|
||||
|
||||
MyTask get_return_object() {
|
||||
auto handle = MyTask::Handle::create("<Coroutine Task>");
|
||||
m_handle = handle;
|
||||
return handle;
|
||||
}
|
||||
|
||||
void return_value(T x) {
|
||||
MyTask::finish(m_handle.lock(), std::move(x));
|
||||
}
|
||||
|
||||
std::suspend_never yield_value(P value) {
|
||||
MyTask::progress(m_handle.lock(), std::move(value));
|
||||
return {};
|
||||
}
|
||||
|
||||
bool isCancelled() {
|
||||
if (auto p = m_handle.lock()) {
|
||||
return p->is(MyTask::Status::Cancelled);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class P>
|
||||
struct TaskAwaiter {
|
||||
Task<T, P> task;
|
||||
|
||||
bool await_ready() {
|
||||
return task.isFinished();
|
||||
}
|
||||
|
||||
template <class U, class V>
|
||||
void await_suspend(std::coroutine_handle<TaskPromise<U, V>> handle) {
|
||||
if (handle.promise().isCancelled()) {
|
||||
handle.destroy();
|
||||
return;
|
||||
}
|
||||
// this should be fine because the parent task can only have
|
||||
// one pending task at a time
|
||||
auto parentHandle = handle.promise().m_handle.lock();
|
||||
if (!parentHandle) {
|
||||
handle.destroy();
|
||||
return;
|
||||
}
|
||||
parentHandle->m_extraData = std::make_unique<typename Task<U, V>::Handle::ExtraData>(
|
||||
static_cast<void*>(new EventListener<Task<T, P>>(
|
||||
[handle](auto* event) {
|
||||
if (event->getValue()) {
|
||||
handle.resume();
|
||||
}
|
||||
if (event->isCancelled()) {
|
||||
handle.destroy();
|
||||
}
|
||||
},
|
||||
task
|
||||
)),
|
||||
+[](void* ptr) {
|
||||
delete static_cast<EventListener<Task<T, P>>*>(ptr);
|
||||
},
|
||||
+[](void* ptr) {
|
||||
static_cast<EventListener<Task<T, P>>*>(ptr)->getFilter().cancel();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
T await_resume() {
|
||||
return std::move(*task.getFinishedValue());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class P>
|
||||
auto operator co_await(geode::Task<T, P> task) {
|
||||
return geode::geode_internal::TaskAwaiter<T, P>{task};
|
||||
}
|
||||
|
||||
template <class T, class P, class... Args>
|
||||
struct std::coroutine_traits<geode::Task<T, P>, Args...> {
|
||||
using promise_type = geode::geode_internal::TaskPromise<T, P>;
|
||||
};
|
|
@ -4,7 +4,8 @@
|
|||
#include <string_view>
|
||||
#include <matjson.hpp>
|
||||
#include <tuple>
|
||||
#include "../utils/Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace geode {
|
||||
enum class VersionCompare {
|
||||
|
@ -186,8 +187,6 @@ namespace geode {
|
|||
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
|
||||
}
|
||||
|
||||
[[deprecated("Use toNonVString or toVString instead")]]
|
||||
std::string toString(bool includeTag = true) const;
|
||||
std::string toVString(bool includeTag = true) const;
|
||||
std::string toNonVString(bool includeTag = true) const;
|
||||
|
||||
|
@ -258,25 +257,15 @@ namespace geode {
|
|||
template <class V>
|
||||
requires std::is_same_v<V, geode::VersionInfo> || std::is_same_v<V, geode::ComparableVersionInfo>
|
||||
struct matjson::Serialize<V> {
|
||||
static matjson::Value to_json(V const& info) {
|
||||
return info.toString();
|
||||
static geode::Result<V, std::string> fromJson(Value const& value) {
|
||||
GEODE_UNWRAP_INTO(auto str, value.asString());
|
||||
GEODE_UNWRAP_INTO(auto version, V::parse(str).mapErr([](auto&& err) {
|
||||
return fmt::format("Invalid version format: {}", err);
|
||||
}));
|
||||
return geode::Ok(version);
|
||||
}
|
||||
|
||||
static bool is_json(matjson::Value const& json) {
|
||||
if (json.is_string()) {
|
||||
auto ver = V::parse(json.as_string());
|
||||
return !ver.isErr();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static V from_json(matjson::Value const& json) {
|
||||
auto ver = V::parse(json.as_string());
|
||||
if (!ver) {
|
||||
throw matjson::JsonException(
|
||||
"Invalid version format: " + ver.unwrapErr()
|
||||
);
|
||||
}
|
||||
return ver.unwrap();
|
||||
static Value toJson(V const& value) {
|
||||
return Value(value.toString());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,22 +10,21 @@
|
|||
#include "../loader/Event.hpp"
|
||||
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||
#include <Geode/binding/CCMenuItemToggler.hpp>
|
||||
#include "MiniFunction.hpp"
|
||||
#include "../ui/Layout.hpp"
|
||||
#include "../ui/SpacerNode.hpp"
|
||||
|
||||
// support converting ccColor3B / ccColor4B to / from json
|
||||
|
||||
template <>
|
||||
struct matjson::Serialize<cocos2d::ccColor3B> {
|
||||
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor3B const& color);
|
||||
static cocos2d::ccColor3B GEODE_DLL from_json(matjson::Value const& color);
|
||||
static bool GEODE_DLL is_json(matjson::Value const& json);
|
||||
static geode::Result<cocos2d::ccColor3B> GEODE_DLL fromJson(Value const& value);
|
||||
static Value GEODE_DLL toJson(cocos2d::ccColor3B const& value);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct matjson::Serialize<cocos2d::ccColor4B> {
|
||||
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor4B const& color);
|
||||
static cocos2d::ccColor4B GEODE_DLL from_json(matjson::Value const& color);
|
||||
static bool GEODE_DLL is_json(matjson::Value const& json);
|
||||
static geode::Result<cocos2d::ccColor4B> GEODE_DLL fromJson(Value const& value);
|
||||
static Value GEODE_DLL toJson(cocos2d::ccColor4B const& value);
|
||||
};
|
||||
|
||||
// operators for CC geometry
|
||||
|
@ -615,18 +614,6 @@ namespace geode::cocos {
|
|||
return static_cast<T*>(x->getChildren()->objectAtIndex(i));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get nth child that is a given type. Checks bounds.
|
||||
* @returns Child at index cast to the given type,
|
||||
* or nullptr if index exceeds bounds
|
||||
*/
|
||||
|
||||
template <class Type = cocos2d::CCNode>
|
||||
[[deprecated("Use CCNode::getChildByType instead")]]
|
||||
static Type* getChildOfType(cocos2d::CCNode* node, int index) {
|
||||
return node->getChildByType<Type>(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a node, or create a default one if it's
|
||||
* nullptr. Syntactic sugar function
|
||||
|
@ -670,7 +657,7 @@ namespace geode::cocos {
|
|||
*/
|
||||
GEODE_DLL cocos2d::CCScene* switchToScene(cocos2d::CCLayer* layer);
|
||||
|
||||
using CreateLayerFunc = utils::MiniFunction<cocos2d::CCLayer*()>;
|
||||
using CreateLayerFunc = std::function<cocos2d::CCLayer*()>;
|
||||
|
||||
/**
|
||||
* Reload textures, overwriting the scene to return to after the loading
|
||||
|
@ -738,7 +725,7 @@ namespace geode::cocos {
|
|||
* there is none
|
||||
*/
|
||||
template <class Type = cocos2d::CCNode>
|
||||
Type* findFirstChildRecursive(cocos2d::CCNode* node, utils::MiniFunction<bool(Type*)> predicate) {
|
||||
Type* findFirstChildRecursive(cocos2d::CCNode* node, std::function<bool(Type*)> predicate) {
|
||||
if (cast::typeinfo_cast<Type*>(node) && predicate(static_cast<Type*>(node)))
|
||||
return static_cast<Type*>(node);
|
||||
|
||||
|
@ -856,30 +843,6 @@ namespace geode::cocos {
|
|||
return {color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f};
|
||||
}
|
||||
|
||||
[[deprecated("This function may have unintended behavior, use cc3bFromHexString or manually expand the color instead")]]
|
||||
constexpr cocos2d::ccColor3B cc3x(int hexValue) {
|
||||
if (hexValue <= 0xf)
|
||||
return cocos2d::ccColor3B{
|
||||
static_cast<GLubyte>(hexValue * 17),
|
||||
static_cast<GLubyte>(hexValue * 17),
|
||||
static_cast<GLubyte>(hexValue * 17)};
|
||||
if (hexValue <= 0xff)
|
||||
return cocos2d::ccColor3B{
|
||||
static_cast<GLubyte>(hexValue),
|
||||
static_cast<GLubyte>(hexValue),
|
||||
static_cast<GLubyte>(hexValue)};
|
||||
if (hexValue <= 0xfff)
|
||||
return cocos2d::ccColor3B{
|
||||
static_cast<GLubyte>((hexValue >> 8 & 0xf) * 17),
|
||||
static_cast<GLubyte>((hexValue >> 4 & 0xf) * 17),
|
||||
static_cast<GLubyte>((hexValue >> 0 & 0xf) * 17)};
|
||||
else
|
||||
return cocos2d::ccColor3B{
|
||||
static_cast<GLubyte>(hexValue >> 16 & 0xff),
|
||||
static_cast<GLubyte>(hexValue >> 8 & 0xff),
|
||||
static_cast<GLubyte>(hexValue >> 0 & 0xff)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a ccColor3B from a hexadecimal string. The string may contain
|
||||
* a leading '#'
|
||||
|
@ -916,7 +879,7 @@ namespace geode::cocos {
|
|||
}
|
||||
|
||||
template <typename T, typename C, typename = std::enable_if_t<std::is_pointer_v<C>>>
|
||||
static cocos2d::CCArray* vectorToCCArray(std::vector<T> const& vec, utils::MiniFunction<C(T)> convFunc) {
|
||||
static cocos2d::CCArray* vectorToCCArray(std::vector<T> const& vec, std::function<C(T)> convFunc) {
|
||||
auto res = cocos2d::CCArray::createWithCapacity(vec.size());
|
||||
for (auto const& item : vec)
|
||||
res->addObject(convFunc(item));
|
||||
|
@ -943,7 +906,7 @@ namespace geode::cocos {
|
|||
template <
|
||||
typename K, typename V, typename C,
|
||||
typename = std::enable_if_t<std::is_same_v<C, std::string> || std::is_same_v<C, intptr_t>>>
|
||||
static cocos2d::CCDictionary* mapToCCDict(std::map<K, V> const& map, utils::MiniFunction<C(K)> convFunc) {
|
||||
static cocos2d::CCDictionary* mapToCCDict(std::map<K, V> const& map, std::function<C(K)> convFunc) {
|
||||
auto res = cocos2d::CCDictionary::create();
|
||||
for (auto const& [key, value] : map)
|
||||
res->setObject(value, convFunc(key));
|
||||
|
@ -969,10 +932,10 @@ namespace std {
|
|||
};
|
||||
|
||||
template <typename T>
|
||||
struct std::hash<geode::WeakRef<T>> {
|
||||
struct hash<geode::WeakRef<T>> {
|
||||
size_t operator()(geode::WeakRef<T> const& ref) const {
|
||||
// the explicit template argument is needed here because it would otherwise cast to WeakRef and recurse
|
||||
return hash<std::shared_ptr<geode::WeakRefController>>{}(ref.m_controller);
|
||||
return std::hash<std::shared_ptr<geode::WeakRefController>>{}(ref.m_controller);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1216,11 +1179,11 @@ namespace geode::cocos {
|
|||
template <class Node>
|
||||
class LambdaCallback : public cocos2d::CCObject {
|
||||
public:
|
||||
utils::MiniFunction<void(Node*)> m_callback;
|
||||
std::function<void(Node*)> m_callback;
|
||||
|
||||
static LambdaCallback* create(utils::MiniFunction<void(Node*)>&& callback) {
|
||||
static LambdaCallback* create(std::function<void(Node*)> callback) {
|
||||
auto ret = new (std::nothrow) LambdaCallback();
|
||||
if (ret->init(std::forward<std::remove_reference_t<decltype(callback)>>(callback))) {
|
||||
if (ret->init(std::move(callback))) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
|
@ -1228,8 +1191,8 @@ namespace geode::cocos {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool init(utils::MiniFunction<void(Node*)>&& callback) {
|
||||
m_callback = std::forward<std::remove_reference_t<decltype(callback)>>(callback);
|
||||
bool init(std::function<void(Node*)> callback) {
|
||||
m_callback = std::move(callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1240,20 +1203,20 @@ namespace geode::cocos {
|
|||
|
||||
public:
|
||||
static cocos2d::CCMenuItem* create(
|
||||
utils::MiniFunction<void(cocos2d::CCMenuItem*)>&& callback
|
||||
std::function<void(cocos2d::CCMenuItem*)> callback
|
||||
) {
|
||||
auto item = cocos2d::CCMenuItem::create();
|
||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
assignCallback(item, std::move(callback));
|
||||
return item;
|
||||
}
|
||||
|
||||
static cocos2d::CCMenuItemSprite* createSprite(
|
||||
cocos2d::CCNode* normalSprite,
|
||||
cocos2d::CCNode* selectedSprite,
|
||||
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback
|
||||
std::function<void(cocos2d::CCMenuItemSprite*)> callback
|
||||
) {
|
||||
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite);
|
||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
assignCallback(item, std::move(callback));
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -1261,57 +1224,57 @@ namespace geode::cocos {
|
|||
cocos2d::CCNode* normalSprite,
|
||||
cocos2d::CCNode* selectedSprite,
|
||||
cocos2d::CCNode* disabledSprite,
|
||||
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback
|
||||
std::function<void(cocos2d::CCMenuItemSprite*)> callback
|
||||
) {
|
||||
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite, disabledSprite);
|
||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
assignCallback(item, std::move(callback));
|
||||
return item;
|
||||
}
|
||||
|
||||
static CCMenuItemSpriteExtra* createSpriteExtra(
|
||||
cocos2d::CCNode* normalSprite,
|
||||
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
|
||||
std::function<void(CCMenuItemSpriteExtra*)> callback
|
||||
) {
|
||||
auto item = CCMenuItemSpriteExtra::create(normalSprite, nullptr, nullptr);
|
||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
assignCallback(item, std::move(callback));
|
||||
return item;
|
||||
}
|
||||
|
||||
static CCMenuItemSpriteExtra* createSpriteExtraWithFilename(
|
||||
std::string_view normalSpriteName,
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
|
||||
std::function<void(CCMenuItemSpriteExtra*)> callback
|
||||
) {
|
||||
auto sprite = cocos2d::CCSprite::create(normalSpriteName.data());
|
||||
sprite->setScale(scale);
|
||||
|
||||
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
return createSpriteExtra(sprite, std::move(callback));
|
||||
}
|
||||
|
||||
static CCMenuItemSpriteExtra* createSpriteExtraWithFrameName(
|
||||
std::string_view normalSpriteName,
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
|
||||
std::function<void(CCMenuItemSpriteExtra*)> callback
|
||||
) {
|
||||
auto sprite = cocos2d::CCSprite::createWithSpriteFrameName(normalSpriteName.data());
|
||||
sprite->setScale(scale);
|
||||
|
||||
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
return createSpriteExtra(sprite, std::move(callback));
|
||||
}
|
||||
|
||||
static CCMenuItemToggler* createToggler(
|
||||
cocos2d::CCNode* onSprite,
|
||||
cocos2d::CCNode* offSprite,
|
||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
||||
std::function<void(CCMenuItemToggler*)> callback
|
||||
) {
|
||||
auto item = CCMenuItemToggler::create(offSprite, onSprite, nullptr, nullptr);
|
||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
assignCallback(item, std::move(callback));
|
||||
return item;
|
||||
}
|
||||
|
||||
static CCMenuItemToggler* createTogglerWithStandardSprites(
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
||||
std::function<void(CCMenuItemToggler*)> callback
|
||||
) {
|
||||
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png");
|
||||
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOn_001.png");
|
||||
|
@ -1319,14 +1282,14 @@ namespace geode::cocos {
|
|||
offSprite->setScale(scale);
|
||||
onSprite->setScale(scale);
|
||||
|
||||
return createToggler(onSprite, offSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
return createToggler(onSprite, offSprite, std::move(callback));
|
||||
}
|
||||
|
||||
static CCMenuItemToggler* createTogglerWithFilename(
|
||||
std::string_view onSpriteName,
|
||||
std::string_view offSpriteName,
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
||||
std::function<void(CCMenuItemToggler*)> callback
|
||||
) {
|
||||
auto offSprite = cocos2d::CCSprite::create(offSpriteName.data());
|
||||
auto onSprite = cocos2d::CCSprite::create(onSpriteName.data());
|
||||
|
@ -1334,14 +1297,14 @@ namespace geode::cocos {
|
|||
offSprite->setScale(scale);
|
||||
onSprite->setScale(scale);
|
||||
|
||||
return createToggler(onSprite, offSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
return createToggler(onSprite, offSprite, std::move(callback));
|
||||
}
|
||||
|
||||
static CCMenuItemToggler* createTogglerWithFrameName(
|
||||
std::string_view onSpriteName,
|
||||
std::string_view offSpriteName,
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
||||
std::function<void(CCMenuItemToggler*)> callback
|
||||
) {
|
||||
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName(offSpriteName.data());
|
||||
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName(onSpriteName.data());
|
||||
|
@ -1349,15 +1312,15 @@ namespace geode::cocos {
|
|||
offSprite->setScale(scale);
|
||||
onSprite->setScale(scale);
|
||||
|
||||
return createToggler(onSprite, offSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
return createToggler(onSprite, offSprite, std::move(callback));
|
||||
}
|
||||
|
||||
template <class Node>
|
||||
static void assignCallback(
|
||||
cocos2d::CCMenuItem* item,
|
||||
utils::MiniFunction<void(Node*)>&& callback
|
||||
std::function<void(Node*)> callback
|
||||
) {
|
||||
auto lambda = LambdaCallback<Node>::create(std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
auto lambda = LambdaCallback<Node>::create(std::move(callback));
|
||||
item->setTarget(lambda, menu_selector(LambdaCallback<Node>::execute));
|
||||
item->setUserObject("lambda-callback", lambda);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "general.hpp"
|
||||
#include "../loader/Event.hpp"
|
||||
#include "Task.hpp"
|
||||
|
@ -13,14 +13,15 @@
|
|||
|
||||
template <>
|
||||
struct matjson::Serialize<std::filesystem::path> {
|
||||
static matjson::Value to_json(std::filesystem::path const& path) {
|
||||
return path.string();
|
||||
static geode::Result<std::filesystem::path, std::string> fromJson(Value const& value)
|
||||
{
|
||||
GEODE_UNWRAP_INTO(auto str, value.asString());
|
||||
return geode::Ok(std::filesystem::path(str).make_preferred());
|
||||
}
|
||||
static std::filesystem::path from_json(matjson::Value const& value) {
|
||||
return std::filesystem::path(value.as_string()).make_preferred();
|
||||
}
|
||||
static bool is_json(matjson::Value const& value) {
|
||||
return value.is_string();
|
||||
|
||||
static Value toJson(std::filesystem::path const& value)
|
||||
{
|
||||
return Value(value.string());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -32,10 +33,7 @@ namespace geode::utils::file {
|
|||
template <class T>
|
||||
Result<T> readFromJson(std::filesystem::path const& file) {
|
||||
GEODE_UNWRAP_INTO(auto json, readJson(file));
|
||||
if (!json.is<T>()) {
|
||||
return Err("JSON is not of type {}", typeid(T).name());
|
||||
}
|
||||
return Ok(json.as<T>());
|
||||
return json.as<T>();
|
||||
}
|
||||
|
||||
GEODE_DLL Result<> writeString(std::filesystem::path const& path, std::string const& data);
|
||||
|
@ -162,7 +160,7 @@ namespace geode::utils::file {
|
|||
* @param callback Callback to call with the progress of the unzip operation
|
||||
*/
|
||||
void setProgressCallback(
|
||||
utils::MiniFunction<void(uint32_t, uint32_t)> callback
|
||||
std::function<void(uint32_t, uint32_t)> callback
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -213,7 +211,7 @@ namespace geode::utils::file {
|
|||
);
|
||||
|
||||
static Result<> intoDir(
|
||||
utils::MiniFunction<void(uint32_t, uint32_t)> progressCallback,
|
||||
std::function<void(uint32_t, uint32_t)> progressCallback,
|
||||
Path const& from,
|
||||
Path const& to,
|
||||
bool deleteZipAfter = false
|
||||
|
@ -281,7 +279,7 @@ namespace geode::utils::file {
|
|||
public:
|
||||
using Callback = void(FileWatchEvent*);
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> callback, FileWatchEvent* event);
|
||||
ListenerResult handle(std::function<Callback> callback, FileWatchEvent* event);
|
||||
FileWatchFilter(std::filesystem::path const& path);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include <chrono>
|
||||
|
@ -13,19 +13,11 @@
|
|||
#include <charconv>
|
||||
#include <clocale>
|
||||
#include <type_traits>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace geode {
|
||||
using ByteVector = std::vector<uint8_t>;
|
||||
|
||||
// todo in v4: remove this
|
||||
template <typename T>
|
||||
[[deprecated("Use geode::toBytes instead")]]
|
||||
ByteVector toByteArray(T const& a) {
|
||||
ByteVector out;
|
||||
out.resize(sizeof(T));
|
||||
std::memcpy(out.data(), &a, sizeof(T));
|
||||
return out;
|
||||
}
|
||||
template <typename T>
|
||||
ByteVector toBytes(T const& a) {
|
||||
ByteVector out;
|
||||
|
@ -125,7 +117,7 @@ namespace geode {
|
|||
* @returns String as number, or Err if the string couldn't be converted
|
||||
*/
|
||||
template <class Num>
|
||||
Result<Num> numFromString(std::string_view const str, int base = 10) {
|
||||
Result<Num> numFromString(std::string_view str, int base = 10) {
|
||||
if constexpr (std::is_floating_point_v<Num>
|
||||
#if defined(__cpp_lib_to_chars)
|
||||
&& false
|
||||
|
@ -168,12 +160,18 @@ namespace geode {
|
|||
*/
|
||||
GEODE_DLL float getDisplayFactor();
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
requires (sizeof...(Args) > 0)
|
||||
constexpr auto Err(fmt::format_string<Args...> fmt, Args&&... args) {
|
||||
return Err(fmt::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
struct matjson::Serialize<geode::ByteVector> {
|
||||
static matjson::Value to_json(geode::ByteVector const& bytes) {
|
||||
return matjson::Array(bytes.begin(), bytes.end());
|
||||
static Value toJson(geode::ByteVector const& bytes) {
|
||||
return std::vector<matjson::Value>(bytes.begin(), bytes.end());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <functional>
|
||||
|
@ -20,7 +20,7 @@ namespace geode::utils::map {
|
|||
* false if not.
|
||||
*/
|
||||
template <typename T, typename R, typename H>
|
||||
bool contains(std::unordered_map<T, R, H> const& map, utils::MiniFunction<bool(R)> containFunc) {
|
||||
bool contains(std::unordered_map<T, R, H> const& map, std::function<bool(R)> containFunc) {
|
||||
for (auto const& [_, r] : map) {
|
||||
if (containFunc(r)) return true;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace geode::utils::map {
|
|||
* a pointer.
|
||||
*/
|
||||
template <class T, class R, class H>
|
||||
R select(std::unordered_map<T, R, H> const& map, utils::MiniFunction<bool(R)> selectFunc) {
|
||||
R select(std::unordered_map<T, R, H> const& map, std::function<bool(R)> selectFunc) {
|
||||
for (auto const& [_, r] : map) {
|
||||
if (selectFunc(r)) return r;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ namespace geode::utils::map {
|
|||
*/
|
||||
template <class T, class R, class H>
|
||||
std::vector<R> selectAll(
|
||||
std::unordered_map<T, R, H> const& map, utils::MiniFunction<bool(R)> selectFunc
|
||||
std::unordered_map<T, R, H> const& map, std::function<bool(R)> selectFunc
|
||||
) {
|
||||
std::vector<R> res;
|
||||
for (auto const& [_, r] : map) {
|
||||
|
@ -111,7 +111,7 @@ namespace geode::utils::map {
|
|||
template <class T1, class V1, class H1, class T2, class V2, class H2>
|
||||
std::unordered_map<T2, V2, H2> remap(
|
||||
std::unordered_map<T1, V1, H1> const& map,
|
||||
utils::MiniFunction<std::pair<T2, V2>(std::pair<T1, V1>)> remapFunc
|
||||
std::function<std::pair<T2, V2>(std::pair<T1, V1>)> remapFunc
|
||||
) {
|
||||
std::unordered_map<T2, V2, H2> res;
|
||||
for (auto const& [t, v] : map) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include "MiniFunction.hpp"
|
||||
#include <string_view>
|
||||
|
||||
namespace geode::utils::permission {
|
||||
|
@ -21,5 +20,5 @@ namespace geode::utils::permission {
|
|||
* @param permission The permission
|
||||
* @param callback The callback, passed value is 'true' if permission was granted and 'false' otherwise.
|
||||
*/
|
||||
void GEODE_DLL requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback);
|
||||
void GEODE_DLL requestPermission(Permission permission, std::function<void(bool)> callback);
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <compare>
|
||||
#include "../DefaultInclude.hpp"
|
||||
|
||||
namespace geode::utils::string {
|
||||
/**
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <Geode/loader/Loader.hpp> // another great circular dependency fix
|
||||
#include <matjson.hpp>
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "Task.hpp"
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -85,6 +85,12 @@
|
|||
"name": "Enable Geode-Themed Colors",
|
||||
"description": "When enabled, the Geode menu has a <ca>Geode-themed color scheme</c>. <cy>This does not affect any other menus!</c>"
|
||||
},
|
||||
"infinite-local-mods-list": {
|
||||
"type": "bool",
|
||||
"default": false,
|
||||
"name": "Expand Installed Mods List",
|
||||
"description": "Make the installed mods list a single infinite scrollable list instead of having pages"
|
||||
},
|
||||
"developer-title": {
|
||||
"type": "title",
|
||||
"name": "Developer Settings"
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace geode::stl {
|
|||
void free();
|
||||
|
||||
char* getStorage();
|
||||
void setStorage(const std::string_view);
|
||||
void setStorage(std::string_view);
|
||||
|
||||
size_t getSize();
|
||||
void setSize(size_t);
|
||||
|
|
|
@ -100,11 +100,11 @@ namespace gd {
|
|||
bool string::operator==(string const& other) const {
|
||||
return std::string_view(*this) == std::string_view(other);
|
||||
}
|
||||
bool string::operator==(std::string_view const other) const {
|
||||
bool string::operator==(std::string_view other) const {
|
||||
return std::string_view(*this) == other;
|
||||
}
|
||||
|
||||
std::strong_ordering string::operator<=>(std::string_view const other) const {
|
||||
std::strong_ordering string::operator<=>(std::string_view other) const {
|
||||
return static_cast<std::strong_ordering>(std::string_view(*this).compare(other) <=> 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -1026,14 +1026,6 @@ std::optional<AxisAlignment> AxisLayoutOptions::getCrossAxisAlignment() const {
|
|||
return m_impl->m_crossAxisAlignment;
|
||||
}
|
||||
|
||||
AxisLayoutOptions* AxisLayoutOptions::setMaxScale(float scale) {
|
||||
m_impl->m_scaleLimits.second = scale;
|
||||
return this;
|
||||
}
|
||||
AxisLayoutOptions* AxisLayoutOptions::setMinScale(float scale) {
|
||||
m_impl->m_scaleLimits.first = scale;
|
||||
return this;
|
||||
}
|
||||
AxisLayoutOptions* AxisLayoutOptions::setScaleLimits(std::optional<float> min, std::optional<float> max) {
|
||||
m_impl->m_scaleLimits = { min, max };
|
||||
return this;
|
||||
|
|
|
@ -62,10 +62,6 @@ public:
|
|||
return meta;
|
||||
}
|
||||
|
||||
FieldContainer* getFieldContainer() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FieldContainer* getFieldContainer(char const* forClass) {
|
||||
if (!m_classFieldContainers.count(forClass)) {
|
||||
m_classFieldContainers[forClass] = new FieldContainer();
|
||||
|
@ -104,16 +100,11 @@ size_t modifier::getFieldIndexForClass(char const* name) {
|
|||
return s_nextIndex[name]++;
|
||||
}
|
||||
|
||||
// not const because might modify contents
|
||||
FieldContainer* CCNode::getFieldContainer() {
|
||||
return GeodeNodeMetadata::set(this)->getFieldContainer();
|
||||
}
|
||||
|
||||
FieldContainer* CCNode::getFieldContainer(char const* forClass) {
|
||||
return GeodeNodeMetadata::set(this)->getFieldContainer(forClass);
|
||||
}
|
||||
|
||||
std::string CCNode::getID() {
|
||||
const std::string& CCNode::getID() {
|
||||
return GeodeNodeMetadata::set(this)->m_id;
|
||||
}
|
||||
|
||||
|
@ -121,7 +112,11 @@ void CCNode::setID(std::string const& id) {
|
|||
GeodeNodeMetadata::set(this)->m_id = id;
|
||||
}
|
||||
|
||||
CCNode* CCNode::getChildByID(std::string const& id) {
|
||||
void CCNode::setID(std::string&& id) {
|
||||
GeodeNodeMetadata::set(this)->m_id = std::move(id);
|
||||
}
|
||||
|
||||
CCNode* CCNode::getChildByID(std::string_view id) {
|
||||
for (auto child : CCArrayExt<CCNode*>(this->getChildren())) {
|
||||
if (child->getID() == id) {
|
||||
return child;
|
||||
|
@ -130,7 +125,7 @@ CCNode* CCNode::getChildByID(std::string const& id) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
CCNode* CCNode::getChildByIDRecursive(std::string const& id) {
|
||||
CCNode* CCNode::getChildByIDRecursive(std::string_view id) {
|
||||
if (auto child = this->getChildByID(id)) {
|
||||
return child;
|
||||
}
|
||||
|
@ -189,7 +184,7 @@ private:
|
|||
std::unique_ptr<NodeQuery> m_next = nullptr;
|
||||
|
||||
public:
|
||||
static Result<std::unique_ptr<NodeQuery>> parse(std::string const& query) {
|
||||
static Result<std::unique_ptr<NodeQuery>> parse(std::string_view query) {
|
||||
if (query.empty()) {
|
||||
return Err("Query may not be empty");
|
||||
}
|
||||
|
@ -287,7 +282,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
CCNode* CCNode::querySelector(std::string const& queryStr) {
|
||||
CCNode* CCNode::querySelector(std::string_view queryStr) {
|
||||
auto res = NodeQuery::parse(queryStr);
|
||||
if (!res) {
|
||||
log::error("Invalid CCNode::querySelector query '{}': {}", queryStr, res.unwrapErr());
|
||||
|
@ -298,7 +293,7 @@ CCNode* CCNode::querySelector(std::string const& queryStr) {
|
|||
return query->match(this);
|
||||
}
|
||||
|
||||
void CCNode::removeChildByID(std::string const& id) {
|
||||
void CCNode::removeChildByID(std::string_view id) {
|
||||
if (auto child = this->getChildByID(id)) {
|
||||
this->removeChild(child);
|
||||
}
|
||||
|
@ -344,7 +339,7 @@ void CCNode::updateLayout(bool updateChildOrder) {
|
|||
UserObjectSetEvent::UserObjectSetEvent(CCNode* node, std::string const& id, CCObject* value)
|
||||
: node(node), id(id), value(value) {}
|
||||
|
||||
ListenerResult AttributeSetFilter::handle(MiniFunction<Callback> fn, UserObjectSetEvent* event) {
|
||||
ListenerResult AttributeSetFilter::handle(std::function<Callback> fn, UserObjectSetEvent* event) {
|
||||
if (event->id == m_targetID) {
|
||||
fn(event);
|
||||
}
|
||||
|
@ -455,13 +450,4 @@ void CCNode::updateAnchoredPosition(Anchor anchor, CCPoint const& offset, CCPoin
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef GEODE_EXPORTING
|
||||
|
||||
void CCNode::setAttribute(std::string const& attr, matjson::Value const& value) {}
|
||||
std::optional<matjson::Value> CCNode::getAttributeInternal(std::string const& attr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#pragma warning(pop)
|
||||
|
|
|
@ -75,6 +75,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
|||
|
||||
this->fixSocialMenu();
|
||||
|
||||
//this code doesnt run have fun figuring out why idc enough
|
||||
if (auto node = this->getChildByID("settings-gamepad-icon")) {
|
||||
node->setPositionX(
|
||||
bottomMenu->getChildByID("settings-button")->getPositionX() + winSize.width / 2
|
||||
|
@ -83,13 +84,12 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
|||
}
|
||||
|
||||
// show if some mods failed to load
|
||||
if (Loader::get()->getProblems().size()) {
|
||||
if (Loader::get()->getLoadProblems().size()) {
|
||||
static bool shownProblemPopup = false;
|
||||
if (!shownProblemPopup) {
|
||||
shownProblemPopup = true;
|
||||
Notification::create("There were errors - see Geode page!", NotificationIcon::Error)->show();
|
||||
}
|
||||
|
||||
if (m_fields->m_geodeButton) {
|
||||
m_fields->m_exclamation = CCSprite::createWithSpriteFrameName("exMark_001.png");
|
||||
m_fields->m_exclamation->setPosition(m_fields->m_geodeButton->getContentSize() - ccp(10, 10));
|
||||
|
|
|
@ -41,9 +41,9 @@ $execute {
|
|||
// hook them to call our own handler
|
||||
if (LoaderImpl::get()->isForwardCompatMode()) return;
|
||||
|
||||
#if GEODE_COMP_GD_VERSION == 22060
|
||||
const uintptr_t offset1 = 0x75d00; // member function in CCEGLView
|
||||
const uintptr_t offset2 = 0x75d60; // static function
|
||||
#if GEODE_COMP_GD_VERSION == 22074
|
||||
const uintptr_t offset1 = 0x75D90; // member function in CCEGLView
|
||||
const uintptr_t offset2 = 0x75DF0; // static function
|
||||
|
||||
(void) Mod::get()->hook(
|
||||
reinterpret_cast<void*>(geode::base::getCocos() + offset1),
|
||||
|
|
|
@ -2,10 +2,15 @@
|
|||
|
||||
using namespace geode::prelude;
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
#include <Geode/modify/AppDelegate.hpp>
|
||||
#else
|
||||
#include <Geode/modify/AchievementNotifier.hpp>
|
||||
#endif
|
||||
|
||||
namespace geode {
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
|
||||
#include <Geode/modify/AppDelegate.hpp>
|
||||
struct SceneSwitch : Modify<SceneSwitch, AppDelegate> {
|
||||
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
|
||||
void willSwitchToScene(CCScene* scene) {
|
||||
|
@ -15,8 +20,6 @@ struct SceneSwitch : Modify<SceneSwitch, AppDelegate> {
|
|||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <Geode/modify/AchievementNotifier.hpp>
|
||||
struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
|
||||
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
|
||||
void willSwitchToScene(CCScene* scene) {
|
||||
|
@ -26,3 +29,5 @@ struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
}
|
|
@ -46,6 +46,11 @@ $register_ids(MenuLayer) {
|
|||
}
|
||||
|
||||
setIDSafe<CCLabelBMFont>(this, labelOffset++, "player-username");
|
||||
|
||||
if(auto node = this->getChildByID("settings-gamepad-icon")) {
|
||||
// hide it until someone figures out how to bind the positioning to the actual button
|
||||
node->setVisible(false);
|
||||
}
|
||||
|
||||
// main menu
|
||||
if (auto menu = this->getChildByType<CCMenu>(0)) {
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
//#include <Geode/utils/general.hpp>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
class FileWatcher {
|
||||
public:
|
||||
using FileWatchCallback = geode::utils::MiniFunction<void(std::filesystem::path)>;
|
||||
using ErrorCallback = geode::utils::MiniFunction<void(std::string)>;
|
||||
using FileWatchCallback = std::function<void(std::filesystem::path)>;
|
||||
using ErrorCallback = std::function<void(std::string)>;
|
||||
|
||||
protected:
|
||||
std::filesystem::path m_file;
|
||||
|
|
|
@ -20,7 +20,7 @@ protected:
|
|||
std::string content;
|
||||
std::string optionA;
|
||||
std::string optionB;
|
||||
MiniFunction<void(bool)> after;
|
||||
std::function<void(bool)> after;
|
||||
};
|
||||
|
||||
EventListener<server::ModDownloadFilter> m_download;
|
||||
|
@ -79,7 +79,7 @@ protected:
|
|||
|
||||
public:
|
||||
void start() {
|
||||
for (auto problem : Loader::get()->getProblems()) {
|
||||
for (auto problem : Loader::get()->getAllProblems()) {
|
||||
switch (problem.type) {
|
||||
// Errors where the correct solution is to just delete the invalid .geode package
|
||||
case LoadProblem::Type::InvalidFile:
|
||||
|
|
|
@ -24,7 +24,8 @@ void crashlog::printGeodeInfo(std::stringstream& stream) {
|
|||
<< "Loader Commit: " << about::getLoaderCommitHash() << "\n"
|
||||
<< "Bindings Commit: " << about::getBindingsCommitHash() << "\n"
|
||||
<< "Installed mods: " << Loader::get()->getAllMods().size() << "\n"
|
||||
<< "Problems: " << Loader::get()->getProblems().size() << "\n";
|
||||
<< "Outdated mods: " << Loader::get()->getOutdated().size() << "\n"
|
||||
<< "Problems: " << Loader::get()->getLoadProblems().size() << "\n";
|
||||
}
|
||||
|
||||
void crashlog::printMods(std::stringstream& stream) {
|
||||
|
@ -44,7 +45,8 @@ void crashlog::printMods(std::stringstream& stream) {
|
|||
stream << fmt::format("{} | [{}] {}\n",
|
||||
mod->isCurrentlyLoading() ? "o"sv :
|
||||
mod->isEnabled() ? "x"sv :
|
||||
mod->hasProblems() ? "!"sv : // thank you for this bug report
|
||||
mod->hasLoadProblems() ? "!"sv : // thank you for this bug report
|
||||
mod->targetsOutdatedVersion() ? "*"sv : // thank you very much for this bug report
|
||||
mod->shouldLoad() ? "~"sv :
|
||||
" "sv,
|
||||
mod->getVersion().toVString(), mod->getID()
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/SettingEvent.hpp>
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <loader/LogImpl.hpp>
|
||||
|
||||
|
@ -30,8 +29,7 @@ $on_mod(Loaded) {
|
|||
std::vector<matjson::Value> res;
|
||||
|
||||
auto args = *event->messageData;
|
||||
JsonChecker checker(args);
|
||||
auto root = checker.root("[ipc/list-mods]").obj();
|
||||
auto root = checkJson(args, "[ipc/list-mods]");
|
||||
|
||||
auto includeRunTimeInfo = root.has("include-runtime-info").get<bool>();
|
||||
auto dontIncludeLoader = root.has("dont-include-loader").get<bool>();
|
||||
|
@ -86,7 +84,7 @@ void tryShowForwardCompat() {
|
|||
#ifdef GEODE_IS_WINDOWS
|
||||
bool safeModeCheck() {
|
||||
// yes this is quite funny
|
||||
if (GetAsyncKeyState(VK_SHIFT) == 0) {
|
||||
if (!(GetAsyncKeyState(VK_SHIFT) & (1 << 15))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ std::string_view Hook::Impl::getDisplayName() const {
|
|||
}
|
||||
|
||||
matjson::Value Hook::Impl::getRuntimeInfo() const {
|
||||
auto json = matjson::Object();
|
||||
matjson::Value json;
|
||||
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
|
||||
json["detour"] = std::to_string(reinterpret_cast<uintptr_t>(m_detour));
|
||||
json["name"] = m_displayName;
|
||||
|
|
|
@ -19,7 +19,7 @@ ipc::IPCEvent::IPCEvent(
|
|||
|
||||
ipc::IPCEvent::~IPCEvent() {}
|
||||
|
||||
ListenerResult ipc::IPCFilter::handle(utils::MiniFunction<Callback> fn, IPCEvent* event) {
|
||||
ListenerResult ipc::IPCFilter::handle(std::function<Callback> fn, IPCEvent* event) {
|
||||
if (event->targetModID == m_modID && event->messageID == m_messageID) {
|
||||
event->replyData = fn(event);
|
||||
return ListenerResult::Stop;
|
||||
|
@ -33,19 +33,18 @@ ipc::IPCFilter::IPCFilter(std::string const& modID, std::string const& messageID
|
|||
matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) {
|
||||
matjson::Value reply;
|
||||
|
||||
std::string error;
|
||||
auto res = matjson::parse(buffer, error);
|
||||
if (error.size() > 0) {
|
||||
log::warn("Received IPC message that isn't valid JSON: {}", error);
|
||||
auto res = matjson::Value::parse(buffer);
|
||||
if (!res) {
|
||||
log::warn("Received IPC message that isn't valid JSON: {}", res.unwrapErr());
|
||||
return reply;
|
||||
}
|
||||
matjson::Value json = res.value();
|
||||
matjson::Value json = res.unwrap();
|
||||
|
||||
if (!json.contains("mod") || !json["mod"].is_string()) {
|
||||
if (!json.contains("mod") || !json["mod"].isString()) {
|
||||
log::warn("Received IPC message without 'mod' field");
|
||||
return reply;
|
||||
}
|
||||
if (!json.contains("message") || !json["message"].is_string()) {
|
||||
if (!json.contains("message") || !json["message"].isString()) {
|
||||
log::warn("Received IPC message without 'message' field");
|
||||
return reply;
|
||||
}
|
||||
|
@ -55,6 +54,6 @@ matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) {
|
|||
}
|
||||
// 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();
|
||||
IPCEvent(rawHandle, json["mod"].asString().unwrap(), json["message"].asString().unwrap(), data, reply).post();
|
||||
return reply;
|
||||
}
|
||||
|
|
|
@ -68,13 +68,19 @@ std::vector<Mod*> Loader::getAllMods() {
|
|||
std::vector<LoadProblem> Loader::getAllProblems() const {
|
||||
return m_impl->getProblems();
|
||||
}
|
||||
std::vector<LoadProblem> Loader::getProblems() const {
|
||||
std::vector<LoadProblem> Loader::getLoadProblems() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type != LoadProblem::Type::Recommendation &&
|
||||
problem.type != LoadProblem::Type::Suggestion
|
||||
) {
|
||||
if (problem.isProblem()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::vector<LoadProblem> Loader::getOutdated() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (problem.isOutdated()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
@ -83,10 +89,7 @@ std::vector<LoadProblem> Loader::getProblems() const {
|
|||
std::vector<LoadProblem> Loader::getRecommendations() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type == LoadProblem::Type::Recommendation ||
|
||||
problem.type == LoadProblem::Type::Suggestion
|
||||
) {
|
||||
if (problem.isSuggestion()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
@ -109,14 +112,14 @@ std::vector<std::string> Loader::getLaunchArgumentNames() const {
|
|||
return m_impl->getLaunchArgumentNames();
|
||||
}
|
||||
|
||||
bool Loader::hasLaunchArgument(std::string_view const name) const {
|
||||
bool Loader::hasLaunchArgument(std::string_view name) const {
|
||||
return m_impl->hasLaunchArgument(name);
|
||||
}
|
||||
|
||||
std::optional<std::string> Loader::getLaunchArgument(std::string_view const name) const {
|
||||
std::optional<std::string> Loader::getLaunchArgument(std::string_view name) const {
|
||||
return m_impl->getLaunchArgument(name);
|
||||
}
|
||||
|
||||
bool Loader::getLaunchFlag(std::string_view const name) const {
|
||||
bool Loader::getLaunchFlag(std::string_view name) const {
|
||||
return m_impl->getLaunchFlag(name);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,11 @@ Loader::Impl::~Impl() = default;
|
|||
// Initialization
|
||||
|
||||
bool Loader::Impl::isForwardCompatMode() {
|
||||
#ifdef GEODE_IS_ANDROID
|
||||
// forward compat mode doesn't really make sense on android
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (!m_forwardCompatMode.has_value()) {
|
||||
m_forwardCompatMode = !this->getGameVersion().empty() &&
|
||||
this->getGameVersion() != GEODE_STR(GEODE_GD_VERSION);
|
||||
|
@ -384,6 +389,37 @@ void Loader::Impl::buildModGraph() {
|
|||
}
|
||||
|
||||
void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||
// Check version first, as it's not worth trying to load a mod with an
|
||||
// invalid target version
|
||||
// Also this makes it so that when GD updates, outdated mods get shown as
|
||||
// "Outdated" in the UI instead of "Missing Dependencies"
|
||||
auto res = node->getMetadata().checkGameVersion();
|
||||
if (!res) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::UnsupportedVersion,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("{}", res.unwrapErr());
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->isModVersionSupported(node->getMetadata().getGeodeVersion())) {
|
||||
this->addProblem({
|
||||
node->getMetadata().getGeodeVersion() > this->getVersion() ? LoadProblem::Type::NeedsNewerGeodeVersion : LoadProblem::Type::UnsupportedGeodeVersion,
|
||||
node,
|
||||
fmt::format(
|
||||
"Geode version {}\nis required to run this mod\n(installed: {})",
|
||||
node->getMetadata().getGeodeVersion().toVString(),
|
||||
this->getVersion().toVString()
|
||||
)
|
||||
});
|
||||
log::error("Unsupported Geode version: {}", node->getMetadata().getGeodeVersion());
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->hasUnresolvedDependencies()) {
|
||||
log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion());
|
||||
return;
|
||||
|
@ -444,35 +480,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
auto res = node->getMetadata().checkGameVersion();
|
||||
if (!res) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::UnsupportedVersion,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("{}", res.unwrapErr());
|
||||
m_refreshingModCount -= 1;
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->isModVersionSupported(node->getMetadata().getGeodeVersion())) {
|
||||
this->addProblem({
|
||||
node->getMetadata().getGeodeVersion() > this->getVersion() ? LoadProblem::Type::NeedsNewerGeodeVersion : LoadProblem::Type::UnsupportedGeodeVersion,
|
||||
node,
|
||||
fmt::format(
|
||||
"Geode version {}\nis required to run this mod\n(installed: {})",
|
||||
node->getMetadata().getGeodeVersion().toVString(),
|
||||
this->getVersion().toVString()
|
||||
)
|
||||
});
|
||||
log::error("Unsupported Geode version: {}", node->getMetadata().getGeodeVersion());
|
||||
m_refreshingModCount -= 1;
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (early) {
|
||||
|
@ -496,20 +503,20 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
thread::setName("Mod Unzip");
|
||||
log::loadNest(nest);
|
||||
auto res = unzipFunction();
|
||||
auto prevNest = log::saveNest();
|
||||
log::loadNest(nest);
|
||||
if (!res) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::UnzipFailed,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("Failed to unzip: {}", res.unwrapErr());
|
||||
m_refreshingModCount -= 1;
|
||||
log::loadNest(prevNest);
|
||||
return;
|
||||
}
|
||||
this->queueInMainThread([=, this]() {
|
||||
auto prevNest = log::saveNest();
|
||||
log::loadNest(nest);
|
||||
if (!res) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::UnzipFailed,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("Failed to unzip: {}", res.unwrapErr());
|
||||
m_refreshingModCount -= 1;
|
||||
log::loadNest(prevNest);
|
||||
return;
|
||||
}
|
||||
loadFunction();
|
||||
log::loadNest(prevNest);
|
||||
});
|
||||
|
@ -524,6 +531,10 @@ void Loader::Impl::findProblems() {
|
|||
log::debug("{} is not enabled", id);
|
||||
continue;
|
||||
}
|
||||
if (mod->targetsOutdatedVersion()) {
|
||||
log::debug("{} is outdated", id);
|
||||
continue;
|
||||
}
|
||||
log::debug("{}", id);
|
||||
log::pushNest();
|
||||
|
||||
|
@ -927,11 +938,11 @@ std::vector<std::string> Loader::Impl::getLaunchArgumentNames() const {
|
|||
return map::keys(m_launchArgs);
|
||||
}
|
||||
|
||||
bool Loader::Impl::hasLaunchArgument(std::string_view const name) const {
|
||||
bool Loader::Impl::hasLaunchArgument(std::string_view 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 {
|
||||
std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view name) const {
|
||||
auto value = m_launchArgs.find(std::string(name));
|
||||
if (value == m_launchArgs.end()) {
|
||||
return std::nullopt;
|
||||
|
@ -939,7 +950,7 @@ std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view cons
|
|||
return std::optional(value->second);
|
||||
}
|
||||
|
||||
bool Loader::Impl::getLaunchFlag(std::string_view const name) const {
|
||||
bool Loader::Impl::getLaunchFlag(std::string_view name) const {
|
||||
auto arg = this->getLaunchArgument(name);
|
||||
return arg.has_value() && arg.value() == "true";
|
||||
}
|
||||
|
|
|
@ -7,10 +7,9 @@
|
|||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/utils/Result.hpp>
|
||||
#include <Geode/Result.hpp>
|
||||
#include <Geode/utils/map.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include "ModImpl.hpp"
|
||||
#include <crashlog.hpp>
|
||||
#include <mutex>
|
||||
|
@ -41,7 +40,7 @@ namespace geode {
|
|||
|
||||
LoadingState m_loadingState = LoadingState::None;
|
||||
|
||||
std::vector<utils::MiniFunction<void(void)>> m_mainThreadQueue;
|
||||
std::vector<std::function<void(void)>> m_mainThreadQueue;
|
||||
mutable std::mutex m_mainThreadMutex;
|
||||
std::vector<std::pair<Hook*, Mod*>> m_uninitializedHooks;
|
||||
bool m_readyToHook = false;
|
||||
|
@ -116,9 +115,9 @@ namespace geode {
|
|||
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 getLaunchFlag(std::string_view const name) const;
|
||||
bool hasLaunchArgument(std::string_view name) const;
|
||||
std::optional<std::string> getLaunchArgument(std::string_view name) const;
|
||||
bool getLaunchFlag(std::string_view name) const;
|
||||
|
||||
void updateResources(bool forceReload);
|
||||
|
||||
|
|
|
@ -20,10 +20,6 @@ std::string Mod::getName() const {
|
|||
return m_impl->getName();
|
||||
}
|
||||
|
||||
std::string Mod::getDeveloper() const {
|
||||
return m_impl->getDevelopers().empty() ? "" : m_impl->getDevelopers().front();
|
||||
}
|
||||
|
||||
std::vector<std::string> Mod::getDevelopers() const {
|
||||
return m_impl->getDevelopers();
|
||||
}
|
||||
|
@ -101,9 +97,6 @@ std::vector<Mod*> Mod::getDependants() const {
|
|||
}
|
||||
#endif
|
||||
|
||||
std::optional<VersionInfo> Mod::hasAvailableUpdate() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
Mod::CheckUpdatesTask Mod::checkUpdates() const {
|
||||
return server::checkUpdates(this).map(
|
||||
[](auto* result) -> Mod::CheckUpdatesTask::Value {
|
||||
|
@ -155,28 +148,14 @@ std::vector<std::string> Mod::getSettingKeys() const {
|
|||
return m_impl->getSettingKeys();
|
||||
}
|
||||
|
||||
bool Mod::hasSetting(std::string_view const key) const {
|
||||
bool Mod::hasSetting(std::string_view key) const {
|
||||
return m_impl->hasSetting(key);
|
||||
}
|
||||
|
||||
std::optional<Setting> Mod::getSettingDefinition(std::string_view const key) const {
|
||||
return m_impl->m_settings->getLegacyDefinition(std::string(key));
|
||||
}
|
||||
|
||||
SettingValue* Mod::getSetting(std::string_view const key) const {
|
||||
return m_impl->m_settings->getLegacy(std::string(key)).get();
|
||||
}
|
||||
|
||||
std::shared_ptr<SettingV3> Mod::getSettingV3(std::string_view const key) const {
|
||||
std::shared_ptr<Setting> Mod::getSetting(std::string_view key) const {
|
||||
return m_impl->m_settings->get(std::string(key));
|
||||
}
|
||||
|
||||
void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value) {
|
||||
auto reg = m_impl->m_settings->registerLegacyCustomSetting(key, std::move(value));
|
||||
if (!reg) {
|
||||
log::error("Unable to register custom setting: {}", reg.unwrapErr());
|
||||
}
|
||||
}
|
||||
Result<> Mod::registerCustomSettingType(std::string_view type, SettingGenerator generator) {
|
||||
return m_impl->m_settings->registerCustomSettingType(type, generator);
|
||||
}
|
||||
|
@ -185,15 +164,15 @@ std::vector<std::string> Mod::getLaunchArgumentNames() const {
|
|||
return m_impl->getLaunchArgumentNames();
|
||||
}
|
||||
|
||||
bool Mod::hasLaunchArgument(std::string_view const name) const {
|
||||
bool Mod::hasLaunchArgument(std::string_view name) const {
|
||||
return m_impl->hasLaunchArgument(name);
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::getLaunchArgument(std::string_view const name) const {
|
||||
std::optional<std::string> Mod::getLaunchArgument(std::string_view name) const {
|
||||
return m_impl->getLaunchArgument(name);
|
||||
}
|
||||
|
||||
bool Mod::getLaunchFlag(std::string_view const name) const {
|
||||
bool Mod::getLaunchFlag(std::string_view name) const {
|
||||
return m_impl->getLaunchFlag(name);
|
||||
}
|
||||
|
||||
|
@ -241,7 +220,7 @@ ModRequestedAction Mod::getRequestedAction() const {
|
|||
return m_impl->getRequestedAction();
|
||||
}
|
||||
|
||||
bool Mod::depends(std::string_view const id) const {
|
||||
bool Mod::depends(std::string_view id) const {
|
||||
return m_impl->depends(id);
|
||||
}
|
||||
|
||||
|
@ -269,12 +248,20 @@ void Mod::setLoggingEnabled(bool enabled) {
|
|||
m_impl->setLoggingEnabled(enabled);
|
||||
}
|
||||
|
||||
bool Mod::hasSavedValue(std::string_view const key) {
|
||||
bool Mod::hasSavedValue(std::string_view key) {
|
||||
return this->getSaveContainer().contains(key);
|
||||
}
|
||||
|
||||
bool Mod::hasProblems() const {
|
||||
return m_impl->hasProblems();
|
||||
bool Mod::hasLoadProblems() const {
|
||||
return m_impl->hasLoadProblems();
|
||||
}
|
||||
std::optional<LoadProblem> Mod::targetsOutdatedVersion() const {
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (problem.isOutdated()) {
|
||||
return problem;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
std::vector<LoadProblem> Mod::getAllProblems() const {
|
||||
return m_impl->getProblems();
|
||||
|
@ -282,10 +269,7 @@ std::vector<LoadProblem> Mod::getAllProblems() const {
|
|||
std::vector<LoadProblem> Mod::getProblems() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type != LoadProblem::Type::Recommendation &&
|
||||
problem.type != LoadProblem::Type::Suggestion
|
||||
) {
|
||||
if (problem.isProblem()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
@ -294,10 +278,7 @@ std::vector<LoadProblem> Mod::getProblems() const {
|
|||
std::vector<LoadProblem> Mod::getRecommendations() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type == LoadProblem::Type::Recommendation ||
|
||||
problem.type == LoadProblem::Type::Suggestion
|
||||
) {
|
||||
if (problem.isSuggestion()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ Mod* ModStateEvent::getMod() const {
|
|||
return m_mod;
|
||||
}
|
||||
|
||||
ListenerResult ModStateFilter::handle(utils::MiniFunction<Callback> fn, ModStateEvent* event) {
|
||||
ListenerResult ModStateFilter::handle(std::function<Callback> fn, ModStateEvent* event) {
|
||||
// log::debug("Event mod filter: {}, {}, {}, {}", m_mod, static_cast<int>(m_type), event->getMod(), static_cast<int>(event->getType()));
|
||||
if ((!m_mod || event->getMod() == m_mod) && event->getType() == m_type) {
|
||||
fn(event);
|
||||
|
|
|
@ -51,7 +51,9 @@ Result<> Mod::Impl::setup() {
|
|||
(void) utils::file::createDirectoryAll(m_saveDirPath);
|
||||
|
||||
// always create temp dir for all mods, even if disabled, so resources can be loaded
|
||||
GEODE_UNWRAP(this->createTempDir().expect("Unable to create temp dir: {error}"));
|
||||
GEODE_UNWRAP(this->createTempDir().mapErr([](auto const& err) {
|
||||
return fmt::format("Unable to create temp dir: {}", err);
|
||||
}));
|
||||
|
||||
m_settings = std::make_unique<ModSettingsManager>(m_metadata);
|
||||
auto loadRes = this->loadData();
|
||||
|
@ -188,15 +190,12 @@ Result<> Mod::Impl::loadData() {
|
|||
auto savedPath = m_saveDirPath / "saved.json";
|
||||
if (std::filesystem::exists(savedPath)) {
|
||||
GEODE_UNWRAP_INTO(auto data, utils::file::readString(savedPath));
|
||||
std::string error;
|
||||
auto res = matjson::parse(data, error);
|
||||
if (error.size() > 0) {
|
||||
return Err("Unable to parse saved values: " + error);
|
||||
}
|
||||
m_saved = res.value();
|
||||
if (!m_saved.is_object()) {
|
||||
m_saved = GEODE_UNWRAP(matjson::parse(data).mapErr([](auto&& err) {
|
||||
return fmt::format("Unable to parse saved values: {}", err);
|
||||
}));
|
||||
if (!m_saved.isObject()) {
|
||||
log::warn("saved.json was somehow not an object, forcing it to one");
|
||||
m_saved = matjson::Object();
|
||||
m_saved = matjson::Value::object();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,19 +228,19 @@ Result<> Mod::Impl::saveData() {
|
|||
}
|
||||
|
||||
bool Mod::Impl::hasSettings() const {
|
||||
return m_metadata.getSettingsV3().size();
|
||||
return m_metadata.getSettings().size();
|
||||
}
|
||||
|
||||
std::vector<std::string> Mod::Impl::getSettingKeys() const {
|
||||
std::vector<std::string> keys;
|
||||
for (auto& [key, _] : m_metadata.getSettingsV3()) {
|
||||
for (auto& [key, _] : m_metadata.getSettings()) {
|
||||
keys.push_back(key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
bool Mod::Impl::hasSetting(std::string_view const key) const {
|
||||
for (auto& setting : m_metadata.getSettingsV3()) {
|
||||
bool Mod::Impl::hasSetting(std::string_view key) const {
|
||||
for (auto& setting : m_metadata.getSettings()) {
|
||||
if (setting.first == key) {
|
||||
return true;
|
||||
}
|
||||
|
@ -249,7 +248,7 @@ bool Mod::Impl::hasSetting(std::string_view const key) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
std::string Mod::Impl::getLaunchArgumentName(std::string_view const name) const {
|
||||
std::string Mod::Impl::getLaunchArgumentName(std::string_view name) const {
|
||||
return this->getID() + "." + std::string(name);
|
||||
}
|
||||
|
||||
|
@ -264,15 +263,15 @@ std::vector<std::string> Mod::Impl::getLaunchArgumentNames() const {
|
|||
return names;
|
||||
}
|
||||
|
||||
bool Mod::Impl::hasLaunchArgument(std::string_view const name) const {
|
||||
bool Mod::Impl::hasLaunchArgument(std::string_view name) const {
|
||||
return Loader::get()->hasLaunchArgument(this->getLaunchArgumentName(name));
|
||||
}
|
||||
|
||||
std::optional<std::string> Mod::Impl::getLaunchArgument(std::string_view const name) const {
|
||||
std::optional<std::string> Mod::Impl::getLaunchArgument(std::string_view name) const {
|
||||
return Loader::get()->getLaunchArgument(this->getLaunchArgumentName(name));
|
||||
}
|
||||
|
||||
bool Mod::Impl::getLaunchFlag(std::string_view const name) const {
|
||||
bool Mod::Impl::getLaunchFlag(std::string_view name) const {
|
||||
return Loader::get()->getLaunchFlag(this->getLaunchArgumentName(name));
|
||||
}
|
||||
|
||||
|
@ -318,7 +317,6 @@ Result<> Mod::Impl::loadBinary() {
|
|||
|
||||
|
||||
ModStateEvent(m_self, ModEventType::Loaded).post();
|
||||
ModStateEvent(m_self, ModEventType::Enabled).post();
|
||||
ModStateEvent(m_self, ModEventType::DataLoaded).post();
|
||||
|
||||
m_isCurrentlyLoading = false;
|
||||
|
@ -385,7 +383,7 @@ Result<> Mod::Impl::uninstall(bool deleteSaveData) {
|
|||
ModRequestedAction::Uninstall;
|
||||
|
||||
// Make loader forget the mod should be disabled
|
||||
Mod::get()->getSaveContainer().try_erase("should-load-" + m_metadata.getID());
|
||||
Mod::get()->getSaveContainer().erase("should-load-" + m_metadata.getID());
|
||||
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(m_metadata.getPath(), ec);
|
||||
|
@ -440,7 +438,7 @@ bool Mod::Impl::hasUnresolvedIncompatibilities() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Mod::Impl::depends(std::string_view const id) const {
|
||||
bool Mod::Impl::depends(std::string_view id) const {
|
||||
return utils::ranges::contains(m_metadata.getDependencies(), [id](ModMetadata::Dependency const& t) {
|
||||
return t.id == id;
|
||||
});
|
||||
|
@ -672,17 +670,15 @@ std::string_view Mod::Impl::expandSpriteName(std::string_view name) {
|
|||
ModJson Mod::Impl::getRuntimeInfo() const {
|
||||
auto json = m_metadata.toJSON();
|
||||
|
||||
auto obj = matjson::Object();
|
||||
obj["hooks"] = matjson::Array();
|
||||
auto obj = matjson::Value::object();
|
||||
obj["hooks"] = matjson::Value::array();
|
||||
for (auto hook : m_hooks) {
|
||||
obj["hooks"].as_array().push_back(ModJson(hook->getRuntimeInfo()));
|
||||
obj["hooks"].push(ModJson(hook->getRuntimeInfo()));
|
||||
}
|
||||
obj["patches"] = matjson::Array();
|
||||
obj["patches"] = matjson::Value::array();
|
||||
for (auto patch : m_patches) {
|
||||
obj["patches"].as_array().push_back(ModJson(patch->getRuntimeInfo()));
|
||||
obj["patches"].push(ModJson(patch->getRuntimeInfo()));
|
||||
}
|
||||
// TODO: so which one is it
|
||||
// obj["enabled"] = m_enabled;
|
||||
obj["loaded"] = m_enabled;
|
||||
obj["temp-dir"] = this->getTempDir();
|
||||
obj["save-dir"] = this->getSaveDir();
|
||||
|
@ -708,11 +704,11 @@ bool Mod::Impl::isCurrentlyLoading() const {
|
|||
return m_isCurrentlyLoading;
|
||||
}
|
||||
|
||||
bool Mod::Impl::hasProblems() const {
|
||||
for (auto const& item : m_problems) {
|
||||
if (item.type <= LoadProblem::Type::Recommendation)
|
||||
continue;
|
||||
return true;
|
||||
bool Mod::Impl::hasLoadProblems() const {
|
||||
for (auto const& problem : m_problems) {
|
||||
if (problem.isProblem()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -722,12 +718,9 @@ std::vector<LoadProblem> Mod::Impl::getProblems() const {
|
|||
}
|
||||
|
||||
static Result<ModMetadata> getModImplInfo() {
|
||||
std::string error;
|
||||
auto res = matjson::parse(about::getLoaderModJson(), error);
|
||||
if (error.size() > 0) {
|
||||
return Err("Unable to parse mod.json: " + error);
|
||||
}
|
||||
matjson::Value json = res.value();
|
||||
auto json = GEODE_UNWRAP(matjson::parse(about::getLoaderModJson()).mapErr([](auto&& err) {
|
||||
return fmt::format("Unable to parse mod.json: {}", err);
|
||||
}));
|
||||
|
||||
GEODE_UNWRAP_INTO(auto info, ModMetadata::create(json));
|
||||
return Ok(info);
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace geode {
|
|||
/**
|
||||
* Saved values
|
||||
*/
|
||||
matjson::Value m_saved = matjson::Object();
|
||||
matjson::Value m_saved = matjson::Value();
|
||||
/**
|
||||
* Setting values. This is behind unique_ptr for interior mutability
|
||||
*/
|
||||
|
@ -112,13 +112,13 @@ namespace geode {
|
|||
|
||||
bool hasSettings() const;
|
||||
std::vector<std::string> getSettingKeys() const;
|
||||
bool hasSetting(std::string_view const key) const;
|
||||
bool hasSetting(std::string_view key) const;
|
||||
|
||||
std::string getLaunchArgumentName(std::string_view const name) const;
|
||||
std::string getLaunchArgumentName(std::string_view 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 getLaunchFlag(std::string_view const name) const;
|
||||
bool hasLaunchArgument(std::string_view name) const;
|
||||
std::optional<std::string> getLaunchArgument(std::string_view name) const;
|
||||
bool getLaunchFlag(std::string_view name) const;
|
||||
|
||||
Result<Hook*> claimHook(std::shared_ptr<Hook> hook);
|
||||
Result<> disownHook(Hook* hook);
|
||||
|
@ -136,7 +136,7 @@ namespace geode {
|
|||
// 1.3.0 additions
|
||||
ModRequestedAction getRequestedAction() const;
|
||||
|
||||
bool depends(std::string_view const id) const;
|
||||
bool depends(std::string_view id) const;
|
||||
Result<> updateDependencies();
|
||||
bool hasUnresolvedDependencies() const;
|
||||
bool hasUnresolvedIncompatibilities() const;
|
||||
|
@ -151,7 +151,7 @@ namespace geode {
|
|||
|
||||
std::vector<LoadProblem> getProblems() const;
|
||||
|
||||
bool hasProblems() const;
|
||||
bool hasLoadProblems() const;
|
||||
bool shouldLoad() const;
|
||||
bool isCurrentlyLoading() const;
|
||||
};
|
||||
|
|
|
@ -113,14 +113,14 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
|
|||
|
||||
auto checkerRoot = fmt::format(
|
||||
"[{}/v0.0.0/mod.json]",
|
||||
rawJson.contains("id") ? rawJson["id"].as_string() : "unknown.mod"
|
||||
rawJson.contains("id") ? GEODE_UNWRAP(rawJson["id"].asString()) : "unknown.mod"
|
||||
);
|
||||
// JsonChecker did it this way too
|
||||
try {
|
||||
checkerRoot = fmt::format(
|
||||
"[{}/{}/mod.json]",
|
||||
rawJson.contains("id") ? rawJson["id"].as_string() : "unknown.mod",
|
||||
rawJson.contains("version") ? rawJson["version"].as<VersionInfo>().toVString() : "v0.0.0"
|
||||
rawJson.contains("id") ? GEODE_UNWRAP(rawJson["id"].asString()) : "unknown.mod",
|
||||
rawJson.contains("version") ? GEODE_UNWRAP(rawJson["version"].as<VersionInfo>()).toVString() : "v0.0.0"
|
||||
);
|
||||
}
|
||||
catch (...) { }
|
||||
|
@ -304,11 +304,14 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
|
|||
Result<ModMetadata> ModMetadata::Impl::create(ModJson const& json) {
|
||||
// Check mod.json target version
|
||||
auto schema = about::getLoaderVersion();
|
||||
if (json.contains("geode") && json["geode"].is_string()) {
|
||||
if (json.contains("geode") && json["geode"].isString()) {
|
||||
GEODE_UNWRAP_INTO(
|
||||
schema,
|
||||
VersionInfo::parse(json["geode"].as_string())
|
||||
.expect("[mod.json] has invalid target loader version: {error}")
|
||||
VersionInfo::parse(GEODE_UNWRAP(json["geode"].asString())).mapErr(
|
||||
[](auto const& err) {
|
||||
return fmt::format("[mod.json] has invalid target loader version: {}", err);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
@ -352,13 +355,9 @@ Result<ModMetadata> ModMetadata::Impl::create(ModJson const& json) {
|
|||
Result<ModMetadata> ModMetadata::Impl::createFromFile(std::filesystem::path const& path) {
|
||||
GEODE_UNWRAP_INTO(auto read, utils::file::readString(path));
|
||||
|
||||
std::string error;
|
||||
auto res = matjson::parse(read, error);
|
||||
if (error.size() > 0) {
|
||||
return Err(std::string("Unable to parse mod.json: ") + error);
|
||||
}
|
||||
|
||||
GEODE_UNWRAP_INTO(auto info, ModMetadata::create(res.value()));
|
||||
GEODE_UNWRAP_INTO(auto info, ModMetadata::create(GEODE_UNWRAP(matjson::parse(read).mapErr([&](auto const& err) {
|
||||
return fmt::format("Unable to parse mod.json: {}", err);
|
||||
}))));
|
||||
|
||||
auto impl = info.m_impl.get();
|
||||
|
||||
|
@ -382,25 +381,24 @@ Result<ModMetadata> ModMetadata::Impl::createFromGeodeZip(file::Unzip& unzip) {
|
|||
|
||||
// Read mod.json & parse if possible
|
||||
GEODE_UNWRAP_INTO(
|
||||
auto jsonData, unzip.extract("mod.json").expect("Unable to read mod.json: {error}")
|
||||
auto jsonData, unzip.extract("mod.json").mapErr([](auto const& err) {
|
||||
return fmt::format("Unable to extract mod.json: {}", err);
|
||||
})
|
||||
);
|
||||
|
||||
std::string error;
|
||||
auto res = matjson::parse(std::string(jsonData.begin(), jsonData.end()), error);
|
||||
if (error.size() > 0) {
|
||||
return Err(std::string("Unable to parse mod.json: ") + error);
|
||||
}
|
||||
ModJson json = res.value();
|
||||
ModJson json = GEODE_UNWRAP(matjson::parse(std::string(jsonData.begin(), jsonData.end())).mapErr([](auto const& err) {
|
||||
return fmt::format("Unable to parse mod.json: {}", err);
|
||||
}));
|
||||
|
||||
auto res2 = ModMetadata::create(json);
|
||||
if (!res2) {
|
||||
return Err("\"" + unzip.getPath().string() + "\" - " + res2.unwrapErr());
|
||||
}
|
||||
auto info = res2.unwrap();
|
||||
auto info = GEODE_UNWRAP(ModMetadata::create(json).mapErr([&](auto const& err) {
|
||||
return fmt::format("\"{}\" - {}", unzip.getPath().string(), err);
|
||||
}));
|
||||
auto impl = info.m_impl.get();
|
||||
impl->m_path = unzip.getPath();
|
||||
|
||||
GEODE_UNWRAP(info.addSpecialFiles(unzip).expect("Unable to add extra files: {error}"));
|
||||
GEODE_UNWRAP(info.addSpecialFiles(unzip).mapErr([](auto const& err) {
|
||||
return fmt::format("Unable to add extra files: {}", err);
|
||||
}));
|
||||
|
||||
return Ok(info);
|
||||
}
|
||||
|
@ -409,7 +407,11 @@ Result<> ModMetadata::Impl::addSpecialFiles(file::Unzip& unzip) {
|
|||
// unzip known MD files
|
||||
for (auto& [file, target] : this->getSpecialFiles()) {
|
||||
if (unzip.hasEntry(file)) {
|
||||
GEODE_UNWRAP_INTO(auto data, unzip.extract(file).expect("Unable to extract \"{}\"", file));
|
||||
// reference to local binding 'file' declared in enclosing function
|
||||
std::string_view fileStr(file);
|
||||
GEODE_UNWRAP_INTO(auto data, unzip.extract(fileStr).mapErr([&](auto const& err) {
|
||||
return fmt::format("Unable to extract \"{}\": {}", fileStr, err);
|
||||
}));
|
||||
*target = sanitizeDetailsData(std::string(data.begin(), data.end()));
|
||||
}
|
||||
}
|
||||
|
@ -477,12 +479,6 @@ std::string ModMetadata::getName() const {
|
|||
return m_impl->m_name;
|
||||
}
|
||||
|
||||
std::string ModMetadata::getDeveloper() const {
|
||||
// m_developers should be guaranteed to never be empty, but this is
|
||||
// just in case it is anyway somehow
|
||||
return m_impl->m_developers.empty() ? "" : m_impl->m_developers.front();
|
||||
}
|
||||
|
||||
std::string ModMetadata::formatDeveloperDisplayString(std::vector<std::string> const& developers) {
|
||||
switch (developers.size()) {
|
||||
case 0: return "Unknown"; break;
|
||||
|
@ -510,9 +506,6 @@ std::optional<std::string> ModMetadata::getChangelog() const {
|
|||
std::optional<std::string> ModMetadata::getSupportInfo() const {
|
||||
return m_impl->m_supportInfo;
|
||||
}
|
||||
std::optional<std::string> ModMetadata::getRepository() const {
|
||||
return m_impl->m_links.getSourceURL();
|
||||
}
|
||||
ModMetadataLinks ModMetadata::getLinks() const {
|
||||
return m_impl->m_links;
|
||||
}
|
||||
|
@ -528,19 +521,7 @@ std::vector<ModMetadata::Incompatibility> ModMetadata::getIncompatibilities() co
|
|||
std::vector<std::string> ModMetadata::getSpritesheets() const {
|
||||
return m_impl->m_spritesheets;
|
||||
}
|
||||
std::vector<std::pair<std::string, Setting>> ModMetadata::getSettings() const {
|
||||
std::vector<std::pair<std::string, Setting>> res;
|
||||
for (auto [key, sett] : m_impl->m_settings) {
|
||||
auto checker = JsonChecker(sett);
|
||||
auto value = checker.root("");
|
||||
auto legacy = Setting::parse(key, m_impl->m_id, value);
|
||||
if (!checker.isError() && legacy.isOk()) {
|
||||
res.push_back(std::make_pair(key, *legacy));
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
std::vector<std::pair<std::string, matjson::Value>> ModMetadata::getSettingsV3() const {
|
||||
std::vector<std::pair<std::string, matjson::Value>> ModMetadata::getSettings() const {
|
||||
return m_impl->m_settings;
|
||||
}
|
||||
std::unordered_set<std::string> ModMetadata::getTags() const {
|
||||
|
@ -641,10 +622,6 @@ void ModMetadata::setIncompatibilities(std::vector<Incompatibility> const& value
|
|||
void ModMetadata::setSpritesheets(std::vector<std::string> const& value) {
|
||||
m_impl->m_spritesheets = value;
|
||||
}
|
||||
void ModMetadata::setSettings(std::vector<std::pair<std::string, Setting>> const& value) {
|
||||
// intentionally no-op because no one is supposed to be using this
|
||||
// without subscribing to "internals are not stable" mentality
|
||||
}
|
||||
void ModMetadata::setSettings(std::vector<std::pair<std::string, matjson::Value>> const& value) {
|
||||
m_impl->m_settings = value;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <Geode/utils/VersionInfo.hpp>
|
||||
#include <Geode/loader/SettingV3.hpp>
|
||||
#include <Geode/loader/Setting.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
@ -74,52 +74,44 @@ namespace geode {
|
|||
|
||||
template <>
|
||||
struct matjson::Serialize<geode::ModMetadata::Dependency::Importance> {
|
||||
static matjson::Value GEODE_DLL to_json(geode::ModMetadata::Dependency::Importance const& importance) {
|
||||
switch (importance) {
|
||||
case geode::ModMetadata::Dependency::Importance::Required: return {"required"};
|
||||
case geode::ModMetadata::Dependency::Importance::Recommended: return {"recommended"};
|
||||
case geode::ModMetadata::Dependency::Importance::Suggested: return {"suggested"};
|
||||
default: return {"unknown"};
|
||||
}
|
||||
}
|
||||
static geode::ModMetadata::Dependency::Importance GEODE_DLL from_json(matjson::Value const& importance) {
|
||||
auto impStr = importance.as_string();
|
||||
if (impStr == "required")
|
||||
return geode::ModMetadata::Dependency::Importance::Required;
|
||||
if (impStr == "recommended")
|
||||
return geode::ModMetadata::Dependency::Importance::Recommended;
|
||||
if (impStr == "suggested")
|
||||
return geode::ModMetadata::Dependency::Importance::Suggested;
|
||||
throw matjson::JsonException(R"(Expected importance to be "required", "recommended" or "suggested")");
|
||||
static geode::Result<geode::ModMetadata::Dependency::Importance, std::string> fromJson(Value const& value)
|
||||
{
|
||||
auto str = GEODE_UNWRAP(value.asString());
|
||||
if (str == "required") return geode::Ok(geode::ModMetadata::Dependency::Importance::Required);
|
||||
if (str == "recommended") return geode::Ok(geode::ModMetadata::Dependency::Importance::Recommended);
|
||||
if (str == "suggested") return geode::Ok(geode::ModMetadata::Dependency::Importance::Suggested);
|
||||
return geode::Err("Invalid importance");
|
||||
}
|
||||
|
||||
static bool is_json(matjson::Value const& value) {
|
||||
return value.is_string();
|
||||
static Value toJson(geode::ModMetadata::Dependency::Importance const& value)
|
||||
{
|
||||
switch (value) {
|
||||
case geode::ModMetadata::Dependency::Importance::Required: return "required";
|
||||
case geode::ModMetadata::Dependency::Importance::Recommended: return "recommended";
|
||||
case geode::ModMetadata::Dependency::Importance::Suggested: return "suggested";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct matjson::Serialize<geode::ModMetadata::Incompatibility::Importance> {
|
||||
static matjson::Value GEODE_DLL to_json(geode::ModMetadata::Incompatibility::Importance const& importance) {
|
||||
switch (importance) {
|
||||
case geode::ModMetadata::Incompatibility::Importance::Breaking: return {"breaking"};
|
||||
case geode::ModMetadata::Incompatibility::Importance::Conflicting: return {"conflicting"};
|
||||
case geode::ModMetadata::Incompatibility::Importance::Superseded: return {"superseded"};
|
||||
default: return {"unknown"};
|
||||
}
|
||||
}
|
||||
static geode::ModMetadata::Incompatibility::Importance GEODE_DLL from_json(matjson::Value const& importance) {
|
||||
auto impStr = importance.as_string();
|
||||
if (impStr == "breaking")
|
||||
return geode::ModMetadata::Incompatibility::Importance::Breaking;
|
||||
if (impStr == "conflicting")
|
||||
return geode::ModMetadata::Incompatibility::Importance::Conflicting;
|
||||
if (impStr == "superseded")
|
||||
return geode::ModMetadata::Incompatibility::Importance::Superseded;
|
||||
throw matjson::JsonException(R"(Expected importance to be "breaking", "conflicting", or "superseded")");
|
||||
static geode::Result<geode::ModMetadata::Incompatibility::Importance, std::string> fromJson(Value const& value)
|
||||
{
|
||||
auto str = GEODE_UNWRAP(value.asString());
|
||||
if (str == "breaking") return geode::Ok(geode::ModMetadata::Incompatibility::Importance::Breaking);
|
||||
if (str == "conflicting") return geode::Ok(geode::ModMetadata::Incompatibility::Importance::Conflicting);
|
||||
if (str == "superseded") return geode::Ok(geode::ModMetadata::Incompatibility::Importance::Superseded);
|
||||
return geode::Err("Invalid importance");
|
||||
}
|
||||
|
||||
static bool is_json(matjson::Value const& value) {
|
||||
return value.is_string();
|
||||
static Value toJson(geode::ModMetadata::Incompatibility::Importance const& value)
|
||||
{
|
||||
switch (value) {
|
||||
case geode::ModMetadata::Incompatibility::Importance::Breaking: return "breaking";
|
||||
case geode::ModMetadata::Incompatibility::Importance::Conflicting: return "conflicting";
|
||||
case geode::ModMetadata::Incompatibility::Importance::Superseded: return "superseded";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
};
|
||||
|
|
|
@ -8,24 +8,36 @@ using namespace geode::prelude;
|
|||
// #1 no need to duplicate the built-in settings between all mods
|
||||
// #2 easier lookup of custom settings if a mod uses another mod's custom setting type
|
||||
|
||||
|
||||
namespace {
|
||||
auto changeToGenerator(auto&& function) {
|
||||
return [function](
|
||||
std::string const& key,
|
||||
std::string const& modID,
|
||||
matjson::Value const& json
|
||||
) -> Result<std::shared_ptr<SettingV3>> {
|
||||
return function(key, modID, json).map([](auto&& ptr) {
|
||||
return std::shared_ptr<SettingV3>(ptr);
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
class SharedSettingTypesPool final {
|
||||
private:
|
||||
std::unordered_map<std::string, SettingGenerator> m_types;
|
||||
|
||||
SharedSettingTypesPool() : m_types({
|
||||
// todo in v4: remove this
|
||||
{ "custom", &LegacyCustomSettingV3::parse },
|
||||
{ "title", &TitleSettingV3::parse },
|
||||
{ "bool", &BoolSettingV3::parse },
|
||||
{ "int", &IntSettingV3::parse },
|
||||
{ "float", &FloatSettingV3::parse },
|
||||
{ "string", &StringSettingV3::parse },
|
||||
{ "file", &FileSettingV3::parse },
|
||||
{ "folder", &FileSettingV3::parse },
|
||||
{ "path", &FileSettingV3::parse },
|
||||
{ "rgb", &Color3BSettingV3::parse },
|
||||
{ "color", &Color3BSettingV3::parse },
|
||||
{ "rgba", &Color4BSettingV3::parse },
|
||||
{ "title", changeToGenerator(TitleSettingV3::parse) },
|
||||
{ "bool", changeToGenerator(BoolSettingV3::parse) },
|
||||
{ "int", changeToGenerator(IntSettingV3::parse) },
|
||||
{ "float", changeToGenerator(FloatSettingV3::parse) },
|
||||
{ "string", changeToGenerator(StringSettingV3::parse) },
|
||||
{ "file", changeToGenerator(FileSettingV3::parse) },
|
||||
{ "folder", changeToGenerator(FileSettingV3::parse) },
|
||||
{ "path", changeToGenerator(FileSettingV3::parse) },
|
||||
{ "rgb", changeToGenerator(Color3BSettingV3::parse) },
|
||||
{ "color", changeToGenerator(Color3BSettingV3::parse) },
|
||||
{ "rgba", changeToGenerator(Color4BSettingV3::parse) },
|
||||
}) {}
|
||||
|
||||
public:
|
||||
|
@ -80,9 +92,7 @@ public:
|
|||
struct SettingInfo final {
|
||||
std::string type;
|
||||
matjson::Value json;
|
||||
std::shared_ptr<SettingV3> v3 = nullptr;
|
||||
// todo: remove in v4
|
||||
std::shared_ptr<SettingValue> legacy = nullptr;
|
||||
std::shared_ptr<Setting> v3 = nullptr;
|
||||
};
|
||||
std::string modID;
|
||||
std::unordered_map<std::string, SettingInfo> settings;
|
||||
|
@ -114,16 +124,11 @@ public:
|
|||
// Store the value in an intermediary so if `save` fails the existing
|
||||
// value loaded from disk isn't overwritten
|
||||
matjson::Value value;
|
||||
try {
|
||||
if (sett.v3->save(value)) {
|
||||
this->savedata[key] = value;
|
||||
}
|
||||
else {
|
||||
log::error("Unable to save setting '{}' for mod {}", key, this->modID);
|
||||
}
|
||||
if (sett.v3->save(value)) {
|
||||
this->savedata[key] = value;
|
||||
}
|
||||
catch(matjson::JsonException const& e) {
|
||||
log::error("Unable to save setting '{}' for mod {} (JSON exception): {}", key, this->modID, e.what());
|
||||
else {
|
||||
log::error("Unable to save setting '{}' for mod {}", key, this->modID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +144,7 @@ public:
|
|||
continue;
|
||||
}
|
||||
if (auto v3 = (*gen)(key, modID, setting.json)) {
|
||||
setting.v3 = *v3;
|
||||
setting.v3 = v3.unwrap();
|
||||
this->loadSettingValueFromSave(key);
|
||||
}
|
||||
else {
|
||||
|
@ -161,7 +166,7 @@ ModSettingsManager::ModSettingsManager(ModMetadata const& metadata)
|
|||
: m_impl(std::make_unique<Impl>())
|
||||
{
|
||||
m_impl->modID = metadata.getID();
|
||||
for (auto const& [key, json] : metadata.getSettingsV3()) {
|
||||
for (auto const& [key, json] : metadata.getSettings()) {
|
||||
auto setting = Impl::SettingInfo();
|
||||
setting.json = json;
|
||||
auto root = checkJson(json, "setting");
|
||||
|
@ -197,33 +202,13 @@ Result<> ModSettingsManager::registerCustomSettingType(std::string_view type, Se
|
|||
m_impl->createSettings();
|
||||
return Ok();
|
||||
}
|
||||
Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr) {
|
||||
auto id = std::string(key);
|
||||
if (!m_impl->settings.count(id)) {
|
||||
return Err("No such setting '{}' in mod {}", id, m_impl->modID);
|
||||
}
|
||||
auto& sett = m_impl->settings.at(id);
|
||||
if (auto custom = typeinfo_pointer_cast<LegacyCustomSettingV3>(sett.v3)) {
|
||||
if (!custom->getValue()) {
|
||||
custom->setValue(std::move(ptr));
|
||||
m_impl->loadSettingValueFromSave(id);
|
||||
}
|
||||
else {
|
||||
return Err("Setting '{}' in mod {} has already been registed", id, m_impl->modID);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return Err("Setting '{}' in mod {} is not a legacy custom setting", id, m_impl->modID);
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
Result<> ModSettingsManager::load(matjson::Value const& json) {
|
||||
if (json.is_object()) {
|
||||
if (json.isObject()) {
|
||||
// Save this so when custom settings are registered they can load their
|
||||
// values properly
|
||||
m_impl->savedata = json.as_object();
|
||||
for (auto const& [key, _] : json.as_object()) {
|
||||
m_impl->savedata = json;
|
||||
for (auto const& [key, _] : json) {
|
||||
m_impl->loadSettingValueFromSave(key);
|
||||
}
|
||||
}
|
||||
|
@ -240,37 +225,10 @@ matjson::Value& ModSettingsManager::getSaveData() {
|
|||
return m_impl->savedata;
|
||||
}
|
||||
|
||||
std::shared_ptr<SettingV3> ModSettingsManager::get(std::string_view key) {
|
||||
std::shared_ptr<Setting> ModSettingsManager::get(std::string_view key) {
|
||||
auto id = std::string(key);
|
||||
return m_impl->settings.count(id) ? m_impl->settings.at(id).v3 : nullptr;
|
||||
}
|
||||
std::shared_ptr<SettingValue> ModSettingsManager::getLegacy(std::string_view key) {
|
||||
auto id = std::string(key);
|
||||
if (!m_impl->settings.count(id)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto& info = m_impl->settings.at(id);
|
||||
// If this setting has alreay been given a legacy interface, give that
|
||||
if (info.legacy) {
|
||||
return info.legacy;
|
||||
}
|
||||
// Uninitialized settings are null
|
||||
if (!info.v3) {
|
||||
return nullptr;
|
||||
}
|
||||
// Generate new legacy interface
|
||||
if (auto legacy = info.v3->convertToLegacyValue()) {
|
||||
info.legacy.swap(*legacy);
|
||||
return info.legacy;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
std::optional<Setting> ModSettingsManager::getLegacyDefinition(std::string_view key) {
|
||||
if (auto s = this->get(key)) {
|
||||
return s->convertToLegacy();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool ModSettingsManager::restartRequired() const {
|
||||
return m_impl->restartRequired;
|
||||
|
|
|
@ -92,8 +92,8 @@ Result<> Patch::Impl::updateBytes(const ByteVector& bytes) {
|
|||
if (m_enabled) {
|
||||
auto res = this->disable();
|
||||
if (!res) return Err("Failed to update patch: {}", res.unwrapErr());
|
||||
res = this->enable();
|
||||
if (!res) return Err("Failed to update patch: {}", res.unwrapErr());
|
||||
auto res2 = this->enable();
|
||||
if (!res2) return Err("Failed to update patch: {}", res2.unwrapErr());
|
||||
}
|
||||
|
||||
return Ok();
|
||||
|
@ -104,7 +104,7 @@ uintptr_t Patch::Impl::getAddress() const {
|
|||
}
|
||||
|
||||
matjson::Value Patch::Impl::getRuntimeInfo() const {
|
||||
auto json = matjson::Object();
|
||||
auto json = matjson::Value::object();
|
||||
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
|
||||
json["original"] = m_original;
|
||||
json["patch"] = m_patch;
|
||||
|
|
|
@ -1,498 +0,0 @@
|
|||
#include <ui/mods/settings/GeodeSettingNode.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/Setting.hpp>
|
||||
#include <Geode/loader/SettingV3.hpp>
|
||||
#include <Geode/loader/SettingEvent.hpp>
|
||||
#include <Geode/loader/SettingNode.hpp>
|
||||
#include <Geode/utils/general.hpp>
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <regex>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
template<class T>
|
||||
static void parseCommon(T& sett, JsonMaybeObject& obj) {
|
||||
obj.has("name").into(sett.name);
|
||||
obj.has("description").into(sett.description);
|
||||
if (auto defValue = obj.needs("default")) {
|
||||
// Platform-specific default value
|
||||
if (defValue.is<matjson::Object>()) {
|
||||
auto def = defValue.obj();
|
||||
if (auto plat = def.has(PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))) {
|
||||
plat.into(sett.defaultValue);
|
||||
}
|
||||
else {
|
||||
defValue.into(sett.defaultValue);
|
||||
}
|
||||
}
|
||||
else {
|
||||
defValue.into(sett.defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result<BoolSetting> BoolSetting::parse(JsonMaybeObject& obj) {
|
||||
BoolSetting sett;
|
||||
parseCommon(sett, obj);
|
||||
return Ok(sett);
|
||||
}
|
||||
|
||||
Result<IntSetting> IntSetting::parse(JsonMaybeObject& obj) {
|
||||
IntSetting sett;
|
||||
parseCommon(sett, obj);
|
||||
obj.has("min").into(sett.min);
|
||||
obj.has("max").into(sett.max);
|
||||
if (auto controls = obj.has("control").obj()) {
|
||||
controls.has("arrows").into(sett.controls.arrows);
|
||||
controls.has("big-arrows").into(sett.controls.bigArrows);
|
||||
controls.has("arrow-step").into(sett.controls.arrowStep);
|
||||
controls.has("big-arrow-step").into(sett.controls.bigArrowStep);
|
||||
controls.has("slider").into(sett.controls.slider);
|
||||
controls.has("slider-step").into(sett.controls.sliderStep);
|
||||
controls.has("input").into(sett.controls.input);
|
||||
}
|
||||
return Ok(sett);
|
||||
}
|
||||
|
||||
Result<FloatSetting> FloatSetting::parse(JsonMaybeObject& obj) {
|
||||
FloatSetting sett;
|
||||
parseCommon(sett, obj);
|
||||
obj.has("min").into(sett.min);
|
||||
obj.has("max").into(sett.max);
|
||||
if (auto controls = obj.has("control").obj()) {
|
||||
controls.has("arrows").into(sett.controls.arrows);
|
||||
controls.has("big-arrows").into(sett.controls.bigArrows);
|
||||
controls.has("arrow-step").into(sett.controls.arrowStep);
|
||||
controls.has("big-arrow-step").into(sett.controls.bigArrowStep);
|
||||
controls.has("slider").into(sett.controls.slider);
|
||||
controls.has("slider-step").into(sett.controls.sliderStep);
|
||||
controls.has("input").into(sett.controls.input);
|
||||
}
|
||||
return Ok(sett);
|
||||
}
|
||||
|
||||
Result<StringSetting> StringSetting::parse(JsonMaybeObject& obj) {
|
||||
StringSetting sett;
|
||||
parseCommon(sett, obj);
|
||||
obj.has("match").into(sett.controls->match);
|
||||
obj.has("filter").into(sett.controls->filter);
|
||||
obj.has("one-of").into(sett.controls->options);
|
||||
return Ok(sett);
|
||||
}
|
||||
|
||||
Result<FileSetting> FileSetting::parse(JsonMaybeObject& obj) {
|
||||
FileSetting sett;
|
||||
parseCommon(sett, obj);
|
||||
if (auto controls = obj.has("control").obj()) {
|
||||
for (auto& item : controls.has("filters").iterate()) {
|
||||
if (auto iobj = item.obj()) {
|
||||
Filter filter;
|
||||
iobj.has("description").into(filter.description);
|
||||
|
||||
std::vector<matjson::Value> files;
|
||||
iobj.has("files").into(files);
|
||||
|
||||
for (auto& i : files) {
|
||||
filter.files.insert(i.as<std::string>());
|
||||
}
|
||||
|
||||
sett.controls.filters.push_back(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(sett);
|
||||
}
|
||||
|
||||
Result<ColorSetting> ColorSetting::parse(JsonMaybeObject& obj) {
|
||||
ColorSetting sett;
|
||||
parseCommon(sett, obj);
|
||||
return Ok(sett);
|
||||
}
|
||||
|
||||
Result<ColorAlphaSetting> ColorAlphaSetting::parse(JsonMaybeObject& obj) {
|
||||
ColorAlphaSetting sett;
|
||||
parseCommon(sett, obj);
|
||||
return Ok(sett);
|
||||
}
|
||||
|
||||
Result<Setting> Setting::parse(
|
||||
std::string const& key,
|
||||
std::string const& mod,
|
||||
JsonMaybeValue& value
|
||||
) {
|
||||
auto sett = Setting();
|
||||
sett.m_key = key;
|
||||
sett.m_modID = mod;
|
||||
if (auto obj = value.obj()) {
|
||||
std::string type;
|
||||
obj.needs("type").into(type);
|
||||
if (type.size()) {
|
||||
switch (hash(type.c_str())) {
|
||||
case hash("bool"): {
|
||||
GEODE_UNWRAP_INTO(sett.m_kind, BoolSetting::parse(obj));
|
||||
} break;
|
||||
|
||||
case hash("int"): {
|
||||
GEODE_UNWRAP_INTO(sett.m_kind, IntSetting::parse(obj));
|
||||
} break;
|
||||
|
||||
case hash("float"): {
|
||||
GEODE_UNWRAP_INTO(sett.m_kind, FloatSetting::parse(obj));
|
||||
} break;
|
||||
|
||||
case hash("string"): {
|
||||
GEODE_UNWRAP_INTO(sett.m_kind, StringSetting::parse(obj));
|
||||
} break;
|
||||
|
||||
case hash("rgb"): case hash("color"): {
|
||||
GEODE_UNWRAP_INTO(sett.m_kind, ColorSetting::parse(obj));
|
||||
} break;
|
||||
|
||||
case hash("rgba"): {
|
||||
GEODE_UNWRAP_INTO(sett.m_kind, ColorAlphaSetting::parse(obj));
|
||||
} break;
|
||||
|
||||
case hash("path"): case hash("file"): {
|
||||
GEODE_UNWRAP_INTO(sett.m_kind, FileSetting::parse(obj));
|
||||
} break;
|
||||
|
||||
case hash("custom"): {
|
||||
sett.m_kind = CustomSetting {
|
||||
.json = std::make_shared<ModJson>(obj.json())
|
||||
};
|
||||
// skip checking unknown keys
|
||||
return Ok(sett);
|
||||
} break;
|
||||
|
||||
default: return Err("Unknown setting type \"" + type + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
// this is handled before the setting is parsed
|
||||
obj.addKnownKey("platforms");
|
||||
|
||||
obj.checkUnknownKeys();
|
||||
}
|
||||
// if the type wasn't an object or a string, the JsonChecker that gave the
|
||||
// JsonMaybeValue will fail eventually so we can continue on
|
||||
return Ok(sett);
|
||||
}
|
||||
|
||||
Setting::Setting(
|
||||
std::string const& key,
|
||||
std::string const& mod,
|
||||
SettingKind const& kind
|
||||
) : m_key(key), m_modID(mod), m_kind(kind) {}
|
||||
|
||||
std::unique_ptr<SettingValue> Setting::createDefaultValue() const {
|
||||
return std::visit(makeVisitor {
|
||||
[&](BoolSetting const& sett) -> std::unique_ptr<SettingValue> {
|
||||
return std::make_unique<BoolSettingValue>(m_key, m_modID, sett);
|
||||
},
|
||||
[&](IntSetting const& sett) -> std::unique_ptr<SettingValue> {
|
||||
return std::make_unique<IntSettingValue>(m_key, m_modID, sett);
|
||||
},
|
||||
[&](FloatSetting const& sett) -> std::unique_ptr<SettingValue> {
|
||||
return std::make_unique<FloatSettingValue>(m_key, m_modID, sett);
|
||||
},
|
||||
[&](StringSetting const& sett) -> std::unique_ptr<SettingValue> {
|
||||
return std::make_unique<StringSettingValue>(m_key, m_modID, sett);
|
||||
},
|
||||
[&](FileSetting const& sett) -> std::unique_ptr<SettingValue> {
|
||||
return std::make_unique<FileSettingValue>(m_key, m_modID, sett);
|
||||
},
|
||||
[&](ColorSetting const& sett) -> std::unique_ptr<SettingValue> {
|
||||
return std::make_unique<ColorSettingValue>(m_key, m_modID, sett);
|
||||
},
|
||||
[&](ColorAlphaSetting const& sett) -> std::unique_ptr<SettingValue> {
|
||||
return std::make_unique<ColorAlphaSettingValue>(m_key, m_modID, sett);
|
||||
},
|
||||
[&](auto const& sett) -> std::unique_ptr<SettingValue> {
|
||||
return nullptr;
|
||||
},
|
||||
}, m_kind);
|
||||
}
|
||||
|
||||
bool Setting::isCustom() const {
|
||||
return std::holds_alternative<CustomSetting>(m_kind);
|
||||
}
|
||||
|
||||
std::string Setting::getDisplayName() const {
|
||||
return std::visit(makeVisitor {
|
||||
[&](CustomSetting const& sett) {
|
||||
return std::string();
|
||||
},
|
||||
[&](auto const& sett) {
|
||||
return sett.name.value_or(m_key);
|
||||
},
|
||||
}, m_kind);
|
||||
}
|
||||
|
||||
std::optional<std::string> Setting::getDescription() const {
|
||||
return std::visit(makeVisitor {
|
||||
[&](CustomSetting const& sett) -> std::optional<std::string> {
|
||||
return std::nullopt;
|
||||
},
|
||||
[&](auto const& sett) {
|
||||
return sett.description;
|
||||
},
|
||||
}, m_kind);
|
||||
}
|
||||
|
||||
std::string Setting::getModID() const {
|
||||
return m_modID;
|
||||
}
|
||||
|
||||
// StringSetting
|
||||
|
||||
StringSetting::StringSetting() : name(), description(), defaultValue(), controls(new Data()) {}
|
||||
StringSetting::StringSetting(StringSetting const& other) : name(other.name), description(other.description),
|
||||
defaultValue(other.defaultValue), controls(new Data(*other.controls)) {}
|
||||
StringSetting::StringSetting(StringSetting&& other) noexcept : name(std::move(other.name)), description(std::move(other.description)),
|
||||
defaultValue(std::move(other.defaultValue)), controls(std::move(other.controls)) {}
|
||||
StringSetting& StringSetting::operator=(StringSetting const& other) {
|
||||
name = other.name;
|
||||
description = other.description;
|
||||
defaultValue = other.defaultValue;
|
||||
*controls = *other.controls;
|
||||
return *this;
|
||||
}
|
||||
StringSetting& StringSetting::operator=(StringSetting&& other) noexcept {
|
||||
name = std::move(other.name);
|
||||
description = std::move(other.description);
|
||||
defaultValue = std::move(other.defaultValue);
|
||||
controls = std::move(other.controls);
|
||||
return *this;
|
||||
}
|
||||
StringSetting::~StringSetting() = default;
|
||||
|
||||
// SettingValue
|
||||
|
||||
SettingValue::SettingValue(std::string const& key, std::string const& mod)
|
||||
: m_key(key), m_modID(mod) {}
|
||||
|
||||
std::string SettingValue::getKey() const {
|
||||
return m_key;
|
||||
}
|
||||
|
||||
std::string SettingValue::getModID() const {
|
||||
return m_modID;
|
||||
}
|
||||
|
||||
void SettingValue::valueChanged() {
|
||||
if (auto mod = Loader::get()->getLoadedMod(m_modID)) {
|
||||
if (auto sett = mod->getSettingV3(m_key)) {
|
||||
sett->markChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GeodeSettingValue & SettingValueSetter specializations
|
||||
|
||||
#define IMPL_NODE_AND_SETTERS(type_) \
|
||||
template<> \
|
||||
SettingNode* GeodeSettingValue< \
|
||||
type_##Setting \
|
||||
>::createNode(float width) { \
|
||||
return type_##SettingNode::create(this, width); \
|
||||
} \
|
||||
template<> \
|
||||
typename GeodeSettingValue<type_##Setting>::ValueType \
|
||||
GeodeSettingValue<type_##Setting>::getValue() const { \
|
||||
using S = typename SettingTypeForValueType<ValueType>::SettingType; \
|
||||
if (auto mod = Loader::get()->getInstalledMod(m_modID)) { \
|
||||
if (auto setting = typeinfo_pointer_cast<S>(mod->getSettingV3(m_key))) {\
|
||||
return setting->getValue(); \
|
||||
} \
|
||||
} \
|
||||
return m_value; \
|
||||
} \
|
||||
template<> \
|
||||
void GeodeSettingValue< \
|
||||
type_##Setting \
|
||||
>::setValue(ValueType const& value) { \
|
||||
using S = typename SettingTypeForValueType<ValueType>::SettingType; \
|
||||
if (auto mod = Loader::get()->getInstalledMod(m_modID)) { \
|
||||
if (auto setting = typeinfo_pointer_cast<S>(mod->getSettingV3(m_key))) {\
|
||||
return setting->setValue(value); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
template<> \
|
||||
Result<> GeodeSettingValue< \
|
||||
type_##Setting \
|
||||
>::validate(ValueType const& value) const { \
|
||||
using S = typename SettingTypeForValueType<ValueType>::SettingType; \
|
||||
if (auto mod = Loader::get()->getInstalledMod(m_modID)) { \
|
||||
if (auto setting = typeinfo_pointer_cast<S>(mod->getSettingV3(m_key))) {\
|
||||
return setting->isValid(value); \
|
||||
} \
|
||||
} \
|
||||
return Ok(); \
|
||||
} \
|
||||
template<> \
|
||||
typename type_##Setting::ValueType SettingValueSetter< \
|
||||
typename type_##Setting::ValueType \
|
||||
>::get(SettingValue* setting) { \
|
||||
using S = typename SettingTypeForValueType<typename type_##Setting::ValueType>::SettingType; \
|
||||
if (auto mod = Loader::get()->getInstalledMod(setting->getModID())) { \
|
||||
if (auto sett = typeinfo_pointer_cast<S>(mod->getSettingV3(setting->getKey()))) { \
|
||||
return sett->getValue(); \
|
||||
} \
|
||||
} \
|
||||
return typename type_##Setting::ValueType(); \
|
||||
} \
|
||||
template<> \
|
||||
void SettingValueSetter< \
|
||||
typename type_##Setting::ValueType \
|
||||
>::set( \
|
||||
SettingValue* setting, \
|
||||
typename type_##Setting::ValueType const& value \
|
||||
) { \
|
||||
using S = typename SettingTypeForValueType<typename type_##Setting::ValueType>::SettingType; \
|
||||
if (auto mod = Loader::get()->getInstalledMod(setting->getModID())) { \
|
||||
if (auto sett = typeinfo_pointer_cast<S>(mod->getSettingV3(setting->getKey()))) { \
|
||||
return sett->setValue(value); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define IMPL_TO_VALID(type_) \
|
||||
template<> \
|
||||
typename GeodeSettingValue<type_##Setting>::Valid \
|
||||
GeodeSettingValue<type_##Setting>::toValid( \
|
||||
typename type_##Setting::ValueType const& value \
|
||||
) const
|
||||
|
||||
// instantiate values
|
||||
|
||||
namespace geode {
|
||||
template class GeodeSettingValue<BoolSetting>;
|
||||
template class GeodeSettingValue<IntSetting>;
|
||||
template class GeodeSettingValue<FloatSetting>;
|
||||
template class GeodeSettingValue<StringSetting>;
|
||||
template class GeodeSettingValue<FileSetting>;
|
||||
template class GeodeSettingValue<ColorSetting>;
|
||||
template class GeodeSettingValue<ColorAlphaSetting>;
|
||||
}
|
||||
|
||||
IMPL_TO_VALID(Bool) {
|
||||
return { value, std::nullopt };
|
||||
}
|
||||
|
||||
IMPL_TO_VALID(Int) {
|
||||
if (m_definition.min && value < m_definition.min) {
|
||||
return { m_definition.min.value(), fmt::format(
|
||||
"Value must be more than or equal to {}",
|
||||
m_definition.min.value()
|
||||
) };
|
||||
}
|
||||
if (m_definition.max && value > m_definition.max) {
|
||||
return { m_definition.max.value(), fmt::format(
|
||||
"Value must be less than or equal to {}",
|
||||
m_definition.max.value()
|
||||
) };
|
||||
}
|
||||
return { value, std::nullopt };
|
||||
}
|
||||
|
||||
IMPL_TO_VALID(Float) {
|
||||
if (m_definition.min && value < m_definition.min) {
|
||||
return { m_definition.min.value(), fmt::format(
|
||||
"Value must be more than or equal to {}",
|
||||
m_definition.min.value()
|
||||
) };
|
||||
}
|
||||
if (m_definition.max && value > m_definition.max) {
|
||||
return { m_definition.max.value(), fmt::format(
|
||||
"Value must be less than or equal to {}",
|
||||
m_definition.max.value()
|
||||
) };
|
||||
}
|
||||
return { value, std::nullopt };
|
||||
}
|
||||
|
||||
IMPL_TO_VALID(String) {
|
||||
if (m_definition.controls->match) {
|
||||
if (!std::regex_match(value, std::regex{m_definition.controls->match.value()})) {
|
||||
return {
|
||||
m_definition.defaultValue,
|
||||
fmt::format(
|
||||
"Value must match regex {}",
|
||||
m_definition.controls->match.value()
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (m_definition.controls->options) {
|
||||
if (std::find(
|
||||
m_definition.controls->options.value().begin(),
|
||||
m_definition.controls->options.value().end(),
|
||||
value
|
||||
) == m_definition.controls->options.value().end()) {
|
||||
return {
|
||||
m_definition.defaultValue,
|
||||
fmt::format(
|
||||
"Value must be one of {}",
|
||||
fmt::join(m_definition.controls->options.value(), ", ")
|
||||
)
|
||||
};
|
||||
}
|
||||
}
|
||||
return { value, std::nullopt };
|
||||
}
|
||||
|
||||
IMPL_TO_VALID(File) {
|
||||
return { value, std::nullopt };
|
||||
}
|
||||
|
||||
IMPL_TO_VALID(Color) {
|
||||
return { value, std::nullopt };
|
||||
}
|
||||
|
||||
IMPL_TO_VALID(ColorAlpha) {
|
||||
return { value, std::nullopt };
|
||||
}
|
||||
|
||||
IMPL_NODE_AND_SETTERS(Bool);
|
||||
IMPL_NODE_AND_SETTERS(Int);
|
||||
IMPL_NODE_AND_SETTERS(Float);
|
||||
IMPL_NODE_AND_SETTERS(String);
|
||||
IMPL_NODE_AND_SETTERS(File);
|
||||
IMPL_NODE_AND_SETTERS(Color);
|
||||
IMPL_NODE_AND_SETTERS(ColorAlpha);
|
||||
|
||||
// instantiate value setters
|
||||
|
||||
namespace geode {
|
||||
template struct SettingValueSetter<typename BoolSetting::ValueType>;
|
||||
template struct SettingValueSetter<typename IntSetting::ValueType>;
|
||||
template struct SettingValueSetter<typename FloatSetting::ValueType>;
|
||||
template struct SettingValueSetter<typename StringSetting::ValueType>;
|
||||
template struct SettingValueSetter<typename FileSetting::ValueType>;
|
||||
template struct SettingValueSetter<typename ColorSetting::ValueType>;
|
||||
template struct SettingValueSetter<typename ColorAlphaSetting::ValueType>;
|
||||
}
|
||||
|
||||
// SettingChangedEvent
|
||||
|
||||
SettingChangedEvent::SettingChangedEvent(Mod* mod, SettingValue* value)
|
||||
: mod(mod), value(value) {}
|
||||
|
||||
// SettingChangedFilter
|
||||
|
||||
ListenerResult SettingChangedFilter::handle(
|
||||
utils::MiniFunction<Callback> fn, SettingChangedEvent* event
|
||||
) {
|
||||
if (m_modID == event->mod->getID() &&
|
||||
(!m_targetKey || m_targetKey.value() == event->value->getKey())
|
||||
) {
|
||||
fn(event->value);
|
||||
}
|
||||
return ListenerResult::Propagate;
|
||||
}
|
||||
|
||||
SettingChangedFilter::SettingChangedFilter(
|
||||
std::string const& modID,
|
||||
std::optional<std::string> const& settingKey
|
||||
) : m_modID(modID), m_targetKey(settingKey) {}
|
|
@ -1,25 +0,0 @@
|
|||
#include <Geode/loader/SettingNode.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
void SettingNode::dispatchChanged() {
|
||||
if (m_delegate) {
|
||||
m_delegate->settingValueChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingNode::dispatchCommitted() {
|
||||
if (m_delegate) {
|
||||
m_delegate->settingValueCommitted(this);
|
||||
}
|
||||
}
|
||||
|
||||
bool SettingNode::init(SettingValue* value) {
|
||||
m_value = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
void SettingNode::setDelegate(SettingNodeDelegate* delegate) {
|
||||
m_delegate = delegate;
|
||||
}
|
16
loader/src/loader/SettingNode.hpp
Normal file
16
loader/src/loader/SettingNode.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#include "SettingNodeV3.hpp"
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
using TitleSettingNode = TitleSettingNodeV3;
|
||||
using BoolSettingNode = BoolSettingNodeV3;
|
||||
template <class S>
|
||||
using NumberSettingNode = NumberSettingNodeV3<S>;
|
||||
using IntSettingNode = IntSettingNodeV3;
|
||||
using FloatSettingNode = FloatSettingNodeV3;
|
||||
using StringSettingNode = StringSettingNodeV3;
|
||||
using FileSettingNode = FileSettingNodeV3;
|
||||
using Color3BSettingNode = Color3BSettingNodeV3;
|
||||
using Color4BSettingNode = Color4BSettingNodeV3;
|
||||
using UnresolvedCustomSettingNode = UnresolvedCustomSettingNodeV3;
|
|
@ -1,5 +1,4 @@
|
|||
#include "SettingNodeV3.hpp"
|
||||
#include <Geode/loader/SettingNode.hpp>
|
||||
#include <Geode/utils/ColorProvider.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <Geode/loader/Dirs.hpp>
|
||||
|
@ -137,7 +136,7 @@ void SettingNodeV3::updateState(CCNode* invoker) {
|
|||
m_impl->bg->setOpacity(75);
|
||||
}
|
||||
|
||||
m_impl->nameMenu->setContentWidth(this->getContentWidth() - m_impl->buttonMenu->getContentWidth() - 20);
|
||||
m_impl->nameMenu->setContentWidth(this->getContentWidth() - m_impl->buttonMenu->getContentWidth() - 25);
|
||||
m_impl->nameMenu->updateLayout();
|
||||
}
|
||||
|
||||
|
@ -463,6 +462,7 @@ void FileSettingNodeV3::updateState(CCNode* invoker) {
|
|||
// which is clever and good UX but also a hack so I also need to hack to support that
|
||||
const auto isTextualDefaultValue = [this, setting = this->getSetting()]() {
|
||||
if (this->hasNonDefaultValue()) return false;
|
||||
if (setting->getDefaultValue().string().size() > 20) return false;
|
||||
std::error_code ec;
|
||||
return setting->isFolder() ?
|
||||
!std::filesystem::is_directory(setting->getDefaultValue(), ec) :
|
||||
|
@ -683,51 +683,3 @@ UnresolvedCustomSettingNodeV3* UnresolvedCustomSettingNodeV3::create(std::string
|
|||
CC_SAFE_DELETE(ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// LegacyCustomSettingToV3Node
|
||||
|
||||
bool LegacyCustomSettingToV3Node::init(std::shared_ptr<LegacyCustomSettingV3> original, float width) {
|
||||
if (!SettingNodeV3::init(original, width))
|
||||
return false;
|
||||
|
||||
this->getNameMenu()->setVisible(false);
|
||||
this->getButtonMenu()->setVisible(false);
|
||||
|
||||
m_original = original->getValue()->createNode(width);
|
||||
m_original->setDelegate(this);
|
||||
this->setContentSize({ width, m_original->getContentHeight() });
|
||||
this->addChildAtPosition(m_original, Anchor::BottomLeft, ccp(0, 0), ccp(0, 0));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LegacyCustomSettingToV3Node::settingValueChanged(SettingNode*) {
|
||||
SettingNodeValueChangeEventV3(this, false).post();
|
||||
}
|
||||
void LegacyCustomSettingToV3Node::settingValueCommitted(SettingNode*) {
|
||||
SettingNodeValueChangeEventV3(this, true).post();
|
||||
}
|
||||
|
||||
void LegacyCustomSettingToV3Node::onCommit() {
|
||||
m_original->commit();
|
||||
}
|
||||
|
||||
bool LegacyCustomSettingToV3Node::hasUncommittedChanges() const {
|
||||
return m_original->hasUncommittedChanges();
|
||||
}
|
||||
bool LegacyCustomSettingToV3Node::hasNonDefaultValue() const {
|
||||
return m_original->hasNonDefaultValue();
|
||||
}
|
||||
void LegacyCustomSettingToV3Node::onResetToDefault() {
|
||||
m_original->resetToDefault();
|
||||
}
|
||||
|
||||
LegacyCustomSettingToV3Node* LegacyCustomSettingToV3Node::create(std::shared_ptr<LegacyCustomSettingV3> original, float width) {
|
||||
auto ret = new LegacyCustomSettingToV3Node();
|
||||
if (ret && ret->init(original, width)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
CC_SAFE_DELETE(ret);
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/loader/SettingV3.hpp>
|
||||
#include <Geode/loader/SettingNode.hpp>
|
||||
#include <Geode/loader/Setting.hpp>
|
||||
#include <Geode/binding/CCMenuItemToggler.hpp>
|
||||
#include <Geode/binding/ColorChannelSprite.hpp>
|
||||
#include <Geode/binding/Slider.hpp>
|
||||
|
@ -314,25 +313,3 @@ public:
|
|||
bool hasNonDefaultValue() const override;
|
||||
void onResetToDefault() override;
|
||||
};
|
||||
|
||||
// If these classes do get exposed in headers,
|
||||
// LegacyCustomSettingToV3Node SHOULD NOT BE EXPOSED!!!!!! DO NOT DO THAT!!!!
|
||||
|
||||
class LegacyCustomSettingToV3Node : public SettingNodeV3, public SettingNodeDelegate {
|
||||
protected:
|
||||
SettingNode* m_original;
|
||||
|
||||
bool init(std::shared_ptr<LegacyCustomSettingV3> original, float width);
|
||||
|
||||
void onCommit() override;
|
||||
|
||||
void settingValueChanged(SettingNode*) override;
|
||||
void settingValueCommitted(SettingNode*) override;
|
||||
|
||||
public:
|
||||
static LegacyCustomSettingToV3Node* create(std::shared_ptr<LegacyCustomSettingV3> original, float width);
|
||||
|
||||
bool hasUncommittedChanges() const override;
|
||||
bool hasNonDefaultValue() const override;
|
||||
void onResetToDefault() override;
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include <Geode/loader/SettingV3.hpp>
|
||||
#include <Geode/loader/SettingEvent.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/Setting.hpp>
|
||||
#include <Geode/loader/ModSettingsManager.hpp>
|
||||
#include <Geode/utils/ranges.hpp>
|
||||
#include <Geode/utils/string.hpp>
|
||||
|
@ -7,6 +7,7 @@
|
|||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <regex>
|
||||
#include "SettingNodeV3.hpp"
|
||||
#include <matjson/std.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
@ -46,7 +47,7 @@ namespace enable_if_parsing {
|
|||
if (!mod->hasSetting(settingID)) {
|
||||
return Err("Mod '{}' does not have setting '{}'", mod->getName(), settingID);
|
||||
}
|
||||
if (!typeinfo_pointer_cast<BoolSettingV3>(mod->getSettingV3(settingID))) {
|
||||
if (!typeinfo_pointer_cast<BoolSettingV3>(mod->getSetting(settingID))) {
|
||||
return Err("Setting '{}' in mod '{}' is not a boolean setting", settingID, mod->getName());
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +61,7 @@ namespace enable_if_parsing {
|
|||
// This is an if-check just in case, even though check() should already
|
||||
// make sure that getSettingV3 is guaranteed to return true
|
||||
auto name = settingID;
|
||||
if (auto sett = mod->getSettingV3(settingID)) {
|
||||
if (auto sett = mod->getSetting(settingID)) {
|
||||
name = sett->getDisplayName();
|
||||
}
|
||||
if (modID == defaultModID) {
|
||||
|
@ -153,18 +154,21 @@ namespace enable_if_parsing {
|
|||
return Ok();
|
||||
}
|
||||
Result<> eval(std::string const& defaultModID) const override {
|
||||
Result<> err = Ok();
|
||||
std::optional<std::string> err;
|
||||
for (auto& comp : components) {
|
||||
auto res = comp->eval(defaultModID);
|
||||
if (res) {
|
||||
return Ok();
|
||||
}
|
||||
// Only show first condition that isn't met
|
||||
if (err.isOk()) {
|
||||
err = Err(res.unwrapErr());
|
||||
if (!err.has_value()) {
|
||||
err = res.unwrapErr();
|
||||
}
|
||||
}
|
||||
return err;
|
||||
if (err.has_value()) {
|
||||
return Err(*err);
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -236,7 +240,10 @@ namespace enable_if_parsing {
|
|||
auto original = m_index;
|
||||
auto ret = this->nextWord();
|
||||
m_index = original;
|
||||
return ret ? *ret : std::nullopt;
|
||||
if (!ret) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return ret.unwrap();
|
||||
}
|
||||
Result<std::unique_ptr<Component>> nextComponent() {
|
||||
GEODE_UNWRAP_INTO(auto maybeWord, this->nextWord());
|
||||
|
@ -423,7 +430,7 @@ public:
|
|||
std::optional<std::string> settingKey;
|
||||
};
|
||||
|
||||
ListenerResult SettingChangedFilterV3::handle(utils::MiniFunction<Callback> fn, SettingChangedEventV3* event) {
|
||||
ListenerResult SettingChangedFilterV3::handle(std::function<Callback> fn, SettingChangedEventV3* event) {
|
||||
if (
|
||||
event->getSetting()->getModID() == m_impl->modID &&
|
||||
!m_impl->settingKey || event->getSetting()->getKey() == m_impl->settingKey
|
||||
|
@ -447,7 +454,7 @@ SettingChangedFilterV3::SettingChangedFilterV3(Mod* mod, std::optional<std::stri
|
|||
|
||||
SettingChangedFilterV3::SettingChangedFilterV3(SettingChangedFilterV3 const&) = default;
|
||||
|
||||
EventListener<SettingChangedFilterV3>* geode::listenForAllSettingChanges(
|
||||
EventListener<SettingChangedFilterV3>* geode::listenForAllSettingChangesV3(
|
||||
std::function<void(std::shared_ptr<SettingV3>)> const& callback,
|
||||
Mod* mod
|
||||
) {
|
||||
|
@ -573,20 +580,7 @@ void SettingV3::markChanged() {
|
|||
manager->markRestartRequired();
|
||||
}
|
||||
SettingChangedEventV3(shared_from_this()).post();
|
||||
if (manager) {
|
||||
// Use ModSettingsManager rather than convertToLegacyValue since it
|
||||
// caches the result and we want to have that for performance
|
||||
SettingChangedEvent(this->getMod(), manager->getLegacy(this->getKey()).get()).post();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Setting> SettingV3::convertToLegacy() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::shared_ptr<SettingValue>> SettingV3::convertToLegacyValue() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
class TitleSettingV3::Impl final {
|
||||
public:
|
||||
};
|
||||
|
@ -618,63 +612,6 @@ bool TitleSettingV3::isDefaultValue() const {
|
|||
}
|
||||
void TitleSettingV3::reset() {}
|
||||
|
||||
class LegacyCustomSettingV3::Impl final {
|
||||
public:
|
||||
matjson::Value json;
|
||||
std::shared_ptr<SettingValue> legacyValue = nullptr;
|
||||
};
|
||||
|
||||
LegacyCustomSettingV3::LegacyCustomSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
|
||||
|
||||
Result<std::shared_ptr<LegacyCustomSettingV3>> LegacyCustomSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
|
||||
auto ret = std::make_shared<LegacyCustomSettingV3>(PrivateMarker());
|
||||
ret->init(key, modID);
|
||||
ret->m_impl->json = json;
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
std::shared_ptr<SettingValue> LegacyCustomSettingV3::getValue() const {
|
||||
return m_impl->legacyValue;
|
||||
}
|
||||
void LegacyCustomSettingV3::setValue(std::shared_ptr<SettingValue> value) {
|
||||
m_impl->legacyValue = value;
|
||||
}
|
||||
|
||||
bool LegacyCustomSettingV3::load(matjson::Value const& json) {
|
||||
if (m_impl->legacyValue) {
|
||||
return m_impl->legacyValue->load(json);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool LegacyCustomSettingV3::save(matjson::Value& json) const {
|
||||
if (m_impl->legacyValue) {
|
||||
return m_impl->legacyValue->save(json);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
SettingNodeV3* LegacyCustomSettingV3::createNode(float width) {
|
||||
if (m_impl->legacyValue) {
|
||||
return LegacyCustomSettingToV3Node::create(
|
||||
std::static_pointer_cast<LegacyCustomSettingV3>(shared_from_this()), width
|
||||
);
|
||||
}
|
||||
return UnresolvedCustomSettingNodeV3::create(this->getKey(), this->getMod(), width);
|
||||
}
|
||||
|
||||
bool LegacyCustomSettingV3::isDefaultValue() const {
|
||||
return true;
|
||||
}
|
||||
void LegacyCustomSettingV3::reset() {}
|
||||
|
||||
std::optional<Setting> LegacyCustomSettingV3::convertToLegacy() const {
|
||||
return Setting(this->getKey(), this->getModID(), SettingKind(CustomSetting {
|
||||
.json = std::make_shared<ModJson>(m_impl->json)
|
||||
}));
|
||||
}
|
||||
std::optional<std::shared_ptr<SettingValue>> LegacyCustomSettingV3::convertToLegacyValue() const {
|
||||
return m_impl->legacyValue ? std::optional(m_impl->legacyValue) : std::nullopt;
|
||||
}
|
||||
|
||||
class BoolSettingV3::Impl final {
|
||||
public:
|
||||
};
|
||||
|
@ -699,17 +636,6 @@ SettingNodeV3* BoolSettingV3::createNode(float width) {
|
|||
);
|
||||
}
|
||||
|
||||
std::optional<Setting> BoolSettingV3::convertToLegacy() const {
|
||||
return Setting(this->getKey(), this->getModID(), SettingKind(BoolSetting {
|
||||
.name = this->getName(),
|
||||
.description = this->getDescription(),
|
||||
.defaultValue = this->getDefaultValue(),
|
||||
}));
|
||||
}
|
||||
std::optional<std::shared_ptr<SettingValue>> BoolSettingV3::convertToLegacyValue() const {
|
||||
return this->convertToLegacy()->createDefaultValue();
|
||||
}
|
||||
|
||||
class IntSettingV3::Impl final {
|
||||
public:
|
||||
std::optional<int64_t> minValue;
|
||||
|
@ -819,28 +745,6 @@ SettingNodeV3* IntSettingV3::createNode(float width) {
|
|||
);
|
||||
}
|
||||
|
||||
std::optional<Setting> IntSettingV3::convertToLegacy() const {
|
||||
return Setting(this->getKey(), this->getModID(), SettingKind(IntSetting {
|
||||
.name = this->getName(),
|
||||
.description = this->getDescription(),
|
||||
.defaultValue = this->getDefaultValue(),
|
||||
.min = this->getMinValue(),
|
||||
.max = this->getMaxValue(),
|
||||
.controls = {
|
||||
.arrows = this->isArrowsEnabled(),
|
||||
.bigArrows = this->isBigArrowsEnabled(),
|
||||
.arrowStep = this->getArrowStepSize(),
|
||||
.bigArrowStep = this->getBigArrowStepSize(),
|
||||
.slider = this->isSliderEnabled(),
|
||||
.sliderStep = this->getSliderSnap(),
|
||||
.input = this->isInputEnabled(),
|
||||
},
|
||||
}));
|
||||
}
|
||||
std::optional<std::shared_ptr<SettingValue>> IntSettingV3::convertToLegacyValue() const {
|
||||
return this->convertToLegacy()->createDefaultValue();
|
||||
}
|
||||
|
||||
class FloatSettingV3::Impl final {
|
||||
public:
|
||||
std::optional<double> minValue;
|
||||
|
@ -948,27 +852,6 @@ SettingNodeV3* FloatSettingV3::createNode(float width) {
|
|||
);
|
||||
}
|
||||
|
||||
std::optional<Setting> FloatSettingV3::convertToLegacy() const {
|
||||
return Setting(this->getKey(), this->getModID(), SettingKind(FloatSetting {
|
||||
.name = this->getName(),
|
||||
.description = this->getDescription(),
|
||||
.defaultValue = this->getDefaultValue(),
|
||||
.min = this->getMinValue(),
|
||||
.max = this->getMaxValue(),
|
||||
.controls = {
|
||||
.arrows = this->isArrowsEnabled(),
|
||||
.bigArrows = this->isBigArrowsEnabled(),
|
||||
.arrowStep = static_cast<size_t>(this->getArrowStepSize()),
|
||||
.bigArrowStep = static_cast<size_t>(this->getBigArrowStepSize()),
|
||||
.slider = this->isSliderEnabled(),
|
||||
.sliderStep = this->getSliderSnap(),
|
||||
.input = this->isInputEnabled(),
|
||||
},
|
||||
}));
|
||||
}
|
||||
std::optional<std::shared_ptr<SettingValue>> FloatSettingV3::convertToLegacyValue() const {
|
||||
return this->convertToLegacy()->createDefaultValue();
|
||||
}
|
||||
|
||||
class StringSettingV3::Impl final {
|
||||
public:
|
||||
|
@ -1026,20 +909,6 @@ SettingNodeV3* StringSettingV3::createNode(float width) {
|
|||
);
|
||||
}
|
||||
|
||||
std::optional<Setting> StringSettingV3::convertToLegacy() const {
|
||||
auto setting = StringSetting();
|
||||
setting.name = this->getName();
|
||||
setting.description = this->getDescription();
|
||||
setting.defaultValue = this->getDefaultValue();
|
||||
setting.controls->filter = this->getAllowedCharacters();
|
||||
setting.controls->match = this->getRegexValidator();
|
||||
setting.controls->options = this->getEnumOptions();
|
||||
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
|
||||
}
|
||||
std::optional<std::shared_ptr<SettingValue>> StringSettingV3::convertToLegacyValue() const {
|
||||
return this->convertToLegacy()->createDefaultValue();
|
||||
}
|
||||
|
||||
class FileSettingV3::Impl final {
|
||||
public:
|
||||
bool folder = false;
|
||||
|
@ -1153,18 +1022,6 @@ SettingNodeV3* FileSettingV3::createNode(float width) {
|
|||
);
|
||||
}
|
||||
|
||||
std::optional<Setting> FileSettingV3::convertToLegacy() const {
|
||||
auto setting = FileSetting();
|
||||
setting.name = this->getName();
|
||||
setting.description = this->getDescription();
|
||||
setting.defaultValue = this->getDefaultValue();
|
||||
setting.controls.filters = this->getFilters().value_or(std::vector<utils::file::FilePickOptions::Filter>());
|
||||
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
|
||||
}
|
||||
std::optional<std::shared_ptr<SettingValue>> FileSettingV3::convertToLegacyValue() const {
|
||||
return this->convertToLegacy()->createDefaultValue();
|
||||
}
|
||||
|
||||
class Color3BSettingV3::Impl final {
|
||||
public:
|
||||
};
|
||||
|
@ -1189,17 +1046,6 @@ SettingNodeV3* Color3BSettingV3::createNode(float width) {
|
|||
);
|
||||
}
|
||||
|
||||
std::optional<Setting> Color3BSettingV3::convertToLegacy() const {
|
||||
auto setting = ColorSetting();
|
||||
setting.name = this->getName();
|
||||
setting.description = this->getDescription();
|
||||
setting.defaultValue = this->getDefaultValue();
|
||||
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
|
||||
}
|
||||
std::optional<std::shared_ptr<SettingValue>> Color3BSettingV3::convertToLegacyValue() const {
|
||||
return this->convertToLegacy()->createDefaultValue();
|
||||
}
|
||||
|
||||
class Color4BSettingV3::Impl final {
|
||||
public:
|
||||
};
|
||||
|
@ -1223,14 +1069,3 @@ SettingNodeV3* Color4BSettingV3::createNode(float width) {
|
|||
std::static_pointer_cast<Color4BSettingV3>(shared_from_this()), width
|
||||
);
|
||||
}
|
||||
|
||||
std::optional<Setting> Color4BSettingV3::convertToLegacy() const {
|
||||
auto setting = ColorAlphaSetting();
|
||||
setting.name = this->getName();
|
||||
setting.description = this->getDescription();
|
||||
setting.defaultValue = this->getDefaultValue();
|
||||
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
|
||||
}
|
||||
std::optional<std::shared_ptr<SettingValue>> Color4BSettingV3::convertToLegacyValue() const {
|
||||
return this->convertToLegacy()->createDefaultValue();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
|
||||
#include <Geode/loader/Tulip.hpp>
|
||||
|
||||
tulip::hook::Result<void*> geode::hook::createWrapper(
|
||||
using namespace geode::prelude;
|
||||
|
||||
Result<void*> geode::hook::createWrapper(
|
||||
void* address,
|
||||
tulip::hook::WrapperMetadata const& metadata
|
||||
) noexcept {
|
||||
|
|
|
@ -16,7 +16,7 @@ updater::ResourceDownloadEvent::ResourceDownloadEvent(
|
|||
) : status(std::move(status)) {}
|
||||
|
||||
ListenerResult updater::ResourceDownloadFilter::handle(
|
||||
const utils::MiniFunction<Callback>& fn,
|
||||
const std::function<Callback>& fn,
|
||||
ResourceDownloadEvent* event
|
||||
) {
|
||||
fn(event);
|
||||
|
@ -30,7 +30,7 @@ updater::LoaderUpdateEvent::LoaderUpdateEvent(
|
|||
) : status(std::move(status)) {}
|
||||
|
||||
ListenerResult updater::LoaderUpdateFilter::handle(
|
||||
const utils::MiniFunction<Callback>& fn,
|
||||
const std::function<Callback>& fn,
|
||||
LoaderUpdateEvent* event
|
||||
) {
|
||||
fn(event);
|
||||
|
@ -45,8 +45,8 @@ 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, bool force
|
||||
const std::function<void(matjson::Value const&)>& then,
|
||||
std::function<void(std::string const&)> expect, bool force
|
||||
) {
|
||||
if (s_latestGithubRelease) {
|
||||
return then(s_latestGithubRelease.value());
|
||||
|
@ -83,7 +83,7 @@ void updater::fetchLatestGithubRelease(
|
|||
}
|
||||
else {
|
||||
Mod::get()->setSavedValue("last-modified-auto-update-check", response->header("Last-Modified").value_or(""));
|
||||
s_latestGithubRelease = *json;
|
||||
s_latestGithubRelease = json.unwrap();
|
||||
then(*s_latestGithubRelease);
|
||||
}
|
||||
}
|
||||
|
@ -102,13 +102,10 @@ void updater::downloadLatestLoaderResources() {
|
|||
log::debug("Downloading latest resources", Loader::get()->getVersion().toVString());
|
||||
fetchLatestGithubRelease(
|
||||
[](matjson::Value const& raw) {
|
||||
auto json = raw;
|
||||
JsonChecker checker(json);
|
||||
auto root = checker.root("[]").obj();
|
||||
auto root = checkJson(raw, "[]");
|
||||
|
||||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
for (auto& obj : root.needs("assets").items()) {
|
||||
if (obj.needs("name").get<std::string>() == "resources.zip") {
|
||||
updater::tryDownloadLoaderResources(
|
||||
obj.needs("browser_download_url").get<std::string>(),
|
||||
|
@ -144,7 +141,7 @@ void updater::tryDownloadLoaderResources(std::string const& url, bool tryLatestO
|
|||
// unzip resources zip
|
||||
auto unzip = file::Unzip::create(response->data());
|
||||
if (unzip) {
|
||||
auto ok = unzip->extractAllTo(resourcesDir);
|
||||
auto ok = unzip.unwrap().extractAllTo(resourcesDir);
|
||||
if (ok) {
|
||||
updater::updateSpecialFiles();
|
||||
ResourceDownloadEvent(UpdateFinished()).post();
|
||||
|
@ -206,13 +203,10 @@ void updater::downloadLoaderResources(bool useLatestRelease) {
|
|||
RUNNING_REQUESTS.erase("@downloadLoaderResources");
|
||||
if (response->ok()) {
|
||||
if (auto ok = response->json()) {
|
||||
auto json = ok.unwrap();
|
||||
JsonChecker checker(json);
|
||||
auto root = checker.root("[]").obj();
|
||||
auto root = checkJson(ok.unwrap(), "[]");
|
||||
|
||||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
for (auto& obj : root.needs("assets").items()) {
|
||||
if (obj.needs("name").get<std::string>() == "resources.zip") {
|
||||
updater::tryDownloadLoaderResources(
|
||||
obj.needs("browser_download_url").get<std::string>(),
|
||||
|
@ -314,7 +308,7 @@ void updater::downloadLoaderUpdate(std::string const& url) {
|
|||
// unzip resources zip
|
||||
auto unzip = file::Unzip::create(response->data());
|
||||
if (unzip) {
|
||||
auto ok = unzip->extractAllTo(targetDir);
|
||||
auto ok = unzip.unwrap().extractAllTo(targetDir);
|
||||
if (ok) {
|
||||
s_isNewUpdateDownloaded = true;
|
||||
LoaderUpdateEvent(UpdateFinished()).post();
|
||||
|
@ -362,9 +356,7 @@ 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();
|
||||
auto root = checkJson(raw, "[]");
|
||||
|
||||
VersionInfo ver { 0, 0, 0 };
|
||||
root.needs("tag_name").into(ver);
|
||||
|
@ -388,8 +380,7 @@ void updater::checkForLoaderUpdates() {
|
|||
}
|
||||
|
||||
// find release asset
|
||||
for (auto asset : root.needs("assets").iterate()) {
|
||||
auto obj = asset.obj();
|
||||
for (auto& obj : root.needs("assets").items()) {
|
||||
if (string::endsWith(
|
||||
obj.needs("name").get<std::string>(),
|
||||
fmt::format("{}.zip", PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <string>
|
||||
#include <matjson.hpp>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include <Geode/loader/Event.hpp>
|
||||
|
||||
namespace geode::updater {
|
||||
|
@ -20,7 +19,7 @@ namespace geode::updater {
|
|||
public:
|
||||
using Callback = void(ResourceDownloadEvent*);
|
||||
|
||||
static ListenerResult handle(const utils::MiniFunction<Callback>& fn, ResourceDownloadEvent* event);
|
||||
static ListenerResult handle(const std::function<Callback>& fn, ResourceDownloadEvent* event);
|
||||
ResourceDownloadFilter();
|
||||
};
|
||||
|
||||
|
@ -33,7 +32,7 @@ namespace geode::updater {
|
|||
public:
|
||||
using Callback = void(LoaderUpdateEvent*);
|
||||
|
||||
static ListenerResult handle(const utils::MiniFunction<Callback>& fn, LoaderUpdateEvent* event);
|
||||
static ListenerResult handle(const std::function<Callback>& fn, LoaderUpdateEvent* event);
|
||||
LoaderUpdateFilter();
|
||||
};
|
||||
|
||||
|
@ -43,8 +42,8 @@ namespace geode::updater {
|
|||
void downloadLatestLoaderResources();
|
||||
void downloadLoaderUpdate(std::string const& url);
|
||||
void fetchLatestGithubRelease(
|
||||
const utils::MiniFunction<void(matjson::Value const&)>& then,
|
||||
utils::MiniFunction<void(std::string const&)> expect,
|
||||
const std::function<void(matjson::Value const&)>& then,
|
||||
std::function<void(std::string const&)> expect,
|
||||
bool force = false
|
||||
);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ std::string Loader::Impl::getGameVersion() {
|
|||
case 37: m_gdVersion = "2.200"; break;
|
||||
case 38: m_gdVersion = "2.205"; break;
|
||||
case 39: m_gdVersion = "2.206"; break;
|
||||
case 40: m_gdVersion = "2.2074"; break;
|
||||
default: m_gdVersion = std::to_string(version_code);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -261,12 +261,12 @@ int writeAndGetPid() {
|
|||
|
||||
auto res = file::readString(pidFile);
|
||||
if (!res) {
|
||||
log::warn("Failed to read last-pid file: {}", res.error());
|
||||
log::warn("Failed to read last-pid file: {}", res.unwrapErr());
|
||||
}
|
||||
else {
|
||||
auto res = numFromString<int>(res.unwrap());
|
||||
if (!res) {
|
||||
log::warn("Failed to parse last-pid file: {}", res.error());
|
||||
log::warn("Failed to parse last-pid file: {}", res.unwrapErr());
|
||||
}
|
||||
else lastPid = res.unwrap();
|
||||
}
|
||||
|
@ -281,7 +281,7 @@ int writeAndGetPid() {
|
|||
|
||||
auto res = file::writeString(pidFile, std::to_string(getpid()));
|
||||
if (!res) {
|
||||
log::warn("Failed to write last-pid file: {}", res.error());
|
||||
log::warn("Failed to write last-pid file: {}", res.unwrapErr());
|
||||
}
|
||||
|
||||
return lastPid;
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace geode::stl {
|
|||
}
|
||||
// TODO: add a copyFrom(string const&) to take advantage
|
||||
// of gnustl refcounted strings
|
||||
void StringImpl::setStorage(const std::string_view str) {
|
||||
void StringImpl::setStorage(std::string_view str) {
|
||||
this->free();
|
||||
|
||||
if (str.size() == 0) {
|
||||
|
|
|
@ -6,14 +6,13 @@ using namespace geode::prelude;
|
|||
#include <Geode/utils/web.hpp>
|
||||
#include <filesystem>
|
||||
#include <Geode/utils/general.hpp>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include <Geode/utils/permission.hpp>
|
||||
#include <Geode/utils/Task.hpp>
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/binding/AppDelegate.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/binding/MenuLayer.hpp>
|
||||
#include <Geode/utils/Result.hpp>
|
||||
#include <Geode/Result.hpp>
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <optional>
|
||||
#include <mutex>
|
||||
|
@ -139,9 +138,9 @@ bool utils::file::openFolder(std::filesystem::path const& path) {
|
|||
}
|
||||
|
||||
std::mutex s_callbackMutex;
|
||||
static utils::MiniFunction<void(Result<std::filesystem::path>)> s_fileCallback {};
|
||||
static utils::MiniFunction<void(Result<std::vector<std::filesystem::path>>)> s_filesCallback {};
|
||||
static utils::MiniFunction<bool()> s_taskCancelled {};
|
||||
static std::function<void(Result<std::filesystem::path>)> s_fileCallback {};
|
||||
static std::function<void(Result<std::vector<std::filesystem::path>>)> s_filesCallback {};
|
||||
static std::function<bool()> s_taskCancelled {};
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_selectFileCallback(
|
||||
|
@ -356,7 +355,7 @@ bool geode::utils::permission::getPermissionStatus(Permission permission) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static MiniFunction<void(bool)> s_permissionCallback;
|
||||
static std::function<void(bool)> s_permissionCallback;
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_permissionCallback(
|
||||
|
@ -371,7 +370,7 @@ JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_permissionCallba
|
|||
}
|
||||
}
|
||||
|
||||
void geode::utils::permission::requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback) {
|
||||
void geode::utils::permission::requestPermission(Permission permission, std::function<void(bool)> callback) {
|
||||
s_permissionCallback = callback;
|
||||
JniMethodInfo info;
|
||||
if (JniHelper::getStaticMethodInfo(info, "com/geode/launcher/utils/GeodeUtils", "requestPermission", "(Ljava/lang/String;)V")) {
|
||||
|
|
|
@ -39,6 +39,6 @@ bool geode::utils::permission::getPermissionStatus(Permission permission) {
|
|||
return true; // unimplemented
|
||||
}
|
||||
|
||||
void geode::utils::permission::requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback) {
|
||||
void geode::utils::permission::requestPermission(Permission permission, std::function<void(bool)> callback) {
|
||||
callback(true); // unimplemented
|
||||
}
|
||||
|
|
|
@ -99,6 +99,7 @@ CFDataRef msgPortCallback(CFMessagePortRef port, SInt32 messageID, CFDataRef dat
|
|||
std::string cdata(reinterpret_cast<char const*>(CFDataGetBytePtr(data)), CFDataGetLength(data));
|
||||
|
||||
std::string reply = geode::ipc::processRaw(port, cdata).dump();
|
||||
|
||||
return CFDataCreate(NULL, (UInt8 const*)reply.data(), reply.size());
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue