Merge pull request #833 from geode-sdk/new-index-but-better
merge new index closes #712
2
.github/workflows/build.yml
vendored
|
@ -287,7 +287,7 @@ jobs:
|
|||
- name: Zip Windows Artifacts
|
||||
uses: vimtor/action-zip@v1.1
|
||||
with:
|
||||
files: geode-win/XInput9_1_0.dll geode-win/Geode.dll geode-win/GeodeUpdater.exe geode-win/Geode.lib geode-win/Geode.pdb
|
||||
files: geode-win/XInput1_4.dll geode-win/Geode.dll geode-win/GeodeUpdater.exe geode-win/Geode.lib geode-win/Geode.pdb
|
||||
dest: geode-${{ steps.ref.outputs.hash }}-win.zip
|
||||
|
||||
- name: Zip Android32 Artifacts
|
||||
|
|
|
@ -172,13 +172,8 @@ include(cmake/Platform.cmake)
|
|||
include(cmake/GeodeFile.cmake)
|
||||
|
||||
if (NOT DEFINED GEODE_GD_VERSION)
|
||||
if (${GEODE_TARGET_PLATFORM} STREQUAL "Win32")
|
||||
set(GEODE_GD_VERSION 2.204)
|
||||
set(GEODE_COMP_GD_VERSION 22040)
|
||||
else()
|
||||
set(GEODE_GD_VERSION 2.206)
|
||||
set(GEODE_COMP_GD_VERSION 22060)
|
||||
endif()
|
||||
set(GEODE_GD_VERSION 2.206)
|
||||
set(GEODE_COMP_GD_VERSION 22060)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE GEODE_GD_VERSION=${GEODE_GD_VERSION} GEODE_COMP_GD_VERSION=${GEODE_COMP_GD_VERSION})
|
||||
|
@ -189,7 +184,7 @@ if (WIN32)
|
|||
add_definitions(-D_HAS_ITERATOR_DEBUGGING=0)
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE _HAS_ITERATOR_DEBUGGING=0)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE delayimp)
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE delayimp ws2_32)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND
|
||||
CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU")
|
||||
# target x86 on windows with clang
|
||||
|
@ -211,7 +206,6 @@ endif()
|
|||
set(MAT_JSON_AS_INTERFACE ON)
|
||||
CPMAddPackage("gh:geode-sdk/json#3fe4752")
|
||||
CPMAddPackage("gh:fmtlib/fmt#10.1.1")
|
||||
CPMAddPackage("gh:gulrak/filesystem#3e5b930")
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE MAT_JSON_DYNAMIC=1)
|
||||
|
||||
|
@ -240,7 +234,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#fd1e02e")
|
||||
CPMAddPackage("gh:geode-sdk/TulipHook#7e52ec4")
|
||||
endif()
|
||||
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)
|
||||
|
||||
|
@ -255,10 +249,6 @@ endif()
|
|||
|
||||
target_sources(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/entry.cpp)
|
||||
|
||||
add_library(GeodeFilesystemImpl ${CMAKE_CURRENT_SOURCE_DIR}/FilesystemImpl.cpp)
|
||||
target_compile_features(GeodeFilesystemImpl PUBLIC cxx_std_20)
|
||||
target_link_libraries(GeodeFilesystemImpl PUBLIC ghc_filesystem)
|
||||
|
||||
add_subdirectory(${GEODE_BINDINGS_REPO_PATH} ${CMAKE_BINARY_DIR}/bindings)
|
||||
|
||||
if (NOT GEODE_DISABLE_PRECOMPILED_HEADERS)
|
||||
|
@ -274,7 +264,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 ghc_filesystem fmt TulipHookInclude mat-json GeodeFilesystemImpl)
|
||||
target_link_libraries(GeodeBindings PUBLIC fmt TulipHookInclude mat-json)
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE GeodeBindings)
|
||||
if (${GEODE_REVERT_TODO_RETURN})
|
||||
target_compile_definitions(GeodeBindings PUBLIC GEODE_REVERT_TODO_RETURN)
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
// filesystem implementation
|
||||
#undef GHC_FILESYSTEM_H
|
||||
#include <ghc/fs_impl.hpp>
|
||||
|
|
@ -25,17 +25,18 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "MacOS")
|
|||
# only exists as a global property
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
target_include_directories(${PROJECT_NAME} INTERFACE
|
||||
${CURL_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE
|
||||
"-framework Cocoa"
|
||||
"-framework OpenGL"
|
||||
${CURL_LIBRARIES}
|
||||
${GEODE_LOADER_PATH}/include/link/libfmod.dylib
|
||||
"-framework SystemConfiguration"
|
||||
${GEODE_LOADER_PATH}/include/link/macos/libfmod.dylib
|
||||
${GEODE_LOADER_PATH}/include/link/macos/libssl.a
|
||||
${GEODE_LOADER_PATH}/include/link/macos/libcrypto.a
|
||||
${GEODE_LOADER_PATH}/include/link/macos/libnghttp2.a
|
||||
${GEODE_LOADER_PATH}/include/link/macos/libngtcp2.a
|
||||
${GEODE_LOADER_PATH}/include/link/macos/libnghttp3.a
|
||||
${GEODE_LOADER_PATH}/include/link/macos/libngtcp2_crypto_boringssl.a
|
||||
${GEODE_LOADER_PATH}/include/link/macos/libcurl.a
|
||||
)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE
|
||||
|
@ -55,12 +56,18 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "Win32")
|
|||
target_compile_definitions(${PROJECT_NAME} INTERFACE NOMINMAX)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE
|
||||
${GEODE_LOADER_PATH}/include/link/libcocos2d.lib
|
||||
${GEODE_LOADER_PATH}/include/link/libExtensions.lib
|
||||
${GEODE_LOADER_PATH}/include/link/libcurl.lib
|
||||
${GEODE_LOADER_PATH}/include/link/glew32.lib
|
||||
${GEODE_LOADER_PATH}/include/link/gdstring.lib
|
||||
${GEODE_LOADER_PATH}/include/link/fmod.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/libcocos2d.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/libExtensions.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/ssl.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/crypto.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/nghttp2.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/nghttp3.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2_crypto_boringssl.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/libcurl.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/glew32.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/gdstring.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/fmod.lib
|
||||
opengl32
|
||||
)
|
||||
|
||||
|
@ -78,6 +85,9 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "Android32")
|
|||
${GEODE_LOADER_PATH}/include/link/android32/libssl.a
|
||||
${GEODE_LOADER_PATH}/include/link/android32/libcrypto.a
|
||||
${GEODE_LOADER_PATH}/include/link/android32/libnghttp2.a
|
||||
${GEODE_LOADER_PATH}/include/link/android32/libngtcp2.a
|
||||
${GEODE_LOADER_PATH}/include/link/android32/libnghttp3.a
|
||||
${GEODE_LOADER_PATH}/include/link/android32/libngtcp2_crypto_boringssl.a
|
||||
${GEODE_LOADER_PATH}/include/link/android32/libcurl.a
|
||||
${GEODE_LOADER_PATH}/include/link/android32/libcocos2dcpp.so
|
||||
${GEODE_LOADER_PATH}/include/link/android32/libfmod.so
|
||||
|
@ -98,6 +108,9 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "Android64")
|
|||
${GEODE_LOADER_PATH}/include/link/android64/libssl.a
|
||||
${GEODE_LOADER_PATH}/include/link/android64/libcrypto.a
|
||||
${GEODE_LOADER_PATH}/include/link/android64/libnghttp2.a
|
||||
${GEODE_LOADER_PATH}/include/link/android64/libngtcp2.a
|
||||
${GEODE_LOADER_PATH}/include/link/android64/libnghttp3.a
|
||||
${GEODE_LOADER_PATH}/include/link/android64/libngtcp2_crypto_boringssl.a
|
||||
${GEODE_LOADER_PATH}/include/link/android64/libcurl.a
|
||||
${GEODE_LOADER_PATH}/include/link/android64/libcocos2dcpp.so
|
||||
${GEODE_LOADER_PATH}/include/link/android64/libfmod.so
|
||||
|
|
|
@ -409,7 +409,7 @@ Function .onVerifyInstDir
|
|||
IfFileExists $INSTDIR\hackpro.dll other_hackpro
|
||||
IfFileExists $INSTDIR\ToastedMarshmellow.dll other_gdhm
|
||||
IfFileExists $INSTDIR\quickldr.dll other_quickldr
|
||||
IfFileExists $INSTDIR\XInput9_1_0.dll other_xinput
|
||||
IfFileExists $INSTDIR\XInput1_4.dll other_xinput
|
||||
IfFileExists $INSTDIR\mimalloc.dll other_mimalloc
|
||||
IfFileExists $INSTDIR\GDH.dll other_GDH
|
||||
|
||||
|
@ -432,7 +432,7 @@ Function .onVerifyInstDir
|
|||
StrCpy $0 "quickldr.dll"
|
||||
Goto other
|
||||
other_xinput:
|
||||
StrCpy $0 "XInput9_1_0.dll"
|
||||
StrCpy $0 "XInput1_4.dll"
|
||||
Goto other
|
||||
other_mimalloc:
|
||||
StrCpy $0 "mimalloc.dll"
|
||||
|
@ -459,7 +459,7 @@ SectionGroup "Geode"
|
|||
File ${BINDIR}\Geode.dll
|
||||
File ${BINDIR}\Geode.pdb
|
||||
File ${BINDIR}\GeodeUpdater.exe
|
||||
File ${BINDIR}\XInput9_1_0.dll
|
||||
File ${BINDIR}\XInput1_4.dll
|
||||
|
||||
WriteUninstaller "GeodeUninstaller.exe"
|
||||
SectionEnd
|
||||
|
@ -512,7 +512,7 @@ Function un.onInit
|
|||
IfFileExists $INSTDIR\libcocos2d.dll 0 invalid
|
||||
|
||||
; check if xinput and geode exist
|
||||
IfFileExists $INSTDIR\XInput9_1_0.dll 0 invalid
|
||||
IfFileExists $INSTDIR\XInput1_4.dll 0 invalid
|
||||
IfFileExists $INSTDIR\Geode.dll 0 invalid
|
||||
Return
|
||||
|
||||
|
@ -527,7 +527,7 @@ Section "Uninstall"
|
|||
Delete $INSTDIR\Geode.pdb
|
||||
Delete $INSTDIR\Geode.lib
|
||||
Delete $INSTDIR\GeodeUpdater.exe
|
||||
Delete $INSTDIR\XInput9_1_0.dll
|
||||
Delete $INSTDIR\XInput1_4.dll
|
||||
|
||||
# default value of DATA is an empty string
|
||||
# if DATA is empty, keep user data
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
cmake_minimum_required(VERSION 3.21 FATAL_ERROR)
|
||||
cmake_policy(SET CMP0097 NEW)
|
||||
|
||||
project(geode-loader VERSION ${GEODE_VERSION} LANGUAGES C CXX)
|
||||
if (GEODE_VERSION_TAG_TYPE)
|
||||
|
@ -67,15 +68,20 @@ file(GLOB SOURCES CONFIGURE_DEPENDS
|
|||
src/hooks/*.cpp
|
||||
src/ids/*.cpp
|
||||
src/internal/*.cpp
|
||||
src/server/*.cpp
|
||||
src/loader/*.cpp
|
||||
src/load.cpp
|
||||
src/utils/*.cpp
|
||||
src/ui/*.cpp
|
||||
src/ui/nodes/*.cpp
|
||||
src/ui/internal/*.cpp
|
||||
src/ui/internal/info/*.cpp
|
||||
src/ui/internal/list/*.cpp
|
||||
src/ui/internal/settings/*.cpp
|
||||
src/ui/other/*.cpp
|
||||
src/ui/mods/*.cpp
|
||||
src/ui/mods/list/*.cpp
|
||||
src/ui/mods/popups/*.cpp
|
||||
src/ui/mods/sources/*.cpp
|
||||
src/ui/mods/settings/*.cpp
|
||||
src/ui/mods/test/*.cpp
|
||||
src/ui/*.cpp
|
||||
src/c++stl/*.cpp
|
||||
hash/hash.cpp
|
||||
)
|
||||
|
@ -166,6 +172,18 @@ if (GEODE_NO_UNDEFINED_VIRTUALS)
|
|||
target_compile_definitions(${PROJECT_NAME} PUBLIC GEODE_NO_UNDEFINED_VIRTUALS)
|
||||
endif()
|
||||
|
||||
# CA Bundle
|
||||
CPMAddPackage(
|
||||
GITHUB_REPOSITORY geode-sdk/net_libs
|
||||
GIT_TAG 1f5ffb3
|
||||
# CPM is bugged and will not allow an empty string.
|
||||
# https://github.com/cpm-cmake/CPM.cmake/issues/467
|
||||
# https://github.com/cpm-cmake/CPM.cmake/issues/461
|
||||
GIT_SUBMODULES "nghttp3"
|
||||
GIT_SUBMODULES_RECURSE OFF
|
||||
)
|
||||
target_link_libraries(${PROJECT_NAME} ca-bundle)
|
||||
|
||||
# Package resources for UI
|
||||
package_geode_resources_now(
|
||||
${PROJECT_NAME}
|
||||
|
@ -176,7 +194,7 @@ package_geode_resources_now(
|
|||
|
||||
if (APPLE)
|
||||
# I don't care a single bit where to put this
|
||||
file(COPY ${GEODE_LOADER_PATH}/include/link/libfmod.dylib DESTINATION ${GEODE_BIN_PATH}/nightly)
|
||||
file(COPY ${GEODE_LOADER_PATH}/include/link/macos/libfmod.dylib DESTINATION ${GEODE_BIN_PATH}/nightly)
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
|
@ -256,7 +274,12 @@ if (NOT GEODE_BUILDING_DOCS)
|
|||
target_link_libraries(${PROJECT_NAME} md4c re2 minizip)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT_NAME} z TulipHook geode-sdk mat-json-impl)
|
||||
target_link_libraries(${PROJECT_NAME} TulipHook geode-sdk mat-json-impl)
|
||||
|
||||
# Required by curl for macos
|
||||
if (APPLE)
|
||||
target_link_libraries(${PROJECT_NAME} z)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
# Disable outputting .exp file
|
||||
|
|
|
@ -23,7 +23,7 @@ void readBuffered(std::ifstream& stream, Func func) {
|
|||
}
|
||||
}
|
||||
|
||||
std::string calculateSHA3_256(ghc::filesystem::path const& path) {
|
||||
std::string calculateSHA3_256(std::filesystem::path const& path) {
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
SHA3 sha;
|
||||
readBuffered(file, [&](const void* data, size_t amt) {
|
||||
|
@ -32,14 +32,14 @@ std::string calculateSHA3_256(ghc::filesystem::path const& path) {
|
|||
return sha.getHash();
|
||||
}
|
||||
|
||||
std::string calculateSHA256(ghc::filesystem::path const& path) {
|
||||
std::string calculateSHA256(std::filesystem::path const& path) {
|
||||
std::vector<uint8_t> hash(picosha2::k_digest_size);
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
picosha2::hash256(file, hash.begin(), hash.end());
|
||||
return picosha2::bytes_to_hex_string(hash.begin(), hash.end());
|
||||
}
|
||||
|
||||
std::string calculateSHA256Text(ghc::filesystem::path const& path) {
|
||||
std::string calculateSHA256Text(std::filesystem::path const& path) {
|
||||
// remove all newlines
|
||||
std::vector<uint8_t> hash(picosha2::k_digest_size);
|
||||
std::ifstream file(path);
|
||||
|
@ -52,6 +52,6 @@ std::string calculateSHA256Text(ghc::filesystem::path const& path) {
|
|||
return picosha2::bytes_to_hex_string(hash.begin(), hash.end());
|
||||
}
|
||||
|
||||
std::string calculateHash(ghc::filesystem::path const& path) {
|
||||
std::string calculateHash(std::filesystem::path const& path) {
|
||||
return calculateSHA3_256(path);
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <ghc/fs_fwd.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
std::string calculateSHA3_256(ghc::filesystem::path const& path);
|
||||
std::string calculateSHA3_256(std::filesystem::path const& path);
|
||||
|
||||
std::string calculateSHA256(ghc::filesystem::path const& path);
|
||||
std::string calculateSHA256(std::filesystem::path const& path);
|
||||
|
||||
std::string calculateSHA256Text(ghc::filesystem::path const& path);
|
||||
std::string calculateSHA256Text(std::filesystem::path const& path);
|
||||
|
||||
std::string calculateHash(ghc::filesystem::path const& path);
|
||||
std::string calculateHash(std::filesystem::path const& path);
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
#if defined(GEODE_IS_ANDROID)
|
||||
#include "gnustl.hpp"
|
||||
#elif defined(GEODE_IS_WINDOWS)
|
||||
#include "msvcstl.hpp"
|
||||
//#elif defined(GEODE_IS_WINDOWS)
|
||||
//#include "msvcstl.hpp"
|
||||
#else
|
||||
#include "aliastl.hpp"
|
||||
#endif
|
||||
|
|
|
@ -638,6 +638,16 @@ namespace gd {
|
|||
bool operator[](size_t index) const {
|
||||
return const_cast<vector&>(*this)[index];
|
||||
}
|
||||
|
||||
_bit_reference at(size_t index) {
|
||||
// TODO: bounds checking
|
||||
return this->operator[](index);
|
||||
}
|
||||
|
||||
bool at(size_t index) const {
|
||||
// TODO: bounds checking
|
||||
return this->operator[](index);
|
||||
}
|
||||
};
|
||||
|
||||
// 2.2 TODO: Implement set, unordered_map and unordered_set
|
||||
|
|
55
loader/include/Geode/cocos/base_nodes/CCNode.h
vendored
|
@ -1021,6 +1021,48 @@ public:
|
|||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void addChildAtPosition(CCNode* child, 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
|
||||
* relatively adjust the node's position
|
||||
* @param child The child to add
|
||||
* @param anchor Where the place the child relative to this node
|
||||
* @param offset Where to place the child relative to the anchor
|
||||
* @param nodeAnchor The child's anchor position
|
||||
* @param useAnchorLayout If true, sets this node's layout to `AnchorLayout`
|
||||
* if no other layout is already specified
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void addChildAtPosition(
|
||||
CCNode* child,
|
||||
Anchor anchor,
|
||||
CCPoint const& offset,
|
||||
CCPoint const& nodeAnchor,
|
||||
bool useAnchorLayout = true
|
||||
);
|
||||
/**
|
||||
* Updates the anchored position of a child. Requires the child to already
|
||||
* have a parent; if the child already has AnchorLayoutOptions set, those
|
||||
* are updated, otherwise nothing is done
|
||||
* @param anchor Where the place the child relative to its parent
|
||||
* @param offset Where to place the child relative to the anchor
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void updateAnchoredPosition(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
|
||||
* are updated, otherwise nothing is done
|
||||
* @param anchor Where the place the child relative to its parent
|
||||
* @param offset Where to place the child relative to the anchor
|
||||
* @param nodeAnchor The child's anchor position
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void updateAnchoredPosition(
|
||||
Anchor anchor,
|
||||
CCPoint const& offset,
|
||||
CCPoint const& nodeAnchor
|
||||
);
|
||||
|
||||
/**
|
||||
* Swap two children
|
||||
|
@ -1030,10 +1072,23 @@ public:
|
|||
*/
|
||||
GEODE_DLL void swapChildIndices(CCNode* first, CCNode* second);
|
||||
|
||||
/**
|
||||
* @note Make sure to set the scale first!
|
||||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void setScaledContentSize(CCSize const& size);
|
||||
// @note Geode addition
|
||||
GEODE_DLL void setContentWidth(float width);
|
||||
// @note Geode addition
|
||||
GEODE_DLL void setContentHeight(float width);
|
||||
// @note Geode addition
|
||||
GEODE_DLL float getContentWidth() const;
|
||||
// @note Geode addition
|
||||
GEODE_DLL float getContentHeight() const;
|
||||
// @note Geode addition
|
||||
GEODE_DLL float getScaledContentWidth() const;
|
||||
// @note Geode addition
|
||||
GEODE_DLL float getScaledContentHeight() const;
|
||||
|
||||
template <class Filter, class... Args>
|
||||
geode::EventListenerProtocol* addEventListener(
|
||||
|
|
|
@ -380,6 +380,8 @@ public:
|
|||
static CCLayerGradient* create(const ccColor4B& start, const ccColor4B& end, const CCPoint& v);
|
||||
|
||||
virtual bool init();
|
||||
|
||||
virtual void updateColor();
|
||||
/** Initializes the CCLayer with a gradient between start and end.
|
||||
* @js init
|
||||
*/
|
||||
|
@ -411,9 +413,6 @@ public:
|
|||
virtual bool isCompressedInterpolation();
|
||||
|
||||
static CCLayerGradient* create();
|
||||
|
||||
protected:
|
||||
virtual void updateColor();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ THE SOFTWARE.
|
|||
#define __SUPPORT_ZIPUTILS_H__
|
||||
|
||||
#include <string>
|
||||
#include <ghc/fs_fwd.hpp>
|
||||
#include <filesystem>
|
||||
#include "../../platform/CCPlatformDefine.h"
|
||||
#include "../../platform/CCPlatformConfig.h"
|
||||
#include "../../include/ccMacros.h"
|
||||
|
@ -217,7 +217,7 @@ namespace cocos2d
|
|||
*/
|
||||
bool isLoaded() const;
|
||||
|
||||
bool unzipAllTo(ghc::filesystem::path const& path);
|
||||
bool unzipAllTo(std::filesystem::path const& path);
|
||||
|
||||
/**
|
||||
* Regenerate accessible file list based on a new filter string.
|
||||
|
|
|
@ -1,59 +1,59 @@
|
|||
#pragma once
|
||||
|
||||
#include <ghc/fs_fwd.hpp>
|
||||
#include <filesystem>
|
||||
#include "../DefaultInclude.hpp"
|
||||
|
||||
namespace geode::dirs {
|
||||
/**
|
||||
* Directory where Geometry Dash is
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getGameDir();
|
||||
GEODE_DLL std::filesystem::path getGameDir();
|
||||
/**
|
||||
* Directory where GD saves its files
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getSaveDir();
|
||||
GEODE_DLL std::filesystem::path getSaveDir();
|
||||
/**
|
||||
* Directory where Geode is
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getGeodeDir();
|
||||
GEODE_DLL std::filesystem::path getGeodeDir();
|
||||
/**
|
||||
* Directory where Geode saves its files
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getGeodeSaveDir();
|
||||
GEODE_DLL std::filesystem::path getGeodeSaveDir();
|
||||
/**
|
||||
* Directory where Geode's resources are stored
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getGeodeResourcesDir();
|
||||
GEODE_DLL std::filesystem::path getGeodeResourcesDir();
|
||||
/**
|
||||
* Directory where Geode's resources are stored
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getGeodeLogDir();
|
||||
GEODE_DLL std::filesystem::path getGeodeLogDir();
|
||||
/**
|
||||
* Directory to store temporary files
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getTempDir();
|
||||
GEODE_DLL std::filesystem::path getTempDir();
|
||||
/**
|
||||
* Directory where mods are stored by default
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getModsDir();
|
||||
GEODE_DLL std::filesystem::path getModsDir();
|
||||
/**
|
||||
* Directory where mods' save data is stored
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getModsSaveDir();
|
||||
GEODE_DLL std::filesystem::path getModsSaveDir();
|
||||
/**
|
||||
* Directory where mods' unzipped packages are stored at runtime
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getModRuntimeDir();
|
||||
GEODE_DLL std::filesystem::path getModRuntimeDir();
|
||||
/**
|
||||
* Directory where mods' config files lie
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getModConfigDir();
|
||||
GEODE_DLL std::filesystem::path getModConfigDir();
|
||||
/**
|
||||
* Directory where Geode stores the cached index
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getIndexDir();
|
||||
GEODE_DLL std::filesystem::path getIndexDir();
|
||||
/**
|
||||
* Directory where crashlogs are stored
|
||||
*/
|
||||
GEODE_DLL ghc::filesystem::path getCrashlogsDir();
|
||||
GEODE_DLL std::filesystem::path getCrashlogsDir();
|
||||
}
|
||||
|
|
|
@ -88,16 +88,20 @@ namespace geode {
|
|||
void setListener(EventListenerProtocol* listener) {
|
||||
m_listener = listener;
|
||||
}
|
||||
|
||||
EventListenerProtocol* getListener() const {
|
||||
return m_listener;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept is_filter = std::is_base_of_v<EventFilter<typename T::Event>, T> &&
|
||||
requires(T a) {
|
||||
a.handle(std::declval<typename T::Callback>(), std::declval<typename T::Event*>());
|
||||
concept is_filter = // # no need to do this IMO - HJfod # std::is_base_of_v<EventFilter<typename T::Event>, T> &&
|
||||
requires(T a, T const& ca) {
|
||||
typename T::Callback;
|
||||
typename T::Event;
|
||||
{ a.handle(std::declval<typename T::Callback>(), std::declval<typename T::Event*>()) } -> std::same_as<ListenerResult>;
|
||||
{ ca.getPool() } -> std::convertible_to<EventListenerPool*>;
|
||||
{ a.setListener(std::declval<EventListenerProtocol*>()) } -> std::same_as<void>;
|
||||
{ ca.getListener() } -> std::convertible_to<EventListenerProtocol*>;
|
||||
};
|
||||
|
||||
template <is_filter T>
|
||||
|
@ -163,7 +167,10 @@ namespace geode {
|
|||
this->enable();
|
||||
}
|
||||
|
||||
void bind(utils::MiniFunction<Callback> fn) {
|
||||
void bind(utils::MiniFunction<Callback> const& fn) {
|
||||
m_callback = fn;
|
||||
}
|
||||
void bind(utils::MiniFunction<Callback>&& fn) {
|
||||
m_callback = fn;
|
||||
}
|
||||
|
||||
|
@ -212,4 +219,130 @@ namespace geode {
|
|||
|
||||
virtual ~Event();
|
||||
};
|
||||
|
||||
// template <is_filter F, std::move_constructible T>
|
||||
// class [[nodiscard]] EventMapper final {
|
||||
// public:
|
||||
// using Value = T;
|
||||
|
||||
// class Handle final {
|
||||
// std::optional<EventListener<F>> m_listener;
|
||||
|
||||
// class PrivateMarker final {};
|
||||
|
||||
// static std::shared_ptr<Handle> create() {
|
||||
// return std::make_shared<Handle>(PrivateMarker());
|
||||
// }
|
||||
|
||||
// friend class EventMapper;
|
||||
|
||||
// public:
|
||||
// Handle(PrivateMarker) {}
|
||||
// };
|
||||
|
||||
// class Event final : public geode::Event {
|
||||
// private:
|
||||
// std::shared_ptr<Handle> m_handle;
|
||||
// T m_value;
|
||||
|
||||
// Event(std::shared_ptr<Handle> handle, T&& value)
|
||||
// : m_handle(handle), m_value(std::move(value)) {}
|
||||
|
||||
// friend class EventMapper;
|
||||
|
||||
// public:
|
||||
// T& getValue() & {
|
||||
// return m_value;
|
||||
// }
|
||||
// T const& getValue() const& {
|
||||
// return m_value;
|
||||
// }
|
||||
// T&& getValue() && {
|
||||
// return std::move(m_value);
|
||||
// }
|
||||
|
||||
// operator T*() const {
|
||||
// return m_value;
|
||||
// }
|
||||
// T* operator*() const {
|
||||
// return m_value;
|
||||
// }
|
||||
// T* operator->() const {
|
||||
// return m_value;
|
||||
// }
|
||||
// };
|
||||
|
||||
// using Mapper = utils::MiniFunction<T(typename F::Event*)>;
|
||||
// using Callback = void(Event*);
|
||||
|
||||
// private:
|
||||
// EventListenerProtocol* m_listener = nullptr;
|
||||
// std::shared_ptr<Handle> m_handle;
|
||||
|
||||
// EventMapper(std::shared_ptr<Handle> handle) : m_handle(handle) {}
|
||||
|
||||
// public:
|
||||
// EventMapper() : m_handle(nullptr) {}
|
||||
|
||||
// static EventMapper immediate(T&& value) {
|
||||
// auto emapper = EventMapper(Handle::create());
|
||||
// Loader::get()->queueInMainThread([handle = emapper.m_handle, value = std::move(value)]() mutable {
|
||||
// EventMapper::Event(handle, std::move(value)).post();
|
||||
// });
|
||||
// return emapper;
|
||||
// }
|
||||
// static EventMapper create(F&& filter, Mapper&& mapper) {
|
||||
// auto emapper = EventMapper(Handle::create());
|
||||
// emapper.m_handle->m_listener.emplace(EventListener(
|
||||
// // The event listener should not own itself (circular ref = memory leak!!)
|
||||
// [handle = std::weak_ptr(emapper.m_handle), mapper = std::move(mapper)](F::Event* event) {
|
||||
// if (auto lock = handle.lock()) {
|
||||
// EventMapper::Event(lock, mapper(event)).post();
|
||||
// }
|
||||
// },
|
||||
// std::move(filter)
|
||||
// ));
|
||||
// return emapper;
|
||||
// }
|
||||
|
||||
// template <class NewMapper>
|
||||
// auto map(NewMapper&& mapper) {
|
||||
// using T2 = decltype(mapper(std::declval<T*>()));
|
||||
// return mapEvent(*this, [mapper = std::move(mapper)](Event* event) -> T2 {
|
||||
// return mapper(&event->getValue());
|
||||
// });
|
||||
// }
|
||||
|
||||
// ListenerResult handle(utils::MiniFunction<Callback> fn, Event* e) {
|
||||
// if (e->m_handle == m_handle) {
|
||||
// fn(e);
|
||||
// }
|
||||
// return ListenerResult::Propagate;
|
||||
// }
|
||||
|
||||
// // todo: i believe alk wanted these to be in their own pool
|
||||
// EventListenerPool* getPool() const {
|
||||
// return DefaultEventListenerPool::get();
|
||||
// }
|
||||
|
||||
// void setListener(EventListenerProtocol* listener) {
|
||||
// m_listener = listener;
|
||||
// }
|
||||
// EventListenerProtocol* getListener() const {
|
||||
// return m_listener;
|
||||
// }
|
||||
// };
|
||||
|
||||
// template <is_filter F, class Mapper>
|
||||
// static auto mapEvent(F&& filter, Mapper&& mapper) {
|
||||
// using T = decltype(mapper(std::declval<typename F::Event*>()));
|
||||
// return EventMapper<F, T>::create(std::move(filter), std::move(mapper));
|
||||
// }
|
||||
|
||||
// template <is_filter F, class Mapper>
|
||||
// requires std::copy_constructible<F>
|
||||
// static auto mapEvent(F const& filter, Mapper&& mapper) {
|
||||
// using T = decltype(mapper(std::declval<typename F::Event*>()));
|
||||
// return EventMapper<F, T>::create(F(filter), std::move(mapper));
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,314 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "Types.hpp"
|
||||
#include "ModMetadata.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "../utils/Result.hpp"
|
||||
#include "../utils/web.hpp"
|
||||
#include <unordered_set>
|
||||
|
||||
namespace geode {
|
||||
class Index;
|
||||
|
||||
/**
|
||||
* Status signifying an index-related download has been finished
|
||||
*/
|
||||
using UpdateFinished = std::monostate;
|
||||
/**
|
||||
* Status signifying an index-related download is in progress. First element
|
||||
* in pair is percentage downloaded, second is status string
|
||||
*/
|
||||
using UpdateProgress = std::pair<uint8_t, std::string>;
|
||||
/**
|
||||
* Status signifying an index-related download has failed. Consists of the
|
||||
* error string
|
||||
*/
|
||||
using UpdateFailed = std::string;
|
||||
/**
|
||||
* Status code for an index-related download
|
||||
*/
|
||||
using UpdateStatus = std::variant<UpdateFinished, UpdateProgress, UpdateFailed>;
|
||||
|
||||
/**
|
||||
* Event for when a mod is being installed from the index. Automatically
|
||||
* broadcast by the mods index; use ModInstallFilter to listen to these
|
||||
* events
|
||||
*/
|
||||
struct GEODE_DLL ModInstallEvent : public Event {
|
||||
/**
|
||||
* The ID of the mod being installed
|
||||
*/
|
||||
const std::string modID;
|
||||
/**
|
||||
* The current status of the installation
|
||||
*/
|
||||
const UpdateStatus status;
|
||||
|
||||
private:
|
||||
ModInstallEvent(std::string const& id, const UpdateStatus status);
|
||||
|
||||
friend class Index;
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic filter for listening to mod installation events. Always propagates
|
||||
* the event down the chain
|
||||
* @example
|
||||
* // Install "steve.hotdogs" and listen for its installation progress
|
||||
*
|
||||
* // Create a listener that listens for when steve.hotdogs is being installed
|
||||
* auto listener = EventListener<ModInstallFilter>(+[](ModInstallEvent* ev) {
|
||||
* // Check the event status using std::visit or other
|
||||
* }, ModInstallFilter("steve.hotdogs"));
|
||||
* // Get the latest version of steve.hotdogs from the index and install it
|
||||
* if (auto mod = Index::get()->getMajorItem("steve.hotdogs")) {
|
||||
* Index::get()->install(mod);
|
||||
* }
|
||||
*/
|
||||
class GEODE_DLL ModInstallFilter : public EventFilter<ModInstallEvent> {
|
||||
protected:
|
||||
std::string m_id;
|
||||
|
||||
public:
|
||||
using Callback = void(ModInstallEvent*);
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, ModInstallEvent* event);
|
||||
ModInstallFilter(std::string const& id);
|
||||
ModInstallFilter(ModInstallFilter const&) = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event broadcast when the index is being updated
|
||||
*/
|
||||
struct GEODE_DLL IndexUpdateEvent : public Event {
|
||||
const UpdateStatus status;
|
||||
IndexUpdateEvent(const UpdateStatus status);
|
||||
};
|
||||
|
||||
/**
|
||||
* Basic filter for listening to index update events. Always propagates
|
||||
* the event down the chain
|
||||
*/
|
||||
class GEODE_DLL IndexUpdateFilter : public EventFilter<IndexUpdateEvent> {
|
||||
public:
|
||||
using Callback = void(IndexUpdateEvent*);
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, IndexUpdateEvent* event);
|
||||
IndexUpdateFilter();
|
||||
IndexUpdateFilter(IndexUpdateFilter const&) = default;
|
||||
};
|
||||
|
||||
class GEODE_DLL IndexItem final {
|
||||
public:
|
||||
class Impl;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Returns the path that contains all the versions
|
||||
*/
|
||||
ghc::filesystem::path getRootPath() const;
|
||||
|
||||
/**
|
||||
* Returns the path to the specific version
|
||||
*/
|
||||
ghc::filesystem::path getPath() const;
|
||||
ModMetadata getMetadata() const;
|
||||
std::string getDownloadURL() const;
|
||||
std::string getPackageHash() const;
|
||||
std::unordered_set<PlatformID> getAvailablePlatforms() const;
|
||||
bool isFeatured() const;
|
||||
std::unordered_set<std::string> getTags() const;
|
||||
bool isInstalled() const;
|
||||
|
||||
#if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE)
|
||||
void setMetadata(ModMetadata const& value);
|
||||
void setDownloadURL(std::string const& value);
|
||||
void setPackageHash(std::string const& value);
|
||||
void setAvailablePlatforms(std::unordered_set<PlatformID> const& value);
|
||||
void setIsFeatured(bool const& value);
|
||||
void setTags(std::unordered_set<std::string> const& value);
|
||||
void setIsInstalled(bool const& value);
|
||||
#endif
|
||||
|
||||
IndexItem();
|
||||
~IndexItem();
|
||||
|
||||
friend class ModMetadata;
|
||||
friend class Index;
|
||||
};
|
||||
using IndexItemHandle = std::shared_ptr<IndexItem>;
|
||||
|
||||
struct IndexInstallList {
|
||||
/**
|
||||
* Mod being installed
|
||||
*/
|
||||
IndexItemHandle target;
|
||||
/**
|
||||
* The mod, its dependencies, everything needed to install it
|
||||
*/
|
||||
std::vector<IndexItemHandle> list;
|
||||
};
|
||||
|
||||
static constexpr size_t MAX_INDEX_API_VERSION = 0;
|
||||
|
||||
class GEODE_DLL Index final {
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
Index();
|
||||
~Index();
|
||||
|
||||
public:
|
||||
static Index* get();
|
||||
|
||||
/**
|
||||
* Get all tags
|
||||
*/
|
||||
std::unordered_set<std::string> getTags() const;
|
||||
|
||||
/**
|
||||
* Get all index items
|
||||
*/
|
||||
std::vector<IndexItemHandle> getItems() const;
|
||||
/**
|
||||
* Get all featured index items
|
||||
*/
|
||||
std::vector<IndexItemHandle> getFeaturedItems() const;
|
||||
/**
|
||||
* Get all latest index items
|
||||
*/
|
||||
std::vector<IndexItemHandle> getLatestItems() const;
|
||||
/**
|
||||
* Get all index items by a developer
|
||||
*/
|
||||
std::vector<IndexItemHandle> getItemsByDeveloper(
|
||||
std::string const& name
|
||||
) const;
|
||||
/**
|
||||
* Get all index items for a specific mod
|
||||
*/
|
||||
std::vector<IndexItemHandle> getItemsByModID(
|
||||
std::string const& modID
|
||||
) const;
|
||||
/**
|
||||
* Check if an item with this ID is found on the index, and optionally
|
||||
* provide the version sought after
|
||||
*/
|
||||
bool isKnownItem(
|
||||
std::string const& id,
|
||||
std::optional<VersionInfo> version
|
||||
) const;
|
||||
/**
|
||||
* Get an item from the index by its ID and optionally version
|
||||
* @param id ID of the mod
|
||||
* @param version Version to match exactly; if you need to match a range
|
||||
* of versions, use the getItem overload that takes a
|
||||
* ComparableVersionInfo
|
||||
* @returns The item, or nullptr if the item was not found
|
||||
*/
|
||||
IndexItemHandle getItem(
|
||||
std::string const& id,
|
||||
std::optional<VersionInfo> version
|
||||
) const;
|
||||
/**
|
||||
* Get an item from the index by its ID and version range
|
||||
* @param id ID of the mod
|
||||
* @param version Version to match
|
||||
* @returns The item, or nullptr if the item was not found
|
||||
*/
|
||||
IndexItemHandle getItem(
|
||||
std::string const& id,
|
||||
ComparableVersionInfo version
|
||||
) const;
|
||||
/**
|
||||
* Get the most major item from the index by its ID
|
||||
* @param id ID of the mod
|
||||
* @returns The item, or nullptr if the item was not found
|
||||
*/
|
||||
IndexItemHandle getMajorItem(
|
||||
std::string const& id
|
||||
) const;
|
||||
/**
|
||||
* Get an item from the index by its mod.json
|
||||
* @param info The mod's metadata
|
||||
* @returns The item, or nullptr if the item was not found
|
||||
*/
|
||||
IndexItemHandle getItem(ModMetadata const& metadata) const;
|
||||
/**
|
||||
* Get an item from the index that corresponds to an installed mod
|
||||
* @param mod An installed mod
|
||||
* @returns The item, or nullptr if the item was not found
|
||||
*/
|
||||
IndexItemHandle getItem(Mod* mod) const;
|
||||
|
||||
/**
|
||||
* Check if an item has updates available
|
||||
* @param item Item to check updates for
|
||||
* @returns True if the version of the item on the index is newer than
|
||||
* its installed counterpart
|
||||
*/
|
||||
bool isUpdateAvailable(IndexItemHandle item) const;
|
||||
/**
|
||||
* Check if any of the mods on the index have updates available
|
||||
*/
|
||||
bool areUpdatesAvailable() const;
|
||||
/**
|
||||
* Checks if the mod and its required dependencies can be installed
|
||||
* @param item Item to get the list for
|
||||
* @returns Success if the mod and its required dependencies can be installed, an error otherwise
|
||||
*/
|
||||
Result<> canInstall(IndexItemHandle item) const;
|
||||
/**
|
||||
* Get the list of items needed to install this item (dependencies, etc.)
|
||||
* @param item Item to get the list for
|
||||
* @returns The list, or an error if some items on the list cannot be installed
|
||||
*/
|
||||
Result<IndexInstallList> getInstallList(IndexItemHandle item) const;
|
||||
/**
|
||||
* Install an index item. Add an event listener for the ModInstallEvent
|
||||
* class to track the installation progress. Automatically also downloads
|
||||
* all missing dependencies for the item
|
||||
* @param item Item to install
|
||||
*/
|
||||
void install(IndexItemHandle item);
|
||||
/**
|
||||
* Install a list of index items. Add an event listener for the
|
||||
* ModInstallEvent class to track the installation progress
|
||||
* @warning Does not download any missing dependencies - use the
|
||||
* `install(IndexItemHandle)` overload if you aren't sure all the
|
||||
* dependencies are installed!
|
||||
* @param list List of items to install
|
||||
*/
|
||||
void install(IndexInstallList const& list);
|
||||
/**
|
||||
* Cancel an installation in progress
|
||||
* @param item Installation to cancel
|
||||
*/
|
||||
void cancelInstall(IndexItemHandle item);
|
||||
|
||||
/**
|
||||
* Check if it has been attempted to update the index. You can check
|
||||
* for errors by doing hasTriedToUpdate() && !isUpToDate()
|
||||
*/
|
||||
bool hasTriedToUpdate() const;
|
||||
/**
|
||||
* Whether the index is up-to-date, i.e. all sources are up-to-date
|
||||
*/
|
||||
bool isUpToDate() const;
|
||||
/**
|
||||
* Whether the index is currently updating
|
||||
*/
|
||||
bool isUpdating() const;
|
||||
/**
|
||||
* Update the index. Add an event listener for the IndexUpdateEvent
|
||||
* class to track updating progress
|
||||
* @param force Forcefully update all sources, even if some have are
|
||||
* already up-to-date
|
||||
*/
|
||||
void update(bool force = false);
|
||||
};
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <ghc/fs_fwd.hpp>
|
||||
#include <filesystem>
|
||||
#include "../utils/Result.hpp"
|
||||
#include "../utils/MiniFunction.hpp"
|
||||
#include "Log.hpp"
|
||||
|
@ -18,7 +18,7 @@ namespace geode {
|
|||
using ScheduledFunction = utils::MiniFunction<void()>;
|
||||
|
||||
struct InvalidGeodeFile {
|
||||
ghc::filesystem::path path;
|
||||
std::filesystem::path path;
|
||||
std::string reason;
|
||||
};
|
||||
|
||||
|
@ -45,7 +45,7 @@ namespace geode {
|
|||
OutdatedIncompatibility,
|
||||
};
|
||||
Type type;
|
||||
std::variant<ghc::filesystem::path, ModMetadata, Mod*> cause;
|
||||
std::variant<std::filesystem::path, ModMetadata, Mod*> cause;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
|
@ -91,7 +91,9 @@ namespace geode {
|
|||
bool isModLoaded(std::string const& id) const;
|
||||
Mod* getLoadedMod(std::string const& id) const;
|
||||
std::vector<Mod*> getAllMods();
|
||||
std::vector<LoadProblem> getAllProblems() const;
|
||||
std::vector<LoadProblem> getProblems() const;
|
||||
std::vector<LoadProblem> getRecommendations() const;
|
||||
|
||||
/**
|
||||
* Returns the available launch argument names.
|
||||
|
@ -151,6 +153,15 @@ namespace geode {
|
|||
friend Mod* takeNextLoaderMod();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Queues a function to run on the main thread
|
||||
*
|
||||
* @param func the function to queue
|
||||
*/
|
||||
inline GEODE_HIDDEN void queueInMainThread(ScheduledFunction func) {
|
||||
Loader::get()->queueInMainThread(func);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Take the next mod to load
|
||||
*
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <ccTypes.h>
|
||||
#include <chrono>
|
||||
#include <ghc/fs_fwd.hpp>
|
||||
#include <filesystem>
|
||||
#include <matjson.hpp>
|
||||
#include <type_traits>
|
||||
#include <fmt/core.h>
|
||||
|
@ -59,8 +59,8 @@ namespace gd {
|
|||
}
|
||||
}
|
||||
|
||||
namespace ghc::filesystem {
|
||||
GEODE_INLINE GEODE_HIDDEN std::string format_as(ghc::filesystem::path const& value) {
|
||||
namespace std::filesystem {
|
||||
GEODE_INLINE GEODE_HIDDEN std::string format_as(std::filesystem::path const& value) {
|
||||
return value.string();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,13 @@ namespace geode {
|
|||
UninstallWithSaveData
|
||||
};
|
||||
|
||||
static constexpr bool modRequestedActionIsToggle(ModRequestedAction action) {
|
||||
return action == ModRequestedAction::Enable || action == ModRequestedAction::Disable;
|
||||
}
|
||||
static constexpr bool modRequestedActionIsUninstall(ModRequestedAction action) {
|
||||
return action == ModRequestedAction::Uninstall || action == ModRequestedAction::UninstallWithSaveData;
|
||||
}
|
||||
|
||||
GEODE_HIDDEN Mod* takeNextLoaderMod();
|
||||
|
||||
class ModImpl;
|
||||
|
@ -84,23 +91,24 @@ namespace geode {
|
|||
std::vector<std::string> getDevelopers() const;
|
||||
std::optional<std::string> getDescription() const;
|
||||
std::optional<std::string> getDetails() const;
|
||||
ghc::filesystem::path getPackagePath() const;
|
||||
std::filesystem::path getPackagePath() const;
|
||||
VersionInfo getVersion() const;
|
||||
bool isEnabled() const;
|
||||
bool isOrWillBeEnabled() const;
|
||||
bool isInternal() const;
|
||||
bool needsEarlyLoad() const;
|
||||
ModMetadata getMetadata() const;
|
||||
ghc::filesystem::path getTempDir() const;
|
||||
std::filesystem::path getTempDir() const;
|
||||
/**
|
||||
* Get the path to the mod's platform binary (.dll on Windows, .dylib
|
||||
* on Mac & iOS, .so on Android)
|
||||
*/
|
||||
ghc::filesystem::path getBinaryPath() const;
|
||||
std::filesystem::path getBinaryPath() const;
|
||||
/**
|
||||
* Get the path to the mod's runtime resources directory (contains all
|
||||
* of its resources)
|
||||
*/
|
||||
ghc::filesystem::path getResourcesDir() const;
|
||||
std::filesystem::path getResourcesDir() const;
|
||||
|
||||
#if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE)
|
||||
void setMetadata(ModMetadata const& metadata);
|
||||
|
@ -123,11 +131,11 @@ namespace geode {
|
|||
/**
|
||||
* Get the mod's save directory path
|
||||
*/
|
||||
ghc::filesystem::path getSaveDir() const;
|
||||
std::filesystem::path getSaveDir() const;
|
||||
/**
|
||||
* Get the mod's config directory path
|
||||
*/
|
||||
ghc::filesystem::path getConfigDir(bool create = true) const;
|
||||
std::filesystem::path getConfigDir(bool create = true) const;
|
||||
|
||||
bool hasSettings() const;
|
||||
std::vector<std::string> getSettingKeys() const;
|
||||
|
@ -427,6 +435,9 @@ namespace geode {
|
|||
void setLoggingEnabled(bool enabled);
|
||||
|
||||
bool hasProblems() const;
|
||||
std::vector<LoadProblem> getAllProblems() const;
|
||||
std::vector<LoadProblem> getProblems() const;
|
||||
std::vector<LoadProblem> getRecommendations() const;
|
||||
bool shouldLoad() const;
|
||||
bool isCurrentlyLoading() const;
|
||||
|
||||
|
|
|
@ -15,6 +15,38 @@ namespace geode {
|
|||
|
||||
class ModMetadataImpl;
|
||||
|
||||
class GEODE_DLL ModMetadataLinks final {
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
friend class ModMetadataImpl;
|
||||
|
||||
public:
|
||||
ModMetadataLinks();
|
||||
ModMetadataLinks(ModMetadataLinks const& other);
|
||||
ModMetadataLinks(ModMetadataLinks&& other) noexcept;
|
||||
ModMetadataLinks& operator=(ModMetadataLinks const& other);
|
||||
ModMetadataLinks& operator=(ModMetadataLinks&& other) noexcept;
|
||||
~ModMetadataLinks();
|
||||
|
||||
/**
|
||||
* Get the URL for the home website for this mod
|
||||
*/
|
||||
std::optional<std::string> getHomepageURL() const;
|
||||
/**
|
||||
* Get the URL for the source code repository for this mod
|
||||
*/
|
||||
std::optional<std::string> getSourceURL() const;
|
||||
/**
|
||||
* Get the URL for the community page (Discord server etc.) for this mod
|
||||
*/
|
||||
std::optional<std::string> getCommunityURL() const;
|
||||
|
||||
#if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE)
|
||||
Impl* getImpl();
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents all the data gather-able
|
||||
* from mod.json
|
||||
|
@ -62,7 +94,7 @@ namespace geode {
|
|||
/**
|
||||
* Path to the mod file
|
||||
*/
|
||||
[[nodiscard]] ghc::filesystem::path getPath() const;
|
||||
[[nodiscard]] std::filesystem::path getPath() const;
|
||||
/**
|
||||
* Name of the platform binary within
|
||||
* the mod zip
|
||||
|
@ -126,7 +158,12 @@ namespace geode {
|
|||
/**
|
||||
* Git Repository of the mod
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::string> getRepository() const;
|
||||
[[nodiscard, deprecated("Use ModMetadata::getLinks instead")]]
|
||||
std::optional<std::string> getRepository() const;
|
||||
/**
|
||||
* Get the links (related websites / servers / etc.) for this mod
|
||||
*/
|
||||
ModMetadataLinks getLinks() const;
|
||||
/**
|
||||
* Info about where users should report issues and request help
|
||||
*/
|
||||
|
@ -148,6 +185,10 @@ namespace geode {
|
|||
* @note Not a map because insertion order must be preserved
|
||||
*/
|
||||
[[nodiscard]] std::vector<std::pair<std::string, Setting>> getSettings() const;
|
||||
/**
|
||||
* Get the tags for this mod
|
||||
*/
|
||||
[[nodiscard]] std::unordered_set<std::string> getTags() const;
|
||||
/**
|
||||
* Whether this mod has to be loaded before the loading screen or not
|
||||
*/
|
||||
|
@ -175,7 +216,7 @@ namespace geode {
|
|||
Result<> checkGameVersion() const;
|
||||
|
||||
#if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE)
|
||||
void setPath(ghc::filesystem::path const& value);
|
||||
void setPath(std::filesystem::path const& value);
|
||||
void setBinaryName(std::string const& value);
|
||||
void setVersion(VersionInfo const& value);
|
||||
void setID(std::string const& value);
|
||||
|
@ -192,8 +233,10 @@ namespace geode {
|
|||
void setIncompatibilities(std::vector<Incompatibility> const& value);
|
||||
void setSpritesheets(std::vector<std::string> const& value);
|
||||
void setSettings(std::vector<std::pair<std::string, Setting>> const& value);
|
||||
void setTags(std::unordered_set<std::string> const& value);
|
||||
void setNeedsEarlyLoad(bool const& value);
|
||||
void setIsAPI(bool const& value);
|
||||
ModMetadataLinks& getLinksMut();
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -203,11 +246,11 @@ namespace geode {
|
|||
/**
|
||||
* Create ModInfo from a .geode package
|
||||
*/
|
||||
static Result<ModMetadata> createFromGeodeFile(ghc::filesystem::path const& path);
|
||||
static Result<ModMetadata> createFromGeodeFile(std::filesystem::path const& path);
|
||||
/**
|
||||
* Create ModInfo from a mod.json file
|
||||
*/
|
||||
static Result<ModMetadata> createFromFile(ghc::filesystem::path const& path);
|
||||
static Result<ModMetadata> createFromFile(std::filesystem::path const& path);
|
||||
/**
|
||||
* Create ModInfo from a parsed json document
|
||||
*/
|
||||
|
@ -244,7 +287,7 @@ namespace geode {
|
|||
*/
|
||||
static Result<ModMetadata> createFromSchemaV010(ModJson const& json);
|
||||
|
||||
Result<> addSpecialFiles(ghc::filesystem::path const& dir);
|
||||
Result<> addSpecialFiles(std::filesystem::path const& dir);
|
||||
Result<> addSpecialFiles(utils::file::Unzip& zip);
|
||||
|
||||
std::vector<std::pair<std::string, std::optional<std::string>*>> getSpecialFiles();
|
||||
|
|
|
@ -109,7 +109,7 @@ namespace geode {
|
|||
* local device
|
||||
*/
|
||||
struct GEODE_DLL FileSetting final {
|
||||
using ValueType = ghc::filesystem::path;
|
||||
using ValueType = std::filesystem::path;
|
||||
using Filter = utils::file::FilePickOptions::Filter;
|
||||
|
||||
std::optional<std::string> name;
|
||||
|
|
|
@ -25,8 +25,10 @@ namespace geode::modifier {
|
|||
public:
|
||||
~FieldContainer() {
|
||||
for (auto i = 0u; i < m_containedFields.size(); i++) {
|
||||
m_destructorFunctions[i](m_containedFields[i]);
|
||||
operator delete(m_containedFields[i]);
|
||||
if (m_destructorFunctions[i] && m_containedFields[i]) {
|
||||
m_destructorFunctions[i](m_containedFields[i]);
|
||||
operator delete(m_containedFields[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ namespace geode {
|
|||
Gray = 2,
|
||||
Blue = 3,
|
||||
Cyan = 4,
|
||||
DarkPurple = 5, // Geode-added color, used in Geode UIs
|
||||
DarkAqua = 6, // Geode-added color, used in Geode UIs
|
||||
};
|
||||
GEODE_DLL const char* baseEnumToString(CircleBaseColor);
|
||||
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include "../loader/Mod.hpp"
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
// Credit to https://github.com/Ikszyon/UI-Recolor for many of these addresses!
|
||||
|
||||
/**
|
||||
* Hardcoded GD colors
|
||||
*/
|
||||
enum class GDColor {
|
||||
NormalModeProgressBar,
|
||||
PracticeModeProgressBar,
|
||||
|
||||
ProfilePostBG,
|
||||
};
|
||||
|
||||
class GEODE_DLL ColorManager {
|
||||
protected:
|
||||
struct Value {
|
||||
cocos2d::ccColor3B value;
|
||||
Mod* setter;
|
||||
};
|
||||
|
||||
std::unordered_map<GDColor, std::vector<Value>> m_colors;
|
||||
|
||||
ColorManager();
|
||||
|
||||
public:
|
||||
static ColorManager* get();
|
||||
|
||||
cocos2d::ccColor3B getColor(GDColor color) const;
|
||||
void setColor(GDColor color, Mod* setter, cocos2d::ccColor3B const& value);
|
||||
void resetColor(GDColor color, Mod* setter);
|
||||
|
||||
template<class = void>
|
||||
void setColor(GDColor color, cocos2d::ccColor3B const& value) {
|
||||
this->setColor(color, Mod::get(), value);
|
||||
}
|
||||
template<class = void>
|
||||
void resetColor(GDColor color) {
|
||||
this->resetColor(color, Mod::get());
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../loader/Mod.hpp"
|
||||
#include "../loader/Index.hpp"
|
||||
|
||||
namespace geode {
|
||||
/**
|
||||
|
@ -20,6 +19,7 @@ namespace geode {
|
|||
* Open the support popup for a mod
|
||||
*/
|
||||
GEODE_DLL void openSupportPopup(Mod* mod);
|
||||
GEODE_DLL void openSupportPopup(ModMetadata const& metadata);
|
||||
/**
|
||||
* Open the store page for a mod (if it exists)
|
||||
*/
|
||||
|
@ -30,23 +30,16 @@ namespace geode {
|
|||
GEODE_DLL void openSettingsPopup(Mod* mod);
|
||||
/**
|
||||
* Create a default logo sprite
|
||||
* @param size Size of the sprite
|
||||
*/
|
||||
GEODE_DLL cocos2d::CCNode* createDefaultLogo(
|
||||
cocos2d::CCSize const& size
|
||||
);
|
||||
GEODE_DLL cocos2d::CCNode* createDefaultLogo();
|
||||
/**
|
||||
* Create a logo sprite for a mod
|
||||
* @param size Size of the sprite
|
||||
*/
|
||||
GEODE_DLL cocos2d::CCNode* createModLogo(
|
||||
Mod* mod, cocos2d::CCSize const& size
|
||||
);
|
||||
GEODE_DLL cocos2d::CCNode* createModLogo(Mod* mod);
|
||||
/**
|
||||
* Create a logo sprite for an index item
|
||||
* @param size Size of the sprite
|
||||
* Create a logo sprite for a mod downloaded from the Geode servers. The
|
||||
* logo is initially a loading circle, with the actual sprite downloaded
|
||||
* asynchronously
|
||||
*/
|
||||
GEODE_DLL cocos2d::CCNode* createIndexItemLogo(
|
||||
IndexItemHandle item, cocos2d::CCSize const& size
|
||||
);
|
||||
GEODE_DLL cocos2d::CCNode* createServerModLogo(std::string const& id);
|
||||
}
|
||||
|
|
|
@ -39,5 +39,8 @@ namespace geode {
|
|||
|
||||
void setString(char const* label) override;
|
||||
char const* getString() override;
|
||||
|
||||
void setColor(cocos2d::ccColor3B const& color) override;
|
||||
void setOpacity(GLubyte opacity) override;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -143,6 +143,21 @@ namespace geode {
|
|||
}
|
||||
m_title->limitLabelWidth(m_size.width - 20.f, scale, .1f);
|
||||
}
|
||||
|
||||
void setCloseButtonSpr(cocos2d::CCSprite* spr, float scale = 1.f) {
|
||||
// Store original attributes of the close button
|
||||
auto origSize = m_closeBtn->getContentSize();
|
||||
auto orig = Ref(m_closeBtn->getNormalImage());
|
||||
|
||||
// Replace the close button sprite
|
||||
m_closeBtn->setNormalImage(spr);
|
||||
|
||||
// Restore size and position
|
||||
spr->setScale(scale);
|
||||
spr->setPosition(orig->getPosition());
|
||||
spr->setAnchorPoint(orig->getAnchorPoint());
|
||||
m_closeBtn->setContentSize(origSize);
|
||||
}
|
||||
};
|
||||
|
||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||
|
|
|
@ -36,5 +36,6 @@ namespace geode {
|
|||
|
||||
void scrollWheel(float y, float) override;
|
||||
void enableScrollWheel(bool enable = true);
|
||||
void scrollToTop();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -29,6 +29,11 @@ namespace geode {
|
|||
|
||||
GEODE_DLL const char* getCommonFilterAllowedChars(CommonFilter filter);
|
||||
|
||||
enum class TextInputAlign {
|
||||
Center,
|
||||
Left,
|
||||
};
|
||||
|
||||
/**
|
||||
* A single-line text input node
|
||||
*/
|
||||
|
@ -37,6 +42,7 @@ namespace geode {
|
|||
cocos2d::extension::CCScale9Sprite* m_bgSprite;
|
||||
CCTextInputNode* m_input;
|
||||
std::function<void(std::string const&)> m_onInput = nullptr;
|
||||
cocos2d::CCLabelBMFont* m_label = nullptr;
|
||||
|
||||
bool init(float width, std::string const& placeholder, std::string const& font);
|
||||
|
||||
|
@ -59,6 +65,11 @@ namespace geode {
|
|||
* Set the placeholder label for this input
|
||||
*/
|
||||
void setPlaceholder(std::string const& placeholder);
|
||||
/**
|
||||
* Set a label on this input that shows up on the top. Set an empty
|
||||
* string to remove the label
|
||||
*/
|
||||
void setLabel(std::string const& label);
|
||||
/**
|
||||
* Set the filter (allowed characters) for this input
|
||||
* @param allowedChars String of allowed characters; each character in
|
||||
|
@ -104,6 +115,10 @@ namespace geode {
|
|||
* Enable/disable the input
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
/**
|
||||
* Align the button's content to the left. If false, aligns to the center
|
||||
*/
|
||||
void setTextAlign(TextInputAlign align);
|
||||
|
||||
/**
|
||||
* Hides the background of this input. Shorthand for
|
||||
|
|
127
loader/include/Geode/utils/ColorProvider.hpp
Normal file
|
@ -0,0 +1,127 @@
|
|||
#pragma once
|
||||
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include "../loader/Event.hpp"
|
||||
|
||||
namespace geode {
|
||||
struct GEODE_DLL ColorProvidedEvent : public Event {
|
||||
std::string id;
|
||||
cocos2d::ccColor4B color;
|
||||
|
||||
ColorProvidedEvent(std::string const& id, cocos2d::ccColor4B const& color);
|
||||
};
|
||||
|
||||
class GEODE_DLL ColorProvidedFilter : public EventFilter<ColorProvidedEvent> {
|
||||
public:
|
||||
using Callback = void(ColorProvidedEvent*);
|
||||
|
||||
protected:
|
||||
std::string m_id;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, ColorProvidedEvent* event);
|
||||
|
||||
ColorProvidedFilter(std::string const& id);
|
||||
};
|
||||
|
||||
/**
|
||||
* GD has a lot of hardcoded colors. In addition, mods may very well also
|
||||
* use hardcoded colors in their UIs, for example for CCLayerColors. This
|
||||
* makes it hard for texture packs to reliably change colors, since they'd
|
||||
* have to manually deal with every mod.
|
||||
*
|
||||
* To help with this, Geode provides the `ColorProvider` class, which is
|
||||
* just an index of colors with associated IDs. Mods should use this to
|
||||
* define their hardcoded colors for their UIs, and texture packs can then
|
||||
* change the color dynamically.
|
||||
*
|
||||
* Mods are not expected to handle in-game color changes, since in nearly
|
||||
* all cases the texture pack will be applied far from the mod's UI and as
|
||||
* such just getting the defined color on layer enter will be more than
|
||||
* enough. However, if the mod does add some static UI that won't be
|
||||
* naturally refreshed after a texture pack is applied, it should listen
|
||||
* for `ColorProvidedEvent`s to react accordingly.
|
||||
*/
|
||||
class GEODE_DLL ColorProvider final {
|
||||
private:
|
||||
class Impl;
|
||||
|
||||
Impl* m_impl;
|
||||
|
||||
ColorProvider();
|
||||
|
||||
public:
|
||||
// @note See class description
|
||||
static ColorProvider* get();
|
||||
|
||||
/**
|
||||
* Define a new color with an associated ID. The ID should be prefixed
|
||||
* with the mod ID. If the color has already been defined, nothing
|
||||
* happens
|
||||
* @param id The ID of the color; should be prefixed with mod ID
|
||||
* @param color The color
|
||||
* @returns The current value of the color with the ID (same as the
|
||||
* `color` function)
|
||||
*/
|
||||
cocos2d::ccColor4B define(std::string const& id, cocos2d::ccColor4B const& color);
|
||||
/**
|
||||
* Define a new color with an associated ID. The ID should be prefixed
|
||||
* with the mod ID. If the color has already been defined, nothing
|
||||
* happens
|
||||
* @param id The ID of the color; should be prefixed with mod ID
|
||||
* @param color The color. Alpha component is assumed to be 255
|
||||
* @returns The current value of the color with the ID (same as the
|
||||
* `color` function, although with the value truncated to cc3b)
|
||||
*/
|
||||
cocos2d::ccColor3B define(std::string const& id, cocos2d::ccColor3B const& color);
|
||||
/**
|
||||
* Override the current value of a color with an associated ID
|
||||
* @param id The ID of the color
|
||||
* @param color The color to override with
|
||||
* @returns The new value of the color, or ccWHITE if the ID doesn't
|
||||
* exist
|
||||
*/
|
||||
cocos2d::ccColor4B override(std::string const& id, cocos2d::ccColor4B const& color);
|
||||
/**
|
||||
* Override the current value of a color with an associated ID
|
||||
* @param id The ID of the color
|
||||
* @param color The color to override with. Alpha component is assumed
|
||||
* to be 255
|
||||
* @returns The new value of the color, or ccWHITE if the ID doesn't
|
||||
* exist (truncated to cc3b)
|
||||
*/
|
||||
cocos2d::ccColor3B override(std::string const& id, cocos2d::ccColor3B const& color);
|
||||
/**
|
||||
* Reset the current value of a color to its original definition
|
||||
* @param id The ID of the color
|
||||
* @returns The original value of the color, or ccWHITE if the ID
|
||||
* doesn't exist
|
||||
*/
|
||||
cocos2d::ccColor4B reset(std::string const& id);
|
||||
/**
|
||||
* Get the current value of a color
|
||||
* @param id The ID of the color
|
||||
* @returns The value of the color, or ccWHITE if the ID doesn't exist
|
||||
*/
|
||||
cocos2d::ccColor4B color(std::string const& id) const;
|
||||
/**
|
||||
* Get the current value of a color as a ccColor3B
|
||||
* @param id The ID of the color
|
||||
* @returns The value of the color, or ccWHITE if the ID doesn't exist
|
||||
*/
|
||||
cocos2d::ccColor3B color3b(std::string const& id) const;
|
||||
};
|
||||
}
|
||||
|
||||
GEODE_HIDDEN inline cocos2d::ccColor4B operator"" _cc4b_gd(const char* str, size_t) {
|
||||
return geode::ColorProvider::get()->color(str);
|
||||
}
|
||||
GEODE_HIDDEN inline cocos2d::ccColor3B operator"" _cc3b_gd(const char* str, size_t) {
|
||||
return geode::ColorProvider::get()->color3b(str);
|
||||
}
|
||||
GEODE_HIDDEN inline cocos2d::ccColor4B operator"" _cc4b(const char* str, size_t) {
|
||||
return geode::ColorProvider::get()->color(geode::Mod::get()->expandSpriteName(str));
|
||||
}
|
||||
GEODE_HIDDEN inline cocos2d::ccColor3B operator"" _cc3b(const char* str, size_t) {
|
||||
return geode::ColorProvider::get()->color3b(geode::Mod::get()->expandSpriteName(str));
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <memory>
|
||||
#include <concepts>
|
||||
#include "terminate.hpp"
|
||||
|
||||
namespace geode::utils {
|
||||
|
||||
|
@ -25,7 +26,7 @@ namespace geode::utils {
|
|||
explicit MiniFunctionState(Type func) : m_func(func) {}
|
||||
|
||||
Ret call(Args... args) const override {
|
||||
return const_cast<Type&>(m_func)(args...);
|
||||
return const_cast<Type&>(m_func)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
MiniFunctionStateBase<Ret, Args...>* clone() const override {
|
||||
|
@ -41,7 +42,7 @@ namespace geode::utils {
|
|||
explicit MiniFunctionStatePointer(Type func) : m_func(func) {}
|
||||
|
||||
Ret call(Args... args) const override {
|
||||
return const_cast<Type&>(*m_func)(args...);
|
||||
return const_cast<Type&>(*m_func)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
MiniFunctionStateBase<Ret, Args...>* clone() const override {
|
||||
|
@ -57,7 +58,7 @@ namespace geode::utils {
|
|||
explicit MiniFunctionStateMemberPointer(Type func) : m_func(func) {}
|
||||
|
||||
Ret call(Class self, Args... args) const override {
|
||||
return const_cast<Type&>(self->*m_func)(args...);
|
||||
return const_cast<Type&>(self->*m_func)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
MiniFunctionStateBase<Ret, Class, Args...>* clone() const override {
|
||||
|
@ -67,7 +68,7 @@ namespace geode::utils {
|
|||
|
||||
template <class Callable, class Ret, class... Args>
|
||||
concept MiniFunctionCallable = requires(Callable&& func, Args... args) {
|
||||
{ func(args...) } -> std::same_as<Ret>;
|
||||
{ func(std::forward<Args>(args)...) } -> std::same_as<Ret>;
|
||||
};
|
||||
|
||||
template <class Ret, class... Args>
|
||||
|
@ -92,7 +93,7 @@ namespace geode::utils {
|
|||
}
|
||||
|
||||
~MiniFunction() {
|
||||
delete m_state;
|
||||
if (m_state) delete m_state;
|
||||
}
|
||||
|
||||
template <class Callable>
|
||||
|
@ -111,21 +112,26 @@ namespace geode::utils {
|
|||
m_state(new MiniFunctionStateMemberPointer<MemberFunctionPointer, Ret, Args...>(func)) {}
|
||||
|
||||
MiniFunction& operator=(MiniFunction const& other) {
|
||||
delete m_state;
|
||||
if (m_state) delete m_state;
|
||||
m_state = other.m_state ? other.m_state->clone() : nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
MiniFunction& operator=(MiniFunction&& other) {
|
||||
delete m_state;
|
||||
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) return Ret();
|
||||
return m_state->call(args...);
|
||||
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 {
|
||||
|
|
|
@ -264,12 +264,14 @@ namespace geode {
|
|||
}
|
||||
|
||||
template <class T>
|
||||
constexpr impl::Success<T> Ok(T&& value) {
|
||||
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) {
|
||||
constexpr impl::Failure<E> Err(E error) {
|
||||
return impl::Failure<E>(std::forward<E>(error));
|
||||
}
|
||||
|
||||
|
|
692
loader/include/Geode/utils/Task.hpp
Normal file
|
@ -0,0 +1,692 @@
|
|||
#pragma once
|
||||
|
||||
#include "general.hpp"
|
||||
#include "MiniFunction.hpp"
|
||||
#include "../loader/Event.hpp"
|
||||
#include "../loader/Loader.hpp"
|
||||
#include <mutex>
|
||||
|
||||
namespace geode {
|
||||
/**
|
||||
* Tasks represent an asynchronous operation that will be finished at some
|
||||
* unknown point in the future. Tasks can report their progress, and will
|
||||
* end either through finishing into a value, or due to being cancelled.
|
||||
* Tasks are designed to provide a thread-safe general purpose abstraction
|
||||
* for dealing with any asynchronous operations.
|
||||
* The `Task` class satisfies `EventFilter` and as such is listened
|
||||
* to using the Geode events system; tasks may have multiple listeners, and
|
||||
* even if a listener is attached after the Task has finished it will
|
||||
* receive the finished value.
|
||||
* Tasks are a very cheap and tiny struct that just have a reference to
|
||||
* a task Handle; as such, Tasks may (and should) be copied around without
|
||||
* worry. It should be noted that a Task never owns itself - the listener(s)
|
||||
* of a Task are expected to hold an instance of the Task for as long as
|
||||
* they intend to listen to it. Usually this is done via just setting
|
||||
* the Task as the filter to an `EventListener`, as the `EventListener`
|
||||
* manages the lifetime of its filter
|
||||
* Task itself does not carry a notion of fallibility aside from
|
||||
* cancellation; it is customary to use the `Result` type in Tasks that
|
||||
* might finish to a failure value.
|
||||
* Once a Task has finished or has been cancelled, it can no longer be
|
||||
* revived
|
||||
* @tparam T The type the Task will eventually finish to. This type must be
|
||||
* move-constructible; though as there is no way to move the value out
|
||||
* of the Task (because of potentially multiple listeners), one
|
||||
* should ensure they can reasonably copy the value out in some form if they
|
||||
* wish to gain ownership of it after the Task is finished
|
||||
* @tparam P The type of the progress values the Task (may) post
|
||||
*/
|
||||
template <std::move_constructible T, std::move_constructible P = std::monostate>
|
||||
class [[nodiscard]] Task final {
|
||||
public:
|
||||
/**
|
||||
* A struct used for cancelling Tasks; Tasks may return an instance of
|
||||
* this struct to cancel themselves, or to mark they have handled
|
||||
* outside cancellation
|
||||
*/
|
||||
struct [[nodiscard]] Cancel final {};
|
||||
|
||||
/**
|
||||
* A simple holder for the result of this task; holds either the finished
|
||||
* value or a mark that the Task was cancelled. The Task body must return
|
||||
* this type (though it is implicitly convertible from T and Cancel
|
||||
* so the programmer rarely needs to explicitly name it)
|
||||
*/
|
||||
class Result final {
|
||||
private:
|
||||
std::variant<T, Cancel> m_value;
|
||||
|
||||
std::optional<T> getValue() && {
|
||||
if (m_value.index() == 0) {
|
||||
return std::optional(std::move(std::get<0>(std::move(m_value))));
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
bool isCancelled() const {
|
||||
return m_value.index() == 1;
|
||||
}
|
||||
|
||||
template <std::move_constructible T2, std::move_constructible P2>
|
||||
friend class Task;
|
||||
|
||||
public:
|
||||
Result(Result&&) = default;
|
||||
Result(Result const&) = delete;
|
||||
Result(T&& value) : m_value(std::in_place_index<0>, std::forward<T>(value)) {}
|
||||
Result(Cancel const&) : m_value(std::in_place_index<1>, Cancel()) {}
|
||||
|
||||
// Allow constructing Results using anything that can be used to construct T
|
||||
template <class V>
|
||||
Result(V&& value) requires std::is_constructible_v<T, V&&>
|
||||
: m_value(std::in_place_index<0>, std::forward<V>(value))
|
||||
{}
|
||||
};
|
||||
|
||||
/// The current status of a Task
|
||||
enum class Status {
|
||||
/// The task is still running or waiting to start
|
||||
Pending,
|
||||
/// The task has succesfully finished
|
||||
Finished,
|
||||
/// The task has been cancelled
|
||||
Cancelled,
|
||||
};
|
||||
|
||||
/**
|
||||
* A handle to a running Task. This is what actually keeps track of
|
||||
* the state of the current task; the `Task` class is simply an owning
|
||||
* reference & interface to one of these
|
||||
*/
|
||||
class Handle final {
|
||||
private:
|
||||
// Handles may contain extra data, for example for holding ownership
|
||||
// of other Tasks for `Task::map` and `Task::all`. This struct
|
||||
// provides type erasure for that extra data
|
||||
struct ExtraData final {
|
||||
// Pointer to the owned extra data
|
||||
void* ptr;
|
||||
// Pointer to a function that deletes that extra data
|
||||
// The function MUST have a static lifetime
|
||||
void(*onDestroy)(void*);
|
||||
// Pointer to a function that handles cancelling any tasks within
|
||||
// that extra data when this task is cancelled. Note that the
|
||||
// task may not free up the memory associated with itself here
|
||||
// and this function may not be called if the user uses
|
||||
// `Task::shallowCancel`. However, this pointer *must* always be
|
||||
// valid
|
||||
// The function MUST have a static lifetime
|
||||
void(*onCancelled)(void*);
|
||||
|
||||
ExtraData(void* ptr, void(*onDestroy)(void*), void(*onCancelled)(void*))
|
||||
: ptr(ptr), onDestroy(onDestroy), onCancelled(onCancelled)
|
||||
{}
|
||||
ExtraData(ExtraData const&) = delete;
|
||||
ExtraData(ExtraData&&) = delete;
|
||||
|
||||
~ExtraData() {
|
||||
onDestroy(ptr);
|
||||
}
|
||||
void cancel() {
|
||||
onCancelled(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
std::recursive_mutex m_mutex;
|
||||
Status m_status = Status::Pending;
|
||||
std::optional<T> m_resultValue;
|
||||
bool m_finalEventPosted = false;
|
||||
std::string m_name;
|
||||
std::unique_ptr<ExtraData> m_extraData = nullptr;
|
||||
|
||||
class PrivateMarker final {};
|
||||
|
||||
static std::shared_ptr<Handle> create(std::string const& name) {
|
||||
return std::make_shared<Handle>(PrivateMarker(), name);
|
||||
}
|
||||
|
||||
bool is(Status status) {
|
||||
std::unique_lock<std::recursive_mutex> lock(m_mutex);
|
||||
return m_status == status;
|
||||
}
|
||||
|
||||
template <std::move_constructible T2, std::move_constructible P2>
|
||||
friend class Task;
|
||||
|
||||
public:
|
||||
Handle(PrivateMarker, std::string const& name) : m_name(name) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* When the Task progresses, finishes, or is cancelled, one of these
|
||||
* is posted; the `Task` class itself is used as an event filter to
|
||||
* catch the task events for that specific task
|
||||
*/
|
||||
class Event final : public geode::Event {
|
||||
private:
|
||||
std::shared_ptr<Handle> m_handle;
|
||||
std::variant<T*, P*, Cancel> m_value;
|
||||
EventListenerProtocol* m_for = nullptr;
|
||||
|
||||
Event(std::shared_ptr<Handle> handle, std::variant<T*, P*, Cancel>&& value)
|
||||
: m_handle(handle), m_value(std::move(value)) {}
|
||||
|
||||
static Event createFinished(std::shared_ptr<Handle> handle, T* value) {
|
||||
return Event(handle, std::variant<T*, P*, Cancel>(std::in_place_index<0>, value));
|
||||
}
|
||||
static Event createProgressed(std::shared_ptr<Handle> handle, P* value) {
|
||||
return Event(handle, std::variant<T*, P*, Cancel>(std::in_place_index<1>, value));
|
||||
}
|
||||
static Event createCancelled(std::shared_ptr<Handle> handle) {
|
||||
return Event(handle, std::variant<T*, P*, Cancel>(std::in_place_index<2>, Cancel()));
|
||||
}
|
||||
|
||||
template <std::move_constructible T2, std::move_constructible P2>
|
||||
friend class Task;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get a reference to the contained finish value, or null if this
|
||||
* event holds a progress value or represents cancellation instead
|
||||
*/
|
||||
T* getValue() {
|
||||
return m_value.index() == 0 ? std::get<0>(m_value) : nullptr;
|
||||
}
|
||||
/**
|
||||
* Get a reference to the contained finish value, or null if this
|
||||
* event holds a progress value or represents cancellation instead
|
||||
*/
|
||||
T const* getValue() const {
|
||||
return m_value.index() == 0 ? std::get<0>(m_value) : nullptr;
|
||||
}
|
||||
/**
|
||||
* Get a reference to the contained progress value, or null if
|
||||
* this event holds a finish value or represents cancellation instead
|
||||
*/
|
||||
P* getProgress() {
|
||||
return m_value.index() == 1 ? std::get<1>(m_value) : nullptr;
|
||||
}
|
||||
/**
|
||||
* Get a reference to the contained progress value, or null if
|
||||
* this event holds a finish value or represents cancellation instead
|
||||
*/
|
||||
P const* getProgress() const {
|
||||
return m_value.index() == 1 ? std::get<1>(m_value) : nullptr;
|
||||
}
|
||||
/**
|
||||
* Check if the Task was cancelled
|
||||
*/
|
||||
bool isCancelled() const {
|
||||
return m_value.index() == 2;
|
||||
}
|
||||
/**
|
||||
* Cancel the Task that posted this event. If the task has
|
||||
* already finished or been cancelled, nothing happens
|
||||
*/
|
||||
void cancel() {
|
||||
Task::cancel(m_handle);
|
||||
}
|
||||
};
|
||||
|
||||
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 Callback = void(Event*);
|
||||
|
||||
private:
|
||||
EventListenerProtocol* m_listener = nullptr;
|
||||
std::shared_ptr<Handle> m_handle;
|
||||
|
||||
Task(std::shared_ptr<Handle> handle) : m_handle(handle) {}
|
||||
|
||||
static void finish(std::shared_ptr<Handle> handle, T&& value) {
|
||||
if (!handle) return;
|
||||
std::unique_lock<std::recursive_mutex> lock(handle->m_mutex);
|
||||
if (handle->m_status == Status::Pending) {
|
||||
handle->m_status = Status::Finished;
|
||||
handle->m_resultValue.emplace(std::move(value));
|
||||
queueInMainThread([handle, value = &*handle->m_resultValue]() mutable {
|
||||
// SAFETY: Task::all() depends on the lifetime of the value pointer
|
||||
// being as long as the lifetime of the task itself
|
||||
Event::createFinished(handle, value).post();
|
||||
std::unique_lock<std::recursive_mutex> lock(handle->m_mutex);
|
||||
handle->m_finalEventPosted = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
static void progress(std::shared_ptr<Handle> handle, P&& value) {
|
||||
if (!handle) return;
|
||||
std::unique_lock<std::recursive_mutex> lock(handle->m_mutex);
|
||||
if (handle->m_status == Status::Pending) {
|
||||
queueInMainThread([handle, value = std::move(value)]() mutable {
|
||||
Event::createProgressed(handle, &value).post();
|
||||
});
|
||||
}
|
||||
}
|
||||
static void cancel(std::shared_ptr<Handle> handle, bool shallow = false) {
|
||||
if (!handle) return;
|
||||
std::unique_lock<std::recursive_mutex> lock(handle->m_mutex);
|
||||
if (handle->m_status == Status::Pending) {
|
||||
handle->m_status = Status::Cancelled;
|
||||
// If this task carries extra data, call the extra data's handling method
|
||||
// (unless shallow cancelling was specifically requested)
|
||||
if (!shallow && handle->m_extraData) {
|
||||
handle->m_extraData->cancel();
|
||||
}
|
||||
queueInMainThread([handle]() mutable {
|
||||
Event::createCancelled(handle).post();
|
||||
std::unique_lock<std::recursive_mutex> lock(handle->m_mutex);
|
||||
handle->m_finalEventPosted = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
template <std::move_constructible T2, std::move_constructible P2>
|
||||
friend class Task;
|
||||
|
||||
public:
|
||||
// Allow default-construction
|
||||
Task() : m_handle(nullptr) {}
|
||||
|
||||
Task(Task const& other) : m_handle(other.m_handle) {}
|
||||
Task(Task&& other) : m_handle(std::move(other.m_handle)) {}
|
||||
Task& operator=(Task const& other) {
|
||||
m_handle = other.m_handle;
|
||||
return *this;
|
||||
}
|
||||
Task& operator=(Task&& other) {
|
||||
m_handle = std::move(other.m_handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(Task const& other) const {
|
||||
return m_handle == other.m_handle;
|
||||
}
|
||||
bool operator!=(Task const& other) const {
|
||||
return m_handle != other.m_handle;
|
||||
}
|
||||
bool operator<(Task const& other) const {
|
||||
return m_handle < other.m_handle;
|
||||
}
|
||||
bool operator<=(Task const& other) const {
|
||||
return m_handle <= other.m_handle;
|
||||
}
|
||||
bool operator>(Task const& other) const {
|
||||
return m_handle > other.m_handle;
|
||||
}
|
||||
bool operator>=(Task const& other) const {
|
||||
return m_handle >= other.m_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value this Task finished to, if the Task had finished,
|
||||
* or null otherwise. Note that this is simply a mutable reference to
|
||||
* the value - *you may not move out of it!*
|
||||
*/
|
||||
T* getFinishedValue() {
|
||||
if (m_handle && m_handle->m_resultValue) {
|
||||
return &*m_handle->m_resultValue;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
/**
|
||||
* Cancel this Task. If this is a Task that owns other Task(s) (for example
|
||||
* one created through `Task::map`) then that Task is cancelled
|
||||
* as well. If this is undesirable, use `shallowCancel()`
|
||||
* instead
|
||||
*/
|
||||
void cancel() {
|
||||
Task::cancel(m_handle);
|
||||
}
|
||||
/**
|
||||
* If this is a Task that owns other Task(s) (for example created
|
||||
* through `Task::map` or `Task::all`), then this method cancels *only*
|
||||
* this Task and *not* any of the Task(s) it is built on top of.
|
||||
* Ownership of the other Task(s) will be released, so if this is the
|
||||
* only Task listening to them, they will still be destroyed due to a
|
||||
* lack of listeners
|
||||
*/
|
||||
void shallowCancel() {
|
||||
Task::cancel(m_handle, true);
|
||||
}
|
||||
bool isPending() const {
|
||||
return m_handle && m_handle->is(Status::Pending);
|
||||
}
|
||||
bool isFinished() const {
|
||||
return m_handle && m_handle->is(Status::Finished);
|
||||
}
|
||||
bool isCancelled() const {
|
||||
return m_handle && m_handle->is(Status::Cancelled);
|
||||
}
|
||||
/**
|
||||
* Check if this Task doesn't actually do anything (for instance it
|
||||
* was default-constructed)
|
||||
*/
|
||||
bool isNull() const {
|
||||
return m_handle == nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Task that immediately finishes with the given
|
||||
* value
|
||||
* @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 const& name = "<Immediate Task>") {
|
||||
auto task = Task(Handle::create(name));
|
||||
Task::finish(task.m_handle, std::move(value));
|
||||
return task;
|
||||
}
|
||||
/**
|
||||
* Create a new Task with a function that returns the finished value.
|
||||
* See the class description for details about Tasks
|
||||
* @param body The body aka actual code of the Task. Note that this
|
||||
* 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 const& name = "<Task>") {
|
||||
auto task = Task(Handle::create(name));
|
||||
std::thread([handle = std::weak_ptr(task.m_handle), name, body = std::move(body)] {
|
||||
utils::thread::setName(fmt::format("Task '{}'", name));
|
||||
auto result = body(
|
||||
[handle](P progress) {
|
||||
Task::progress(handle.lock(), std::move(progress));
|
||||
},
|
||||
[handle]() -> bool {
|
||||
// The task has been cancelled if the user has explicitly cancelled it,
|
||||
// or if there is no one listening anymore
|
||||
auto lock = handle.lock();
|
||||
return !(lock && lock->is(Status::Pending));
|
||||
}
|
||||
);
|
||||
if (result.isCancelled()) {
|
||||
Task::cancel(handle.lock());
|
||||
}
|
||||
else {
|
||||
Task::finish(handle.lock(), std::move(*std::move(result).getValue()));
|
||||
}
|
||||
}).detach();
|
||||
return task;
|
||||
}
|
||||
/**
|
||||
* Create a Task using a body that may need to create additional
|
||||
* threads within itself; for example due to using an external
|
||||
* library that creates its own thread
|
||||
* @param body The body aka actual code of the Task. The body may
|
||||
* call its provided finish callback *exactly once* - subsequent
|
||||
* calls will always be ignored
|
||||
* @param name The name of the Task; used for debugging
|
||||
*/
|
||||
static Task runWithCallback(RunWithCallback&& body, std::string const& name = "<Callback Task>") {
|
||||
auto task = Task(Handle::create(name));
|
||||
std::thread([handle = std::weak_ptr(task.m_handle), name, body = std::move(body)] {
|
||||
utils::thread::setName(fmt::format("Task '{}'", name));
|
||||
body(
|
||||
[handle](Result result) {
|
||||
if (result.isCancelled()) {
|
||||
Task::cancel(handle.lock());
|
||||
}
|
||||
else {
|
||||
Task::finish(handle.lock(), std::move(*std::move(result).getValue()));
|
||||
}
|
||||
},
|
||||
[handle](P progress) {
|
||||
Task::progress(handle.lock(), std::move(progress));
|
||||
},
|
||||
[handle]() -> bool {
|
||||
// The task has been cancelled if the user has explicitly cancelled it,
|
||||
// or if there is no one listening anymore
|
||||
auto lock = handle.lock();
|
||||
return !lock || lock->is(Status::Cancelled);
|
||||
}
|
||||
);
|
||||
}).detach();
|
||||
return task;
|
||||
}
|
||||
/**
|
||||
* Create a Task that waits for a list of other Tasks to finish, and then
|
||||
* finishes with a list of their finish values
|
||||
* @param tasks The tasks to wait for
|
||||
* @param name The name of the Task; used for debugging
|
||||
* @warning The result vector may contain nulls if any of the tasks
|
||||
* were cancelled!
|
||||
*/
|
||||
template <std::move_constructible NP>
|
||||
static Task<std::vector<T*>, std::monostate> all(std::vector<Task<T, NP>>&& tasks, std::string const& name = "<Multiple Tasks>") {
|
||||
using AllTask = Task<std::vector<T*>, std::monostate>;
|
||||
|
||||
// If there are no tasks, return an immediate task that does nothing
|
||||
if (tasks.empty()) {
|
||||
return AllTask::immediate({}, name);
|
||||
}
|
||||
|
||||
// Create a new supervising task for all of the provided tasks
|
||||
auto task = AllTask(AllTask::Handle::create(name));
|
||||
|
||||
// Storage for storing the results received so far & keeping
|
||||
// ownership of the running tasks
|
||||
struct Waiting final {
|
||||
std::vector<T*> taskResults;
|
||||
std::vector<Task<std::monostate>> taskListeners;
|
||||
size_t taskCount;
|
||||
};
|
||||
task.m_handle->m_extraData = std::make_unique<typename AllTask::Handle::ExtraData>(
|
||||
// Create the data
|
||||
static_cast<void*>(new Waiting()),
|
||||
// When the task is destroyed
|
||||
+[](void* ptr) {
|
||||
delete static_cast<Waiting*>(ptr);
|
||||
},
|
||||
// If the task is cancelled
|
||||
+[](void* ptr) {
|
||||
// The move clears the `taskListeners` vector (important!)
|
||||
for (auto task : std::move(static_cast<Waiting*>(ptr)->taskListeners)) {
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Store the task count in case some tasks finish immediately during the loop
|
||||
static_cast<Waiting*>(task.m_handle->m_extraData->ptr)->taskCount = tasks.size();
|
||||
|
||||
// Make sure to only give a weak pointer to avoid circular references!
|
||||
// (Tasks should NEVER own themselves!!)
|
||||
auto markAsDone = [handle = std::weak_ptr(task.m_handle)](T* result) {
|
||||
auto lock = handle.lock();
|
||||
|
||||
// If this task handle has expired, consider the task cancelled
|
||||
// (We don't have to do anything because the lack of a handle
|
||||
// means all the memory has been freed or is managed by
|
||||
// something else)
|
||||
if (!lock) return;
|
||||
|
||||
// Get the waiting handle from the task handle
|
||||
auto waiting = static_cast<Waiting*>(lock->m_extraData->ptr);
|
||||
|
||||
// SAFETY: The lifetime of result pointer is the same as the task that
|
||||
// produced that pointer, so as long as we have an owning reference to
|
||||
// the tasks through `taskListeners` we can be sure `result` is valid
|
||||
waiting->taskResults.push_back(result);
|
||||
|
||||
// If all tasks are done, finish
|
||||
if (waiting->taskResults.size() >= waiting->taskCount) {
|
||||
// SAFETY: The task results' lifetimes are tied to the tasks
|
||||
// which could have their only owner be `waiting->taskListeners`,
|
||||
// but since Waiting is owned by the returned AllTask it should
|
||||
// be safe to access as long as it's accessible
|
||||
AllTask::finish(lock, std::move(waiting->taskResults));
|
||||
}
|
||||
};
|
||||
|
||||
// Iterate the tasks & start listening to them using
|
||||
for (auto& taskToWait : tasks) {
|
||||
static_cast<Waiting*>(task.m_handle->m_extraData->ptr)->taskListeners.emplace_back(taskToWait.map(
|
||||
[markAsDone](auto* result) {
|
||||
markAsDone(result);
|
||||
return std::monostate();
|
||||
},
|
||||
[](auto*) { return std::monostate(); },
|
||||
[markAsDone]() { markAsDone(nullptr); }
|
||||
));
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Task that listens to this Task and maps the values using
|
||||
* the provided functions.
|
||||
* The new Task takes (shared) ownership of this Task, so the new Task
|
||||
* may very well be its only listener
|
||||
* @param resultMapper Function that converts the finished values of
|
||||
* the mapped Task to a desired type. Note that the function is only
|
||||
* given a pointer to the finish value, as `T` is not guaranteed to be
|
||||
* copiable - the mapper may NOT move out of the value!
|
||||
* @param progressMapper Function that converts the progress values of
|
||||
* the mapped Task to a desired type
|
||||
* @param onCancelled Function that is called if the mapped Task is
|
||||
* cancelled
|
||||
* @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, class OnCancelled>
|
||||
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, OnCancelled&& onCancelled, std::string const& name = "<Mapping Task>") const {
|
||||
using T2 = decltype(resultMapper(std::declval<T*>()));
|
||||
using P2 = decltype(progressMapper(std::declval<P*>()));
|
||||
|
||||
static_assert(std::is_move_constructible_v<T2>, "The type being mapped to must be move-constructible!");
|
||||
static_assert(std::is_move_constructible_v<P2>, "The type being mapped to must be move-constructible!");
|
||||
|
||||
Task<T2, P2> task = Task<T2, P2>::Handle::create(fmt::format("{} <= {}", name, m_handle->m_name));
|
||||
|
||||
// Lock the current task until we have managed to create our new one
|
||||
std::unique_lock<std::recursive_mutex> lock(m_handle->m_mutex);
|
||||
|
||||
// If the current task is cancelled, cancel the new one immediately
|
||||
if (m_handle->m_status == Status::Cancelled) {
|
||||
onCancelled();
|
||||
Task<T2, P2>::cancel(task.m_handle);
|
||||
}
|
||||
// If the current task is finished, immediately map the value and post that
|
||||
else if (m_handle->m_status == Status::Finished) {
|
||||
Task<T2, P2>::finish(task.m_handle, std::move(resultMapper(&*m_handle->m_resultValue)));
|
||||
}
|
||||
// Otherwise start listening and waiting for the current task to finish
|
||||
else {
|
||||
task.m_handle->m_extraData = std::make_unique<typename Task<T2, P2>::Handle::ExtraData>(
|
||||
static_cast<void*>(new EventListener<Task>(
|
||||
[
|
||||
handle = std::weak_ptr(task.m_handle),
|
||||
resultMapper = std::move(resultMapper),
|
||||
progressMapper = std::move(progressMapper),
|
||||
onCancelled = std::move(onCancelled)
|
||||
](Event* event) mutable {
|
||||
if (auto v = event->getValue()) {
|
||||
Task<T2, P2>::finish(handle.lock(), std::move(resultMapper(v)));
|
||||
}
|
||||
else if (auto p = event->getProgress()) {
|
||||
Task<T2, P2>::progress(handle.lock(), std::move(progressMapper(p)));
|
||||
}
|
||||
else if (event->isCancelled()) {
|
||||
onCancelled();
|
||||
Task<T2, P2>::cancel(handle.lock());
|
||||
}
|
||||
},
|
||||
*this
|
||||
)),
|
||||
+[](void* ptr) {
|
||||
delete static_cast<EventListener<Task>*>(ptr);
|
||||
},
|
||||
+[](void* ptr) {
|
||||
// Cancel the mapped task too
|
||||
static_cast<EventListener<Task>*>(ptr)->getFilter().cancel();
|
||||
}
|
||||
);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Task that listens to this Task and maps the values using
|
||||
* the provided functions.
|
||||
* The new Task takes (shared) ownership of this Task, so the new Task
|
||||
* may very well be its only listener
|
||||
* @param resultMapper Function that converts the finished values of
|
||||
* the mapped Task to a desired type. Note that the function is only
|
||||
* given a pointer to the finish value, as `T` is not guaranteed to be
|
||||
* copiable - the mapper may NOT move out of the value!
|
||||
* @param progressMapper Function that converts the progress values of
|
||||
* the mapped Task to a desired type
|
||||
* @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 const& name = "<Mapping Task>") const {
|
||||
return this->map(std::move(resultMapper), std::move(progressMapper), +[]() {}, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Task that listens to this Task and maps the finish value
|
||||
* using the provided function. Progress is mapped by copy-constructing
|
||||
* the value as-is.
|
||||
* The new Task takes (shared) ownership of this Task, so the new Task
|
||||
* may very well be its only listener
|
||||
* @param resultMapper Function that converts the finished values of
|
||||
* the mapped Task to a desired type. Note that the function is only
|
||||
* given a pointer to the finish value, as `T` is not guaranteed to be
|
||||
* copiable - the mapper may NOT move out of the value!
|
||||
* @param name The name of the Task; used for debugging. The name of
|
||||
* the mapped task is appended to the end
|
||||
*/ template <class ResultMapper>
|
||||
requires std::copy_constructible<P>
|
||||
auto map(ResultMapper&& resultMapper, std::string const& name = "<Mapping Task>") const {
|
||||
return this->map(std::move(resultMapper), +[](P* p) -> P { return *p; }, name);
|
||||
}
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, Event* e) {
|
||||
if (e->m_handle == m_handle && (!e->m_for || e->m_for == m_listener)) {
|
||||
fn(e);
|
||||
}
|
||||
return ListenerResult::Propagate;
|
||||
}
|
||||
|
||||
// todo: i believe alk wanted tasks to be in their own pool
|
||||
EventListenerPool* getPool() const {
|
||||
return DefaultEventListenerPool::get();
|
||||
}
|
||||
|
||||
void setListener(EventListenerProtocol* listener) {
|
||||
m_listener = listener;
|
||||
|
||||
if (!m_handle) return;
|
||||
|
||||
// If this task has already been finished and the finish event
|
||||
// isn't pending in the event queue, immediately queue up a
|
||||
// finish event for this listener
|
||||
std::unique_lock<std::recursive_mutex> lock(m_handle->m_mutex);
|
||||
if (m_handle->m_finalEventPosted) {
|
||||
if (m_handle->m_status == Status::Finished) {
|
||||
queueInMainThread([handle = m_handle, listener = m_listener, value = &*m_handle->m_resultValue]() {
|
||||
auto ev = Event::createFinished(handle, value);
|
||||
ev.m_for = listener;
|
||||
ev.post();
|
||||
});
|
||||
}
|
||||
else {
|
||||
queueInMainThread([handle = m_handle, listener = m_listener]() {
|
||||
auto ev = Event::createCancelled(handle);
|
||||
ev.m_for = listener;
|
||||
ev.post();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
EventListenerProtocol* getListener() const {
|
||||
return m_listener;
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(is_filter<Task<int>>, "The Task class must be a valid event filter!");
|
||||
}
|
|
@ -186,8 +186,11 @@ 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;
|
||||
|
||||
friend GEODE_DLL std::string format_as(VersionInfo const& version);
|
||||
};
|
||||
|
||||
|
@ -238,6 +241,13 @@ namespace geode {
|
|||
}
|
||||
}
|
||||
|
||||
constexpr VersionCompare getComparison() const {
|
||||
return m_compare;
|
||||
}
|
||||
constexpr VersionInfo getUnderlyingVersion() const {
|
||||
return m_version;
|
||||
}
|
||||
|
||||
std::string toString() const;
|
||||
friend GEODE_DLL std::string format_as(ComparableVersionInfo const& version);
|
||||
};
|
||||
|
|
|
@ -64,7 +64,7 @@ namespace geode::addresser {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
static ptrdiff_t thunkOf(T ptr) {
|
||||
static uint32_t thunkOf(T ptr) {
|
||||
// msvc
|
||||
if (sizeof(T) == sizeof(ptrdiff_t)) return 0;
|
||||
|
||||
|
|
|
@ -35,26 +35,22 @@ namespace cocos2d {
|
|||
pos.y *= mul;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static cocos2d::CCSize& operator*=(cocos2d::CCSize& size, float mul) {
|
||||
size.width *= mul;
|
||||
size.height *= mul;
|
||||
return size;
|
||||
}
|
||||
|
||||
static cocos2d::CCSize operator*(cocos2d::CCSize const& size, cocos2d::CCPoint const& point) {
|
||||
return {
|
||||
size.width * point.x,
|
||||
size.height * point.y,
|
||||
};
|
||||
}
|
||||
|
||||
static cocos2d::CCRect operator*=(cocos2d::CCRect& rect, float mul) {
|
||||
rect.origin *= mul;
|
||||
rect.size *= mul;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static cocos2d::CCRect operator*(cocos2d::CCRect const& rect, float mul) {
|
||||
return {
|
||||
rect.origin.x * mul,
|
||||
|
@ -63,147 +59,130 @@ namespace cocos2d {
|
|||
rect.size.height * mul,
|
||||
};
|
||||
}
|
||||
|
||||
static cocos2d::CCPoint operator/=(cocos2d::CCPoint& pos, float div) {
|
||||
pos.x /= div;
|
||||
pos.y /= div;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static cocos2d::CCSize operator/=(cocos2d::CCSize& size, float div) {
|
||||
size.width /= div;
|
||||
size.height /= div;
|
||||
return size;
|
||||
}
|
||||
|
||||
static cocos2d::CCRect operator/=(cocos2d::CCRect& rect, float div) {
|
||||
rect.origin /= div;
|
||||
rect.size /= div;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static cocos2d::CCPoint operator+=(cocos2d::CCPoint& pos, cocos2d::CCPoint const& add) {
|
||||
pos.x += add.x;
|
||||
pos.y += add.y;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static cocos2d::CCSize operator+=(cocos2d::CCSize& size, cocos2d::CCPoint const& add) {
|
||||
size.width += add.x;
|
||||
size.height += add.y;
|
||||
return size;
|
||||
}
|
||||
|
||||
static cocos2d::CCSize operator+=(cocos2d::CCSize& size, cocos2d::CCSize const& add) {
|
||||
size.width += add.width;
|
||||
size.height += add.height;
|
||||
return size;
|
||||
}
|
||||
|
||||
static cocos2d::CCRect operator+=(cocos2d::CCRect& rect, cocos2d::CCPoint const& add) {
|
||||
rect.origin += add;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static cocos2d::CCRect operator+=(cocos2d::CCRect& rect, cocos2d::CCSize const& add) {
|
||||
rect.size += add;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static cocos2d::CCRect operator+=(cocos2d::CCRect& rect, cocos2d::CCRect const& add) {
|
||||
rect.origin += add.origin;
|
||||
rect.size += add.size;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static cocos2d::CCPoint operator-=(cocos2d::CCPoint& pos, cocos2d::CCPoint const& add) {
|
||||
pos.x -= add.x;
|
||||
pos.y -= add.y;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static cocos2d::CCSize operator-=(cocos2d::CCSize& size, cocos2d::CCPoint const& add) {
|
||||
size.width -= add.x;
|
||||
size.height -= add.y;
|
||||
return size;
|
||||
}
|
||||
|
||||
static cocos2d::CCSize operator-=(cocos2d::CCSize& size, cocos2d::CCSize const& add) {
|
||||
size.width -= add.width;
|
||||
size.height -= add.height;
|
||||
return size;
|
||||
}
|
||||
|
||||
static cocos2d::CCRect operator-=(cocos2d::CCRect& rect, cocos2d::CCPoint const& add) {
|
||||
rect.origin -= add;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static cocos2d::CCRect operator-=(cocos2d::CCRect& rect, cocos2d::CCSize const& add) {
|
||||
rect.size -= add;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static cocos2d::CCRect operator-=(cocos2d::CCRect& rect, cocos2d::CCRect const& add) {
|
||||
rect.origin -= add.origin;
|
||||
rect.size -= add.size;
|
||||
return rect;
|
||||
}
|
||||
|
||||
static cocos2d::CCSize operator-(cocos2d::CCSize const& size, float f) {
|
||||
return {size.width - f, size.height - f};
|
||||
}
|
||||
|
||||
static cocos2d::CCSize operator-(cocos2d::CCSize const& size) {
|
||||
return {-size.width, -size.height};
|
||||
}
|
||||
|
||||
static bool operator==(cocos2d::CCPoint const& p1, cocos2d::CCPoint const& p2) {
|
||||
return p1.x == p2.x && p1.y == p2.y;
|
||||
}
|
||||
|
||||
static bool operator!=(cocos2d::CCPoint const& p1, cocos2d::CCPoint const& p2) {
|
||||
return p1.x != p2.x || p1.y != p2.y;
|
||||
}
|
||||
|
||||
static bool operator==(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width == s2.width && s1.height == s2.height;
|
||||
}
|
||||
|
||||
static bool operator!=(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width != s2.width || s1.height != s2.height;
|
||||
}
|
||||
|
||||
static bool operator<(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width < s2.width || s1.height < s2.height;
|
||||
}
|
||||
static bool operator<=(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width <= s2.width || s1.height <= s2.height;
|
||||
}
|
||||
static bool operator>(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width > s2.width || s1.height > s2.height;
|
||||
}
|
||||
static bool operator>=(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width >= s2.width || s1.height >= s2.height;
|
||||
}
|
||||
static bool operator==(cocos2d::CCRect const& r1, cocos2d::CCRect const& r2) {
|
||||
return r1.origin == r2.origin && r1.size == r2.size;
|
||||
}
|
||||
|
||||
static bool operator!=(cocos2d::CCRect const& r1, cocos2d::CCRect const& r2) {
|
||||
return r1.origin != r2.origin || r1.size != r2.size;
|
||||
}
|
||||
|
||||
static bool operator==(cocos2d::ccColor4B const& c1, cocos2d::ccColor4B const& c2) {
|
||||
return c1.r == c2.r && c1.g == c2.g && c1.b == c2.b && c1.a == c2.a;
|
||||
}
|
||||
|
||||
static bool operator!=(cocos2d::ccColor4B const& c1, cocos2d::ccColor4B const& c2) {
|
||||
return c1.r != c2.r || c1.g != c2.g || c1.b != c2.b || c1.a != c2.a;
|
||||
}
|
||||
|
||||
static bool operator==(cocos2d::ccColor3B const& c1, cocos2d::ccColor3B const& c2) {
|
||||
return c1.r == c2.r && c1.g == c2.g && c1.b == c2.b;
|
||||
}
|
||||
|
||||
static bool operator!=(cocos2d::ccColor3B const& c1, cocos2d::ccColor3B const& c2) {
|
||||
return c1.r != c2.r || c1.g != c2.g || c1.b != c2.b;
|
||||
}
|
||||
|
||||
static bool operator==(cocos2d::ccHSVValue const& c1, cocos2d::ccHSVValue const& c2) {
|
||||
return c1.h == c2.h && c1.s == c2.s && c1.v == c2.v &&
|
||||
c1.absoluteSaturation == c2.absoluteSaturation &&
|
||||
c1.absoluteBrightness == c2.absoluteBrightness;
|
||||
}
|
||||
|
||||
static bool operator!=(cocos2d::ccHSVValue const& c1, cocos2d::ccHSVValue const& c2) {
|
||||
return !(c1 == c2);
|
||||
}
|
||||
|
@ -572,6 +551,45 @@ namespace geode {
|
|||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A simple `CCObject` wrapper for a non-`CCObject` type
|
||||
*/
|
||||
template <class T>
|
||||
requires (!std::is_base_of_v<T, cocos2d::CCObject>)
|
||||
class ObjWrapper : public cocos2d::CCObject {
|
||||
protected:
|
||||
T m_value;
|
||||
|
||||
ObjWrapper(T&& value) : m_value(std::forward<T>(value)) {
|
||||
this->autorelease();
|
||||
}
|
||||
ObjWrapper(T const& value) : m_value(value) {
|
||||
this->autorelease();
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct an object wrapper
|
||||
*/
|
||||
static ObjWrapper* create(T&& value) {
|
||||
return new ObjWrapper(std::forward<T>(value));
|
||||
}
|
||||
/**
|
||||
* Construct an object wrapper
|
||||
*/
|
||||
static ObjWrapper* create(T const& value) {
|
||||
return new ObjWrapper(value);
|
||||
}
|
||||
|
||||
// @note This returns a const& to allow move-only types to be returned!
|
||||
T const& getValue() const& {
|
||||
return m_value;
|
||||
}
|
||||
void setValue(T&& value) {
|
||||
m_value = std::forward<T>(value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Cocos2d utils
|
||||
|
|
|
@ -3,19 +3,20 @@
|
|||
#include "Result.hpp"
|
||||
#include "general.hpp"
|
||||
#include "../loader/Event.hpp"
|
||||
#include "Task.hpp"
|
||||
|
||||
#include <matjson.hpp>
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <ghc/fs_fwd.hpp>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
template <>
|
||||
struct matjson::Serialize<ghc::filesystem::path> {
|
||||
static matjson::Value to_json(ghc::filesystem::path const& path) {
|
||||
struct matjson::Serialize<std::filesystem::path> {
|
||||
static matjson::Value to_json(std::filesystem::path const& path) {
|
||||
return path.string();
|
||||
}
|
||||
static ghc::filesystem::path from_json(matjson::Value const& value) {
|
||||
static std::filesystem::path from_json(matjson::Value const& value) {
|
||||
return value.as_string();
|
||||
}
|
||||
static bool is_json(matjson::Value const& value) {
|
||||
|
@ -24,12 +25,12 @@ struct matjson::Serialize<ghc::filesystem::path> {
|
|||
};
|
||||
|
||||
namespace geode::utils::file {
|
||||
GEODE_DLL Result<std::string> readString(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<matjson::Value> readJson(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<ByteVector> readBinary(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<std::string> readString(std::filesystem::path const& path);
|
||||
GEODE_DLL Result<matjson::Value> readJson(std::filesystem::path const& path);
|
||||
GEODE_DLL Result<ByteVector> readBinary(std::filesystem::path const& path);
|
||||
|
||||
template <class T>
|
||||
Result<T> readFromJson(ghc::filesystem::path const& file) {
|
||||
Result<T> readFromJson(std::filesystem::path const& file) {
|
||||
GEODE_UNWRAP_INTO(auto json, readJson(file));
|
||||
if (!json.template is<T>()) {
|
||||
return Err("JSON is not of type {}", typeid(T).name());
|
||||
|
@ -37,26 +38,26 @@ namespace geode::utils::file {
|
|||
return Ok(json.template as<T>());
|
||||
}
|
||||
|
||||
GEODE_DLL Result<> writeString(ghc::filesystem::path const& path, std::string const& data);
|
||||
GEODE_DLL Result<> writeBinary(ghc::filesystem::path const& path, ByteVector const& data);
|
||||
GEODE_DLL Result<> writeString(std::filesystem::path const& path, std::string const& data);
|
||||
GEODE_DLL Result<> writeBinary(std::filesystem::path const& path, ByteVector const& data);
|
||||
|
||||
template <class T>
|
||||
Result<> writeToJson(ghc::filesystem::path const& path, T const& data) {
|
||||
Result<> writeToJson(std::filesystem::path const& path, T const& data) {
|
||||
GEODE_UNWRAP(writeString(path, matjson::Value(data).dump()));
|
||||
return Ok();
|
||||
}
|
||||
|
||||
GEODE_DLL Result<> createDirectory(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<> createDirectoryAll(ghc::filesystem::path const& path);
|
||||
GEODE_DLL Result<std::vector<ghc::filesystem::path>> readDirectory(
|
||||
ghc::filesystem::path const& path, bool recursive = false
|
||||
GEODE_DLL Result<> createDirectory(std::filesystem::path const& path);
|
||||
GEODE_DLL Result<> createDirectoryAll(std::filesystem::path const& path);
|
||||
GEODE_DLL Result<std::vector<std::filesystem::path>> readDirectory(
|
||||
std::filesystem::path const& path, bool recursive = false
|
||||
);
|
||||
|
||||
class Unzip;
|
||||
|
||||
class GEODE_DLL Zip final {
|
||||
public:
|
||||
using Path = ghc::filesystem::path;
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
|
@ -142,7 +143,7 @@ namespace geode::utils::file {
|
|||
Unzip(Unzip&& other);
|
||||
~Unzip();
|
||||
|
||||
using Path = ghc::filesystem::path;
|
||||
using Path = std::filesystem::path;
|
||||
|
||||
/**
|
||||
* Create unzipper for file
|
||||
|
@ -223,7 +224,7 @@ namespace geode::utils::file {
|
|||
* Open a folder / file in the system's file explorer
|
||||
* @param path Folder / file to open
|
||||
*/
|
||||
GEODE_DLL bool openFolder(ghc::filesystem::path const& path);
|
||||
GEODE_DLL bool openFolder(std::filesystem::path const& path);
|
||||
|
||||
enum class PickMode {
|
||||
OpenFile,
|
||||
|
@ -244,7 +245,7 @@ namespace geode::utils::file {
|
|||
* to be a filename, unless it points to an extant directory.
|
||||
* On PickMode::OpenFolder, path is treated as leading up to a directory
|
||||
*/
|
||||
std::optional<ghc::filesystem::path> defaultPath;
|
||||
std::optional<std::filesystem::path> defaultPath;
|
||||
/**
|
||||
* File extension filters to show on the file picker
|
||||
*/
|
||||
|
@ -257,47 +258,49 @@ namespace geode::utils::file {
|
|||
* @param mode Type of file selection prompt to show
|
||||
* @param options Picker options
|
||||
*/
|
||||
[[deprecated("Use overload with callback instead, this will be removed in a later version.")]]
|
||||
GEODE_DLL Result<ghc::filesystem::path> pickFile(PickMode mode, FilePickOptions const& options);
|
||||
[[deprecated("Use pick() instead, this will be removed in a later version.")]]
|
||||
GEODE_DLL Result<std::filesystem::path> pickFile(PickMode mode, FilePickOptions const& options);
|
||||
|
||||
GEODE_DLL void pickFile(
|
||||
PickMode mode, FilePickOptions const& options,
|
||||
utils::MiniFunction<void(ghc::filesystem::path)> callback,
|
||||
utils::MiniFunction<void(std::filesystem::path)> callback,
|
||||
utils::MiniFunction<void()> failed = {}
|
||||
);
|
||||
GEODE_DLL Task<Result<std::filesystem::path>> pick(PickMode mode, FilePickOptions const& options);
|
||||
|
||||
/**
|
||||
* Prompt the user to pick a bunch of files for opening using the system's file system picker
|
||||
* @deprecated Will not work on Android, use the callback version instead
|
||||
* @param options Picker options
|
||||
*/
|
||||
[[deprecated("Use overload with callback instead, this will be removed in a later version.")]]
|
||||
GEODE_DLL Result<std::vector<ghc::filesystem::path>> pickFiles(FilePickOptions const& options);
|
||||
[[deprecated("Use pickMany() instead, this will be removed in a later version.")]]
|
||||
GEODE_DLL Result<std::vector<std::filesystem::path>> pickFiles(FilePickOptions const& options);
|
||||
|
||||
GEODE_DLL void pickFiles(
|
||||
FilePickOptions const& options,
|
||||
utils::MiniFunction<void(std::vector<ghc::filesystem::path>)> callback,
|
||||
utils::MiniFunction<void(std::vector<std::filesystem::path>)> callback,
|
||||
utils::MiniFunction<void()> failed = {}
|
||||
);
|
||||
GEODE_DLL Task<Result<std::vector<std::filesystem::path>>> pickMany(FilePickOptions const& options);
|
||||
|
||||
class GEODE_DLL FileWatchEvent : public Event {
|
||||
protected:
|
||||
ghc::filesystem::path m_path;
|
||||
std::filesystem::path m_path;
|
||||
|
||||
public:
|
||||
FileWatchEvent(ghc::filesystem::path const& path);
|
||||
ghc::filesystem::path getPath() const;
|
||||
FileWatchEvent(std::filesystem::path const& path);
|
||||
std::filesystem::path getPath() const;
|
||||
};
|
||||
|
||||
class GEODE_DLL FileWatchFilter : public EventFilter<FileWatchEvent> {
|
||||
protected:
|
||||
ghc::filesystem::path m_path;
|
||||
std::filesystem::path m_path;
|
||||
|
||||
public:
|
||||
using Callback = void(FileWatchEvent*);
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> callback, FileWatchEvent* event);
|
||||
FileWatchFilter(ghc::filesystem::path const& path);
|
||||
FileWatchFilter(std::filesystem::path const& path);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -309,10 +312,10 @@ namespace geode::utils::file {
|
|||
* so different paths that point to the same file will be considered the
|
||||
* same
|
||||
*/
|
||||
GEODE_DLL Result<> watchFile(ghc::filesystem::path const& file);
|
||||
GEODE_DLL Result<> watchFile(std::filesystem::path const& file);
|
||||
/**
|
||||
* Stop watching a file for changes
|
||||
* @param file The file to unwatch
|
||||
*/
|
||||
GEODE_DLL void unwatchFile(ghc::filesystem::path const& file);
|
||||
GEODE_DLL void unwatchFile(std::filesystem::path const& file);
|
||||
}
|
||||
|
|
|
@ -8,20 +8,11 @@
|
|||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ghc/fs_fwd.hpp>
|
||||
#include <filesystem>
|
||||
#include <matjson.hpp>
|
||||
#include <charconv>
|
||||
#include <clocale>
|
||||
|
||||
// for some reason std::filesystem::path doesn't have std::hash defined in C++17
|
||||
// and ghc seems to have inherited this limitation
|
||||
template<>
|
||||
struct std::hash<ghc::filesystem::path> {
|
||||
std::size_t operator()(ghc::filesystem::path const& path) const noexcept {
|
||||
return ghc::filesystem::hash_value(path);
|
||||
}
|
||||
};
|
||||
|
||||
namespace geode {
|
||||
using ByteVector = std::vector<uint8_t>;
|
||||
|
||||
|
@ -55,15 +46,19 @@ namespace geode {
|
|||
constexpr unsigned int hash(char const* str, int h = 0) {
|
||||
return !str[h] ? 5381 : (hash(str, h + 1) * 33) ^ str[h];
|
||||
}
|
||||
|
||||
constexpr unsigned int hash(std::string_view str, int h = 0) {
|
||||
return h >= str.size() ? 5381 : (hash(str, h + 1) * 33) ^ str[h];
|
||||
}
|
||||
constexpr unsigned int hash(wchar_t const* str, int h = 0) {
|
||||
return !str[h] ? 5381 : (hash(str, h + 1) * 33) ^ str[h];
|
||||
}
|
||||
constexpr unsigned int hash(std::wstring_view str, int h = 0) {
|
||||
return h >= str.size() ? 5381 : (hash(str, h + 1) * 33) ^ str[h];
|
||||
}
|
||||
|
||||
constexpr size_t operator"" _h(char const* txt, size_t) {
|
||||
return geode::utils::hash(txt);
|
||||
}
|
||||
|
||||
constexpr size_t operator"" _h(wchar_t const* txt, size_t) {
|
||||
return geode::utils::hash(txt);
|
||||
}
|
||||
|
@ -97,6 +92,17 @@ namespace geode {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a number into an abbreviated string, like `1253` to `1.25K`
|
||||
*/
|
||||
template <std::integral Num>
|
||||
std::string numToAbbreviatedString(Num num) {
|
||||
if (num >= 1'000'000'000) return fmt::format("{:0.3}B", num / 1'000'000'000.f);
|
||||
if (num >= 1'000'000) return fmt::format("{:0.3}M", num / 1'000'000.f);
|
||||
if (num >= 1'000) return fmt::format("{:0.3}K", num / 1'000.f);
|
||||
return numToString(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a number from a string
|
||||
* @param str The string to parse
|
||||
|
|
63
loader/include/Geode/utils/terminate.hpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "../DefaultInclude.hpp"
|
||||
|
||||
namespace geode {
|
||||
class Mod;
|
||||
Mod* getMod();
|
||||
}
|
||||
|
||||
namespace geode::utils {
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
static constexpr size_t GEODE_TERMINATE_EXCEPTION_CODE = 0x4000;
|
||||
static constexpr size_t GEODE_UNREACHABLE_EXCEPTION_CODE = 0x4001;
|
||||
|
||||
static constexpr bool isGeodeExceptionCode(size_t code) {
|
||||
return GEODE_TERMINATE_EXCEPTION_CODE <= code && code <= GEODE_UNREACHABLE_EXCEPTION_CODE;
|
||||
}
|
||||
#else
|
||||
static constexpr size_t GEODE_TERMINATE_EXCEPTION_CODE = 0;
|
||||
static constexpr size_t GEODE_UNREACHABLE_EXCEPTION_CODE = 0;
|
||||
|
||||
static constexpr bool isGeodeExceptionCode(size_t code) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace detail {
|
||||
// This needs to do stuff with `Mod*` which is not included in the file
|
||||
GEODE_DLL void logTerminationError(const char* reason, Mod* mod);
|
||||
}
|
||||
|
||||
template <class = void>
|
||||
[[noreturn]]
|
||||
void terminate(std::string const& reason, Mod* mod = getMod(), size_t platformCode = GEODE_TERMINATE_EXCEPTION_CODE) {
|
||||
// Add the error to the logfile
|
||||
detail::logTerminationError(reason.c_str(), mod);
|
||||
|
||||
#ifdef GEODE_IS_WINDOWS
|
||||
// If a debugger is attached, start debugging
|
||||
if (IsDebuggerPresent()) {
|
||||
OutputDebugStringA(reason.c_str());
|
||||
DebugBreak();
|
||||
}
|
||||
// Otherwise terminate by raising an exception (which is caught by the crashlog handler)
|
||||
else {
|
||||
std::array<const void*, 2> errorList { static_cast<const void*>(reason.c_str()), mod };
|
||||
RaiseException(
|
||||
platformCode,
|
||||
EXCEPTION_NONCONTINUABLE,
|
||||
2, reinterpret_cast<ULONG_PTR*>(errorList.data())
|
||||
);
|
||||
}
|
||||
#else
|
||||
std::terminate();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <class = void>
|
||||
[[noreturn]]
|
||||
void unreachable(std::string const& reason = "Unspecified", Mod* mod = getMod()) {
|
||||
terminate(reason, mod, GEODE_UNREACHABLE_EXCEPTION_CODE);
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
#include "Result.hpp"
|
||||
#include "general.hpp"
|
||||
|
||||
#include <ghc/fs_fwd.hpp>
|
||||
#include <filesystem>
|
||||
#include <chrono>
|
||||
|
||||
namespace geode::utils::web {
|
||||
|
@ -19,6 +19,7 @@ namespace geode::utils::web {
|
|||
* @param url URL to fetch
|
||||
* @returns Returned data as bytes, or error on error
|
||||
*/
|
||||
[[deprecated("Use the WebRequest class from the web2.hpp header instead")]]
|
||||
GEODE_DLL Result<ByteVector> fetchBytes(std::string const& url);
|
||||
|
||||
/**
|
||||
|
@ -26,6 +27,7 @@ namespace geode::utils::web {
|
|||
* @param url URL to fetch
|
||||
* @returns Returned data as string, or error on error
|
||||
*/
|
||||
[[deprecated("Use the WebRequest class from the web2.hpp header instead")]]
|
||||
GEODE_DLL Result<std::string> fetch(std::string const& url);
|
||||
|
||||
/**
|
||||
|
@ -38,8 +40,9 @@ namespace geode::utils::web {
|
|||
* automatically remove the file that was being downloaded
|
||||
* @returns Returned data as JSON, or error on error
|
||||
*/
|
||||
[[deprecated("Use the WebRequest class from the web2.hpp header instead")]]
|
||||
GEODE_DLL Result<> fetchFile(
|
||||
std::string const& url, ghc::filesystem::path const& into, FileProgressCallback prog = nullptr
|
||||
std::string const& url, std::filesystem::path const& into, FileProgressCallback prog = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -47,6 +50,7 @@ namespace geode::utils::web {
|
|||
* @param url URL to fetch
|
||||
* @returns Returned data as JSON, or error on error
|
||||
*/
|
||||
[[deprecated("Use the WebRequest class from the web2.hpp header instead")]]
|
||||
GEODE_DLL Result<matjson::Value> fetchJSON(std::string const& url);
|
||||
|
||||
class SentAsyncWebRequest;
|
||||
|
@ -65,7 +69,7 @@ namespace geode::utils::web {
|
|||
* A handle to an in-progress sent asynchronous web request. Use this to
|
||||
* cancel the request / query information about it
|
||||
*/
|
||||
class GEODE_DLL SentAsyncWebRequest {
|
||||
class GEODE_DLL [[deprecated("Use the WebRequest class from the web2.hpp header instead")]] SentAsyncWebRequest {
|
||||
private:
|
||||
class Impl;
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
@ -114,7 +118,7 @@ namespace geode::utils::web {
|
|||
* internet without slowing the main thread. All callbacks are run in the
|
||||
* GD thread, so interacting with the Cocos2d UI is perfectly safe
|
||||
*/
|
||||
class GEODE_DLL AsyncWebRequest {
|
||||
class GEODE_DLL [[deprecated("Use the WebRequest class from the web2.hpp header instead")]] AsyncWebRequest {
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
@ -262,7 +266,7 @@ namespace geode::utils::web {
|
|||
};
|
||||
|
||||
template <class T>
|
||||
class AsyncWebResult {
|
||||
class [[deprecated("Use the WebRequest class from the web2.hpp header instead")]] AsyncWebResult {
|
||||
private:
|
||||
AsyncWebRequest& m_request;
|
||||
DataConverter<T> m_converter;
|
||||
|
@ -291,7 +295,7 @@ namespace geode::utils::web {
|
|||
AsyncWebRequest& then(utils::MiniFunction<void(SentAsyncWebRequest&, T)> handle);
|
||||
};
|
||||
|
||||
class GEODE_DLL AsyncWebResponse {
|
||||
class GEODE_DLL [[deprecated("Use the WebRequest class from the web2.hpp header instead")]] AsyncWebResponse {
|
||||
private:
|
||||
AsyncWebRequest& m_request;
|
||||
|
||||
|
@ -303,7 +307,7 @@ namespace geode::utils::web {
|
|||
/**
|
||||
* Download into a stream. Make sure the stream lives for the entire
|
||||
* duration of the request. If you want to download a file, use the
|
||||
* `ghc::filesystem::path` overload of `into` instead
|
||||
* `std::filesystem::path` overload of `into` instead
|
||||
* @param stream Stream to download into. Make sure it lives long
|
||||
* enough, otherwise the web request will crash
|
||||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
|
@ -321,7 +325,7 @@ namespace geode::utils::web {
|
|||
* template parameter, as it can be assumed you know what you passed
|
||||
* into `into`
|
||||
*/
|
||||
AsyncWebResult<std::monostate> into(ghc::filesystem::path const& path);
|
||||
AsyncWebResult<std::monostate> into(std::filesystem::path const& path);
|
||||
/**
|
||||
* Download into memory as a string
|
||||
* @returns AsyncWebResult, where you can specify the `then` action for
|
||||
|
|
125
loader/include/Geode/utils/web2.hpp
Normal file
|
@ -0,0 +1,125 @@
|
|||
#pragma once
|
||||
|
||||
#include <matjson.hpp>
|
||||
#include "Result.hpp"
|
||||
#include "Task.hpp"
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
namespace geode::utils::web {
|
||||
// https://curl.se/libcurl/c/CURLOPT_HTTPAUTH.html
|
||||
namespace http_auth {
|
||||
constexpr static long BASIC = 0x0001;
|
||||
constexpr static long DIGEST = 0x0002;
|
||||
constexpr static long DIGEST_IE = 0x0004;
|
||||
constexpr static long BEARER = 0x0008;
|
||||
constexpr static long NEGOTIATE = 0x0010;
|
||||
constexpr static long NTLM = 0x0020;
|
||||
constexpr static long NTLM_WB = 0x0040;
|
||||
constexpr static long ANY = 0x0080;
|
||||
constexpr static long ANYSAFE = 0x0100;
|
||||
constexpr static long ONLY = 0x0200;
|
||||
constexpr static long AWS_SIGV4 = 0x0400;
|
||||
}
|
||||
|
||||
// https://curl.se/libcurl/c/CURLOPT_PROXYTYPE.html
|
||||
enum class ProxyType {
|
||||
HTTP, // HTTP
|
||||
HTTPS, // HTTPS
|
||||
HTTPS2, // HTTPS (attempt to use HTTP/2)
|
||||
SOCKS4, // Socks4
|
||||
SOCKS4A, // Socks4 with hostname resolution
|
||||
SOCKS5, // Socks5
|
||||
SOCKS5H, // Socks5 with hostname resolution
|
||||
};
|
||||
|
||||
struct ProxyOpts {
|
||||
std::string address; // Proxy address/FQDN
|
||||
std::optional<std::uint16_t> port; // Proxy port
|
||||
ProxyType type = ProxyType::HTTP; // Proxy type
|
||||
long auth = http_auth::BASIC; // HTTP proxy auth method
|
||||
std::string username; // Proxy username
|
||||
std::string password; // Proxy password
|
||||
bool tunneling = false; // Enable HTTP tunneling
|
||||
bool certVerification = true; // Enable HTTPS certificate verification
|
||||
};
|
||||
|
||||
class WebRequest;
|
||||
|
||||
class GEODE_DLL WebResponse final {
|
||||
private:
|
||||
class Impl;
|
||||
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
||||
friend class WebRequest;
|
||||
|
||||
public:
|
||||
// Must be default-constructible for use in Promise
|
||||
WebResponse();
|
||||
|
||||
bool ok() const;
|
||||
int code() const;
|
||||
|
||||
Result<std::string> string() const;
|
||||
Result<matjson::Value> json() const;
|
||||
ByteVector data() const;
|
||||
|
||||
std::vector<std::string> headers() const;
|
||||
std::optional<std::string> header(std::string_view name) const;
|
||||
};
|
||||
|
||||
class GEODE_DLL WebProgress final {
|
||||
private:
|
||||
class Impl;
|
||||
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
||||
friend class WebRequest;
|
||||
|
||||
public:
|
||||
// Must be default-constructible for use in Promise
|
||||
WebProgress();
|
||||
|
||||
size_t downloaded() const;
|
||||
size_t downloadTotal() const;
|
||||
std::optional<float> downloadProgress() const;
|
||||
|
||||
size_t uploaded() const;
|
||||
size_t uploadTotal() const;
|
||||
std::optional<float> uploadProgress() const;
|
||||
};
|
||||
|
||||
using WebTask = Task<WebResponse, WebProgress>;
|
||||
|
||||
class GEODE_DLL WebRequest final {
|
||||
private:
|
||||
class Impl;
|
||||
|
||||
std::shared_ptr<Impl> m_impl;
|
||||
|
||||
public:
|
||||
WebRequest();
|
||||
~WebRequest();
|
||||
|
||||
WebTask send(std::string_view method, std::string_view url);
|
||||
WebTask post(std::string_view url);
|
||||
WebTask get(std::string_view url);
|
||||
WebTask put(std::string_view url);
|
||||
WebTask patch(std::string_view url);
|
||||
|
||||
WebRequest& header(std::string_view name, std::string_view value);
|
||||
WebRequest& param(std::string_view name, std::string_view value);
|
||||
WebRequest& userAgent(std::string_view name);
|
||||
|
||||
WebRequest& timeout(std::chrono::seconds time);
|
||||
|
||||
WebRequest& certVerification(bool enabled);
|
||||
WebRequest& CABundleContent(std::string_view content);
|
||||
WebRequest& proxyOpts(ProxyOpts const& proxyOpts);
|
||||
|
||||
WebRequest& body(ByteVector raw);
|
||||
WebRequest& bodyString(std::string_view str);
|
||||
WebRequest& bodyJSON(matjson::Value const& json);
|
||||
};
|
||||
}
|
BIN
loader/include/link/android32/libnghttp3.a
Normal file
BIN
loader/include/link/android32/libngtcp2.a
Normal file
BIN
loader/include/link/android32/libngtcp2_crypto_boringssl.a
Normal file
BIN
loader/include/link/android64/libnghttp3.a
Normal file
BIN
loader/include/link/android64/libngtcp2.a
Normal file
BIN
loader/include/link/android64/libngtcp2_crypto_boringssl.a
Normal file
BIN
loader/include/link/macos/libcrypto.a
Normal file
BIN
loader/include/link/macos/libcurl.a
Normal file
BIN
loader/include/link/macos/libnghttp2.a
Normal file
BIN
loader/include/link/macos/libnghttp3.a
Normal file
BIN
loader/include/link/macos/libngtcp2.a
Normal file
BIN
loader/include/link/macos/libngtcp2_crypto_boringssl.a
Normal file
BIN
loader/include/link/macos/libssl.a
Normal file
BIN
loader/include/link/win64/crypto.lib
Normal file
BIN
loader/include/link/win64/libcurl.lib
Normal file
BIN
loader/include/link/win64/nghttp2.lib
Normal file
BIN
loader/include/link/win64/nghttp3.lib
Normal file
BIN
loader/include/link/win64/ngtcp2.lib
Normal file
BIN
loader/include/link/win64/ngtcp2_crypto_boringssl.lib
Normal file
BIN
loader/include/link/win64/ssl.lib
Normal file
|
@ -1,9 +1,9 @@
|
|||
cmake_minimum_required(VERSION 3.21)
|
||||
|
||||
add_library(ProxyLoader SHARED proxyLoader.c)
|
||||
add_library(ProxyLoader SHARED proxyLoader.cpp)
|
||||
set_target_properties(ProxyLoader PROPERTIES
|
||||
PREFIX ""
|
||||
OUTPUT_NAME "XInput9_1_0"
|
||||
OUTPUT_NAME "XInput1_4"
|
||||
LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${GEODE_BIN_PATH}/nightly"
|
||||
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${GEODE_BIN_PATH}/nightly"
|
||||
ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${GEODE_BIN_PATH}/nightly"
|
||||
|
@ -39,7 +39,6 @@ set_target_properties(Updater PROPERTIES
|
|||
RUNTIME_OUTPUT_DIRECTORY "${GEODE_BIN_PATH}/nightly"
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${GEODE_BIN_PATH}/nightly"
|
||||
)
|
||||
target_link_libraries(Updater PUBLIC ghc_filesystem)
|
||||
|
||||
if (MSVC)
|
||||
include(CheckLinkerFlag)
|
||||
|
|
|
@ -105,7 +105,7 @@ int main(int argc, char* argv[]) {
|
|||
|
||||
if (std::filesystem::exists(geodeDir) && std::filesystem::exists(updatesDir)) {
|
||||
bool updateSuccess = true;
|
||||
updateSuccess &= updateFile("XInput9_1_0.dll");
|
||||
updateSuccess &= updateFile("XInput1_4.dll");
|
||||
updateSuccess &= updateFile("Geode.dll");
|
||||
updateSuccess &= updateFile("Geode.pdb");
|
||||
updateResources();
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
__declspec(dllexport) void fake() { }
|
|
@ -1,80 +0,0 @@
|
|||
#include <Windows.h>
|
||||
#include <Xinput.h>
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifndef MAX_PATH
|
||||
#define MAX_PATH 260
|
||||
#endif
|
||||
|
||||
HMODULE xinput = NULL;
|
||||
DWORD(WINAPI* getState)(DWORD, XINPUT_STATE*) = NULL;
|
||||
DWORD(WINAPI* setState)(DWORD, XINPUT_VIBRATION*) = NULL;
|
||||
DWORD(WINAPI* getCapabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*) = NULL;
|
||||
DWORD(WINAPI* getDSoundAudioDeviceGuids)(DWORD, GUID*, GUID*) = NULL;
|
||||
void lazyLoadXInput() {
|
||||
if (xinput)
|
||||
return;
|
||||
char path[MAX_PATH];
|
||||
GetSystemDirectoryA(path, MAX_PATH);
|
||||
strcat_s(path, MAX_PATH, "\\XInput9_1_0.dll");
|
||||
xinput = LoadLibraryA(path);
|
||||
getState = (DWORD(WINAPI*)(DWORD, XINPUT_STATE*))GetProcAddress(xinput, "XInputGetState");
|
||||
setState = (DWORD(WINAPI*)(DWORD, XINPUT_VIBRATION*))GetProcAddress(xinput, "XInputSetState");
|
||||
getCapabilities = (DWORD(WINAPI*)(DWORD, DWORD, XINPUT_CAPABILITIES*))GetProcAddress(xinput, "XInputGetCapabilities");
|
||||
getDSoundAudioDeviceGuids = (DWORD(WINAPI*)(DWORD, GUID*, GUID*))GetProcAddress(xinput, "XInputGetDSoundAudioDeviceGuids");
|
||||
}
|
||||
|
||||
DWORD WINAPI xinputGetState(DWORD dwUserIndex, XINPUT_STATE* pState) {
|
||||
lazyLoadXInput();
|
||||
return getState(dwUserIndex, pState);
|
||||
}
|
||||
DWORD WINAPI xinputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration) {
|
||||
lazyLoadXInput();
|
||||
return setState(dwUserIndex, pVibration);
|
||||
}
|
||||
DWORD WINAPI xinputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities) {
|
||||
lazyLoadXInput();
|
||||
return getCapabilities(dwUserIndex, dwFlags, pCapabilities);
|
||||
}
|
||||
DWORD WINAPI xinputGetDSoundAudioDeviceGuids(DWORD dwUserIndex, GUID* pDSoundRenderGuid, GUID* pDSoundCaptureGuid) {
|
||||
lazyLoadXInput();
|
||||
return getDSoundAudioDeviceGuids(dwUserIndex, pDSoundRenderGuid, pDSoundCaptureGuid);
|
||||
}
|
||||
|
||||
// https://github.com/mrexodia/perfect-dll-proxy
|
||||
#if defined(_WIN64)
|
||||
#define PROXY_PATH(export) \
|
||||
"/export:" #export "=\\\\.\\GLOBALROOT\\SystemRoot\\System32\\XInput9_1_0.dll." #export
|
||||
#else
|
||||
#define PROXY_PATH(export) \
|
||||
"/export:" #export "=\\\\.\\GLOBALROOT\\SystemRoot\\SysWOW64\\XInput9_1_0.dll." #export
|
||||
#endif
|
||||
|
||||
#pragma comment(linker, PROXY_PATH(XInputGetState))
|
||||
#pragma comment(linker, PROXY_PATH(XInputSetState))
|
||||
#pragma comment(linker, PROXY_PATH(XInputGetCapabilities))
|
||||
#pragma comment(linker, PROXY_PATH(XInputGetDSoundAudioDeviceGuids))
|
||||
|
||||
BOOL fileExists(char const* path) {
|
||||
DWORD attrib = GetFileAttributesA(path);
|
||||
return (attrib != INVALID_FILE_ATTRIBUTES && !(attrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID _) {
|
||||
if (reason != DLL_PROCESS_ATTACH)
|
||||
return TRUE;
|
||||
DisableThreadLibraryCalls(module);
|
||||
|
||||
if (fileExists("Geode.dll")) {
|
||||
// somehow, this works fine inside of dllmain :-)
|
||||
// yes, even on wine.
|
||||
/* * * * * * * * * * * * * * * * * *\
|
||||
* The Shadows Shall Smite You 01 *
|
||||
* - ConfiG *
|
||||
\* * * * * * * * * * * * * * * * * */
|
||||
LoadLibraryA("Geode.dll");
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
70
loader/launcher/windows/proxyLoader.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include <Windows.h>
|
||||
#include <string>
|
||||
|
||||
#define XINPUT_PATH "\\\\.\\GLOBALROOT\\SystemRoot\\System32\\XInput1_4.dll"
|
||||
|
||||
// https://github.com/mrexodia/perfect-dll-proxy
|
||||
#define PROXY_EXPORT(sym) \
|
||||
"/export:" #sym "=" XINPUT_PATH "." #sym
|
||||
|
||||
#pragma comment(linker, PROXY_EXPORT(XInputSetState))
|
||||
#pragma comment(linker, PROXY_EXPORT(XInputGetCapabilities))
|
||||
#pragma comment(linker, PROXY_EXPORT(XInputEnable))
|
||||
#pragma comment(linker, PROXY_EXPORT(XInputGetBatteryInformation))
|
||||
#pragma comment(linker, PROXY_EXPORT(XInputGetKeystroke))
|
||||
#pragma comment(linker, PROXY_EXPORT(XInputGetAudioDeviceIds))
|
||||
|
||||
struct XINPUT_STATE;
|
||||
struct XINPUT_CAPABILITIES;
|
||||
|
||||
// libcocos2d.dll requires for this function to have ordinal 2.
|
||||
#pragma comment(linker, "/export:XInputGetState,@2")
|
||||
extern "C" DWORD XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState) {
|
||||
auto xinput = GetModuleHandleA(XINPUT_PATH);
|
||||
if (!xinput)
|
||||
xinput = LoadLibraryA(XINPUT_PATH);
|
||||
|
||||
if (xinput) {
|
||||
auto fp = GetProcAddress(xinput, "XInputGetState");
|
||||
if (fp) {
|
||||
using FPType = decltype(&XInputGetState);
|
||||
return reinterpret_cast<FPType>(fp)(dwUserIndex, pState);
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
// Hi nikki please leave this as is, it fixes wine :)
|
||||
#pragma comment(linker, "/export:XInputGetCapabilities,@4")
|
||||
extern "C" DWORD XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities) {
|
||||
auto xinput = GetModuleHandleA(XINPUT_PATH);
|
||||
if (!xinput)
|
||||
xinput = LoadLibraryA(XINPUT_PATH);
|
||||
|
||||
if (xinput) {
|
||||
auto fp = GetProcAddress(xinput, "XInputGetCapabilities");
|
||||
if (fp) {
|
||||
using FPType = decltype(&XInputGetCapabilities);
|
||||
return reinterpret_cast<FPType>(fp)(dwUserIndex, dwFlags, pCapabilities);
|
||||
}
|
||||
}
|
||||
|
||||
return ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
static std::wstring getErrorString() {
|
||||
return L"Could not load Geode. Error code: " + std::to_wstring(GetLastError());
|
||||
}
|
||||
|
||||
__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID _) {
|
||||
if (reason == DLL_PROCESS_ATTACH) {
|
||||
DisableThreadLibraryCalls(module);
|
||||
|
||||
// This is UB.
|
||||
if (LoadLibraryW(L"Geode.dll") == NULL)
|
||||
MessageBoxW(NULL, getErrorString().c_str(), L"Load failed" , MB_OK | MB_ICONWARNING);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
BIN
loader/resources/blanks/baseCircle_BigAlt_DarkAqua.png
Normal file
After ![]() (image error) Size: 20 KiB |
BIN
loader/resources/blanks/baseCircle_BigAlt_DarkPurple.png
Normal file
After ![]() (image error) Size: 19 KiB |
BIN
loader/resources/blanks/baseCircle_Big_DarkPurple.png
Normal file
After ![]() (image error) Size: 16 KiB |
Before ![]() (image error) Size: 12 KiB After ![]() (image error) Size: 23 KiB ![]() ![]() |
BIN
loader/resources/blanks/baseCircle_Large_DarkAqua.png
Normal file
After ![]() (image error) Size: 27 KiB |
BIN
loader/resources/blanks/baseCircle_Large_DarkPurple.png
Normal file
After ![]() (image error) Size: 26 KiB |
BIN
loader/resources/blanks/baseCircle_MediumAlt_DarkAqua.png
Normal file
After ![]() (image error) Size: 16 KiB |
BIN
loader/resources/blanks/baseCircle_MediumAlt_DarkPurple.png
Normal file
After ![]() (image error) Size: 15 KiB |
BIN
loader/resources/blanks/baseCircle_Medium_DarkAqua.png
Normal file
After ![]() (image error) Size: 15 KiB |
BIN
loader/resources/blanks/baseCircle_Medium_DarkPurple.png
Normal file
After ![]() (image error) Size: 14 KiB |
BIN
loader/resources/blanks/baseCircle_SmallAlt_DarkAqua.png
Normal file
After ![]() (image error) Size: 13 KiB |
BIN
loader/resources/blanks/baseCircle_SmallAlt_DarkPurple.png
Normal file
After ![]() (image error) Size: 12 KiB |
BIN
loader/resources/blanks/baseCircle_Small_DarkAqua.png
Normal file
After ![]() (image error) Size: 12 KiB |
BIN
loader/resources/blanks/baseCircle_Small_DarkPurple.png
Normal file
After ![]() (image error) Size: 12 KiB |
BIN
loader/resources/blanks/baseCircle_Tiny_DarkAqua.png
Normal file
After ![]() (image error) Size: 8.1 KiB |
BIN
loader/resources/blanks/baseCircle_Tiny_DarkPurple.png
Normal file
After ![]() (image error) Size: 7.8 KiB |
BIN
loader/resources/close.png
Normal file
After ![]() (image error) Size: 4.9 KiB |
BIN
loader/resources/delete-white.png
Normal file
After ![]() (image error) Size: 1 KiB |