Merge pull request from geode-sdk/new-index-but-better

merge new index
closes 
This commit is contained in:
mat 2024-06-03 12:15:35 -03:00 committed by GitHub
commit cdbed58178
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
285 changed files with 16586 additions and 6755 deletions
.github/workflows
CMakeLists.txtFilesystemImpl.cpp
cmake
installer/windows
loader
CMakeLists.txt
hash
include
launcher/windows
resources

View file

@ -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

View file

@ -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)

View file

@ -1,4 +0,0 @@
// filesystem implementation
#undef GHC_FILESYSTEM_H
#include <ghc/fs_impl.hpp>

View file

@ -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

View file

@ -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

View file

@ -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

View 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);
}

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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(

View file

@ -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();
};

View file

@ -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.

View file

@ -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();
}

View file

@ -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));
// }
}

View file

@ -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);
};
}

View file

@ -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
*

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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]);
}
}
}

View file

@ -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);

View file

@ -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());
}
};
}

View file

@ -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);
}

View file

@ -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;
};
}

View file

@ -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(

View file

@ -36,5 +36,6 @@ namespace geode {
void scrollWheel(float y, float) override;
void enableScrollWheel(bool enable = true);
void scrollToTop();
};
}

View file

@ -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

View 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));
}

View file

@ -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 {

View file

@ -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));
}

View 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!");
}

View file

@ -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);
};

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View 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

View 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);
}
}

View file

@ -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

View 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);
};
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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)

View file

@ -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();

View file

@ -1 +0,0 @@
__declspec(dllexport) void fake() { }

View file

@ -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;
}

View 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;
}

Binary file not shown.

After

(image error) Size: 20 KiB

Binary file not shown.

After

(image error) Size: 19 KiB

Binary file not shown.

After

(image error) Size: 16 KiB

Binary file not shown.

Before

(image error) Size: 12 KiB

After

(image error) Size: 23 KiB

Binary file not shown.

After

(image error) Size: 27 KiB

Binary file not shown.

After

(image error) Size: 26 KiB

Binary file not shown.

After

(image error) Size: 16 KiB

Binary file not shown.

After

(image error) Size: 15 KiB

Binary file not shown.

After

(image error) Size: 15 KiB

Binary file not shown.

After

(image error) Size: 14 KiB

Binary file not shown.

After

(image error) Size: 13 KiB

Binary file not shown.

After

(image error) Size: 12 KiB

Binary file not shown.

After

(image error) Size: 12 KiB

Binary file not shown.

After

(image error) Size: 12 KiB

Binary file not shown.

After

(image error) Size: 8.1 KiB

Binary file not shown.

After

(image error) Size: 7.8 KiB

BIN
loader/resources/close.png Normal file

Binary file not shown.

After

(image error) Size: 4.9 KiB

Binary file not shown.

After

(image error) Size: 1 KiB

Some files were not shown because too many files have changed in this diff Show more