Merge branch 'main' of https://github.com/geode-sdk/geode into copy-mods

This commit is contained in:
Justin Pridgen 2024-11-02 19:09:45 -04:00
commit 69a480b341
107 changed files with 15200 additions and 569 deletions
CHANGELOG.mdCMakeLists.txtVERSION
installer/windows/Language Files
loader
include/Geode
launcher/windows
resources
src

View file

@ -1,5 +1,49 @@
# Geode Changelog
## v3.9.0
* Many changes to the settings ui (#1108)
* Fuzzy search is now more reasonable
* Add `CCNode::getChildByType` utility method, replacing the `getChildOfType` util (0089d13, c8aa2e3, 5f70080)
* Add `geode::utils::string::caseInsensitiveCompare` (f3d38a7)
* Now local mods are ordered correctly
* Add `CCCallFuncExt` util, which accepts lambdas and such (d1053b1, 1d31576)
* Add missing `m_nExtraKerning` to `CCLabelBMFont` (#1088)
* Truncate numbers in `numToAbbreviatedString`, no longer rounding up (82e703b)
* Fix string settings not having their character filters applied (6d0b583)
* Error on `numFromString` when string has left over data (d4ca28c)
* Change positioning of MenuLayer `top-right-menu` menu (6d2557b)
* Add `SceneManager::getPersistedNodes` (5009caa)
* Fix android `std::unordered_map` copy constructor (67f59e7)
* Fix crash on Linux when piping output to terminal (8ecb1c5)
* Fix crash when geode menu is disabled (b69f810)
* Fix infinite recursion in `WeakRef` hashing (d68e358)
## v3.8.1
* Fix CCLightning header
* Fix server query default value (8be97b7)
* Fix importance resolving in disabled mods (d40ba6d)
## v3.8.0
* Add Modtober integration! For more about Modtober, join [the GDP Discord server](https://discord.gg/gd-programming-646101505417674758) (964624b)
* Add `Popup::CloseEvent` (6270e1c)
* Add `openSettingsPopup` overload that returns the created `Popup` (dc8d271)
* Fix `CCNode::querySelector` logspamming (b53759f)
* Fix `followThunkFunction` following through into hook handlers (ad26357)
## v3.7.1
* Fix crash with saving older custom settings (c3e7f23)
* Add missing CCDrawNode::drawRect overload (145adb2)
## v3.7.0
* Add persistent directory for mods to save data that won't be deleted when the mod/Geode is uninstalled (68ab475)
* Fix touch priority issues in the settings popup (b3d5474)
* Fix settings with custom types not being saved & loaded properly (f0b6a70)
## v3.6.1
* Fix modrm rip relative instruction displacement saving the clobbered register (ddfe8e2)
* Fix `DataSaved` and `DataLoaded` events (8615bbd, 43adb35)
* Fix file settings mixed separators (1281c76)
## v3.6.0
* Major rework of the entire settings system with lots of new features; see the [docs page](https://docs.geode-sdk.org/mods/settings) for more
* Rework JSON validation; now uses the `JsonExpectedValue` class with the `checkJson` helper (89d1a51)
@ -17,9 +61,16 @@
* Fix issues with file dialogs on Windows (62b6241, 971e3fb)
* Fix PCH issues (aedd986)
* Mod incompatibilities may now be platform-specific (9f1c70a)
* Show appropriate popup for unavailable mods (6944f80, 30dc70c)
* Add support for description as a default value on file settings (75186f6)
* Remove early ub related to console (54ff48c)
* Temporary workaround for Wine crash due to console (dc170d7)
* Check also for incompatibilities with newly installed mods when downloading (9115091)
* Make cocos geometry classes constexpr (db8a6c8)
* Fix rip displacement issues when hooking some functions on Windows (175dc27)
## v3.5.0
* Move CCLighting to cocos headers (#1036)
* Move CCLightning to cocos headers (#1036)
* Add new `gd::string` constructor (bae22b4)
* Use `getChildren` instead of member in `getChildByID` (fe730ed)
* Fix sprite order in `CCMenuItemExt::createToggler` (d729a12, 59a0ade)

View file

@ -193,7 +193,12 @@ if (NOT DEFINED GEODE_GD_VERSION)
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})
target_compile_definitions(
${PROJECT_NAME} INTERFACE
GEODE_GD_VERSION=${GEODE_GD_VERSION}
GEODE_COMP_GD_VERSION=${GEODE_COMP_GD_VERSION}
GEODE_GD_VERSION_STRING="${GEODE_GD_VERSION}"
)
if (WIN32)
# This allows you to compile in debug mode
@ -212,6 +217,11 @@ if (WIN32)
endif()
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# force an error on a missing return type, as this is unfortunately a common mistake people make
target_compile_options(${PROJECT_NAME} INTERFACE -Werror=return-type)
endif()
# if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
# set_property(TARGET ${PROJECT_NAME} PROPERTY LINKER_TYPE LLD)
# target_link_options(${PROJECT_NAME} INTERFACE -fuse-ld=lld)
@ -256,7 +266,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#c8a445c")
CPMAddPackage("gh:geode-sdk/TulipHook#d1d9559")
endif()
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)

View file

@ -1 +1 @@
3.6.0
3.9.0

View file

@ -8,8 +8,8 @@ ${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "Le programme d'installation vous
; installer
${LangFileString} GEODE_TEXT_GD_MISSING "$\r$\n$\r$\nGeometry Dash n'est pas installé sur ce chemin !"
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\nYour version of Geometry Dash is too old for this version of Geode!"
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "This path has other mods installed!$\r$\nThey will be overwritten by Geode. (the dll trademark)"
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\nVotre version de Geometry Dash est trop ancienne pour cette version de Geode !"
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "Ce chemin a d'autres mods installés !$\r$\nIls seront écrasés par Geode. (the dll trademark)"
; uninstaller

View file

@ -15,6 +15,12 @@ namespace geode::base {
#if defined(GEODE_IS_ANDROID)
#include "gnustl-map.hpp"
#include "gnustl/unordered_map.hpp"
#include "gnustl/unordered_set.hpp"
#include "gnustl/hash_specialization.hpp"
#undef _GLIBCXX_RELEASE
#undef __GLIBCXX__
#undef _GLIBCXX_USE_DUAL_ABI
namespace gd {
using namespace geode::stl;
@ -686,11 +692,11 @@ namespace gd {
template <class V>
using set = void*[6];
template <class K, class V>
using unordered_map = void*[7];
template <class Key, class Tp, class Hash = geode::stl::hash<Key>, class Pred = geode::stl::equal_to<Key>, class Alloc = std::allocator<std::pair<const Key, Tp>>>
using unordered_map = geode::stl::unordered_map<Key, Tp, Hash, Pred, Alloc>;
template <class V>
using unordered_set = void*[7];
template <class Value, class Hash = geode::stl::hash<Value>, class Pred = geode::stl::equal_to<Value>, class Alloc = std::allocator<Value>>
using unordered_set = geode::stl::unordered_set<Value, Hash, Pred, Alloc>;
};
#elif defined(GEODE_IS_IOS)

View file

@ -0,0 +1,531 @@
// Allocator traits -*- C++ -*-
// Copyright (C) 2011-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/alloc_traits.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{memory}
*/
#pragma once
#if __cplusplus >= 201103L
#include "memoryfwd.h"
#include "ptr_traits.h"
#include "type_traits.h"
#include "ext/numeric_traits.h"
namespace geode::stl {
_GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Alloc, typename _Tp>
class __alloctr_rebind_helper
{
template<typename _Alloc2, typename _Tp2>
static constexpr std::true_type
_S_chk(typename _Alloc2::template rebind<_Tp2>::other*);
template<typename, typename>
static constexpr std::false_type
_S_chk(...);
public:
using __type = decltype(_S_chk<_Alloc, _Tp>(nullptr));
};
template<typename _Alloc, typename _Tp,
bool = __alloctr_rebind_helper<_Alloc, _Tp>::__type::value>
struct __alloctr_rebind;
template<typename _Alloc, typename _Tp>
struct __alloctr_rebind<_Alloc, _Tp, true>
{
typedef typename _Alloc::template rebind<_Tp>::other __type;
};
template<template<typename, typename...> class _Alloc, typename _Tp,
typename _Up, typename... _Args>
struct __alloctr_rebind<_Alloc<_Up, _Args...>, _Tp, false>
{
typedef _Alloc<_Tp, _Args...> __type;
};
/**
* @brief Uniform interface to all allocator types.
* @ingroup allocators
*/
template<typename _Alloc>
struct allocator_traits
{
/// The allocator type
typedef _Alloc allocator_type;
/// The allocated type
typedef typename _Alloc::value_type value_type;
#define _GLIBCXX_ALLOC_TR_NESTED_TYPE(_NTYPE, _ALT) \
private: \
template<typename _Tp> \
static typename _Tp::_NTYPE _S_##_NTYPE##_helper(_Tp*); \
static _ALT _S_##_NTYPE##_helper(...); \
typedef decltype(_S_##_NTYPE##_helper((_Alloc*)0)) __##_NTYPE; \
public:
_GLIBCXX_ALLOC_TR_NESTED_TYPE(pointer, value_type*)
/**
* @brief The allocator's pointer type.
*
* @c Alloc::pointer if that type exists, otherwise @c value_type*
*/
typedef __pointer pointer;
_GLIBCXX_ALLOC_TR_NESTED_TYPE(const_pointer,
typename pointer_traits<pointer>::template rebind<const value_type>)
/**
* @brief The allocator's const pointer type.
*
* @c Alloc::const_pointer if that type exists, otherwise
* <tt> pointer_traits<pointer>::rebind<const value_type> </tt>
*/
typedef __const_pointer const_pointer;
_GLIBCXX_ALLOC_TR_NESTED_TYPE(void_pointer,
typename pointer_traits<pointer>::template rebind<void>)
/**
* @brief The allocator's void pointer type.
*
* @c Alloc::void_pointer if that type exists, otherwise
* <tt> pointer_traits<pointer>::rebind<void> </tt>
*/
typedef __void_pointer void_pointer;
_GLIBCXX_ALLOC_TR_NESTED_TYPE(const_void_pointer,
typename pointer_traits<pointer>::template rebind<const void>)
/**
* @brief The allocator's const void pointer type.
*
* @c Alloc::const_void_pointer if that type exists, otherwise
* <tt> pointer_traits<pointer>::rebind<const void> </tt>
*/
typedef __const_void_pointer const_void_pointer;
_GLIBCXX_ALLOC_TR_NESTED_TYPE(difference_type,
typename pointer_traits<pointer>::difference_type)
/**
* @brief The allocator's difference type
*
* @c Alloc::difference_type if that type exists, otherwise
* <tt> pointer_traits<pointer>::difference_type </tt>
*/
typedef __difference_type difference_type;
_GLIBCXX_ALLOC_TR_NESTED_TYPE(size_type,
typename std::make_unsigned<difference_type>::type)
/**
* @brief The allocator's size type
*
* @c Alloc::size_type if that type exists, otherwise
* <tt> make_unsigned<difference_type>::type </tt>
*/
typedef __size_type size_type;
_GLIBCXX_ALLOC_TR_NESTED_TYPE(propagate_on_container_copy_assignment,
std::false_type)
/**
* @brief How the allocator is propagated on copy assignment
*
* @c Alloc::propagate_on_container_copy_assignment if that type exists,
* otherwise @c false_type
*/
typedef __propagate_on_container_copy_assignment
propagate_on_container_copy_assignment;
_GLIBCXX_ALLOC_TR_NESTED_TYPE(propagate_on_container_move_assignment,
std::false_type)
/**
* @brief How the allocator is propagated on move assignment
*
* @c Alloc::propagate_on_container_move_assignment if that type exists,
* otherwise @c false_type
*/
typedef __propagate_on_container_move_assignment
propagate_on_container_move_assignment;
_GLIBCXX_ALLOC_TR_NESTED_TYPE(propagate_on_container_swap,
std::false_type)
/**
* @brief How the allocator is propagated on swap
*
* @c Alloc::propagate_on_container_swap if that type exists,
* otherwise @c false_type
*/
typedef __propagate_on_container_swap propagate_on_container_swap;
#undef _GLIBCXX_ALLOC_TR_NESTED_TYPE
template<typename _Tp>
using rebind_alloc = typename __alloctr_rebind<_Alloc, _Tp>::__type;
template<typename _Tp>
using rebind_traits = allocator_traits<rebind_alloc<_Tp>>;
private:
template<typename _Alloc2>
struct __allocate_helper
{
template<typename _Alloc3,
typename = decltype(std::declval<_Alloc3*>()->allocate(
std::declval<size_type>(),
std::declval<const_void_pointer>()))>
static std::true_type __test(int);
template<typename>
static std::false_type __test(...);
using type = decltype(__test<_Alloc>(0));
};
template<typename _Alloc2>
using __has_allocate = typename __allocate_helper<_Alloc2>::type;
template<typename _Alloc2,
typename = _Require<__has_allocate<_Alloc2>>>
static pointer
_S_allocate(_Alloc2& __a, size_type __n, const_void_pointer __hint)
{ return __a.allocate(__n, __hint); }
template<typename _Alloc2, typename _UnusedHint,
typename = _Require<__not_<__has_allocate<_Alloc2>>>>
static pointer
_S_allocate(_Alloc2& __a, size_type __n, _UnusedHint)
{ return __a.allocate(__n); }
template<typename _Tp, typename... _Args>
struct __construct_helper
{
template<typename _Alloc2,
typename = decltype(std::declval<_Alloc2*>()->construct(
std::declval<_Tp*>(), std::declval<_Args>()...))>
static std::true_type __test(int);
template<typename>
static std::false_type __test(...);
using type = decltype(__test<_Alloc>(0));
};
template<typename _Tp, typename... _Args>
using __has_construct
= typename __construct_helper<_Tp, _Args...>::type;
template<typename _Tp, typename... _Args>
static _Require<__has_construct<_Tp, _Args...>>
_S_construct(_Alloc& __a, _Tp* __p, _Args&&... __args)
{ __a.construct(__p, std::forward<_Args>(__args)...); }
template<typename _Tp, typename... _Args>
static
_Require<std::conjunction<__not_<__has_construct<_Tp, _Args...>>,
std::is_constructible<_Tp, _Args...>>>
_S_construct(_Alloc&, _Tp* __p, _Args&&... __args)
{ ::new((void*)__p) _Tp(std::forward<_Args>(__args)...); }
template<typename _Tp>
struct __destroy_helper
{
template<typename _Alloc2,
typename = decltype(std::declval<_Alloc2*>()->destroy(
std::declval<_Tp*>()))>
static std::true_type __test(int);
template<typename>
static std::false_type __test(...);
using type = decltype(__test<_Alloc>(0));
};
template<typename _Tp>
using __has_destroy = typename __destroy_helper<_Tp>::type;
template<typename _Tp>
static _Require<__has_destroy<_Tp>>
_S_destroy(_Alloc& __a, _Tp* __p)
{ __a.destroy(__p); }
template<typename _Tp>
static _Require<__not_<__has_destroy<_Tp>>>
_S_destroy(_Alloc&, _Tp* __p)
{ __p->~_Tp(); }
template<typename _Alloc2>
struct __maxsize_helper
{
template<typename _Alloc3,
typename = decltype(std::declval<_Alloc3*>()->max_size())>
static std::true_type __test(int);
template<typename>
static std::false_type __test(...);
using type = decltype(__test<_Alloc2>(0));
};
template<typename _Alloc2>
using __has_max_size = typename __maxsize_helper<_Alloc2>::type;
template<typename _Alloc2,
typename = _Require<__has_max_size<_Alloc2>>>
static size_type
_S_max_size(_Alloc2& __a, int)
{ return __a.max_size(); }
template<typename _Alloc2,
typename = _Require<__not_<__has_max_size<_Alloc2>>>>
static size_type
_S_max_size(_Alloc2&, ...)
{ return __gnu_cxx::__numeric_traits<size_type>::__max; }
template<typename _Alloc2>
struct __select_helper
{
template<typename _Alloc3, typename
= decltype(std::declval<_Alloc3*>()
->select_on_container_copy_construction())>
static std::true_type __test(int);
template<typename>
static std::false_type __test(...);
using type = decltype(__test<_Alloc2>(0));
};
template<typename _Alloc2>
using __has_soccc = typename __select_helper<_Alloc2>::type;
template<typename _Alloc2,
typename = _Require<__has_soccc<_Alloc2>>>
static _Alloc2
_S_select(_Alloc2& __a, int)
{ return __a.select_on_container_copy_construction(); }
template<typename _Alloc2,
typename = _Require<__not_<__has_soccc<_Alloc2>>>>
static _Alloc2
_S_select(_Alloc2& __a, ...)
{ return __a; }
public:
/**
* @brief Allocate memory.
* @param __a An allocator.
* @param __n The number of objects to allocate space for.
*
* Calls @c a.allocate(n)
*/
static pointer
allocate(_Alloc& __a, size_type __n)
{ return __a.allocate(__n); }
/**
* @brief Allocate memory.
* @param __a An allocator.
* @param __n The number of objects to allocate space for.
* @param __hint Aid to locality.
* @return Memory of suitable size and alignment for @a n objects
* of type @c value_type
*
* Returns <tt> a.allocate(n, hint) </tt> if that expression is
* well-formed, otherwise returns @c a.allocate(n)
*/
static pointer
allocate(_Alloc& __a, size_type __n, const_void_pointer __hint)
{ return _S_allocate(__a, __n, __hint); }
/**
* @brief Deallocate memory.
* @param __a An allocator.
* @param __p Pointer to the memory to deallocate.
* @param __n The number of objects space was allocated for.
*
* Calls <tt> a.deallocate(p, n) </tt>
*/
static void deallocate(_Alloc& __a, pointer __p, size_type __n)
{ __a.deallocate(__p, __n); }
/**
* @brief Construct an object of type @a _Tp
* @param __a An allocator.
* @param __p Pointer to memory of suitable size and alignment for Tp
* @param __args Constructor arguments.
*
* Calls <tt> __a.construct(__p, std::forward<Args>(__args)...) </tt>
* if that expression is well-formed, otherwise uses placement-new
* to construct an object of type @a _Tp at location @a __p from the
* arguments @a __args...
*/
template<typename _Tp, typename... _Args>
static auto construct(_Alloc& __a, _Tp* __p, _Args&&... __args)
-> decltype(_S_construct(__a, __p, std::forward<_Args>(__args)...))
{ _S_construct(__a, __p, std::forward<_Args>(__args)...); }
/**
* @brief Destroy an object of type @a _Tp
* @param __a An allocator.
* @param __p Pointer to the object to destroy
*
* Calls @c __a.destroy(__p) if that expression is well-formed,
* otherwise calls @c __p->~_Tp()
*/
template <class _Tp>
static void destroy(_Alloc& __a, _Tp* __p)
{ _S_destroy(__a, __p); }
/**
* @brief The maximum supported allocation size
* @param __a An allocator.
* @return @c __a.max_size() or @c numeric_limits<size_type>::max()
*
* Returns @c __a.max_size() if that expression is well-formed,
* otherwise returns @c numeric_limits<size_type>::max()
*/
static size_type max_size(const _Alloc& __a) noexcept
{ return _S_max_size(__a, 0); }
/**
* @brief Obtain an allocator to use when copying a container.
* @param __rhs An allocator.
* @return @c __rhs.select_on_container_copy_construction() or @a __rhs
*
* Returns @c __rhs.select_on_container_copy_construction() if that
* expression is well-formed, otherwise returns @a __rhs
*/
static _Alloc
select_on_container_copy_construction(const _Alloc& __rhs)
{ return _S_select(__rhs, 0); }
};
template<typename _Alloc>
inline void
__do_alloc_on_copy(_Alloc& __one, const _Alloc& __two, std::true_type)
{ __one = __two; }
template<typename _Alloc>
inline void
__do_alloc_on_copy(_Alloc&, const _Alloc&, std::false_type)
{ }
template<typename _Alloc>
inline void __alloc_on_copy(_Alloc& __one, const _Alloc& __two)
{
typedef allocator_traits<_Alloc> __traits;
typedef typename __traits::propagate_on_container_copy_assignment __pocca;
__do_alloc_on_copy(__one, __two, __pocca());
}
template<typename _Alloc>
inline _Alloc __alloc_on_copy(const _Alloc& __a)
{
typedef allocator_traits<_Alloc> __traits;
return __traits::select_on_container_copy_construction(__a);
}
template<typename _Alloc>
inline void __do_alloc_on_move(_Alloc& __one, _Alloc& __two, std::true_type)
{ __one = std::move(__two); }
template<typename _Alloc>
inline void __do_alloc_on_move(_Alloc&, _Alloc&, std::false_type)
{ }
template<typename _Alloc>
inline void __alloc_on_move(_Alloc& __one, _Alloc& __two)
{
typedef allocator_traits<_Alloc> __traits;
typedef typename __traits::propagate_on_container_move_assignment __pocma;
__do_alloc_on_move(__one, __two, __pocma());
}
template<typename _Alloc>
inline void __do_alloc_on_swap(_Alloc& __one, _Alloc& __two, std::true_type)
{
using std::swap;
swap(__one, __two);
}
template<typename _Alloc>
inline void __do_alloc_on_swap(_Alloc&, _Alloc&, std::false_type)
{ }
template<typename _Alloc>
inline void __alloc_on_swap(_Alloc& __one, _Alloc& __two)
{
typedef allocator_traits<_Alloc> __traits;
typedef typename __traits::propagate_on_container_swap __pocs;
__do_alloc_on_swap(__one, __two, __pocs());
}
template<typename _Alloc>
class __is_copy_insertable_impl
{
typedef allocator_traits<_Alloc> _Traits;
template<typename _Up, typename
= decltype(_Traits::construct(std::declval<_Alloc&>(),
std::declval<_Up*>(),
std::declval<const _Up&>()))>
static std::true_type
_M_select(int);
template<typename _Up>
static std::false_type
_M_select(...);
public:
typedef decltype(_M_select<typename _Alloc::value_type>(0)) type;
};
// true if _Alloc::value_type is CopyInsertable into containers using _Alloc
template<typename _Alloc>
struct __is_copy_insertable
: __is_copy_insertable_impl<_Alloc>::type
{ };
// std::allocator<_Tp> just requires CopyConstructible
template<typename _Tp>
struct __is_copy_insertable<allocator<_Tp>>
: std::is_copy_constructible<_Tp>
{ };
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif

View file

@ -0,0 +1,220 @@
// Allocators -*- C++ -*-
// Copyright (C) 2001-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/*
* Copyright (c) 1996-1997
* Silicon Graphics Computer Systems, Inc.
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. Silicon Graphics makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*/
/** @file bits/allocator.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{memory}
*/
#ifndef _ALLOCATOR_H
#define _ALLOCATOR_H 1
#include "c++allocator.hpp" // Define the base class to std::allocator.
#include "memoryfwd.h"
#if __cplusplus >= 201103L
#include <type_traits>
#endif
namespace geode::stl {
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/**
* @addtogroup allocators
* @{
*/
/// allocator<void> specialization.
template<>
class allocator<void>
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
template<typename _Tp1>
struct rebind
{ typedef allocator<_Tp1> other; };
#if __cplusplus >= 201103L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2103. std::allocator propagate_on_container_move_assignment
typedef std::true_type propagate_on_container_move_assignment;
#endif
};
/**
* @brief The @a standard allocator, as per [20.4].
*
* See http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt04ch11.html
* for further details.
*
* @tparam _Tp Type of allocated object.
*/
template<typename _Tp>
class allocator: public __allocator_base<_Tp>
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
template<typename _Tp1>
struct rebind
{ typedef allocator<_Tp1> other; };
#if __cplusplus >= 201103L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2103. std::allocator propagate_on_container_move_assignment
typedef std::true_type propagate_on_container_move_assignment;
#endif
allocator() throw() { }
allocator(const allocator& __a) throw()
: __allocator_base<_Tp>(__a) { }
template<typename _Tp1>
allocator(const allocator<_Tp1>&) throw() { }
~allocator() throw() { }
// Inherit everything else.
};
template<typename _T1, typename _T2>
inline bool
operator==(const allocator<_T1>&, const allocator<_T2>&)
{ return true; }
template<typename _Tp>
inline bool
operator==(const allocator<_Tp>&, const allocator<_Tp>&)
{ return true; }
template<typename _T1, typename _T2>
inline bool
operator!=(const allocator<_T1>&, const allocator<_T2>&)
{ return false; }
template<typename _Tp>
inline bool
operator!=(const allocator<_Tp>&, const allocator<_Tp>&)
{ return false; }
/// @} group allocator
// Inhibit implicit instantiations for required instantiations,
// which are defined via explicit instantiations elsewhere.
#if _GLIBCXX_EXTERN_TEMPLATE
extern template class allocator<char>;
extern template class allocator<wchar_t>;
#endif
// Undefine.
#undef __allocator_base
// To implement Option 3 of DR 431.
template<typename _Alloc, bool = __is_empty(_Alloc)>
struct __alloc_swap
{ static void _S_do_it(_Alloc&, _Alloc&) _GLIBCXX_NOEXCEPT { } };
template<typename _Alloc>
struct __alloc_swap<_Alloc, false>
{
static void
_S_do_it(_Alloc& __one, _Alloc& __two) _GLIBCXX_NOEXCEPT
{
// Precondition: swappable allocators.
if (__one != __two)
swap(__one, __two);
}
};
// Optimize for stateless allocators.
template<typename _Alloc, bool = __is_empty(_Alloc)>
struct __alloc_neq
{
static bool
_S_do_it(const _Alloc&, const _Alloc&)
{ return false; }
};
template<typename _Alloc>
struct __alloc_neq<_Alloc, false>
{
static bool
_S_do_it(const _Alloc& __one, const _Alloc& __two)
{ return __one != __two; }
};
#if __cplusplus >= 201103L
template<typename _Tp, bool
= std::disjunction<std::is_copy_constructible<typename _Tp::value_type>,
std::is_nothrow_move_constructible<typename _Tp::value_type>>::value>
struct __shrink_to_fit_aux
{ static bool _S_do_it(_Tp&) noexcept { return false; } };
template<typename _Tp>
struct __shrink_to_fit_aux<_Tp, true>
{
static bool
_S_do_it(_Tp& __c) noexcept
{
try
{
_Tp(__make_move_if_noexcept_iterator(__c.begin()),
__make_move_if_noexcept_iterator(__c.end()),
__c.get_allocator()).swap(__c);
return true;
}
catch(...)
{ return false; }
}
};
#endif
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif

View file

@ -0,0 +1,54 @@
// Base to std::allocator -*- C++ -*-
// Copyright (C) 2004-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/c++allocator.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{memory}
*/
#ifndef _GLIBCXX_CXX_ALLOCATOR_H
#define _GLIBCXX_CXX_ALLOCATOR_H 1
#include "ext/new_allocator.h"
#if __cplusplus >= 201103L
namespace geode::stl {
/**
* @brief An alias to the base class for std::allocator.
* @ingroup allocators
*
* Used to set the std::allocator base class to
* __gnu_cxx::new_allocator.
*
* @tparam _Tp Type of allocated object.
*/
template<typename _Tp>
using __allocator_base = __gnu_cxx::new_allocator<_Tp>;
}
#else
// Define new_allocator as the base class to std::allocator.
# define __allocator_base __gnu_cxx::new_allocator
#endif
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
#if UINTPTR_MAX > 0xffffffff
# include "c++config-arm64.h"
#else
# include "c++config-armeabi-v7a.h"
#endif
// i put this here cuz it's used all over the place
namespace geode::stl {
template <typename T> using __not_ = std::integral_constant<bool, !T::value>;
}

View file

@ -0,0 +1,45 @@
// -fno-exceptions Support -*- C++ -*-
// Copyright (C) 2001-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/exception_defines.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{exception}
*/
#ifndef _EXCEPTION_DEFINES_H
#define _EXCEPTION_DEFINES_H 1
#ifndef __EXCEPTIONS
// Iff -fno-exceptions, transform error handling code to work without it.
# define __try if (true)
# define __catch(X) if (false)
# define __throw_exception_again
#else
// Else proceed normally.
# define __try try
# define __catch(X) catch(X)
# define __throw_exception_again throw
#endif
#endif

View file

@ -0,0 +1,111 @@
// Aligned memory buffer -*- C++ -*-
// Copyright (C) 2013-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file ext/aligned_buffer.h
* This file is a GNU extension to the Standard C++ Library.
*/
#pragma once
#pragma GCC system_header
#if __cplusplus >= 201103L
# include <type_traits>
#else
# include <bits/c++0x_warning.h>
#endif
namespace __gnu_cxx {
// A utility type containing a POD object that can hold an object of type
// _Tp initialized via placement new or allocator_traits::construct.
// Intended for use as a data member subobject, use __aligned_buffer for
// complete objects.
template<typename _Tp>
struct __aligned_membuf
{
// Target macro ADJUST_FIELD_ALIGN can produce different alignment for
// types when used as class members. __aligned_membuf is intended
// for use as a class member, so align the buffer as for a class member.
struct _Tp2 { _Tp _M_t; };
alignas(__alignof__(_Tp2::_M_t)) unsigned char _M_storage[sizeof(_Tp)];
__aligned_membuf() = default;
// Can be used to avoid value-initialization zeroing _M_storage.
__aligned_membuf(std::nullptr_t) { }
void*
_M_addr() noexcept
{ return static_cast<void*>(&_M_storage); }
const void*
_M_addr() const noexcept
{ return static_cast<const void*>(&_M_storage); }
_Tp*
_M_ptr() noexcept
{ return static_cast<_Tp*>(_M_addr()); }
const _Tp*
_M_ptr() const noexcept
{ return static_cast<const _Tp*>(_M_addr()); }
};
// Similar to __aligned_membuf but aligned for complete objects, not members.
// This type is used in <forward_list>, <future>, <bits/shared_ptr_base.h>
// and <bits/hashtable_policy.h>, but ideally they would use __aligned_membuf
// instead, as it has smaller size for some types on some targets.
// This type is still used to avoid an ABI change.
template<typename _Tp>
struct __aligned_buffer
: std::aligned_storage<sizeof(_Tp), std::alignment_of<_Tp>::value>
{
typename
std::aligned_storage<sizeof(_Tp), std::alignment_of<_Tp>::value>::type
_M_storage;
void*
_M_addr() noexcept
{
return static_cast<void*>(&_M_storage);
}
const void*
_M_addr() const noexcept
{
return static_cast<const void*>(&_M_storage);
}
_Tp*
_M_ptr() noexcept
{ return static_cast<_Tp*>(_M_addr()); }
const _Tp*
_M_ptr() const noexcept
{ return static_cast<const _Tp*>(_M_addr()); }
};
} // namespace

View file

@ -0,0 +1,211 @@
// Allocator traits -*- C++ -*-
// Copyright (C) 2011-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file ext/alloc_traits.h
* This file is a GNU extension to the Standard C++ Library.
*/
#pragma once
#pragma GCC system_header
#if __cplusplus >= 201103L
# include "../alloc_traits.h"
#else
# include <bits/allocator.h> // for __alloc_swap
#endif
#include "../c++config.h"
namespace __gnu_cxx {
_GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cplusplus >= 201103L
template<typename _Alloc>
struct __allocator_always_compares_equal : std::false_type { };
template<typename _Tp>
struct __allocator_always_compares_equal<std::allocator<_Tp>>
: std::true_type { };
template<typename, typename> struct array_allocator;
template<typename _Tp, typename _Array>
struct __allocator_always_compares_equal<array_allocator<_Tp, _Array>>
: std::true_type { };
template<typename> struct bitmap_allocator;
template<typename _Tp>
struct __allocator_always_compares_equal<bitmap_allocator<_Tp>>
: std::true_type { };
template<typename> struct malloc_allocator;
template<typename _Tp>
struct __allocator_always_compares_equal<malloc_allocator<_Tp>>
: std::true_type { };
template<typename> struct mt_allocator;
template<typename _Tp>
struct __allocator_always_compares_equal<mt_allocator<_Tp>>
: std::true_type { };
template<typename> struct new_allocator;
template<typename _Tp>
struct __allocator_always_compares_equal<new_allocator<_Tp>>
: std::true_type { };
template<typename> struct pool_allocator;
template<typename _Tp>
struct __allocator_always_compares_equal<pool_allocator<_Tp>>
: std::true_type { };
#endif
/**
* @brief Uniform interface to C++98 and C++0x allocators.
* @ingroup allocators
*/
template<typename _Alloc>
struct __alloc_traits
#if __cplusplus >= 201103L
: std::allocator_traits<_Alloc>
#endif
{
typedef _Alloc allocator_type;
#if __cplusplus >= 201103L
typedef std::allocator_traits<_Alloc> _Base_type;
typedef typename _Base_type::value_type value_type;
typedef typename _Base_type::pointer pointer;
typedef typename _Base_type::const_pointer const_pointer;
typedef typename _Base_type::size_type size_type;
typedef typename _Base_type::difference_type difference_type;
// C++11 allocators do not define reference or const_reference
typedef value_type& reference;
typedef const value_type& const_reference;
using _Base_type::allocate;
using _Base_type::deallocate;
using _Base_type::construct;
using _Base_type::destroy;
using _Base_type::max_size;
private:
template<typename _Ptr>
using __is_custom_pointer
= std::conjunction<std::is_same<pointer, _Ptr>,
geode::stl::__not_<std::is_pointer<_Ptr>>>;
public:
// overload construct for non-standard pointer types
template<typename _Ptr, typename... _Args>
static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type
construct(_Alloc& __a, _Ptr __p, _Args&&... __args)
{
_Base_type::construct(__a, std::addressof(*__p),
std::forward<_Args>(__args)...);
}
// overload destroy for non-standard pointer types
template<typename _Ptr>
static typename std::enable_if<__is_custom_pointer<_Ptr>::value>::type
destroy(_Alloc& __a, _Ptr __p)
{ _Base_type::destroy(__a, std::addressof(*__p)); }
static _Alloc _S_select_on_copy(const _Alloc& __a)
{ return _Base_type::select_on_container_copy_construction(__a); }
static void _S_on_swap(_Alloc& __a, _Alloc& __b)
{ __alloc_on_swap(__a, __b); }
static constexpr bool _S_propagate_on_copy_assign()
{ return _Base_type::propagate_on_container_copy_assignment::value; }
static constexpr bool _S_propagate_on_move_assign()
{ return _Base_type::propagate_on_container_move_assignment::value; }
static constexpr bool _S_propagate_on_swap()
{ return _Base_type::propagate_on_container_swap::value; }
static constexpr bool _S_always_equal()
{ return __allocator_always_compares_equal<_Alloc>::value; }
static constexpr bool _S_nothrow_move()
{ return _S_propagate_on_move_assign() || _S_always_equal(); }
static constexpr bool _S_nothrow_swap()
{
using std::swap;
return !_S_propagate_on_swap()
|| noexcept(swap(std::declval<_Alloc&>(), std::declval<_Alloc&>()));
}
template<typename _Tp>
struct rebind
{ typedef typename _Base_type::template rebind_alloc<_Tp> other; };
#else
typedef typename _Alloc::pointer pointer;
typedef typename _Alloc::const_pointer const_pointer;
typedef typename _Alloc::value_type value_type;
typedef typename _Alloc::reference reference;
typedef typename _Alloc::const_reference const_reference;
typedef typename _Alloc::size_type size_type;
typedef typename _Alloc::difference_type difference_type;
static pointer
allocate(_Alloc& __a, size_type __n)
{ return __a.allocate(__n); }
static void deallocate(_Alloc& __a, pointer __p, size_type __n)
{ __a.deallocate(__p, __n); }
template<typename _Tp>
static void construct(_Alloc& __a, pointer __p, const _Tp& __arg)
{ __a.construct(__p, __arg); }
static void destroy(_Alloc& __a, pointer __p)
{ __a.destroy(__p); }
static size_type max_size(const _Alloc& __a)
{ return __a.max_size(); }
static const _Alloc& _S_select_on_copy(const _Alloc& __a) { return __a; }
static void _S_on_swap(_Alloc& __a, _Alloc& __b)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 431. Swapping containers with unequal allocators.
std::__alloc_swap<_Alloc>::_S_do_it(__a, __b);
}
template<typename _Tp>
struct rebind
{ typedef typename _Alloc::template rebind<_Tp>::other other; };
#endif
};
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std

View file

@ -0,0 +1,155 @@
// Allocator that wraps operator new -*- C++ -*-
// Copyright (C) 2001-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file ext/new_allocator.h
* This file is a GNU extension to the Standard C++ Library.
*/
#ifndef _NEW_ALLOCATOR_H
#define _NEW_ALLOCATOR_H 1
#include "../c++config.h"
#include <new>
#if __cplusplus >= 201103L
#include <type_traits>
#endif
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
using std::size_t;
using std::ptrdiff_t;
/**
* @brief An allocator that uses global new, as per [20.4].
* @ingroup allocators
*
* This is precisely the allocator defined in the C++ Standard.
* - all allocation calls operator new
* - all deallocation calls operator delete
*
* @tparam _Tp Type of allocated object.
*/
template<typename _Tp>
class new_allocator
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
template<typename _Tp1>
struct rebind
{ typedef new_allocator<_Tp1> other; };
#if __cplusplus >= 201103L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2103. propagate_on_container_move_assignment
typedef std::true_type propagate_on_container_move_assignment;
#endif
new_allocator() _GLIBCXX_USE_NOEXCEPT { }
new_allocator(const new_allocator&) _GLIBCXX_USE_NOEXCEPT { }
template<typename _Tp1>
new_allocator(const new_allocator<_Tp1>&) _GLIBCXX_USE_NOEXCEPT { }
~new_allocator() _GLIBCXX_USE_NOEXCEPT { }
pointer
address(reference __x) const _GLIBCXX_NOEXCEPT
{ return std::addressof(__x); }
const_pointer
address(const_reference __x) const _GLIBCXX_NOEXCEPT
{ return std::addressof(__x); }
// NB: __n is permitted to be 0. The C++ standard says nothing
// about what the return value is when __n == 0.
pointer
allocate(size_type __n, const void* = 0)
{
if (__n > this->max_size())
std::__throw_bad_alloc();
return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
}
#ifdef __GXX_DELETE_WITH_SIZE__
// __p is not permitted to be a null pointer.
void
deallocate(pointer __p, size_type __t)
{ ::operator delete(__p, __t * sizeof(_Tp)); }
#else
// __p is not permitted to be a null pointer.
void
deallocate(pointer __p, size_type)
{ ::operator delete(__p); }
#endif
size_type
max_size() const _GLIBCXX_USE_NOEXCEPT
{ return size_t(-1) / sizeof(_Tp); }
#if __cplusplus >= 201103L
template<typename _Up, typename... _Args>
void
construct(_Up* __p, _Args&&... __args)
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
template<typename _Up>
void
destroy(_Up* __p) { __p->~_Up(); }
#else
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_] allocator::construct
void
construct(pointer __p, const _Tp& __val)
{ ::new((void *)__p) _Tp(__val); }
void
destroy(pointer __p) { __p->~_Tp(); }
#endif
};
template<typename _Tp>
inline bool
operator==(const new_allocator<_Tp>&, const new_allocator<_Tp>&)
{ return true; }
template<typename _Tp>
inline bool
operator!=(const new_allocator<_Tp>&, const new_allocator<_Tp>&)
{ return false; }
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif

View file

@ -0,0 +1,135 @@
// -*- C++ -*-
// Copyright (C) 2007-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the terms
// of the GNU General Public License as published by the Free Software
// Foundation; either version 3, or (at your option) any later
// version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file ext/numeric_traits.h
* This file is a GNU extension to the Standard C++ Library.
*/
#pragma once
#pragma GCC system_header
#include <type_traits>
#include "../c++config.h"
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
// Compile time constants for builtin types.
// Sadly std::numeric_limits member functions cannot be used for this.
#define __glibcxx_signed(_Tp) ((_Tp)(-1) < 0)
#define __glibcxx_digits(_Tp) \
(sizeof(_Tp) * __CHAR_BIT__ - __glibcxx_signed(_Tp))
#define __glibcxx_min(_Tp) \
(__glibcxx_signed(_Tp) ? (_Tp)1 << __glibcxx_digits(_Tp) : (_Tp)0)
#define __glibcxx_max(_Tp) \
(__glibcxx_signed(_Tp) ? \
(((((_Tp)1 << (__glibcxx_digits(_Tp) - 1)) - 1) << 1) + 1) : ~(_Tp)0)
template<typename _Value>
struct __numeric_traits_integer
{
// Only integers for initialization of member constant.
static const _Value __min = __glibcxx_min(_Value);
static const _Value __max = __glibcxx_max(_Value);
// NB: these two also available in std::numeric_limits as compile
// time constants, but <limits> is big and we avoid including it.
static const bool __is_signed = __glibcxx_signed(_Value);
static const int __digits = __glibcxx_digits(_Value);
};
template<typename _Value>
const _Value __numeric_traits_integer<_Value>::__min;
template<typename _Value>
const _Value __numeric_traits_integer<_Value>::__max;
template<typename _Value>
const bool __numeric_traits_integer<_Value>::__is_signed;
template<typename _Value>
const int __numeric_traits_integer<_Value>::__digits;
#undef __glibcxx_signed
#undef __glibcxx_digits
#undef __glibcxx_min
#undef __glibcxx_max
#define __glibcxx_floating(_Tp, _Fval, _Dval, _LDval) \
(std::is_same<_Tp, float>::__value ? _Fval \
: std::is_same<_Tp, double>::__value ? _Dval : _LDval)
#define __glibcxx_max_digits10(_Tp) \
(2 + __glibcxx_floating(_Tp, __FLT_MANT_DIG__, __DBL_MANT_DIG__, \
__LDBL_MANT_DIG__) * 643L / 2136)
#define __glibcxx_digits10(_Tp) \
__glibcxx_floating(_Tp, __FLT_DIG__, __DBL_DIG__, __LDBL_DIG__)
#define __glibcxx_max_exponent10(_Tp) \
__glibcxx_floating(_Tp, __FLT_MAX_10_EXP__, __DBL_MAX_10_EXP__, \
__LDBL_MAX_10_EXP__)
template<typename _Value>
struct __numeric_traits_floating
{
// Only floating point types. See N1822.
static const int __max_digits10 = __glibcxx_max_digits10(_Value);
// See above comment...
static const bool __is_signed = true;
static const int __digits10 = __glibcxx_digits10(_Value);
static const int __max_exponent10 = __glibcxx_max_exponent10(_Value);
};
template<typename _Value>
const int __numeric_traits_floating<_Value>::__max_digits10;
template<typename _Value>
const bool __numeric_traits_floating<_Value>::__is_signed;
template<typename _Value>
const int __numeric_traits_floating<_Value>::__digits10;
template<typename _Value>
const int __numeric_traits_floating<_Value>::__max_exponent10;
template<typename _Value>
struct __numeric_traits
: public std::conditional<std::is_integral<_Value>::__value,
__numeric_traits_integer<_Value>,
__numeric_traits_floating<_Value> >::__type
{ };
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#undef __glibcxx_floating
#undef __glibcxx_max_digits10
#undef __glibcxx_digits10
#undef __glibcxx_max_exponent10

View file

@ -0,0 +1,211 @@
// functional_hash.h header -*- C++ -*-
// Copyright (C) 2007-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/functional_hash.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{functional}
*/
#ifndef _FUNCTIONAL_HASH_H
#define _FUNCTIONAL_HASH_H 1
#pragma GCC system_header
#include "hash_bytes.h"
namespace geode::stl {
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/** @defgroup hashes Hashes
* @ingroup functors
*
* Hashing functors taking a variable type and returning a @c std::size_t.
*
* @{
*/
template<typename _Result, typename _Arg>
struct __hash_base
{
typedef _Result result_type;
typedef _Arg argument_type;
};
/// Primary class template hash.
template<typename _Tp>
struct hash;
/// Partial specializations for pointer types.
template<typename _Tp>
struct hash<_Tp*> : public __hash_base<size_t, _Tp*>
{
size_t
operator()(_Tp* __p) const noexcept
{ return reinterpret_cast<size_t>(__p); }
};
// Explicit specializations for integer types.
#define _Cxx_hashtable_define_trivial_hash(_Tp) \
template<> \
struct hash<_Tp> : public __hash_base<size_t, _Tp> \
{ \
size_t \
operator()(_Tp __val) const noexcept \
{ return static_cast<size_t>(__val); } \
};
/// Explicit specialization for bool.
_Cxx_hashtable_define_trivial_hash(bool)
/// Explicit specialization for char.
_Cxx_hashtable_define_trivial_hash(char)
/// Explicit specialization for signed char.
_Cxx_hashtable_define_trivial_hash(signed char)
/// Explicit specialization for unsigned char.
_Cxx_hashtable_define_trivial_hash(unsigned char)
/// Explicit specialization for wchar_t.
_Cxx_hashtable_define_trivial_hash(wchar_t)
/// Explicit specialization for char16_t.
_Cxx_hashtable_define_trivial_hash(char16_t)
/// Explicit specialization for char32_t.
_Cxx_hashtable_define_trivial_hash(char32_t)
/// Explicit specialization for short.
_Cxx_hashtable_define_trivial_hash(short)
/// Explicit specialization for int.
_Cxx_hashtable_define_trivial_hash(int)
/// Explicit specialization for long.
_Cxx_hashtable_define_trivial_hash(long)
/// Explicit specialization for long long.
_Cxx_hashtable_define_trivial_hash(long long)
/// Explicit specialization for unsigned short.
_Cxx_hashtable_define_trivial_hash(unsigned short)
/// Explicit specialization for unsigned int.
_Cxx_hashtable_define_trivial_hash(unsigned int)
/// Explicit specialization for unsigned long.
_Cxx_hashtable_define_trivial_hash(unsigned long)
/// Explicit specialization for unsigned long long.
_Cxx_hashtable_define_trivial_hash(unsigned long long)
#undef _Cxx_hashtable_define_trivial_hash
struct _Hash_impl
{
static size_t
hash(const void* __ptr, size_t __clength,
size_t __seed = static_cast<size_t>(0xc70f6907UL))
{ return _Hash_bytes(__ptr, __clength, __seed); }
template<typename _Tp>
static size_t
hash(const _Tp& __val)
{ return hash(&__val, sizeof(__val)); }
template<typename _Tp>
static size_t
__hash_combine(const _Tp& __val, size_t __hash)
{ return hash(&__val, sizeof(__val), __hash); }
};
struct _Fnv_hash_impl
{
static size_t
hash(const void* __ptr, size_t __clength,
size_t __seed = static_cast<size_t>(2166136261UL))
{ return _Fnv_hash_bytes(__ptr, __clength, __seed); }
template<typename _Tp>
static size_t
hash(const _Tp& __val)
{ return hash(&__val, sizeof(__val)); }
template<typename _Tp>
static size_t
__hash_combine(const _Tp& __val, size_t __hash)
{ return hash(&__val, sizeof(__val), __hash); }
};
/// Specialization for float.
template<>
struct hash<float> : public __hash_base<size_t, float>
{
size_t
operator()(float __val) const noexcept
{
// 0 and -0 both hash to zero.
return __val != 0.0f ? _Hash_impl::hash(__val) : 0;
}
};
/// Specialization for double.
template<>
struct hash<double> : public __hash_base<size_t, double>
{
size_t
operator()(double __val) const noexcept
{
// 0 and -0 both hash to zero.
return __val != 0.0 ? _Hash_impl::hash(__val) : 0;
}
};
/// Specialization for long double.
template<>
struct hash<long double>
: public __hash_base<size_t, long double>
{
_GLIBCXX_PURE size_t
operator()(long double __val) const noexcept;
};
// @} group hashes
// Hint about performance of hash functor. If not fast the hash based
// containers will cache the hash code.
// Default behavior is to consider that hasher are fast unless specified
// otherwise.
template<typename _Hash>
struct __is_fast_hash : public std::true_type
{ };
template<>
struct __is_fast_hash<hash<long double>> : public std::false_type
{ };
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif // _FUNCTIONAL_HASH_H

View file

@ -0,0 +1,58 @@
// Declarations for hash functions. -*- C++ -*-
// Copyright (C) 2010-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/hash_bytes.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{functional}
*/
#ifndef _HASH_BYTES_H
#define _HASH_BYTES_H 1
#pragma GCC system_header
#include "c++config.h"
namespace geode::stl {
_GLIBCXX_BEGIN_NAMESPACE_VERSION
// Hash function implementation for the nontrivial specialization.
// All of them are based on a primitive that hashes a pointer to a
// byte array. The actual hash algorithm is not guaranteed to stay
// the same from release to release -- it may be updated or tuned to
// improve hash quality or speed.
size_t
_Hash_bytes(const void* __ptr, size_t __len, size_t __seed);
// A similar hash primitive, using the FNV hash algorithm. This
// algorithm is guaranteed to stay the same from release to release.
// (although it might not produce the same values on different
// machines.)
size_t
_Fnv_hash_bytes(const void* __ptr, size_t __len, size_t __seed);
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif

View file

@ -0,0 +1,11 @@
#pragma once
#include "functional_hash.h"
namespace geode::stl {
template <>
struct hash<gd::string> : public __hash_base<size_t, gd::string> {
size_t operator()(const gd::string& s) const noexcept {
return _Hash_impl::hash(s.data(), s.size());
}
};
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,77 @@
// <memory> Forward declarations -*- C++ -*-
// Copyright (C) 2001-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/*
* Copyright (c) 1996-1997
* Silicon Graphics Computer Systems, Inc.
*
* Permission to use, copy, modify, distribute and sell this software
* and its documentation for any purpose is hereby granted without fee,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation. Silicon Graphics makes no
* representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*/
/** @file bits/memoryfwd.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{memory}
*/
#ifndef _MEMORYFWD_H
#define _MEMORYFWD_H 1
#pragma GCC system_header
#include "c++config.h"
namespace geode::stl {
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/**
* @defgroup allocators Allocators
* @ingroup memory
*
* Classes encapsulating memory operations.
*
* @{
*/
template<typename>
class allocator;
template<>
class allocator<void>;
/// Declare uses_allocator so it can be specialized in \<queue\> etc.
template<typename, typename>
struct uses_allocator;
/// @} group memory
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif

View file

@ -0,0 +1,157 @@
// Allocator that wraps operator new -*- C++ -*-
// Copyright (C) 2001-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file ext/new_allocator.h
* This file is a GNU extension to the Standard C++ Library.
*/
#ifndef _NEW_ALLOCATOR_H
#define _NEW_ALLOCATOR_H 1
#include "c++config.h"
#include <new>
#include <bits/functexcept.h>
#include <bits/move.h>
#if __cplusplus >= 201103L
#include <type_traits>
#endif
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
using std::size_t;
using std::ptrdiff_t;
/**
* @brief An allocator that uses global new, as per [20.4].
* @ingroup allocators
*
* This is precisely the allocator defined in the C++ Standard.
* - all allocation calls operator new
* - all deallocation calls operator delete
*
* @tparam _Tp Type of allocated object.
*/
template<typename _Tp>
class new_allocator
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
template<typename _Tp1>
struct rebind
{ typedef new_allocator<_Tp1> other; };
#if __cplusplus >= 201103L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2103. propagate_on_container_move_assignment
typedef std::true_type propagate_on_container_move_assignment;
#endif
new_allocator() _GLIBCXX_USE_NOEXCEPT { }
new_allocator(const new_allocator&) _GLIBCXX_USE_NOEXCEPT { }
template<typename _Tp1>
new_allocator(const new_allocator<_Tp1>&) _GLIBCXX_USE_NOEXCEPT { }
~new_allocator() _GLIBCXX_USE_NOEXCEPT { }
pointer
address(reference __x) const _GLIBCXX_NOEXCEPT
{ return std::__addressof(__x); }
const_pointer
address(const_reference __x) const _GLIBCXX_NOEXCEPT
{ return std::__addressof(__x); }
// NB: __n is permitted to be 0. The C++ standard says nothing
// about what the return value is when __n == 0.
pointer
allocate(size_type __n, const void* = 0)
{
if (__n > this->max_size())
std::__throw_bad_alloc();
return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
}
#ifdef __GXX_DELETE_WITH_SIZE__
// __p is not permitted to be a null pointer.
void
deallocate(pointer __p, size_type __t)
{ ::operator delete(__p, __t * sizeof(_Tp)); }
#else
// __p is not permitted to be a null pointer.
void
deallocate(pointer __p, size_type)
{ ::operator delete(__p); }
#endif
size_type
max_size() const _GLIBCXX_USE_NOEXCEPT
{ return size_t(-1) / sizeof(_Tp); }
#if __cplusplus >= 201103L
template<typename _Up, typename... _Args>
void
construct(_Up* __p, _Args&&... __args)
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
template<typename _Up>
void
destroy(_Up* __p) { __p->~_Up(); }
#else
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_] allocator::construct
void
construct(pointer __p, const _Tp& __val)
{ ::new((void *)__p) _Tp(__val); }
void
destroy(pointer __p) { __p->~_Tp(); }
#endif
};
template<typename _Tp>
inline bool
operator==(const new_allocator<_Tp>&, const new_allocator<_Tp>&)
{ return true; }
template<typename _Tp>
inline bool
operator!=(const new_allocator<_Tp>&, const new_allocator<_Tp>&)
{ return false; }
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif

View file

@ -0,0 +1,184 @@
// Pointer Traits -*- C++ -*-
// Copyright (C) 2011-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/ptr_traits.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{memory}
*/
#pragma once
#if __cplusplus >= 201103L
#include "c++config.h"
#include <type_traits>
#define _GLIBCXX_HAS_NESTED_TYPE(_NTYPE) \
template<typename _Tp, typename = std::void_t<>> \
struct __has_##_NTYPE \
: std::false_type \
{ }; \
template<typename _Tp> \
struct __has_##_NTYPE<_Tp, std::void_t<typename _Tp::_NTYPE>> \
: std::true_type \
{ };
namespace geode::stl {
_GLIBCXX_BEGIN_NAMESPACE_VERSION
_GLIBCXX_HAS_NESTED_TYPE(element_type)
_GLIBCXX_HAS_NESTED_TYPE(difference_type)
template<typename _Tp, bool = __has_element_type<_Tp>::value>
struct __ptrtr_elt_type;
template<typename _Tp>
struct __ptrtr_elt_type<_Tp, true>
{
typedef typename _Tp::element_type __type;
};
template<template<typename, typename...> class _SomePtr, typename _Tp,
typename... _Args>
struct __ptrtr_elt_type<_SomePtr<_Tp, _Args...>, false>
{
typedef _Tp __type;
};
template<typename _Tp, bool = __has_difference_type<_Tp>::value>
struct __ptrtr_diff_type
{
typedef typename _Tp::difference_type __type;
};
template<typename _Tp>
struct __ptrtr_diff_type<_Tp, false>
{
typedef ptrdiff_t __type;
};
template<typename _Ptr, typename _Up>
class __ptrtr_rebind_helper
{
template<typename _Ptr2, typename _Up2>
static constexpr std::true_type
_S_chk(typename _Ptr2::template rebind<_Up2>*);
template<typename, typename>
static constexpr std::false_type
_S_chk(...);
public:
using __type = decltype(_S_chk<_Ptr, _Up>(nullptr));
};
template<typename _Tp, typename _Up,
bool = __ptrtr_rebind_helper<_Tp, _Up>::__type::value>
struct __ptrtr_rebind;
template<typename _Tp, typename _Up>
struct __ptrtr_rebind<_Tp, _Up, true>
{
typedef typename _Tp::template rebind<_Up> __type;
};
template<template<typename, typename...> class _SomePtr, typename _Up,
typename _Tp, typename... _Args>
struct __ptrtr_rebind<_SomePtr<_Tp, _Args...>, _Up, false>
{
typedef _SomePtr<_Up, _Args...> __type;
};
template<typename _Tp, typename = typename std::remove_cv<_Tp>::type>
struct __ptrtr_not_void
{
typedef _Tp __type;
};
template<typename _Tp>
struct __ptrtr_not_void<_Tp, void>
{
struct __type { };
};
template<typename _Ptr>
class __ptrtr_pointer_to
{
typedef typename __ptrtr_elt_type<_Ptr>::__type __orig_type;
typedef typename __ptrtr_not_void<__orig_type>::__type __element_type;
public:
static _Ptr pointer_to(__element_type& __e)
{ return _Ptr::pointer_to(__e); }
};
/**
* @brief Uniform interface to all pointer-like types
* @ingroup pointer_abstractions
*/
template<typename _Ptr>
struct pointer_traits : __ptrtr_pointer_to<_Ptr>
{
/// The pointer type
typedef _Ptr pointer;
/// The type pointed to
typedef typename __ptrtr_elt_type<_Ptr>::__type element_type;
/// Type used to represent the difference between two pointers
typedef typename __ptrtr_diff_type<_Ptr>::__type difference_type;
template<typename _Up>
using rebind = typename __ptrtr_rebind<_Ptr, _Up>::__type;
};
/**
* @brief Partial specialization for built-in pointers.
* @ingroup pointer_abstractions
*/
template<typename _Tp>
struct pointer_traits<_Tp*>
{
/// The pointer type
typedef _Tp* pointer;
/// The type pointed to
typedef _Tp element_type;
/// Type used to represent the difference between two pointers
typedef ptrdiff_t difference_type;
template<typename _Up>
using rebind = _Up*;
/**
* @brief Obtain a pointer to an object
* @param __r A reference to an object of type @c element_type
* @return @c addressof(__r)
*/
static pointer
pointer_to(typename __ptrtr_not_void<element_type>::__type& __r) noexcept
{ return std::addressof(__r); }
};
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif

View file

@ -0,0 +1,103 @@
// <range_access.h> -*- C++ -*-
// Copyright (C) 2010-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file bits/range_access.h
* This is an internal header file, included by other library headers.
* Do not attempt to use it directly. @headername{iterator}
*/
#pragma once
#pragma GCC system_header
#if __cplusplus >= 201103L
#include "c++config.h"
namespace geode::stl {
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/**
* @brief Return an iterator pointing to the first element of
* the container.
* @param __cont Container.
*/
template<class _Container>
inline auto
begin(_Container& __cont) -> decltype(__cont.begin())
{ return __cont.begin(); }
/**
* @brief Return an iterator pointing to the first element of
* the const container.
* @param __cont Container.
*/
template<class _Container>
inline auto
begin(const _Container& __cont) -> decltype(__cont.begin())
{ return __cont.begin(); }
/**
* @brief Return an iterator pointing to one past the last element of
* the container.
* @param __cont Container.
*/
template<class _Container>
inline auto
end(_Container& __cont) -> decltype(__cont.end())
{ return __cont.end(); }
/**
* @brief Return an iterator pointing to one past the last element of
* the const container.
* @param __cont Container.
*/
template<class _Container>
inline auto
end(const _Container& __cont) -> decltype(__cont.end())
{ return __cont.end(); }
/**
* @brief Return an iterator pointing to the first element of the array.
* @param __arr Array.
*/
template<class _Tp, size_t _Nm>
inline _Tp*
begin(_Tp (&__arr)[_Nm])
{ return __arr; }
/**
* @brief Return an iterator pointing to one past the last element
* of the array.
* @param __arr Array.
*/
template<class _Tp, size_t _Nm>
inline _Tp*
end(_Tp (&__arr)[_Nm])
{ return __arr + _Nm; }
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif // C++11

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
#pragma once
#include "c++config.h"
namespace geode::stl {
// Helper for SFINAE constraints
template<typename... _Cond>
using _Require = std::enable_if_t<std::conjunction<_Cond...>::value>;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,48 @@
// <unordered_map> -*- C++ -*-
// Copyright (C) 2007-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file include/unordered_map
* This is a Standard C++ Library header.
*/
#pragma once
#if __cplusplus < 201103L
# error not c++11
#else
#include <utility>
#include <type_traits>
#include <initializer_list>
#include <tuple>
#include "allocator.h"
#include "ext/alloc_traits.h"
#include "ext/aligned_buffer.h"
#include "stl_function.h" // equal_to, _Identity, _Select1st
#include "functional_hash.h"
#include "hashtable.h"
#include "unordered_map.h"
#include "range_access.h"
#endif // C++11

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,53 @@
// <unordered_set> -*- C++ -*-
// Copyright (C) 2007-2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
/** @file include/unordered_set
* This is a Standard C++ Library header.
*/
#ifndef _GLIBCXX_UNORDERED_SET
#define _GLIBCXX_UNORDERED_SET 1
#pragma GCC system_header
#if __cplusplus < 201103L
# error "not c++11"
#else
#include <utility>
#include <type_traits>
#include <initializer_list>
#include <tuple>
#include "allocator.h"
#include "ext/alloc_traits.h"
#include "ext/aligned_buffer.h"
#include "stl_function.h" // equal_to, _Identity, _Select1st
#include "functional_hash.h"
#include "hashtable.h"
#include "unordered_set.h"
#include "range_access.h"
#endif // C++11
#endif // _GLIBCXX_UNORDERED_SET

View file

@ -39,6 +39,7 @@
#include "../include/CCProtocols.h"
#include "Layout.hpp"
#include "../../loader/Event.hpp"
#include <Geode/utils/casts.hpp>
#ifndef GEODE_IS_MEMBER_TEST
#include <matjson.hpp>
@ -1115,7 +1116,7 @@ public:
geode::utils::MiniFunction<typename Filter::Callback> callback,
Args&&... args
) {
return this->template addEventListener<Filter, Args...>(
return this->addEventListener<Filter, Args...>(
"", callback, std::forward<Args>(args)...
);
}
@ -1123,6 +1124,44 @@ public:
GEODE_DLL void removeEventListener(std::string const& id);
GEODE_DLL geode::EventListenerProtocol* getEventListener(std::string const& id);
GEODE_DLL size_t getEventListenerCount();
/**
* Get nth child that is a given type. Checks bounds.
* @returns Child at index cast to the given type,
* or nullptr if index exceeds bounds
*/
template <class T = CCNode>
T* getChildByType(int index) {
size_t indexCounter = 0;
if (this->getChildrenCount() == 0) return nullptr;
// start from end for negative index
if (index < 0) {
index = -index - 1;
for (size_t i = this->getChildrenCount() - 1; i >= 0; i--) {
auto obj = geode::cast::typeinfo_cast<T*>(this->getChildren()->objectAtIndex(i));
if (obj != nullptr) {
if (indexCounter == index) {
return obj;
}
++indexCounter;
}
if (i == 0) break;
}
}
else {
for (size_t i = 0; i < this->getChildrenCount(); i++) {
auto obj = geode::cast::typeinfo_cast<T*>(this->getChildren()->objectAtIndex(i));
if (obj != nullptr) {
if (indexCounter == index) {
return obj;
}
++indexCounter;
}
}
}
return nullptr;
}
/// @{
/// @name Shader Program

View file

@ -181,6 +181,7 @@ public:
* @lua NA
*/
CCDictionary();
GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCDictionary, CCObject);
/**
* The destructor of CCDictionary

View file

@ -55,64 +55,64 @@ public:
float y;
// alk modifications: all defined
inline CCPoint() : x(0), y(0) {}
inline CCPoint(float x, float y) : x(x), y(y) {}
inline constexpr CCPoint() : x(0), y(0) {}
inline constexpr CCPoint(float x, float y) : x(x), y(y) {}
/**
* @lua NA
*/
inline CCPoint(const CCPoint& other) : x(other.x), y(other.y) {}
inline constexpr CCPoint(const CCPoint& other) : x(other.x), y(other.y) {}
/**
* @lua NA
*/
inline CCPoint(const CCSize& size);
inline constexpr CCPoint(const CCSize& size);
/**
* @lua NA
*/
inline CCPoint& operator= (const CCPoint& other) {
inline constexpr CCPoint& operator= (const CCPoint& other) {
setPoint(other.x, other.y);
return *this;
}
/**
* @lua NA
*/
inline CCPoint& operator= (const CCSize& size);
inline constexpr CCPoint& operator= (const CCSize& size);
/**
* @lua NA
*/
inline CCPoint operator+(const CCPoint& right) const {
inline constexpr CCPoint operator+(const CCPoint& right) const {
return CCPoint(this->x + right.x, this->y + right.y);
}
/**
* @lua NA
*/
inline CCPoint operator-(const CCPoint& right) const {
inline constexpr CCPoint operator-(const CCPoint& right) const {
return CCPoint(this->x - right.x, this->y - right.y);
}
/**
* @lua NA
*/
inline CCPoint operator-() const {
inline constexpr CCPoint operator-() const {
return CCPoint(-x, -y);
}
/**
* @lua NA
*/
inline CCPoint operator*(float a) const {
inline constexpr CCPoint operator*(float a) const {
return CCPoint(this->x * a, this->y * a);
}
/**
* @lua NA
*/
inline CCPoint operator/(float a) const {
inline constexpr CCPoint operator/(float a) const {
return CCPoint(this->x / a, this->y / a);
}
// camila modification
inline CCPoint operator*(const CCPoint& right) const {
inline constexpr CCPoint operator*(const CCPoint& right) const {
return CCPoint(x * right.x, y * right.y);
}
inline CCPoint operator/(const CCPoint& right) const {
inline constexpr CCPoint operator/(const CCPoint& right) const {
return CCPoint(x / right.x, y / right.y);
}
//314
@ -120,20 +120,20 @@ public:
/**
* @lua NA
*/
inline void setPoint(float x, float y) {
inline constexpr void setPoint(float x, float y) {
this->x = x;
this->y = y;
}
inline bool equals(const CCPoint& target) const {
return (fabs(this->x - target.x) < FLT_EPSILON) && (fabs(this->y - target.y) < FLT_EPSILON);
inline constexpr bool equals(const CCPoint& target) const {
return this->fuzzyEquals(target, FLT_EPSILON);
}
/** @returns if points have fuzzy equality which means equal with some degree of variance.
* @since v2.1.4
* @lua NA
*/
inline bool fuzzyEquals(const CCPoint& b, float var) const {
inline constexpr bool fuzzyEquals(const CCPoint& b, float var) const {
if(x - var <= b.x && b.x <= x + var)
if(y - var <= b.y && b.y <= y + var)
return true;
@ -154,7 +154,7 @@ public:
* @since v2.1.4
* @lua NA
*/
inline float getLengthSq() const {
inline constexpr float getLengthSq() const {
return dot(*this); //x*x + y*y;
};
@ -162,7 +162,7 @@ public:
@return float
@since v2.1.4
*/
inline float getDistanceSq(const CCPoint& other) const {
inline constexpr float getDistanceSq(const CCPoint& other) const {
return (*this - other).getLengthSq();
};
@ -196,7 +196,7 @@ public:
@return float
@since v2.1.4
*/
inline float dot(const CCPoint& other) const {
inline constexpr float dot(const CCPoint& other) const {
return x*other.x + y*other.y;
};
@ -204,7 +204,7 @@ public:
@return float
@since v2.1.4
*/
inline float cross(const CCPoint& other) const {
inline constexpr float cross(const CCPoint& other) const {
return x*other.y - y*other.x;
};
@ -212,7 +212,7 @@ public:
@return CCPoint
@since v2.1.4
*/
inline CCPoint getPerp() const {
inline constexpr CCPoint getPerp() const {
return CCPoint(-y, x);
};
@ -220,7 +220,7 @@ public:
@return CCPoint
@since v2.1.4
*/
inline CCPoint getRPerp() const {
inline constexpr CCPoint getRPerp() const {
return CCPoint(y, -x);
};
@ -228,7 +228,7 @@ public:
@return CCPoint
@since v2.1.4
*/
inline CCPoint project(const CCPoint& other) const {
inline constexpr CCPoint project(const CCPoint& other) const {
return other * (dot(other)/other.dot(other));
};
@ -237,7 +237,7 @@ public:
and a length of this.getLength() * other.getLength().
@since v2.1.4
*/
inline CCPoint rotate(const CCPoint& other) const {
inline constexpr CCPoint rotate(const CCPoint& other) const {
return CCPoint(x*other.x - y*other.y, x*other.y + y*other.x);
};
@ -246,7 +246,7 @@ public:
and a length of this.getLength() * other.getLength().
@since v2.1.4
*/
inline CCPoint unrotate(const CCPoint& other) const {
inline constexpr CCPoint unrotate(const CCPoint& other) const {
return CCPoint(x*other.x + y*other.y, y*other.x - x*other.y);
};
@ -268,7 +268,7 @@ public:
otherwise a value between a..b
@since v2.1.4
*/
inline CCPoint lerp(const CCPoint& other, float alpha) const {
inline constexpr CCPoint lerp(const CCPoint& other, float alpha) const {
return *this * (1.f - alpha) + other * alpha;
};
@ -287,11 +287,21 @@ public:
return CCPoint(cosf(a), sinf(a));
}
void add(int, float);
float at(int);
bool isZero() const;
void set(int, float);
void swap();
constexpr void add(int idx, float val) {
idx == 0 ? x += val : y += val;
}
constexpr float at(int idx) {
return idx == 0 ? x : y;
}
constexpr bool isZero() const {
return x == 0.f && y == 0.f;
}
constexpr void set(int idx, float val) {
idx == 0 ? x = val : y = val;
}
constexpr void swap() {
std::swap(x, y);
}
};
@ -306,86 +316,104 @@ public:
float height;
public:
inline CCSize() : width(0), height(0) {}
inline CCSize(float width, float height) : width(width), height(height) {}
inline constexpr CCSize() : width(0), height(0) {}
inline constexpr CCSize(float width, float height) : width(width), height(height) {}
/**
* @lua NA
*/
inline CCSize(const CCSize& other) : width(other.width), height(other.height) {}
inline constexpr CCSize(const CCSize& other) : width(other.width), height(other.height) {}
/**
* @lua NA
*/
inline CCSize(const CCPoint& point) : width(point.x), height(point.y) {}
inline constexpr CCSize(const CCPoint& point) : width(point.x), height(point.y) {}
/**
* @lua NA
*/
inline CCSize& operator= (const CCSize& other) {
inline constexpr CCSize& operator= (const CCSize& other) {
setSize(other.width, other.height);
return *this;
}
/**
* @lua NA
*/
inline CCSize& operator= (const CCPoint& point) {
inline constexpr CCSize& operator= (const CCPoint& point) {
setSize(point.x, point.y);
return *this;
}
/**
* @lua NA
*/
inline CCSize operator+(const CCSize& right) const {
inline constexpr CCSize operator+(const CCSize& right) const {
return CCSize(this->width + right.width, this->height + right.height);
}
/**
* @lua NA
*/
inline CCSize operator-(const CCSize& right) const {
inline constexpr CCSize operator-(const CCSize& right) const {
return CCSize(this->width - right.width, this->height - right.height);
}
/**
* @lua NA
*/
inline CCSize operator*(float a) const {
inline constexpr CCSize operator*(float a) const {
return CCSize(this->width * a, this->height * a);
}
/**
* @lua NA
*/
inline CCSize operator/(float a) const {
inline constexpr CCSize operator/(float a) const {
return CCSize(this->width / a, this->height / a);
}
/**
* @lua NA
*/
inline void setSize(float width, float height) {
inline constexpr void setSize(float width, float height) {
this->width = width;
this->height = height;
}
/**
* @lua NA
*/
inline bool equals(const CCSize& target) const {
return (fabs(this->width - target.width) < FLT_EPSILON) && (fabs(this->height - target.height) < FLT_EPSILON);
inline constexpr bool equals(const CCSize& target) const {
return this->fuzzyEquals(target, FLT_EPSILON);
}
inline constexpr bool fuzzyEquals(const CCSize& b, float var) const {
if(width - var <= b.width && b.width <= width + var)
if(height - var <= b.height && b.height <= height + var)
return true;
return false;
}
/**
* Get the aspect ratio of this CCSize
* @note Geode addition
*/
inline float aspect() const {
inline constexpr float aspect() const {
return this->width / this->height;
}
void add(int, float);
float at(int);
void set(int, float);
constexpr void add(int idx, float val) {
idx == 0 ? width += val : height += val;
}
constexpr float at(int idx) {
return idx == 0 ? width : height;
}
constexpr bool isZero() const {
return width == 0.f && height == 0.f;
}
constexpr void set(int idx, float val) {
idx == 0 ? width = val : height = val;
}
constexpr void swap() {
std::swap(width, height);
}
};
// alk cont
CCPoint::CCPoint(const CCSize& size) : x(size.width), y(size.height) {}
constexpr CCPoint::CCPoint(const CCSize& size) : x(size.width), y(size.height) {}
CCPoint& CCPoint::operator= (const CCSize& size) {
constexpr CCPoint& CCPoint::operator= (const CCSize& size) {
setPoint(size.width, size.height);
return *this;
}
@ -402,29 +430,29 @@ public:
CCSize size;
public:
inline CCRect() {
inline constexpr CCRect() {
setRect(0.0f, 0.0f, 0.0f, 0.0f);
}
inline CCRect(float x, float y, float width, float height) {
inline constexpr CCRect(float x, float y, float width, float height) {
setRect(x, y, width, height);
}
inline CCRect(CCPoint const& a, CCPoint const& b) {
inline constexpr CCRect(CCPoint const& a, CCPoint const& b) {
setRect(a.x, a.y, b.x, b.y);
}
/**
* @lua NA
*/
inline CCRect(const CCRect& other) {
inline constexpr CCRect(const CCRect& other) {
setRect(other.origin.x, other.origin.y, other.size.width, other.size.height);
}
/**
* @lua NA
*/
inline CCRect& operator= (const CCRect& other) {
inline constexpr CCRect& operator= (const CCRect& other) {
setRect(other.origin.x, other.origin.y, other.size.width, other.size.height);
return *this;
}
inline void setRect(float x, float y, float width, float height) {
inline constexpr void setRect(float x, float y, float width, float height) {
// CGRect can support width<0 or height<0
// CCAssert(width >= 0.0f && height >= 0.0f, "width and height of Rect must not less than 0.");
@ -435,28 +463,28 @@ public:
size.height = height;
}
inline float getMinX() const {
inline constexpr float getMinX() const {
return origin.x;
} /// return the leftmost x-value of current rect
inline float getMidX() const {
inline constexpr float getMidX() const {
return (float)(origin.x + size.width / 2.0);
} /// return the midpoint x-value of current rect
inline float getMaxX() const {
inline constexpr float getMaxX() const {
return (float)(origin.x + size.width);
} /// return the rightmost x-value of current rect
inline float getMinY() const {
inline constexpr float getMinY() const {
return origin.y;
} /// return the bottommost y-value of current rect
inline float getMidY() const {
inline constexpr float getMidY() const {
return (float)(origin.y + size.height / 2.0);
} /// return the midpoint y-value of current rect
inline float getMaxY() const {
inline constexpr float getMaxY() const {
return origin.y + size.height;
} /// return the topmost y-value of current rect
inline bool equals(const CCRect& rect) const {
inline constexpr bool equals(const CCRect& rect) const {
return (origin.equals(rect.origin) && size.equals(rect.size));
}
inline bool containsPoint(const CCPoint& point) const {
inline constexpr bool containsPoint(const CCPoint& point) const {
bool bRet = false;
if (point.x >= getMinX() && point.x <= getMaxX()
@ -468,7 +496,7 @@ public:
return bRet;
}
inline bool intersectsRect(const CCRect& rect) const {
inline constexpr bool intersectsRect(const CCRect& rect) const {
// lmao
return !( getMaxX() < rect.getMinX() ||
rect.getMaxX() < getMinX() ||
@ -476,8 +504,12 @@ public:
rect.getMaxY() < getMinY());
}
float getMax(int);
float getMin(int);
constexpr float getMax(int idx) {
return idx == 0 ? getMaxX() : getMaxY();
}
constexpr float getMin(int idx) {
return idx == 0 ? getMinX() : getMinY();
}
};

View file

@ -99,6 +99,7 @@ public:
void drawPreciseCubicBezier(cocos2d::CCPoint const&, cocos2d::CCPoint const&, cocos2d::CCPoint const&, cocos2d::CCPoint const&, unsigned int, cocos2d::_ccColor4F const&);
bool drawLines(cocos2d::CCPoint*, unsigned int, float, cocos2d::_ccColor4F const&);
bool drawRect(cocos2d::CCPoint const&, cocos2d::CCPoint const&, cocos2d::_ccColor4F const&, float, cocos2d::_ccColor4F const&);
bool drawRect(cocos2d::CCRect const&, cocos2d::_ccColor4F const&, float, cocos2d::_ccColor4F const&);
void disableDrawArea();
void enableDrawArea(cocos2d::CCRect& rect);
#else

View file

@ -274,9 +274,9 @@ public:
void limitLabelWidth(float width, float defaultScale, float minScale);
// @note RobTop Addition
int getExtraKerning() const;
inline int getExtraKerning() const { return m_nExtraKerning; }
// @note RobTop Addition
void setExtraKerning(int);
inline void setExtraKerning(int extraKerning) { m_nExtraKerning = extraKerning; }
// @note RobTop Addition
bool getIsBatched() const;
@ -337,7 +337,7 @@ protected:
// @note RobTop Addition
CCTexture2D* m_pSomeTexture;
// @note RobTop Addition
void* m_pUnknown; // 2.2 addition, might be positioned somewhere else
int m_nExtraKerning;
};

View file

@ -71,13 +71,7 @@ THE SOFTWARE.
#include "./compat/stdint.h"
#endif
#define _WINSOCKAPI_
// Structure timeval has define in winsock.h, include windows.h for it.
#include <Windows.h>
#ifndef __MINGW32__
// #include <WinSock2.h>
struct timeval;
NS_CC_BEGIN
@ -91,11 +85,5 @@ int CC_DLL gettimeofday(struct timeval *, struct timezone *);
NS_CC_END
#else
#include <winsock.h>
#endif // __MINGW32__
#endif // __CC_STD_C_H__

View file

@ -11,6 +11,7 @@ class CCLightning : public CCNode, public CCRGBAProtocol {
public:
CCLightning();
virtual ~CCLightning();
GEODE_CUTOFF_CONSTRUCTOR_GD(CCLightning, CCNode);
static CCLightning* lightningWithStrikePoint(CCPoint strikePoint, CCPoint strikePoint2, float duration);
static CCLightning* lightningWithStrikePoint(CCPoint strikePoint);
@ -67,4 +68,4 @@ protected:
NS_CC_END
#endif //__CCLIGHTNING_H__
#endif //__CCLIGHTNING_H__

View file

@ -56,4 +56,9 @@ namespace geode::dirs {
* Directory where crashlogs are stored
*/
GEODE_DLL std::filesystem::path getCrashlogsDir();
/**
* Directory where mods' persistent files lie
* This directory is not deleted even when Geode is uninstalled
*/
GEODE_DLL std::filesystem::path getModPersistentDir();
}

View file

@ -172,6 +172,11 @@ namespace geode {
* Get the mod's config directory path
*/
std::filesystem::path getConfigDir(bool create = true) const;
/**
* Get the mod's persistent directory path
* This directory is not deleted even when Geode/mod is uninstalled
*/
std::filesystem::path getPersistentDir(bool create = true) const;
/**
* Returns true if this mod has any settings

View file

@ -4,12 +4,16 @@
#include "SettingV3.hpp"
namespace geode {
class Mod;
class SettingV3;
class GEODE_DLL ModSettingsManager final {
private:
class Impl;
std::unique_ptr<Impl> m_impl;
friend class ::geode::SettingV3;
friend class ::geode::Mod;
void markRestartRequired();
@ -36,9 +40,22 @@ namespace geode {
* The format of the savedata will be an object with the keys being
* setting IDs and then the values the values of the saved settings
* @note If saving a setting fails, it will log a warning to the console
* @warning This will overwrite the whole `json` parameter - be sure to
* pass the full settings savedata to `load()` so you can be sure that
* unregistered custom settings' saved values don't disappear!
* @todo in v4: make this return the value instead lol
*/
void save(matjson::Value& json);
/**
* Get the savedata for settings, aka the JSON object that contains all
* the settings' saved states that was loaded up from disk and will be
* saved to disk
* @warning Modifying this will modify the value of the settings - use
* carefully!
*/
matjson::Value& getSaveData();
Result<> registerCustomSettingType(std::string_view type, SettingGenerator generator);
// todo in v4: remove this
Result<> registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr);

View file

@ -324,8 +324,8 @@ namespace geode {
}
bool load(matjson::Value const& json) override {
if (json.template is<T>()) {
m_impl->value = json.template as<T>();
if (json.is<T>()) {
m_impl->value = json.as<T>();
return true;
}
return false;

View file

@ -86,7 +86,7 @@ namespace geode {
public:
static ListBorders* create();
void setSpriteFrames(const char* topAndBottom, const char* sides, float topPadding = 7.5f);
void setSpriteFrames(const char* topAndBottom, const char* sides, float horizontalPadding = 7.5f);
void setSprites(
cocos2d::extension::CCScale9Sprite* top,
cocos2d::extension::CCScale9Sprite* bottom,

View file

@ -2,6 +2,7 @@
#include "../loader/Mod.hpp"
#include <Geode/binding/FLAlertLayer.hpp>
#include <Geode/ui/Popup.hpp>
class ModPopup;
class ModItem;
@ -141,6 +142,16 @@ namespace geode {
* Open the settings popup for a mod (if it has any settings)
*/
GEODE_DLL void openSettingsPopup(Mod* mod);
/**
* Open the settings popup for a mod (if it has any settings)
* @param mod Mod the open the popup for
* @param disableGeodeTheme If false, the popup follows the user's chosen
* theme options. If true, the popup is always in the GD theme (not Geode's
* dark purple colors)
* @returns A pointer to the created Popup, or null if the mod has no
* settings
*/
GEODE_DLL Popup<Mod*>* openSettingsPopup(Mod* mod, bool disableGeodeTheme);
/**
* Create a default logo sprite
*/

View file

@ -6,8 +6,59 @@
#include <Geode/utils/cocos.hpp>
namespace geode {
template <typename... InitArgs>
template <class... InitArgs>
class Popup : public FLAlertLayer {
public:
/**
* Event posted when this popup is being closed
*/
class CloseEvent final : public ::geode::Event {
private:
class Impl final {
private:
Popup* popup;
friend class CloseEvent;
};
std::shared_ptr<Impl> m_impl;
friend class Popup;
CloseEvent(Popup* popup) : m_impl(std::make_shared<Impl>()) {
m_impl->popup = popup;
}
public:
Popup* getPopup() const {
return m_impl->popup;
}
};
class CloseEventFilter final : public ::geode::EventFilter<CloseEvent> {
public:
using Callback = void(CloseEvent*);
private:
class Impl final {
private:
Popup* popup;
friend class CloseEventFilter;
};
std::shared_ptr<Impl> m_impl;
friend class Popup;
CloseEventFilter(Popup* popup) : m_impl(std::make_shared<Impl>()) {
m_impl->popup = popup;
}
public:
ListenerResult handle(utils::MiniFunction<Callback> fn, CloseEvent* event) {
if (event->getPopup() == m_impl->popup) {
fn(event);
}
return ListenerResult::Propagate;
}
};
protected:
cocos2d::CCSize m_size;
cocos2d::extension::CCScale9Sprite* m_bgSprite;
@ -115,6 +166,7 @@ namespace geode {
}
virtual void onClose(cocos2d::CCObject*) {
CloseEvent(this).post();
this->setKeypadEnabled(false);
this->setTouchEnabled(false);
this->removeFromParentAndCleanup(true);
@ -158,6 +210,13 @@ namespace geode {
spr->setAnchorPoint(orig->getAnchorPoint());
m_closeBtn->setContentSize(origSize);
}
/**
* Returns an event filter that listens for when this popup is closed
*/
CloseEventFilter listenForClose() {
return CloseEventFilter(this);
}
};
GEODE_DLL FLAlertLayer* createQuickPopup(

View file

@ -3,28 +3,40 @@
#include "../DefaultInclude.hpp"
#include <cocos2d.h>
namespace cocos2d {
class CCArray;
class CCNode;
}
#include <vector>
#include <span>
#include <Geode/utils/cocos.hpp>
namespace geode {
class GEODE_DLL SceneManager final {
protected:
cocos2d::CCArray* m_persistedNodes;
std::vector<Ref<cocos2d::CCNode>> m_persistedNodes;
cocos2d::CCScene* m_lastScene = nullptr;
bool setup();
virtual ~SceneManager();
public:
static SceneManager* get();
/**
* Adds a node to the list of persisted nodes, which are kept across scene changes.
* @param node The node to keep across scenes.
*/
void keepAcrossScenes(cocos2d::CCNode* node);
/**
* Removes a node from the list of persisted nodes.
* @param node The node to forget.
*/
void forget(cocos2d::CCNode* node);
/**
* Gets a span of the persisted nodes. To add new nodes to the list, use keepAcrossScenes.
*/
std::span<Ref<cocos2d::CCNode> const> getPersistedNodes();
// This method should only be called by geode itself
// TODO(v4): hide this
void willSwitchToScene(cocos2d::CCScene* scene);
};
}

View file

@ -5,6 +5,7 @@
#include <set>
#include <variant>
#include <Geode/utils/MiniFunction.hpp>
#include <Geode/utils/Result.hpp>
namespace geode {
struct JsonChecker;
@ -147,14 +148,14 @@ namespace geode {
template <class T>
bool is() {
if (this->isError()) return false;
return self().m_json.template is<T>();
return self().m_json.is<T>();
}
template <class T>
JsonMaybeValue& validate(JsonValueValidator<T> validator) {
if (this->isError()) return *this;
if (self().m_json.template is<T>()) {
if (!validator(self().m_json.template as<T>())) {
if (self().m_json.is<T>()) {
if (!validator(self().m_json.as<T>())) {
this->setError(self().m_hierarchy + ": Invalid value format");
}
}
@ -195,9 +196,9 @@ namespace geode {
this->inferType<A>();
if (this->isError()) return *this;
if (self().m_json.template is<A>()) {
if (self().m_json.is<A>()) {
try {
target = self().m_json.template as<A>();
target = self().m_json.as<A>();
}
catch(matjson::JsonException const& e) {
this->setError(
@ -219,8 +220,8 @@ namespace geode {
T get() {
this->inferType<T>();
if (this->isError()) return T();
if (self().m_json.template is<T>()) {
return self().m_json.template as<T>();
if (self().m_json.is<T>()) {
return self().m_json.as<T>();
}
return T();
}
@ -325,8 +326,8 @@ namespace geode {
}
else {
try {
if (this->getJSONRef().template is<T>()) {
return this->getJSONRef().template as<T>();
if (this->getJSONRef().is<T>()) {
return this->getJSONRef().as<T>();
}
else {
this->setError(
@ -396,21 +397,21 @@ namespace geode {
template <class T>
T get(T const& defaultValue = T()) {
if (auto v = this->template tryGet<T>()) {
if (auto v = this->tryGet<T>()) {
return *std::move(v);
}
return defaultValue;
}
template <class T>
JsonExpectedValue& into(T& value) {
if (auto v = this->template tryGet<T>()) {
if (auto v = this->tryGet<T>()) {
value = *std::move(v);
}
return *this;
}
template <class T>
JsonExpectedValue& into(std::optional<T>& value) {
if (auto v = this->template tryGet<T>()) {
if (auto v = this->tryGet<T>()) {
value.emplace(*std::move(v));
}
return *this;
@ -420,7 +421,7 @@ namespace geode {
{ predicate(std::declval<T>()) } -> std::convertible_to<bool>;
} {
if (this->hasError()) return *this;
if (auto v = this->template tryGet<T>()) {
if (auto v = this->tryGet<T>()) {
if (!predicate(*v)) {
this->setError("json value is not {}", name);
}
@ -432,7 +433,7 @@ namespace geode {
{ predicate(std::declval<T>()) } -> std::convertible_to<Result<>>;
} {
if (this->hasError()) return *this;
if (auto v = this->template tryGet<T>()) {
if (auto v = this->tryGet<T>()) {
auto p = predicate(*v);
if (!p) {
this->setError("json value is not {}: {}", name, p.unwrapErr());

View file

@ -14,7 +14,7 @@ namespace geode::node_ids {
return child;
}
} else {
if (auto child = cocos::getChildOfType<T>(node, index)) {
if (auto child = node->getChildByType<T>(index)) {
child->setID(id);
return child;
}

View file

@ -30,28 +30,28 @@ struct matjson::Serialize<cocos2d::ccColor4B> {
// operators for CC geometry
namespace cocos2d {
static cocos2d::CCPoint& operator*=(cocos2d::CCPoint& pos, float mul) {
static constexpr cocos2d::CCPoint& operator*=(cocos2d::CCPoint& pos, float mul) {
pos.x *= mul;
pos.y *= mul;
return pos;
}
static cocos2d::CCSize& operator*=(cocos2d::CCSize& size, float mul) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr cocos2d::CCRect operator*(cocos2d::CCRect const& rect, float mul) {
return {
rect.origin.x * mul,
rect.origin.y * mul,
@ -59,131 +59,131 @@ namespace cocos2d {
rect.size.height * mul,
};
}
static cocos2d::CCPoint operator/=(cocos2d::CCPoint& pos, float div) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr cocos2d::CCSize operator-(cocos2d::CCSize const& size, float f) {
return {size.width - f, size.height - f};
}
static cocos2d::CCSize operator-(cocos2d::CCSize const& size) {
static constexpr cocos2d::CCSize operator-(cocos2d::CCSize const& size) {
return {-size.width, -size.height};
}
static bool operator==(cocos2d::CCPoint const& p1, cocos2d::CCPoint const& p2) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr 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) {
static constexpr bool operator!=(cocos2d::ccHSVValue const& c1, cocos2d::ccHSVValue const& c2) {
return !(c1 == c2);
}
}
@ -620,37 +620,11 @@ namespace geode::cocos {
* @returns Child at index cast to the given type,
* or nullptr if index exceeds bounds
*/
template <class Type = cocos2d::CCNode>
static Type* getChildOfType(cocos2d::CCNode* node, int index) {
size_t indexCounter = 0;
if (node->getChildrenCount() == 0) return nullptr;
// start from end for negative index
if (index < 0) {
index = -index - 1;
for (size_t i = node->getChildrenCount() - 1; i >= 0; i--) {
auto obj = cast::typeinfo_cast<Type*>(node->getChildren()->objectAtIndex(i));
if (obj != nullptr) {
if (indexCounter == index) {
return obj;
}
++indexCounter;
}
if (i == 0) break;
}
}
else {
for (size_t i = 0; i < node->getChildrenCount(); i++) {
auto obj = cast::typeinfo_cast<Type*>(node->getChildren()->objectAtIndex(i));
if (obj != nullptr) {
if (indexCounter == index) {
return obj;
}
++indexCounter;
}
}
}
return nullptr;
template <class Type = cocos2d::CCNode>
[[deprecated("Use CCNode::getChildByType instead")]]
static Type* getChildOfType(cocos2d::CCNode* node, int index) {
return node->getChildByType<Type>(index);
}
/**
@ -997,7 +971,8 @@ namespace std {
template <typename T>
struct std::hash<geode::WeakRef<T>> {
size_t operator()(geode::WeakRef<T> const& ref) const {
return hash{}(ref.m_controller);
// the explicit template argument is needed here because it would otherwise cast to WeakRef and recurse
return hash<std::shared_ptr<geode::WeakRefController>>{}(ref.m_controller);
}
};
}
@ -1105,7 +1080,7 @@ namespace geode::cocos {
}
T* pop_back() {
T ret = m_arr->lastObject();
T* ret = static_cast<T*>(m_arr->lastObject());
m_arr->removeLastObject();
return ret;
}
@ -1388,6 +1363,42 @@ namespace geode::cocos {
}
};
// CCCallFunc alternative that accepts a lambda (or any function object)
template <std::invocable F>
class CallFuncExtImpl : public cocos2d::CCActionInstant {
public:
static CallFuncExtImpl* create(const F& func) {
auto ret = new CallFuncExtImpl;
ret->m_func = func;
ret->autorelease();
return ret;
}
static CallFuncExtImpl* create(F&& func) {
auto ret = new CallFuncExtImpl;
ret->m_func = std::move(func);
ret->autorelease();
return ret;
}
private:
F m_func;
void update(float) override {
if (m_func) this->m_func();
}
};
// small hack to allow template deduction
struct CallFuncExt {
template <std::invocable F>
static auto create(F&& func) {
using Fd = std::decay_t<F>;
return CallFuncExtImpl<Fd>::create(std::forward<F>(func));
}
};
void GEODE_DLL handleTouchPriorityWith(cocos2d::CCNode* node, int priority, bool force = false);
void GEODE_DLL handleTouchPriority(cocos2d::CCNode* node, bool force = false);
}

View file

@ -17,7 +17,7 @@ struct matjson::Serialize<std::filesystem::path> {
return path.string();
}
static std::filesystem::path from_json(matjson::Value const& value) {
return value.as_string();
return std::filesystem::path(value.as_string()).make_preferred();
}
static bool is_json(matjson::Value const& value) {
return value.is_string();
@ -32,10 +32,10 @@ namespace geode::utils::file {
template <class T>
Result<T> readFromJson(std::filesystem::path const& file) {
GEODE_UNWRAP_INTO(auto json, readJson(file));
if (!json.template is<T>()) {
if (!json.is<T>()) {
return Err("JSON is not of type {}", typeid(T).name());
}
return Ok(json.template as<T>());
return Ok(json.as<T>());
}
GEODE_DLL Result<> writeString(std::filesystem::path const& path, std::string const& data);

View file

@ -99,9 +99,22 @@ namespace geode {
*/
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);
// it's a mess... i'm sorry...
constexpr auto numToFixedTrunc = [](float num) {
// calculate the number of digits we keep from the decimal
auto remaining = std::max(3 - static_cast<int>(std::log10(num)) - 1, 0);
auto factor = std::pow(10, remaining);
auto trunc = std::trunc(num * factor) / factor;
// doing this dynamic format thing lets the .0 show when needed
return fmt::format("{:0.{}f}", trunc, static_cast<int>(remaining));
};
if (num >= 1'000'000'000) return fmt::format("{}B", numToFixedTrunc(num / 1'000'000'000.f));
if (num >= 1'000'000) return fmt::format("{}M", numToFixedTrunc(num / 1'000'000.f));
if (num >= 1'000) return fmt::format("{}K", numToFixedTrunc(num / 1'000.f));
return numToString(num);
}
@ -137,8 +150,9 @@ namespace geode {
if constexpr (std::is_floating_point_v<Num>) res = std::from_chars(str.data(), str.data() + str.size(), result);
else res = std::from_chars(str.data(), str.data() + str.size(), result, base);
auto [_, ec] = res;
auto [ptr, ec] = res;
if (ec == std::errc()) return Ok(result);
else if (ptr != str.data() + str.size()) return Err("String contains trailing extra data");
else if (ec == std::errc::invalid_argument) return Err("String is not a number");
else if (ec == std::errc::result_out_of_range) return Err("Number is too large to fit");
else return Err("Unknown error");

View file

@ -1,9 +1,9 @@
#pragma once
#include <Geode/DefaultInclude.hpp>
#include <functional>
#include <string_view>
#include <string>
#include <vector>
#include <compare>
namespace geode::utils::string {
/**
@ -64,4 +64,10 @@ namespace geode::utils::string {
GEODE_DLL bool startsWith(std::string const& str, std::string const& prefix);
GEODE_DLL bool endsWith(std::string const& str, std::string const& suffix);
/**
* Similar to strcmp, but case insensitive.
* Uses std::tolower, but could change in the future for better locale support
*/
GEODE_DLL std::strong_ordering caseInsensitiveCompare(std::string_view a, std::string_view b);
}

View file

@ -12,7 +12,7 @@ constexpr static auto MAX_PATH_CHARS = 32768u;
static HMODULE getXInput() {
static auto xinput = []() -> HMODULE {
std::wstring path(MAX_PATH_CHARS, L'\0');
auto size = GetSystemDirectoryW(const_cast<wchar_t*>(path.data()), path.size());
auto size = GetSystemDirectoryW(path.data(), path.size());
if (size) {
path.resize(size);
return LoadLibraryW((path + L"\\XInput1_4.dll").c_str());

View file

@ -60,6 +60,9 @@
],
"BlankSheet": [
"blanks/*.png"
],
"EventSheet": [
"modtober/*.png"
]
}
},

Binary file not shown.

After

(image error) Size: 101 KiB

Binary file not shown.

After

(image error) Size: 83 KiB

Binary file not shown.

After

(image error) Size: 1 MiB

Binary file not shown.

After

(image error) Size: 12 KiB

BIN
loader/resources/save.png Normal file

Binary file not shown.

After

(image error) Size: 8.9 KiB

Binary file not shown.

Before

(image error) Size: 9.4 KiB

After

(image error) Size: 11 KiB

Before After
Before After

Binary file not shown.

After

(image error) Size: 39 KiB

View file

@ -123,7 +123,7 @@ struct FileUtilsUpdatePaths : Modify<FileUtilsUpdatePaths, CCFileUtils> {
// this is only an issue because cocos itself requests the full path for this in CCSprite,
// and with a lot of search paths (specially ones added by geode), this can cause a significant amount of lag.
// GJ_GameSheetIcons.png comes from an improper plist distributed in GDS :P
if (filename == "cc_2x2_white_image"sv || filename == "GJ_GameSheetIcons.png"sv) {
if (filename == "cc_2x2_white_image"sv) {
return filename;
}

View file

@ -294,7 +294,7 @@ CCNode* CCNode::querySelector(std::string const& queryStr) {
return nullptr;
}
auto query = std::move(res.unwrap());
log::info("parsed query: {}", query->toString());
// log::info("parsed query: {}", query->toString());
return query->match(this);
}

View file

@ -90,12 +90,14 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
Notification::create("There were errors - see Geode page!", NotificationIcon::Error)->show();
}
m_fields->m_exclamation = CCSprite::createWithSpriteFrameName("exMark_001.png");
m_fields->m_exclamation->setPosition(m_fields->m_geodeButton->getContentSize() - ccp(10, 10));
m_fields->m_exclamation->setID("errors-found");
m_fields->m_exclamation->setZOrder(99);
m_fields->m_exclamation->setScale(.6f);
m_fields->m_geodeButton->addChild(m_fields->m_exclamation);
if (m_fields->m_geodeButton) {
m_fields->m_exclamation = CCSprite::createWithSpriteFrameName("exMark_001.png");
m_fields->m_exclamation->setPosition(m_fields->m_geodeButton->getContentSize() - ccp(10, 10));
m_fields->m_exclamation->setID("errors-found");
m_fields->m_exclamation->setZOrder(99);
m_fields->m_exclamation->setScale(.6f);
m_fields->m_geodeButton->addChild(m_fields->m_exclamation);
}
}
// show if the user tried to be naughty and load arbitrary DLLs
@ -103,19 +105,21 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
if (!shownTriedToLoadDlls) {
shownTriedToLoadDlls = true;
if (LoaderImpl::get()->userTriedToLoadDLLs()) {
auto popup = FLAlertLayer::create(
"Hold up!",
"It appears that you have tried to <cr>load DLLs</c> with Geode. "
"Please note that <cy>Geode is incompatible with ALL DLLs</c>, "
"as they can cause Geode mods to <cr>error</c>, or even "
"<cr>crash</c>.\n\n"
"Remove the DLLs / other mod loaders you have, or <cr>proceed at "
"your own risk.</c>",
"OK"
);
popup->m_scene = this;
popup->m_noElasticity = true;
popup->show();
Loader::get()->queueInMainThread([] {
auto popup = FLAlertLayer::create(
"Hold up!",
"It appears that you have tried to <cr>load DLLs</c> with Geode. "
"Please note that <cy>Geode is incompatible with ALL DLLs</c>, "
"as they can cause Geode mods to <cr>error</c>, or even "
"<cr>crash</c>.\n\n"
"Remove the DLLs / other mod loaders you have, or <cr>proceed at "
"your own risk.</c>",
"OK"
);
popup->m_noElasticity = true;
popup->show();
});
}
}
@ -123,15 +127,18 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
static bool shownUpdateInfo = false;
if (updater::isNewUpdateDownloaded() && !shownUpdateInfo) {
shownUpdateInfo = true;
auto popup = FLAlertLayer::create(
"Update downloaded",
"A new <cy>update</c> for Geode has been installed! "
"Please <cy>restart the game</c> to apply.",
"OK"
);
popup->m_scene = this;
popup->m_noElasticity = true;
popup->show();
Loader::get()->queueInMainThread([] {
auto popup = FLAlertLayer::create(
"Update downloaded",
"A new <cy>update</c> for Geode has been installed! "
"Please <cy>restart the game</c> to apply.",
"OK"
);
popup->m_noElasticity = true;
popup->show();
});
}
// show crash info
@ -139,25 +146,29 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
if (
crashlog::didLastLaunchCrash() &&
!shownLastCrash &&
!Mod::get()->template getSettingValue<bool>("disable-last-crashed-popup")
!Mod::get()->getSettingValue<bool>("disable-last-crashed-popup")
) {
shownLastCrash = true;
auto popup = createQuickPopup(
"Crashed",
"It appears that the last session crashed. Would you like to "
"open the <cy>crashlog folder</c>?",
"No",
"Yes",
[](auto, bool btn2) {
if (btn2) {
file::openFolder(dirs::getCrashlogsDir());
}
},
false
);
popup->m_scene = this;
popup->m_noElasticity = true;
popup->show();
// open the dialog a frame later (after the scene is set) for proper key priority
Loader::get()->queueInMainThread([] {
auto popup = createQuickPopup(
"Crashed",
"It appears that the last session crashed. Would you like to "
"open the <cy>crashlog folder</c>?",
"No",
"Yes",
[](auto, bool btn2) {
if (btn2) {
file::openFolder(dirs::getCrashlogsDir());
}
},
false
);
popup->m_noElasticity = true;
popup->show();
});
}
// Check for mod updates
@ -169,9 +180,9 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
[this](server::ServerRequest<std::vector<std::string>>::Value* result) {
if (result->isOk()) {
auto updatesFound = result->unwrap();
if (updatesFound.size() && !m_fields->m_geodeButton->getChildByID("updates-available")) {
if (updatesFound.size() && m_fields->m_geodeButton && !m_fields->m_geodeButton->getChildByID("updates-available")) {
log::info("Found updates for mods: {}!", updatesFound);
if(auto icon = CCSprite::createWithSpriteFrameName("updates-available.png"_spr)) {
// Remove errors icon if it was added, to prevent overlap
if (m_fields->m_exclamation) {

View file

@ -48,7 +48,7 @@ $register_ids(MenuLayer) {
setIDSafe<CCLabelBMFont>(this, labelOffset++, "player-username");
// main menu
if (auto menu = getChildOfType<CCMenu>(this, 0)) {
if (auto menu = this->getChildByType<CCMenu>(0)) {
menu->setID("main-menu");
auto playBtn = setIDSafe(menu, 0, "play-button");
auto iconBtn = setIDSafe(menu, 1, "icon-kit-button");
@ -83,7 +83,7 @@ $register_ids(MenuLayer) {
}
// bottom menu
if (auto menu = getChildOfType<CCMenu>(this, 1)) {
if (auto menu = this->getChildByType<CCMenu>(1)) {
menu->setID("bottom-menu");
auto ach = setIDSafe(menu, 0, "achievements-button");
setIDSafe(menu, 1, "settings-button");
@ -110,7 +110,7 @@ $register_ids(MenuLayer) {
}
// social media menu
if (auto menu = getChildOfType<CCMenu>(this, 2)) {
if (auto menu = this->getChildByType<CCMenu>(2)) {
menu->setID("social-media-menu");
setIDSafe(menu, 0, "robtop-logo-button");
setIDSafe(menu, 1, "facebook-button");
@ -121,7 +121,7 @@ $register_ids(MenuLayer) {
}
// more games menu
if (auto menu = getChildOfType<CCMenu>(this, 3)) {
if (auto menu = this->getChildByType<CCMenu>(3)) {
menu->setID("more-games-menu");
auto moreGamesBtn = setIDSafe(menu, 0, "more-games-button");
@ -159,7 +159,7 @@ $register_ids(MenuLayer) {
// but prolly a place mods want to add stuff
auto topRightMenu = CCMenu::create();
topRightMenu->setPosition(winSize.width - 200.f / 2, winSize.height - 50.f / 2);
topRightMenu->setPosition(winSize.width - 210.f / 2, winSize.height - 50.f / 2);
topRightMenu->setID("top-right-menu");
topRightMenu->setContentSize({ 200.f, 50.f });
topRightMenu->setLayout(

View file

@ -33,8 +33,8 @@ $on_mod(Loaded) {
JsonChecker checker(args);
auto root = checker.root("[ipc/list-mods]").obj();
auto includeRunTimeInfo = root.has("include-runtime-info").template get<bool>();
auto dontIncludeLoader = root.has("dont-include-loader").template get<bool>();
auto includeRunTimeInfo = root.has("include-runtime-info").get<bool>();
auto dontIncludeLoader = root.has("dont-include-loader").get<bool>();
if (!dontIncludeLoader) {
res.push_back(

View file

@ -24,15 +24,15 @@ std::filesystem::path dirs::getGeodeLogDir() {
}
std::filesystem::path dirs::getTempDir() {
return getGeodeDir() / "temp";
return dirs::getGeodeDir() / "temp";
}
std::filesystem::path dirs::getModsDir() {
return getGeodeDir() / "mods";
return dirs::getGeodeDir() / "mods";
}
std::filesystem::path dirs::getModsSaveDir() {
return getGeodeSaveDir() / "mods";
return dirs::getGeodeSaveDir() / "mods";
}
std::filesystem::path dirs::getModConfigDir() {
@ -46,3 +46,7 @@ std::filesystem::path dirs::getIndexDir() {
std::filesystem::path dirs::getCrashlogsDir() {
return crashlog::getCrashLogDirectory();
}
std::filesystem::path dirs::getModPersistentDir() {
return dirs::getSaveDir() / "geode-persistent";
}

View file

@ -535,7 +535,7 @@ void Loader::Impl::findProblems() {
switch(dep.importance) {
case ModMetadata::Dependency::Importance::Suggested:
if (!Mod::get()->template getSavedValue<bool>(dismissKey)) {
if (!Mod::get()->getSavedValue<bool>(dismissKey)) {
this->addProblem({
LoadProblem::Type::Suggestion,
mod,
@ -548,7 +548,7 @@ void Loader::Impl::findProblems() {
}
break;
case ModMetadata::Dependency::Importance::Recommended:
if (!Mod::get()->template getSavedValue<bool>(dismissKey)) {
if (!Mod::get()->getSavedValue<bool>(dismissKey)) {
this->addProblem({
LoadProblem::Type::Recommendation,
mod,

View file

@ -49,7 +49,7 @@ matjson::Value& Mod::getSaveContainer() {
}
matjson::Value& Mod::getSavedSettingsData() {
return m_impl->getSavedSettingsData();
return m_impl->m_settings->getSaveData();
}
bool Mod::isEnabled() const {
@ -143,6 +143,10 @@ std::filesystem::path Mod::getConfigDir(bool create) const {
return m_impl->getConfigDir(create);
}
std::filesystem::path Mod::getPersistentDir(bool create) const {
return m_impl->getPersistentDir(create);
}
bool Mod::hasSettings() const {
return m_impl->hasSettings();
}

View file

@ -66,7 +66,10 @@ Result<> Mod::Impl::setup() {
CCFileUtils::get()->addSearchPath(searchPathRoot.string().c_str());
});
const auto binariesDir = searchPathRoot / m_metadata.getID() / "binaries" / PlatformID::toShortString(GEODE_PLATFORM_TARGET);
// binaries on macos are merged, so make the platform binaries merged as well
auto const binaryPlatformId = PlatformID::toShortString(GEODE_PLATFORM_TARGET GEODE_MACOS(, true));
auto const binariesDir = searchPathRoot / m_metadata.getID() / "binaries" / binaryPlatformId;
if (std::filesystem::exists(binariesDir))
LoaderImpl::get()->addNativeBinariesPath(binariesDir);
@ -135,10 +138,6 @@ matjson::Value& Mod::Impl::getSaveContainer() {
return m_saved;
}
matjson::Value& Mod::Impl::getSavedSettingsData() {
return m_savedSettingsData;
}
bool Mod::Impl::isEnabled() const {
return m_enabled || this->isInternal();
}
@ -174,16 +173,11 @@ std::vector<Patch*> Mod::Impl::getPatches() const {
// Settings and saved values
Result<> Mod::Impl::loadData() {
Loader::get()->queueInMainThread([&]() {
ModStateEvent(m_self, ModEventType::DataLoaded).post();
});
// Settings
// Check if settings exist
auto settingPath = m_saveDirPath / "settings.json";
if (std::filesystem::exists(settingPath)) {
GEODE_UNWRAP_INTO(auto json, utils::file::readJson(settingPath));
m_savedSettingsData = json;
auto load = m_settings->load(json);
if (!load) {
log::warn("Unable to load settings: {}", load.unwrapErr());
@ -215,16 +209,13 @@ Result<> Mod::Impl::saveData() {
return Ok();
}
// Data saving should be fully fail-safe
// If some settings weren't provided a custom settings handler (for example,
// the mod was not loaded) then make sure to save their previous state in
// order to not lose data
if (!m_savedSettingsData.is_object()) {
m_savedSettingsData = matjson::Object();
}
matjson::Value json = m_savedSettingsData;
// ModSettingsManager keeps track of the whole savedata
matjson::Value json;
m_settings->save(json);
// saveData is expected to be synchronous, and always called from GD thread
ModStateEvent(m_self, ModEventType::DataSaved).post();
auto res = utils::file::writeString(m_saveDirPath / "settings.json", json.dump());
if (!res) {
log::error("Unable to save settings: {}", res.unwrapErr());
@ -234,9 +225,6 @@ Result<> Mod::Impl::saveData() {
log::error("Unable to save values: {}", res2.unwrapErr());
}
// saveData is expected to be synchronous, and always called from GD thread
ModStateEvent(m_self, ModEventType::DataSaved).post();
return Ok();
}
@ -331,6 +319,7 @@ Result<> Mod::Impl::loadBinary() {
ModStateEvent(m_self, ModEventType::Loaded).post();
ModStateEvent(m_self, ModEventType::Enabled).post();
ModStateEvent(m_self, ModEventType::DataLoaded).post();
m_isCurrentlyLoading = false;
@ -659,6 +648,14 @@ std::filesystem::path Mod::Impl::getConfigDir(bool create) const {
return dir;
}
std::filesystem::path Mod::Impl::getPersistentDir(bool create) const {
auto dir = dirs::getModPersistentDir() / m_metadata.getID();
if (create) {
(void)file::createDirectoryAll(dir);
}
return dir;
}
std::string_view Mod::Impl::expandSpriteName(std::string_view name) {
std::string nameKey(name);
if (m_expandedSprites.contains(nameKey)) return m_expandedSprites[nameKey];

View file

@ -52,10 +52,6 @@ namespace geode {
* Setting values. This is behind unique_ptr for interior mutability
*/
std::unique_ptr<ModSettingsManager> m_settings = nullptr;
/**
* Settings save data. Stored for efficient loading of custom settings
*/
matjson::Value m_savedSettingsData = matjson::Object();
/**
* Whether the mod resources are loaded or not
*/
@ -101,7 +97,6 @@ namespace geode {
std::filesystem::path getBinaryPath() const;
matjson::Value& getSaveContainer();
matjson::Value& getSavedSettingsData();
#if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE)
void setMetadata(ModMetadata const& metadata);
@ -113,6 +108,7 @@ namespace geode {
std::filesystem::path getSaveDir() const;
std::filesystem::path getConfigDir(bool create = true) const;
std::filesystem::path getPersistentDir(bool create = true) const;
bool hasSettings() const;
std::vector<std::string> getSettingKeys() const;

View file

@ -57,7 +57,7 @@ bool ModMetadata::Dependency::isResolved() const {
bool ModMetadata::Incompatibility::isResolved() const {
return this->importance != Importance::Breaking ||
(!this->mod || !this->version.compare(this->mod->getVersion()));
(!this->mod || !this->mod->isEnabled() || !this->version.compare(this->mod->getVersion()));
}
static std::string sanitizeDetailsData(std::string const& str) {
@ -133,11 +133,17 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
// change all of this to the gd.needs(...) stuff
gd.assertIs({ matjson::Type::Object, matjson::Type::String });
if (gd.isObject()) {
gd.needs(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH)
.mustBe<std::string>("a valid gd version", [](auto const& str) {
return str == "*" || numFromString<double>(str).isOk();
})
.into(impl->m_gdVersion);
if (gd.has(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH)) {
gd.needs(GEODE_PLATFORM_SHORT_IDENTIFIER_NOARCH)
.mustBe<std::string>("a valid gd version", [](auto const& str) {
return str == "*" || numFromString<double>(str).isOk();
})
.into(impl->m_gdVersion);
} else {
// this will error later on, but try to load the rest of the metadata
// so that the mod can show up in the mods listing
impl->m_gdVersion = "0.000";
}
}
else if (gd.isString()) {
impl->m_softInvalidReason = "mod.json uses old syntax";
@ -164,7 +170,7 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
return Err("[mod.json] can not have both \"developer\" and \"developers\" specified");
}
for (auto& dev : root.needs("developers").items()) {
impl->m_developers.push_back(dev.template get<std::string>());
impl->m_developers.push_back(dev.get<std::string>());
}
}
else {
@ -284,7 +290,7 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
// Tags. Actual validation is done when interacting with the server in the UI
for (auto& tag : root.has("tags").items()) {
impl->m_tags.insert(tag.template get<std::string>());
impl->m_tags.insert(tag.get<std::string>());
}
// with new cli, binary name is always mod id

View file

@ -83,11 +83,51 @@ public:
std::shared_ptr<SettingV3> v3 = nullptr;
// todo: remove in v4
std::shared_ptr<SettingValue> legacy = nullptr;
};
};
std::string modID;
std::unordered_map<std::string, SettingInfo> settings;
// Stored so custom settings registered after the fact can be loaded
// If the ability to unregister custom settings is ever added, remember to
// update this by calling saveSettingValueToSave
matjson::Value savedata;
bool restartRequired = false;
void loadSettingValueFromSave(std::string const& key) {
if (this->savedata.contains(key) && this->settings.contains(key)) {
auto& sett = this->settings.at(key);
if (!sett.v3) return;
try {
if (!sett.v3->load(this->savedata[key])) {
log::error("Unable to load setting '{}' for mod {}", key, this->modID);
}
}
// matjson::JsonException doesn't catch all possible json errors
catch(std::exception const& e) {
log::error("Unable to load setting '{}' for mod {} (JSON exception): {}", key, this->modID, e.what());
}
}
}
void saveSettingValueToSave(std::string const& key) {
if (this->settings.contains(key)) {
auto& sett = this->settings.at(key);
if (!sett.v3) return;
// Store the value in an intermediary so if `save` fails the existing
// value loaded from disk isn't overwritten
matjson::Value value;
try {
if (sett.v3->save(value)) {
this->savedata[key] = value;
}
else {
log::error("Unable to save setting '{}' for mod {}", key, this->modID);
}
}
catch(matjson::JsonException const& e) {
log::error("Unable to save setting '{}' for mod {} (JSON exception): {}", key, this->modID, e.what());
}
}
}
void createSettings() {
for (auto& [key, setting] : settings) {
if (setting.v3) {
@ -100,6 +140,7 @@ public:
}
if (auto v3 = (*gen)(key, modID, setting.json)) {
setting.v3 = *v3;
this->loadSettingValueFromSave(key);
}
else {
log::error(
@ -131,7 +172,8 @@ ModSettingsManager::ModSettingsManager(ModMetadata const& metadata)
"Setting \"{}\" in mod {} has the old \"custom\" type - "
"this type has been deprecated and will be removed in Geode v4.0.0. "
"Use the new \"custom:type-name-here\" syntax for defining custom "
"setting types - see more in INSERT TUTORIAL HERE",
"setting types - see more in "
"https://docs.geode-sdk.org/mods/settings/#custom-settings",
key, m_impl->modID
);
}
@ -164,6 +206,7 @@ Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, s
if (auto custom = typeinfo_pointer_cast<LegacyCustomSettingV3>(sett.v3)) {
if (!custom->getValue()) {
custom->setValue(std::move(ptr));
m_impl->loadSettingValueFromSave(id);
}
else {
return Err("Setting '{}' in mod {} has already been registed", id, m_impl->modID);
@ -176,44 +219,25 @@ Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, s
}
Result<> ModSettingsManager::load(matjson::Value const& json) {
auto root = checkJson(json, "Settings");
for (auto const& [key, value] : root.properties()) {
if (m_impl->settings.contains(key)) {
auto& sett = m_impl->settings.at(key);
if (!sett.v3) continue;
try {
if (!sett.v3->load(value.json())) {
log::error("Unable to load setting '{}' for mod {}", key, m_impl->modID);
}
}
// matjson::JsonException doesn't catch all possible json errors
catch(std::exception const& e) {
log::error("Unable to load setting '{}' for mod {} (JSON exception): {}", key, m_impl->modID, e.what());
}
if (json.is_object()) {
// Save this so when custom settings are registered they can load their
// values properly
m_impl->savedata = json.as_object();
for (auto const& [key, _] : json.as_object()) {
m_impl->loadSettingValueFromSave(key);
}
}
return Ok();
}
void ModSettingsManager::save(matjson::Value& json) {
for (auto& [key, sett] : m_impl->settings) {
if (!sett.v3) {
continue;
}
// Store the value in an intermediary so if `save` fails the existing
// value loaded from disk isn't overwritten
matjson::Value value;
try {
if (sett.v3->save(value)) {
json[key] = value;
}
else {
log::error("Unable to save setting '{}' for mod {}", key, m_impl->modID);
}
}
catch(matjson::JsonException const& e) {
log::error("Unable to save setting '{}' for mod {} (JSON exception): {}", key, m_impl->modID, e.what());
}
for (auto& [key, _] : m_impl->settings) {
m_impl->saveSettingValueToSave(key);
}
// Doing this since `ModSettingsManager` is expected to manage savedata fully
json = m_impl->savedata;
}
matjson::Value& ModSettingsManager::getSaveData() {
return m_impl->savedata;
}
std::shared_ptr<SettingV3> ModSettingsManager::get(std::string_view key) {

View file

@ -16,7 +16,7 @@ static void parseCommon(T& sett, JsonMaybeObject& obj) {
obj.has("description").into(sett.description);
if (auto defValue = obj.needs("default")) {
// Platform-specific default value
if (defValue.template is<matjson::Object>()) {
if (defValue.is<matjson::Object>()) {
auto def = defValue.obj();
if (auto plat = def.has(PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))) {
plat.into(sett.defaultValue);

View file

@ -1,6 +1,8 @@
#include "SettingNodeV3.hpp"
#include <Geode/loader/SettingNode.hpp>
#include <Geode/utils/ColorProvider.hpp>
#include <Geode/utils/ranges.hpp>
#include <Geode/loader/Dirs.hpp>
#include <ui/mods/GeodeStyle.hpp>
class SettingNodeSizeChangeEventV3::Impl final {
@ -349,6 +351,10 @@ bool StringSettingNodeV3::init(std::shared_ptr<StringSettingV3> setting, float w
});
m_input->setScale(.7f);
m_input->setString(this->getSetting()->getValue());
if (auto filter = this->getSetting()->getAllowedCharacters()) {
m_input->setFilter(*filter);
}
this->getButtonMenu()->addChildAtPosition(m_input, Anchor::Center);
if (setting->getEnumOptions()) {
@ -453,13 +459,28 @@ bool FileSettingNodeV3::init(std::shared_ptr<FileSettingV3> setting, float width
}
void FileSettingNodeV3::updateState(CCNode* invoker) {
// This is because people tend to put `"default": "Please pick a good file"`
// which is clever and good UX but also a hack so I also need to hack to support that
const auto isTextualDefaultValue = [this, setting = this->getSetting()]() {
if (this->hasNonDefaultValue()) return false;
std::error_code ec;
return setting->isFolder() ?
!std::filesystem::is_directory(setting->getDefaultValue(), ec) :
!std::filesystem::is_regular_file(setting->getDefaultValue(), ec);
}();
SettingValueNodeV3::updateState(invoker);
m_fileIcon->setDisplayFrame(CCSpriteFrameCache::get()->spriteFrameByName(
this->getSetting()->isFolder() ? "folderIcon_001.png" : "file.png"_spr
));
limitNodeSize(m_fileIcon, ccp(10, 10), 1.f, .1f);
if (this->getValue().empty()) {
m_nameLabel->setString(this->getSetting()->isFolder() ? "No Folder Selected" : "No File Selected");
if (this->getValue().empty() || isTextualDefaultValue) {
if (isTextualDefaultValue) {
m_nameLabel->setString(this->getSetting()->getDefaultValue().string().c_str());
}
else {
m_nameLabel->setString(this->getSetting()->isFolder() ? "No Folder Selected" : "No File Selected");
}
m_nameLabel->setColor(ccGRAY);
m_nameLabel->setOpacity(155);
}

View file

@ -3,6 +3,8 @@
#include <Geode/loader/SettingV3.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp>
#include <Geode/binding/ColorChannelSprite.hpp>
#include <Geode/binding/Slider.hpp>
#include <Geode/ui/ColorPickPopup.hpp>
using namespace geode::prelude;

View file

@ -1,6 +1,9 @@
#include <Geode/loader/SettingV3.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/loader/ModSettingsManager.hpp>
#include <Geode/utils/ranges.hpp>
#include <Geode/utils/string.hpp>
#include <Geode/loader/Dirs.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include <regex>
#include "SettingNodeV3.hpp"
@ -51,7 +54,7 @@ namespace enable_if_parsing {
}
Result<> eval(std::string const& defaultModID) const override {
if (auto mod = Loader::get()->getLoadedMod(modID)) {
if (mod->template getSettingValue<bool>(settingID)) {
if (mod->getSettingValue<bool>(settingID)) {
return Ok();
}
// This is an if-check just in case, even though check() should already
@ -83,7 +86,7 @@ namespace enable_if_parsing {
}
Result<> eval(std::string const& defaultModID) const override {
if (auto mod = Loader::get()->getLoadedMod(modID)) {
if (mod->template getSavedValue<bool>(savedValue)) {
if (mod->getSavedValue<bool>(savedValue)) {
return Ok();
}
if (modID == defaultModID) {
@ -482,7 +485,7 @@ void SettingV3::init(std::string const& key, std::string const& modID, JsonExpec
// Keys every setting must have
json.needs("type");
for (auto& plat : json.has("platforms").items()) {
ranges::push(m_impl->platforms, PlatformID::getCovered(plat.template get<std::string>()));
ranges::push(m_impl->platforms, PlatformID::getCovered(plat.get<std::string>()));
}
}
@ -492,7 +495,7 @@ void SettingV3::parseNameAndDescription(JsonExpectedValue& json) {
}
void SettingV3::parseEnableIf(JsonExpectedValue& json) {
json.has("enable-if")
.template mustBe<std::string>("a valid \"enable-if\" scheme", [this](std::string const& str) -> Result<> {
.mustBe<std::string>("a valid \"enable-if\" scheme", [this](std::string const& str) -> Result<> {
GEODE_UNWRAP_INTO(auto tree, enable_if_parsing::Parser::parse(str, m_impl->modID));
GEODE_UNWRAP(tree->check());
m_impl->enableIfTree = std::move(tree);
@ -638,9 +641,15 @@ void LegacyCustomSettingV3::setValue(std::shared_ptr<SettingValue> value) {
}
bool LegacyCustomSettingV3::load(matjson::Value const& json) {
if (m_impl->legacyValue) {
return m_impl->legacyValue->load(json);
}
return true;
}
bool LegacyCustomSettingV3::save(matjson::Value& json) const {
if (m_impl->legacyValue) {
return m_impl->legacyValue->save(json);
}
return true;
}
SettingNodeV3* LegacyCustomSettingV3::createNode(float width) {
@ -739,10 +748,10 @@ Result<std::shared_ptr<IntSettingV3>> IntSettingV3::parse(std::string const& key
// This silly code is because step size being 0 is what defines if they are enabled
// Small arrows are enabled by default
if (!root.has("control").has("arrows").template get<bool>(true)) {
if (!root.has("control").has("arrows").get<bool>(true)) {
ret->m_impl->controls.arrowStepSize = 0;
}
if (!root.has("control").has("big-arrows").template get<bool>()) {
if (!root.has("control").has("big-arrows").get<bool>()) {
ret->m_impl->controls.bigArrowStepSize = 0;
}
@ -870,10 +879,10 @@ Result<std::shared_ptr<FloatSettingV3>> FloatSettingV3::parse(std::string const&
// Disable arrows if they aren't enabled
// Small arrows are enabled by default
if (!root.has("control").has("arrows").template get<bool>(true)) {
if (!root.has("control").has("arrows").get<bool>(true)) {
ret->m_impl->controls.arrowStepSize = 0;
}
if (!root.has("control").has("big-arrows").template get<bool>()) {
if (!root.has("control").has("big-arrows").get<bool>()) {
ret->m_impl->controls.bigArrowStepSize = 0;
}
@ -1046,6 +1055,8 @@ Result<std::shared_ptr<FileSettingV3>> FileSettingV3::parse(std::string const& k
auto root = checkJson(json, "FileSettingV3");
ret->parseBaseProperties(key, modID, root);
ret->setDefaultValue(ret->getDefaultValue().make_preferred());
// Replace known paths like `{gd-save-dir}/`
try {
ret->setDefaultValue(fmt::format(
@ -1106,6 +1117,11 @@ Result<std::shared_ptr<FileSettingV3>> FileSettingV3::parse(std::string const& k
}
Result<> FileSettingV3::isValid(std::filesystem::path const& value) const {
// This is because people tend to put `"default": "Please pick a good file"`
// which is clever and good UX but also a hack so I also need to hack to support that
if (value == this->getDefaultValue()) {
return Ok();
}
std::error_code ec;
if (m_impl->folder) {
if (!std::filesystem::is_directory(value, ec)) {

View file

@ -109,9 +109,9 @@ void updater::downloadLatestLoaderResources() {
// find release asset
for (auto asset : root.needs("assets").iterate()) {
auto obj = asset.obj();
if (obj.needs("name").template get<std::string>() == "resources.zip") {
if (obj.needs("name").get<std::string>() == "resources.zip") {
updater::tryDownloadLoaderResources(
obj.needs("browser_download_url").template get<std::string>(),
obj.needs("browser_download_url").get<std::string>(),
false
);
return;
@ -213,9 +213,9 @@ void updater::downloadLoaderResources(bool useLatestRelease) {
// find release asset
for (auto asset : root.needs("assets").iterate()) {
auto obj = asset.obj();
if (obj.needs("name").template get<std::string>() == "resources.zip") {
if (obj.needs("name").get<std::string>() == "resources.zip") {
updater::tryDownloadLoaderResources(
obj.needs("browser_download_url").template get<std::string>(),
obj.needs("browser_download_url").get<std::string>(),
false
);
return *response;
@ -391,11 +391,11 @@ void updater::checkForLoaderUpdates() {
for (auto asset : root.needs("assets").iterate()) {
auto obj = asset.obj();
if (string::endsWith(
obj.needs("name").template get<std::string>(),
obj.needs("name").get<std::string>(),
fmt::format("{}.zip", PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))
)) {
updater::downloadLoaderUpdate(
obj.needs("browser_download_url").template get<std::string>()
obj.needs("browser_download_url").get<std::string>()
);
return;
}

View file

@ -8,6 +8,7 @@
#include <loader/ModImpl.hpp>
#include <sys/stat.h>
#include <loader/LogImpl.hpp>
#include <dlfcn.h>
using namespace geode::prelude;
@ -132,7 +133,31 @@ bool Loader::Impl::userTriedToLoadDLLs() const {
}
void Loader::Impl::addNativeBinariesPath(std::filesystem::path const& path) {
log::warn("LoaderImpl::addNativeBinariesPath not implement on this platform, not adding path {}", path.string());
// this takes advantage of dyld using already loaded binaries when loading relative shared libraries
// however, this also means that the binaries are loaded, which could have some weird side effects
// but if you could use dlopen (and thus control when libraries are loaded), then you wouldn't be using this, would you?
for (const auto& entry : std::filesystem::directory_iterator(path)) {
if (!entry.is_regular_file()) {
continue;
}
auto& entry_path = entry.path();
if (entry_path.extension() != ".dylib") {
continue;
}
auto handle = dlopen(entry_path.string().c_str(), RTLD_LAZY);
if (!handle) {
auto err = dlerror();
log::warn("failed to load native binary at {}: dlerror returned ({})", entry_path.string(), err);
continue;
}
dlclose(handle);
}
}
std::string Loader::Impl::getGameVersion() {

View file

@ -115,18 +115,19 @@ void console::setup() {
path = std::string(buf, count - 1);
}
// count == 0 => not a console and not a file, assume it's closed
// wine does something weird with /dev/null? not sure tbh but it's definitely up to no good
if ((count == 0 || path.ends_with("\\dev\\null")) && Mod::get()->getSettingValue<bool>("show-platform-console")) {
s_outHandle = nullptr;
CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE));
CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
CloseHandle(GetStdHandle(STD_ERROR_HANDLE));
FreeConsole();
SetStdHandle(STD_OUTPUT_HANDLE, nullptr);
SetStdHandle(STD_INPUT_HANDLE, nullptr);
SetStdHandle(STD_ERROR_HANDLE, nullptr);
}
// TODO: this code causes a crash when piping game's output somewhere (and in some other cases), so it's removed for now
// // count == 0 => not a console and not a file, assume it's closed
// // wine does something weird with /dev/null? not sure tbh but it's definitely up to no good
// if ((count == 0 || path.ends_with("\\dev\\null"))) {
// s_outHandle = nullptr;
// CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE));
// CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
// CloseHandle(GetStdHandle(STD_ERROR_HANDLE));
// FreeConsole();
// SetStdHandle(STD_OUTPUT_HANDLE, nullptr);
// SetStdHandle(STD_INPUT_HANDLE, nullptr);
// SetStdHandle(STD_ERROR_HANDLE, nullptr);
// }
}
// clion console supports escape codes but we can't query that because it's a named pipe

View file

@ -101,7 +101,7 @@ public:
.percentage = 0,
};
m_downloadListener.bind([this, hash = version.hash](web::WebTask::Event* event) {
m_downloadListener.bind([this, hash = version.hash, version = version](web::WebTask::Event* event) {
if (auto value = event->getValue()) {
if (value->ok()) {
if (auto actualHash = ::calculateHash(value->data()); actualHash != hash) {
@ -134,7 +134,9 @@ public:
};
}
else {
m_status = DownloadStatusDone();
m_status = DownloadStatusDone {
.version = version
};
}
}
}
@ -319,6 +321,11 @@ bool ModDownloadManager::checkAutoConfirm() {
if (inc.mod && (!download.getVersion().has_value() || inc.version.compare(download.getVersion().value()))) {
return false;
}
for (auto download : ModDownloadManager::get()->getDownloads()) {
if (download.isDone() && inc.id == download.getID() && (!download.getVersion().has_value() || inc.version.compare(download.getVersion().value()))) {
return false;
}
}
}
// If some installed mod is incompatible with this one,
// we need to ask for confirmation
@ -329,6 +336,19 @@ bool ModDownloadManager::checkAutoConfirm() {
}
}
}
// If some newly downloaded mods are incompatible with this one,
// we need to ask for confirmation
for (auto download : ModDownloadManager::get()->getDownloads()) {
auto status = download.getStatus();
if (auto done = std::get_if<DownloadStatusDone>(&status)) {
for (auto inc : done->version.metadata.getIncompatibilities()) {
if (inc.id == download.getID() && inc.version.compare(done->version.metadata.getVersion())) {
return false;
}
}
}
}
}
// If there are mods we aren't sure about yet, we can't auto-confirm
else if (std::holds_alternative<DownloadStatusFetching>(status)) {

View file

@ -16,6 +16,7 @@ namespace server {
bool operator==(DownloadStatusDownloading const&) const = default;
};
struct DownloadStatusDone {
ServerModVersion version;
bool operator==(DownloadStatusDone const&) const = default;
};
struct DownloadStatusError {

View file

@ -189,7 +189,7 @@ static ServerError parseServerError(web::WebResponse const& error) {
if (json.is_object() && json.contains("error")) {
return ServerError(
error.code(),
"{}", json.template get<std::string>("error")
"{}", json.get<std::string>("error")
);
}
else {
@ -264,13 +264,13 @@ Result<ServerModVersion> ServerModVersion::parse(matjson::Value const& raw) {
auto res = ServerModVersion();
res.metadata.setGeodeVersion(root.needs("geode").template get<VersionInfo>());
res.metadata.setGeodeVersion(root.needs("geode").get<VersionInfo>());
// Verify target GD version
auto gd_obj = root.needs("gd").obj();
std::string gd = "0.000";
if (gd_obj.has(GEODE_PLATFORM_SHORT_IDENTIFIER)) {
gd = gd_obj.has(GEODE_PLATFORM_SHORT_IDENTIFIER).template get<std::string>();
gd = gd_obj.has(GEODE_PLATFORM_SHORT_IDENTIFIER). get<std::string>();
}
if (gd != "*") {
@ -283,11 +283,11 @@ Result<ServerModVersion> ServerModVersion::parse(matjson::Value const& raw) {
root.needs("hash").into(res.hash);
// Get mod metadata info
res.metadata.setID(root.needs("mod_id").template get<std::string>());
res.metadata.setName(root.needs("name").template get<std::string>());
res.metadata.setDescription(root.needs("description").template get<std::string>());
res.metadata.setVersion(root.needs("version").template get<VersionInfo>());
res.metadata.setIsAPI(root.needs("api").template get<bool>());
res.metadata.setID(root.needs("mod_id").get<std::string>());
res.metadata.setName(root.needs("name").get<std::string>());
res.metadata.setDescription(root.needs("description").get<std::string>());
res.metadata.setVersion(root.needs("version").get<VersionInfo>());
res.metadata.setIsAPI(root.needs("api").get<bool>());
std::vector<ModMetadata::Dependency> dependencies {};
for (auto dep : root.has("dependencies").iterate()) {
@ -433,10 +433,10 @@ Result<ServerModMetadata> ServerModMetadata::parse(matjson::Value const& raw) {
root.has("changelog").into(res.changelog);
root.has("repository").into(res.repository);
if (root.has("created_at")) {
GEODE_UNWRAP_INTO(res.createdAt, ServerDateTime::parse(root.has("created_at").template get<std::string>()));
GEODE_UNWRAP_INTO(res.createdAt, ServerDateTime::parse(root.has("created_at").get<std::string>()));
}
if (root.has("updated_at")) {
GEODE_UNWRAP_INTO(res.updatedAt, ServerDateTime::parse(root.has("updated_at").template get<std::string>()));
GEODE_UNWRAP_INTO(res.updatedAt, ServerDateTime::parse(root.has("updated_at").get<std::string>()));
}
std::vector<std::string> developerNames;
@ -470,7 +470,7 @@ Result<ServerModMetadata> ServerModMetadata::parse(matjson::Value const& raw) {
}
for (auto item : root.has("tags").iterate()) {
res.tags.insert(item.template get<std::string>());
res.tags.insert(item.get<std::string>());
}
root.needs("download_count").into(res.downloadCount);

View file

@ -159,9 +159,15 @@ void geode::openChangelogPopup(Mod* mod) {
}
void geode::openSettingsPopup(Mod* mod) {
openSettingsPopup(mod, true);
}
Popup<Mod*>* geode::openSettingsPopup(Mod* mod, bool disableGeodeTheme) {
if (mod->hasSettings()) {
ModSettingsPopup::create(mod)->show();
auto popup = ModSettingsPopup::create(mod, disableGeodeTheme);
popup->show();
return popup;
}
return nullptr;
}
class ModLogoSprite : public CCNode {
@ -233,7 +239,7 @@ protected:
else {
auto data = result->unwrap();
auto image = Ref(new CCImage());
image->initWithImageData(const_cast<uint8_t*>(data.data()), data.size());
image->initWithImageData(data.data(), data.size());
auto texture = CCTextureCache::get()->addUIImage(image, m_modID.c_str());
this->setSprite(CCSprite::createWithTexture(texture), true);

View file

@ -70,16 +70,21 @@ $on_mod(Loaded) {
Loader::get()->queueInMainThread([updateColors = updateColors] {
// this code is ran during static init, where settings aren't loaded yet, and getSettingValue will always return false.
// because of that, we have to delay it until next frame.
updateColors(Mod::get()->template getSettingValue<bool>("enable-geode-theme"));
updateColors(Mod::get()->getSettingValue<bool>("enable-geode-theme"));
});
}
bool GeodeSquareSprite::init(CCSprite* top, bool* state) {
if (!CCSprite::initWithFile(isGeodeTheme() ? "GE_button_05.png"_spr : "GJ_button_01.png"))
bool isGeodeTheme(bool forceDisableTheme) {
return !forceDisableTheme && Mod::get()->getSettingValue<bool>("enable-geode-theme");
}
bool GeodeSquareSprite::init(CCSprite* top, bool* state, bool forceDisableTheme) {
if (!CCSprite::initWithFile(isGeodeTheme(forceDisableTheme) ? "GE_button_05.png"_spr : "GJ_button_01.png"))
return false;
m_stateSrc = state;
m_topSprite = top;
m_forceDisableTheme = forceDisableTheme;
limitNodeSize(top, m_obContentSize * .65f, 2.f, .1f);
this->addChildAtPosition(top, Anchor::Center);
@ -94,7 +99,7 @@ bool GeodeSquareSprite::init(CCSprite* top, bool* state) {
void GeodeSquareSprite::updateImage() {
this->setTexture(CCTextureCache::get()->addImage(
(m_state ? "GJ_button_02.png" : (isGeodeTheme() ? "GE_button_05.png"_spr : "GJ_button_01.png")),
(m_state ? "GJ_button_02.png" : (isGeodeTheme(m_forceDisableTheme) ? "GE_button_05.png"_spr : "GJ_button_01.png")),
false
));
}
@ -106,18 +111,18 @@ void GeodeSquareSprite::update(float dt) {
}
}
GeodeSquareSprite* GeodeSquareSprite::create(const char* top, bool* state) {
GeodeSquareSprite* GeodeSquareSprite::create(const char* top, bool* state, bool forceDisableTheme) {
auto ret = new GeodeSquareSprite();
if (ret->init(CCSprite::create(top), state)) {
if (ret->init(CCSprite::create(top), state, forceDisableTheme)) {
ret->autorelease();
return ret;
}
delete ret;
return nullptr;
}
GeodeSquareSprite* GeodeSquareSprite::createWithSpriteFrameName(const char* top, bool* state) {
GeodeSquareSprite* GeodeSquareSprite::createWithSpriteFrameName(const char* top, bool* state, bool forceDisableTheme) {
auto ret = new GeodeSquareSprite();
if (ret->init(CCSprite::createWithSpriteFrameName(top), state)) {
if (ret->init(CCSprite::createWithSpriteFrameName(top), state, forceDisableTheme)) {
ret->autorelease();
return ret;
}
@ -142,8 +147,8 @@ CCNode* createLoadingCircle(float sideLength, const char* id) {
return spinner;
}
const char* getGeodeButtonSpriteName(GeodeButtonSprite spr) {
if (isGeodeTheme()) {
const char* getGeodeButtonSpriteName(GeodeButtonSprite spr, bool forceDisableTheme) {
if (isGeodeTheme(forceDisableTheme)) {
switch (spr) {
default:
case GeodeButtonSprite::Default: return "GE_button_05.png"_spr;
@ -165,18 +170,18 @@ const char* getGeodeButtonSpriteName(GeodeButtonSprite spr) {
}
}
IconButtonSprite* createGeodeButton(CCNode* icon, std::string const& text, GeodeButtonSprite bg) {
return IconButtonSprite::create(getGeodeButtonSpriteName(bg), icon, text.c_str(), "bigFont.fnt");
IconButtonSprite* createGeodeButton(CCNode* icon, std::string const& text, GeodeButtonSprite bg, bool forceDisableTheme) {
return IconButtonSprite::create(getGeodeButtonSpriteName(bg, forceDisableTheme), icon, text.c_str(), "bigFont.fnt");
}
ButtonSprite* createGeodeButton(std::string const& text, int width, bool gold, bool absolute, GeodeButtonSprite bg) {
return ButtonSprite::create(text.c_str(), width, absolute, gold ? "goldFont.fnt" : "bigFont.fnt", getGeodeButtonSpriteName(bg), 0.0f, .8f);
ButtonSprite* createGeodeButton(std::string const& text, int width, bool gold, bool absolute, GeodeButtonSprite bg, bool forceDisableTheme) {
return ButtonSprite::create(text.c_str(), width, absolute, gold ? "goldFont.fnt" : "bigFont.fnt", getGeodeButtonSpriteName(bg, forceDisableTheme), 0.0f, .8f);
}
ButtonSprite* createGeodeButton(std::string const& text, bool gold, GeodeButtonSprite bg) {
return ButtonSprite::create(text.c_str(), gold ? "goldFont.fnt" : "bigFont.fnt", getGeodeButtonSpriteName(bg), .8f);
ButtonSprite* createGeodeButton(std::string const& text, bool gold, GeodeButtonSprite bg, bool forceDisableTheme) {
return ButtonSprite::create(text.c_str(), gold ? "goldFont.fnt" : "bigFont.fnt", getGeodeButtonSpriteName(bg, forceDisableTheme), .8f);
}
CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale, CircleBaseSize size, bool altColor) {
const auto geodeTheme = isGeodeTheme();
CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale, CircleBaseSize size, bool altColor, bool forceDisableTheme) {
const auto geodeTheme = isGeodeTheme(forceDisableTheme);
auto ret = CircleButtonSprite::create(
top, geodeTheme ? (altColor ? CircleBaseColor::DarkAqua : CircleBaseColor::DarkPurple) : CircleBaseColor::Green, size
);
@ -184,21 +189,16 @@ CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale, CircleBa
return ret;
}
ButtonSprite* createGeodeTagLabel(std::string const& text, std::optional<std::pair<ccColor3B, ccColor3B>> const& color) {
ButtonSprite* createTagLabel(std::string const& text, std::pair<ccColor3B, ccColor3B> const& color) {
auto label = ButtonSprite::create(text.c_str(), "bigFont.fnt", "white-square.png"_spr, .8f);
if (color) {
label->m_label->setColor(color->first);
label->m_BGSprite->setColor(color->second);
}
else {
auto def = geodeTagColor(text);
label->m_label->setColor(def.first);
label->m_BGSprite->setColor(def.second);
}
label->m_label->setColor(color.first);
label->m_BGSprite->setColor(color.second);
return label;
}
std::pair<ccColor3B, ccColor3B> geodeTagColor(std::string_view const& text) {
ButtonSprite* createGeodeTagLabel(std::string_view tag) {
return createTagLabel(geodeTagName(tag), geodeTagColors(tag));
}
std::pair<ccColor3B, ccColor3B> geodeTagColors(std::string_view tag) {
static std::array TAG_COLORS {
std::make_pair(ccc3(240, 233, 255), ccc3(130, 123, 163)),
std::make_pair(ccc3(234, 255, 245), ccc3(123, 163, 136)),
@ -206,20 +206,32 @@ std::pair<ccColor3B, ccColor3B> geodeTagColor(std::string_view const& text) {
std::make_pair(ccc3(255, 253, 240), ccc3(163, 157, 123)),
std::make_pair(ccc3(255, 242, 240), ccc3(163, 128, 123)),
};
return TAG_COLORS[hash(text) % 5932 % TAG_COLORS.size()];
}
ListBorders* createGeodeListBorders(CCSize const& size) {
auto ret = ListBorders::create();
if (isGeodeTheme()) {
ret->setSpriteFrames("geode-list-top.png"_spr, "geode-list-side.png"_spr, 2);
if (tag == "modtober24") {
return std::make_pair(ccc3(225, 236, 245), ccc3(82, 139, 201));
}
ret->setContentSize(size);
return ret;
return TAG_COLORS[hash(tag) % 5932 % TAG_COLORS.size()];
}
std::string geodeTagName(std::string_view tag) {
// todo in v4: rework tags to use a server-provided display name instead
if (tag == "modtober24") {
return "Modtober 2024";
}
// Everything else just capitalize and that's it
auto readable = std::string(tag);
readable[0] = std::toupper(readable[0]);
return readable;
}
bool isGeodeTheme() {
return Mod::get()->template getSettingValue<bool>("enable-geode-theme");
ListBorders* createGeodeListBorders(CCSize const& size, bool forceDisableTheme) {
auto ret = ListBorders::create();
const bool geodeTheme = isGeodeTheme(forceDisableTheme);
if (geodeTheme) {
ret->setSpriteFrames("geode-list-top.png"_spr, "geode-list-side.png"_spr, 2);
ret->setContentSize(size);
} else {
ret->setContentSize(size + ccp(5, 5));
}
return ret;
}
bool GeodeTabSprite::init(const char* iconFrame, const char* text, float width, bool altColor) {

View file

@ -16,11 +16,16 @@ enum class GeodePopupStyle {
Alt2,
};
bool isGeodeTheme(bool forceDisableTheme = false);
template <class... Args>
class GeodePopup : public Popup<Args...> {
protected:
bool m_forceDisableTheme = false;
bool init(float width, float height, Args... args, GeodePopupStyle style = GeodePopupStyle::Default, bool forceDisableTheme = false) {
const bool geodeTheme = !forceDisableTheme && Mod::get()->template getSettingValue<bool>("enable-geode-theme");
m_forceDisableTheme = forceDisableTheme;
const bool geodeTheme = isGeodeTheme(forceDisableTheme);
const char* bg;
switch (style) {
default:
@ -49,16 +54,17 @@ class GeodeSquareSprite : public CCSprite {
protected:
bool* m_stateSrc = nullptr;
bool m_state = false;
bool m_forceDisableTheme = false;
CCSprite* m_topSprite;
bool init(CCSprite* top, bool* state);
bool init(CCSprite* top, bool* state, bool forceDisableTheme = false);
void update(float dt) override;
void updateImage();
public:
static GeodeSquareSprite* create(const char* top, bool* state = nullptr);
static GeodeSquareSprite* createWithSpriteFrameName(const char* top, bool* state = nullptr);
static GeodeSquareSprite* create(const char* top, bool* state = nullptr, bool forceDisableTheme = false);
static GeodeSquareSprite* createWithSpriteFrameName(const char* top, bool* state = nullptr, bool forceDisableTheme = false);
CCSprite* getTopSprite() const;
void setState(bool state);
@ -73,19 +79,19 @@ enum class GeodeButtonSprite {
Enable,
Gray,
};
const char* getGeodeButtonSpriteName(GeodeButtonSprite spr);
IconButtonSprite* createGeodeButton(CCNode* icon, std::string const& text, GeodeButtonSprite bg = GeodeButtonSprite::Default);
ButtonSprite* createGeodeButton(std::string const& text, int width, bool absolute = false, bool gold = false, GeodeButtonSprite bg = GeodeButtonSprite::Default);
ButtonSprite* createGeodeButton(std::string const& text, bool gold = false, GeodeButtonSprite bg = GeodeButtonSprite::Default);
const char* getGeodeButtonSpriteName(GeodeButtonSprite spr, bool forceDisableTheme = false);
IconButtonSprite* createGeodeButton(CCNode* icon, std::string const& text, GeodeButtonSprite bg = GeodeButtonSprite::Default, bool forceDisableTheme = false);
ButtonSprite* createGeodeButton(std::string const& text, int width, bool absolute = false, bool gold = false, GeodeButtonSprite bg = GeodeButtonSprite::Default, bool forceDisableTheme = false);
ButtonSprite* createGeodeButton(std::string const& text, bool gold = false, GeodeButtonSprite bg = GeodeButtonSprite::Default, bool forceDisableTheme = false);
CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale = 1.f, CircleBaseSize size = CircleBaseSize::Medium, bool altColor = false);
CircleButtonSprite* createGeodeCircleButton(CCSprite* top, float scale = 1.f, CircleBaseSize size = CircleBaseSize::Medium, bool altColor = false, bool forceDisableTheme = false);
ButtonSprite* createGeodeTagLabel(std::string const& text, std::optional<std::pair<ccColor3B, ccColor3B>> const& color = std::nullopt);
std::pair<ccColor3B, ccColor3B> geodeTagColor(std::string_view const& text);
ButtonSprite* createTagLabel(std::string const& text, std::pair<ccColor3B, ccColor3B> const& color);
ButtonSprite* createGeodeTagLabel(std::string_view tag);
std::pair<ccColor3B, ccColor3B> geodeTagColors(std::string_view tag);
std::string geodeTagName(std::string_view tag);
ListBorders* createGeodeListBorders(CCSize const& size);
bool isGeodeTheme();
ListBorders* createGeodeListBorders(CCSize const& size, bool forceDisableTheme = false);
class GeodeTabSprite : public CCNode {
protected:

View file

@ -430,14 +430,15 @@ bool ModsLayer::init() {
// Increment touch priority so the mods in the list don't override
mainTabs->setTouchPriority(-150);
for (auto item : std::initializer_list<std::tuple<const char*, const char*, ModListSource*, const char*>> {
{ "download.png"_spr, "Installed", InstalledModListSource::get(InstalledModListType::All), "installed-button" },
{ "GJ_starsIcon_001.png", "Featured", ServerModListSource::get(ServerModListType::Featured), "featured-button" },
{ "globe.png"_spr, "Download", ServerModListSource::get(ServerModListType::Download), "download-button" },
{ "GJ_timeIcon_001.png", "Recent", ServerModListSource::get(ServerModListType::Recent), "recent-button" },
for (auto item : std::initializer_list<std::tuple<const char*, const char*, ModListSource*, const char*, bool>> {
{ "download.png"_spr, "Installed", InstalledModListSource::get(InstalledModListType::All), "installed-button", false },
{ "GJ_starsIcon_001.png", "Featured", ServerModListSource::get(ServerModListType::Featured), "featured-button", false },
{ "globe.png"_spr, "Download", ServerModListSource::get(ServerModListType::Download), "download-button", false },
{ "GJ_timeIcon_001.png", "Recent", ServerModListSource::get(ServerModListType::Recent), "recent-button", false },
{ "d_artCloud_03_001.png", "Modtober", ServerModListSource::get(ServerModListType::Modtober24), "modtober-button", true },
}) {
auto btn = CCMenuItemSpriteExtra::create(
GeodeTabSprite::create(std::get<0>(item), std::get<1>(item), 120),
GeodeTabSprite::create(std::get<0>(item), std::get<1>(item), 100, std::get<4>(item)),
this, menu_selector(ModsLayer::onTab)
);
btn->setUserData(std::get<2>(item));
@ -683,7 +684,7 @@ void ModsLayer::onSearch(CCObject*) {
}
}
void ModsLayer::onTheme(CCObject*) {
auto old = Mod::get()->template getSettingValue<bool>("enable-geode-theme");
auto old = Mod::get()->getSettingValue<bool>("enable-geode-theme");
createQuickPopup(
"Switch Theme",
fmt::format(
@ -704,7 +705,7 @@ void ModsLayer::onTheme(CCObject*) {
);
}
void ModsLayer::onSettings(CCObject*) {
openSettingsPopup(Mod::get());
openSettingsPopup(Mod::get(), false);
}
ModsLayer* ModsLayer::create() {

View file

@ -84,12 +84,12 @@ bool ModItem::init(ModSource&& source) {
);
m_infoContainer->addChild(m_developers);
m_restartRequiredLabel = createGeodeTagLabel(
m_restartRequiredLabel = createTagLabel(
"Restart Required",
{{
{
to3B(ColorProvider::get()->color("mod-list-restart-required-label"_spr)),
to3B(ColorProvider::get()->color("mod-list-restart-required-label-bg"_spr))
}}
}
);
m_restartRequiredLabel->setID("restart-required-label");
m_restartRequiredLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(std::nullopt, .75f));
@ -140,7 +140,7 @@ bool ModItem::init(ModSource&& source) {
auto geodeValid = Loader::get()->isModVersionSupported(version.getGeodeVersion());
auto gameVersion = version.getGameVersion();
auto gdValid = gameVersion == "*" || gameVersion == GEODE_STR(GEODE_GD_VERSION);
auto gdValid = !gameVersion || gameVersion == "*" || gameVersion == GEODE_STR(GEODE_GD_VERSION);
if (!geodeValid || !gdValid) {
spr = createGeodeButton("N/A", 50, false, true, GeodeButtonSprite::Gray);
@ -208,6 +208,11 @@ bool ModItem::init(ModSource&& source) {
paidModLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
m_titleContainer->addChild(paidModLabel);
}
if (metadata.tags.contains("modtober24")) {
auto modtoberLabel = CCSprite::createWithSpriteFrameName("tag-modtober.png"_spr);
modtoberLabel->setLayoutOptions(AxisLayoutOptions::create()->setScaleLimits(.1f, .8f));
m_titleContainer->addChild(modtoberLabel);
}
// Show mod download count here already so people can make informed decisions
// on which mods to install
@ -363,7 +368,10 @@ void ModItem::updateState() {
m_bg->setColor("mod-list-paid-color"_cc3b);
m_bg->setOpacity(55);
}
if (metadata.tags.contains("modtober24")) {
m_bg->setColor(ccc3(63, 91, 138));
m_bg->setOpacity(85);
}
if (isGeodeTheme() && metadata.featured) {
m_bg->setColor("mod-list-featured-color"_cc3b);
m_bg->setOpacity(65);
@ -496,21 +504,28 @@ void ModItem::onView(CCObject*) {
// Show popups for invalid mods
if (m_source.asServer()) {
auto version = m_source.asServer()->latestVersion();
if (!Loader::get()->isModVersionSupported(version.getGeodeVersion())) {
auto gameVersion = version.getGameVersion();
if (gameVersion == "0.000") {
return FLAlertLayer::create(
nullptr,
"Outdated",
"This mod is targets an <cr>outdated version of Geode</c>. "
"<co>Please wait for its developer to update it.</c>",
"Invalid Platform",
"This mod is <cr>not available</c> for your current platform.",
"OK", nullptr, 360
)->show();
}
if (version.getGameVersion() != "*" && version.getGameVersion() != GEODE_STR(GEODE_GD_VERSION)) {
if (gameVersion && gameVersion != "*" && gameVersion != GEODE_STR(GEODE_GD_VERSION)) {
return FLAlertLayer::create(
nullptr,
"Outdated",
"This mod is targets a <cr>different version of Geometry Dash</c>. "
"<co>Please wait for its developer to update it.</c>",
"Unavailable",
"This mod targets an <cr>unsupported version of Geometry Dash</c>.",
"OK", nullptr, 360
)->show();
}
if (!Loader::get()->isModVersionSupported(version.getGeodeVersion())) {
return FLAlertLayer::create(
nullptr,
"Unavailable",
"This mod targets an <cr>unsupported version of Geode</c>.",
"OK", nullptr, 360
)->show();
}

View file

@ -4,6 +4,7 @@
#include "../popups/SortPopup.hpp"
#include "../GeodeStyle.hpp"
#include "../ModsLayer.hpp"
#include "../popups/ModtoberPopup.hpp"
bool ModList::init(ModListSource* src, CCSize const& size) {
if (!CCNode::init())
@ -249,6 +250,34 @@ bool ModList::init(ModListSource* src, CCSize const& size) {
m_topContainer->addChild(m_searchMenu);
// Modtober banner; this can be removed after Modtober 2024 is over!
if (
auto src = typeinfo_cast<ServerModListSource*>(m_source);
src && src->getType() == ServerModListType::Modtober24
) {
auto menu = CCMenu::create();
menu->setID("modtober-banner");
menu->ignoreAnchorPointForPosition(false);
menu->setContentSize({ size.width, 30 });
auto banner = CCSprite::createWithSpriteFrameName("modtober24-banner.png"_spr);
limitNodeWidth(banner, size.width, 1.f, .1f);
menu->addChildAtPosition(banner, Anchor::Center);
auto label = CCLabelBMFont::create("Modtober 2024 is Here!", "bigFont.fnt");
label->setScale(.5f);
menu->addChildAtPosition(label, Anchor::Left, ccp(10, 0), ccp(0, .5f));
auto aboutSpr = createGeodeButton("About");
aboutSpr->setScale(.5f);
auto aboutBtn = CCMenuItemSpriteExtra::create(
aboutSpr, this, menu_selector(ModList::onModtoberInfo)
);
menu->addChildAtPosition(aboutBtn, Anchor::Right, ccp(-35, 0));
m_topContainer->addChild(menu);
}
m_topContainer->setLayout(
ColumnLayout::create()
->setGap(0)
@ -492,7 +521,7 @@ void ModList::updateTopContainer() {
auto oldPosition = oldPositionArea > 0.f ?
m_list->m_contentLayer->getPositionY() / oldPositionArea :
-1.f;
// Update list size to account for the top menu
// (giving a little bit of extra padding for it, the same size as gap)
m_list->setContentHeight(
@ -501,6 +530,8 @@ void ModList::updateTopContainer() {
static_cast<AxisLayout*>(m_list->m_contentLayer->getLayout())->getGap() :
this->getContentHeight()
);
static_cast<ColumnLayout*>(m_list->m_contentLayer->getLayout())->setAutoGrowAxis(m_list->getContentHeight());
m_list->m_contentLayer->updateLayout();
// Preserve relative scroll position
m_list->m_contentLayer->setPositionY((
@ -659,6 +690,9 @@ void ModList::onToggleErrors(CCObject*) {
void ModList::onUpdateAll(CCObject*) {
server::ModDownloadManager::get()->startUpdateAll();
}
void ModList::onModtoberInfo(CCObject*) {
ModtoberPopup::create()->show();
}
size_t ModList::getPage() const {
return m_page;

View file

@ -71,6 +71,7 @@ protected:
void onToggleUpdates(CCObject*);
void onToggleErrors(CCObject*);
void onUpdateAll(CCObject*);
void onModtoberInfo(CCObject*);
public:
static ModList* create(ModListSource* src, CCSize const& size);

View file

@ -12,6 +12,7 @@ void askConfirmModInstalls() {
size_t replacementCount = 0;
size_t dependencyCount = 0;
std::unordered_set<Mod*> toDisable;
std::unordered_set<std::string> toDisableModId;
std::unordered_set<Mod*> toEnable;
};
@ -39,6 +40,11 @@ void askConfirmModInstalls() {
if (inc.mod && inc.version.compare(conf->version.metadata.getVersion()) && inc.mod->isOrWillBeEnabled()) {
toConfirm.toDisable.insert(inc.mod);
}
for (auto download : ModDownloadManager::get()->getDownloads()) {
if (download.isDone() && inc.id == download.getID() && inc.version.compare(conf->version.metadata.getVersion())) {
toConfirm.toDisableModId.insert(inc.id);
}
}
}
// If some installed mods are incompatible with this one, disable them
for (auto mod : Loader::get()->getAllMods()) {
@ -48,6 +54,17 @@ void askConfirmModInstalls() {
}
}
}
// If some newly downloaded mods are incompatible with this one, disable them
for (auto download : ModDownloadManager::get()->getDownloads()) {
auto status = download.getStatus();
if (auto done = std::get_if<DownloadStatusDone>(&status)) {
for (auto inc : done->version.metadata.getIncompatibilities()) {
if (inc.id == conf->version.metadata.getID() && inc.version.compare(done->version.metadata.getVersion())) {
toConfirm.toDisableModId.insert(download.getID());
}
}
}
}
// If this mod has required dependencies that are disabled, enable them
for (auto dep : conf->version.metadata.getDependencies()) {
@ -71,6 +88,20 @@ void askConfirmModInstalls() {
);
};
auto joinIdsToIDs = [](std::unordered_set<std::string> const& ids) {
return ranges::join(
ranges::map<std::vector<std::string>>(
ids, [](std::string const& id) { return fmt::format("<cp>{}</c>", id); }
),
", "
);
};
std::unordered_set<std::string> idsToDisable = toConfirm.toDisableModId;
for (auto mod : toConfirm.toDisable) {
idsToDisable.insert(mod->getID());
}
createQuickPopup(
"Confirm Install",
fmt::format(
@ -78,7 +109,7 @@ void askConfirmModInstalls() {
"<cr>{} mods will be force-disabled, as they are incompatible</c>: {}\n"
"<cg>{} mods will be force-enabled</c>: {}",
toConfirm.modCount, toConfirm.dependencyCount, toConfirm.replacementCount,
toConfirm.toDisable.size(), joinModsToIDs(toConfirm.toDisable),
idsToDisable.size(), joinIdsToIDs(idsToDisable),
toConfirm.toEnable.size(), joinModsToIDs(toConfirm.toEnable)
),
"Cancel", "Continue",
@ -87,6 +118,9 @@ void askConfirmModInstalls() {
for (auto mod : toConfirm.toDisable) {
(void)mod->disable();
}
for (auto modId : toConfirm.toDisableModId) {
Mod::get()->setSavedValue("should-load-" + modId, false);
}
for (auto mod : toConfirm.toEnable) {
(void)mod->enable();
}

View file

@ -131,7 +131,7 @@ bool FiltersPopup::setup(ModListSource* src) {
m_mainLayer->addChildAtPosition(inputContainer, Anchor::Bottom, ccp(0, 60), ccp(.5f, .5f));
}
auto okSpr = createGeodeButton("OK");
auto okSpr = createGeodeButton("OK", false, GeodeButtonSprite::Default, m_forceDisableTheme);
okSpr->setScale(.7f);
auto okBtn = CCMenuItemSpriteExtra::create(
okSpr, this, menu_selector(FiltersPopup::onClose)

View file

@ -1,6 +1,7 @@
#include "ModPopup.hpp"
#include <Geode/binding/ButtonSprite.hpp>
#include <Geode/ui/MDTextArea.hpp>
#include <Geode/ui/TextInput.hpp>
#include <Geode/utils/web.hpp>
#include <Geode/loader/Loader.hpp>
#include <Geode/loader/ModSettingsManager.hpp>
@ -10,6 +11,7 @@
#include "../settings/ModSettingsPopup.hpp"
#include "../../../internal/about.hpp"
#include "../../GeodeUIEvent.hpp"
#include "../popups/ModtoberPopup.hpp"
class FetchTextArea : public CCNode {
public:
@ -300,12 +302,12 @@ bool ModPopup::setup(ModSource&& src) {
manageTitle->setOpacity(195);
manageContainer->addChildAtPosition(manageTitle, Anchor::Left, ccp(0, 0), ccp(0, .5f));
m_restartRequiredLabel = createGeodeTagLabel(
m_restartRequiredLabel = createTagLabel(
"Restart Required",
{{
{
to3B(ColorProvider::get()->color("mod-list-restart-required-label"_spr)),
to3B(ColorProvider::get()->color("mod-list-restart-required-label-bg"_spr))
}}
}
);
m_restartRequiredLabel->setScale(.3f);
manageContainer->addChildAtPosition(m_restartRequiredLabel, Anchor::Right, ccp(0, 0), ccp(1, .5f));
@ -334,7 +336,8 @@ bool ModPopup::setup(ModSource&& src) {
auto updateModSpr = createGeodeButton(
CCSprite::createWithSpriteFrameName("update.png"_spr),
"Update",
GeodeButtonSprite::Install
GeodeButtonSprite::Install,
m_forceDisableTheme
);
updateModSpr->setScale(.5f);
m_updateBtn = CCMenuItemSpriteExtra::create(
@ -345,13 +348,15 @@ bool ModPopup::setup(ModSource&& src) {
auto enableModOffSpr = createGeodeButton(
CCSprite::createWithSpriteFrameName("GJ_completesIcon_001.png"),
"Enable",
GeodeButtonSprite::Enable
GeodeButtonSprite::Enable,
m_forceDisableTheme
);
enableModOffSpr->setScale(.5f);
auto enableModOnSpr = createGeodeButton(
CCSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png"),
"Disable",
GeodeButtonSprite::Delete
GeodeButtonSprite::Delete,
m_forceDisableTheme
);
enableModOnSpr->setScale(.5f);
m_enableBtn = CCMenuItemToggler::create(
@ -364,7 +369,8 @@ bool ModPopup::setup(ModSource&& src) {
auto reenableModOffSpr = createGeodeButton(
CCSprite::createWithSpriteFrameName("reset.png"_spr),
"Re-Enable",
GeodeButtonSprite::Default
GeodeButtonSprite::Default,
m_forceDisableTheme
);
reenableModOffSpr->setScale(.5f);
auto reenableModOnSpr = createGeodeButton(
@ -383,7 +389,8 @@ bool ModPopup::setup(ModSource&& src) {
auto installModSpr = createGeodeButton(
CCSprite::createWithSpriteFrameName("GJ_downloadsIcon_001.png"),
"Install",
GeodeButtonSprite::Install
GeodeButtonSprite::Install,
m_forceDisableTheme
);
installModSpr->setScale(.5f);
m_installBtn = CCMenuItemSpriteExtra::create(
@ -394,7 +401,8 @@ bool ModPopup::setup(ModSource&& src) {
auto uninstallModSpr = createGeodeButton(
CCSprite::createWithSpriteFrameName("delete-white.png"_spr),
"Uninstall",
GeodeButtonSprite::Default
GeodeButtonSprite::Default,
m_forceDisableTheme
);
uninstallModSpr->setScale(.5f);
m_uninstallBtn = CCMenuItemSpriteExtra::create(
@ -405,7 +413,8 @@ bool ModPopup::setup(ModSource&& src) {
auto cancelDownloadSpr = createGeodeButton(
CCSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png"),
"Cancel",
GeodeButtonSprite::Default
GeodeButtonSprite::Default,
m_forceDisableTheme
);
cancelDownloadSpr->setScale(.5f);
m_cancelBtn = CCMenuItemSpriteExtra::create(
@ -565,7 +574,7 @@ bool ModPopup::setup(ModSource&& src) {
m_settingsBG->setContentSize(ccp(35, 30) / linksBG->getScale());
m_buttonMenu->addChildAtPosition(m_settingsBG, Anchor::BottomLeft, ccp(28, 25));
auto settingsSpr = createGeodeCircleButton(CCSprite::createWithSpriteFrameName("settings.png"_spr));
auto settingsSpr = createGeodeCircleButton(CCSprite::createWithSpriteFrameName("settings.png"_spr), 1.f, CircleBaseSize::Medium, false, m_forceDisableTheme);
settingsSpr->setScale(.6f);
auto settingsBtn = CCMenuItemSpriteExtra::create(
settingsSpr, this, menu_selector(ModPopup::onSettings)
@ -880,10 +889,7 @@ void ModPopup::onLoadTags(typename server::ServerRequest<std::unordered_set<std:
m_tags->removeAllChildren();
for (auto& tag : data) {
auto readable = tag;
readable[0] = std::toupper(readable[0]);
auto colors = geodeTagColor(tag);
m_tags->addChild(createGeodeTagLabel(readable));
m_tags->addChild(createGeodeTagLabel(tag));
}
if (data.empty()) {
@ -891,6 +897,50 @@ void ModPopup::onLoadTags(typename server::ServerRequest<std::unordered_set<std:
label->setOpacity(120);
m_tags->addChild(label);
}
// This should probably be kept even after modtober ends,
// so the banner sprite must be kept
// If the build times from the cool popup become too long then we can
// probably move that to a normal FLAlert that explains "Modtober was
// this contest blah blah this mod was made for it"
else if (data.contains("modtober24")) {
auto menu = CCMenu::create();
menu->setID("modtober-banner");
menu->ignoreAnchorPointForPosition(false);
menu->setContentSize({ m_rightColumn->getContentWidth(), 25 });
auto banner = CCSprite::createWithSpriteFrameName("modtober24-banner-2.png"_spr);
limitNodeWidth(banner, m_rightColumn->getContentWidth(), 1.f, .1f);
menu->addChildAtPosition(banner, Anchor::Center);
auto label = CCLabelBMFont::create("Entry for Modtober 2024", "bigFont.fnt");
label->setScale(.35f);
menu->addChildAtPosition(label, Anchor::Left, ccp(10, 0), ccp(0, .5f));
auto aboutSpr = createGeodeButton("About", false, GeodeButtonSprite::Default, m_forceDisableTheme);
aboutSpr->setScale(.35f);
auto aboutBtn = CCMenuItemSpriteExtra::create(
aboutSpr, this, menu_selector(ModPopup::onModtoberInfo)
);
menu->addChildAtPosition(aboutBtn, Anchor::Right, ccp(-25, 0));
m_rightColumn->addChildAtPosition(menu, Anchor::Bottom, ccp(0, 0), ccp(.5f, 0));
m_modtoberBanner = menu;
// Force reload of all the tabs since otherwise their contents will overflow
for (auto& [_, tab] : m_tabs) {
if (tab.second && tab.second->getParent()) {
tab.second->removeFromParent();
}
tab.second = nullptr;
}
m_currentTabPage = nullptr;
// This might cause a minor inconvenience to someone who opens the popup and
// immediately switches to changelog but is then forced back into details
this->loadTab(Tab::Details);
}
m_tags->updateLayout();
@ -920,12 +970,17 @@ void ModPopup::loadTab(ModPopup::Tab tab) {
btn.first->select(value == tab);
}
float modtoberBannerHeight = 0;
if (m_modtoberBanner) {
modtoberBannerHeight = 30;
}
if (auto existing = m_tabs.at(tab).second) {
m_currentTabPage = existing;
m_rightColumn->addChildAtPosition(existing, Anchor::Bottom);
m_rightColumn->addChildAtPosition(existing, Anchor::Bottom, ccp(0, modtoberBannerHeight));
}
else {
const auto size = (m_rightColumn->getContentSize() - ccp(0, 30));
const auto size = (m_rightColumn->getContentSize() - ccp(0, 30 + modtoberBannerHeight));
const float mdScale = .85f;
switch (tab) {
case Tab::Details: {
@ -955,7 +1010,7 @@ void ModPopup::loadTab(ModPopup::Tab tab) {
} break;
}
m_currentTabPage->setAnchorPoint({ .5f, .0f });
m_rightColumn->addChildAtPosition(m_currentTabPage, Anchor::Bottom);
m_rightColumn->addChildAtPosition(m_currentTabPage, Anchor::Bottom, ccp(0, modtoberBannerHeight));
m_tabs.at(tab).second = m_currentTabPage;
}
}
@ -1030,6 +1085,12 @@ void ModPopup::onLink(CCObject* sender) {
void ModPopup::onSupport(CCObject*) {
openSupportPopup(m_source.getMetadata());
}
void ModPopup::onModtoberInfo(CCObject*) {
// todo: if we want to get rid of the modtober popup sprite (because it's fucking massive)
// then we can just replace this with a normal FLAlert explaining
// "this mod was an entry for modtober 2024 blah blah blah"
ModtoberPopup::create()->show();
}
ModPopup* ModPopup::create(ModSource&& src) {
auto ret = new ModPopup();

View file

@ -35,6 +35,7 @@ protected:
ButtonSprite* m_restartRequiredLabel;
CCNode* m_rightColumn;
CCNode* m_currentTabPage = nullptr;
CCNode* m_modtoberBanner = nullptr;
std::unordered_map<Tab, std::pair<GeodeTabSprite*, Ref<CCNode>>> m_tabs;
EventListener<server::ServerRequest<server::ServerModMetadata>> m_statsListener;
EventListener<server::ServerRequest<std::unordered_set<std::string>>> m_tagsListener;
@ -63,6 +64,7 @@ protected:
void onSettings(CCObject*);
void onLink(CCObject*);
void onSupport(CCObject*);
void onModtoberInfo(CCObject*);
public:
void loadTab(Tab tab);

View file

@ -0,0 +1,44 @@
#include "ModtoberPopup.hpp"
#include <Geode/utils/web.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/binding/ButtonSprite.hpp>
bool ModtoberPopup::setup() {
m_bgSprite->setVisible(false);
auto bg = CCSprite::createWithSpriteFrameName("modtober24-popup.png"_spr);
m_mainLayer->addChildAtPosition(bg, Anchor::Center);
auto supportSpr = createGeodeButton("Join", false, GeodeButtonSprite::Default, m_forceDisableTheme);
supportSpr->setScale(.8f);
auto supportBtn = CCMenuItemSpriteExtra::create(
supportSpr, this, menu_selector(ModtoberPopup::onDiscord)
);
m_buttonMenu->addChildAtPosition(supportBtn, Anchor::BottomRight, ccp(-65, 50));
return true;
}
void ModtoberPopup::onDiscord(CCObject*) {
createQuickPopup(
"Join Modtober",
"<cf>Modtober</c> is being hosted on the <cg>GD Programming</c> <ca>Discord Server</c>.\n"
"To participate, join GDP and read the rules for the contest in <co>#modtober-2024</c>",
"Cancel", "Join Discord",
[](auto, bool btn2) {
if (btn2) {
web::openLinkInBrowser("https://discord.gg/gd-programming-646101505417674758");
}
}
);
}
ModtoberPopup* ModtoberPopup::create() {
auto ret = new ModtoberPopup();
if (ret && ret->init(410, 270)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}

View file

@ -0,0 +1,16 @@
#pragma once
#include <Geode/ui/Popup.hpp>
#include "../GeodeStyle.hpp"
using namespace geode::prelude;
class ModtoberPopup : public GeodePopup<> {
protected:
bool setup() override;
void onDiscord(CCObject*);
public:
static ModtoberPopup* create();
};

View file

@ -6,6 +6,7 @@
#include <Geode/ui/ScrollLayer.hpp>
#include <Geode/utils/cocos.hpp>
#include <Geode/ui/General.hpp>
#include <Geode/ui/Scrollbar.hpp>
#include <loader/SettingNodeV3.hpp>
// needed for weightedFuzzyMatch
#include <ui/mods/sources/ModListSource.hpp>
@ -25,10 +26,7 @@ static bool matchSearch(SettingNodeV3* node, std::string const& query) {
else {
addToList |= weightedFuzzyMatch(setting->getKey(), query, 1, weighted);
}
if (auto desc = setting->getDescription()) {
addToList |= weightedFuzzyMatch(*desc, query, 0.02, weighted);
}
if (weighted < 2) {
if (weighted < 60 + 10 * query.size()) {
addToList = false;
}
return addToList;
@ -62,7 +60,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
m_searchInput->setID("search-input");
searchContainer->addChildAtPosition(m_searchInput, Anchor::Left, ccp(7.5f, 0), ccp(0, .5f));
auto searchClearSpr = GeodeSquareSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png");
auto searchClearSpr = GeodeSquareSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png", nullptr, m_forceDisableTheme);
searchClearSpr->setScale(.45f);
m_searchClearBtn = CCMenuItemSpriteExtra::create(
searchClearSpr, this, menu_selector(ModSettingsPopup::onClearSearch)
@ -83,11 +81,6 @@ bool ModSettingsPopup::setup(Mod* mod) {
else {
node = UnresolvedCustomSettingNodeV3::create(key, mod, layerSize.width);
}
// auto separator = CCLayerColor::create({ 0, 0, 0, 50 }, layerSize.width, 1.f);
// separator->setOpacity(bg ? 100 : 50);
// separator->ignoreAnchorPointForPosition(false);
// bg->addChildAtPosition(separator, Anchor::Bottom, ccp(0, 0), ccp(.5f, .5f));
m_settings.push_back(node);
m_list->m_contentLayer->addChild(node);
@ -95,18 +88,23 @@ bool ModSettingsPopup::setup(Mod* mod) {
m_list->m_contentLayer->setLayout(
ColumnLayout::create()
->setAxisReverse(true)
->setAutoGrowAxis(layerSize.height)
->setAutoGrowAxis(m_list->getContentHeight())
->setCrossAxisOverflow(false)
->setAxisAlignment(AxisAlignment::End)
->setGap(0)
);
m_list->moveToTop();
const int buttonPriority = m_list->getTouchPriority() - 1;
m_buttonMenu->setTouchPriority(buttonPriority);
searchContainer->setTouchPriority(buttonPriority);
layerBG->addChildAtPosition(m_list, Anchor::BottomLeft);
// layer borders
m_mainLayer->addChildAtPosition(createGeodeListBorders(layerSize), Anchor::Center);
m_mainLayer->addChildAtPosition(createGeodeListBorders(layerSize, m_forceDisableTheme), Anchor::Center);
auto scrollBar = Scrollbar::create(m_list);
m_mainLayer->addChildAtPosition(
@ -119,15 +117,16 @@ bool ModSettingsPopup::setup(Mod* mod) {
m_applyMenu->setContentWidth(150);
m_applyMenu->setLayout(RowLayout::create());
m_applyMenu->getLayout()->ignoreInvisibleChildren(true);
m_applyMenu->setTouchPriority(buttonPriority);
auto restartBtnSpr = createGeodeButton("Restart Now", true);
auto restartBtnSpr = createGeodeButton("Restart Now", true, GeodeButtonSprite::Default, m_forceDisableTheme);
restartBtnSpr->setScale(.6f);
m_restartBtn = CCMenuItemSpriteExtra::create(
restartBtnSpr, this, menu_selector(ModSettingsPopup::onRestart)
);
m_applyMenu->addChildAtPosition(m_restartBtn, Anchor::Bottom, ccp(0, 20));
m_applyBtnSpr = createGeodeButton("Apply", true);
m_applyBtnSpr = createGeodeButton("Apply", true, GeodeButtonSprite::Default, m_forceDisableTheme);
m_applyBtnSpr->setScale(.6f);
m_applyBtn = CCMenuItemSpriteExtra::create(
m_applyBtnSpr, this, menu_selector(ModSettingsPopup::onApply)
@ -136,7 +135,7 @@ bool ModSettingsPopup::setup(Mod* mod) {
m_mainLayer->addChildAtPosition(m_applyMenu, Anchor::Bottom, ccp(0, 20));
auto resetBtnSpr = createGeodeButton("Reset All", true);
auto resetBtnSpr = createGeodeButton("Reset All", true, GeodeButtonSprite::Default, m_forceDisableTheme);
resetBtnSpr->setScale(.6f);
auto resetBtn = CCMenuItemSpriteExtra::create(
@ -144,28 +143,46 @@ bool ModSettingsPopup::setup(Mod* mod) {
);
m_buttonMenu->addChildAtPosition(resetBtn, Anchor::BottomLeft, ccp(45, 20));
auto configFolderSpr = CCSprite::createWithSpriteFrameName("folderIcon_001.png");
m_openConfigDirBtnSpr = createGeodeButton(configFolderSpr, "");
m_openConfigDirBtnSpr->setScale(.6f);
m_openConfigDirBtnSpr->getIcon()->setScale(m_openConfigDirBtnSpr->getIcon()->getScale() * 1.4f);
auto openConfigDirBtn = CCMenuItemSpriteExtra::create(
m_openConfigDirBtnSpr, this, menu_selector(ModSettingsPopup::onOpenConfigDirectory)
);
m_buttonMenu->addChildAtPosition(openConfigDirBtn, Anchor::BottomRight, ccp(-50, 20));
auto foldersMenu = CCMenu::create();
foldersMenu->setContentSize({ 0, 30 });
foldersMenu->setAnchorPoint(ccp(1, 0));
foldersMenu->setLayout(RowLayout::create()
->setAxisReverse(true)
->setGap(5)
->setAutoGrowAxis(30)
->setGrowCrossAxis(true));
m_buttonMenu->addChildAtPosition(foldersMenu, Anchor::BottomRight, ccp(-5, 10));
auto settingFolderSpr = CCSprite::createWithSpriteFrameName("folderIcon_001.png");
auto settingFolderSprSub = CCSprite::createWithSpriteFrameName("settings.png"_spr);
settingFolderSprSub->setColor(ccBLACK);
settingFolderSprSub->setOpacity(155);
settingFolderSprSub->setScale(.55f);
settingFolderSpr->addChildAtPosition(settingFolderSprSub, Anchor::Center, ccp(0, -3));
auto openDirBtnSpr = createGeodeButton(settingFolderSpr, "");
openDirBtnSpr->setScale(.6f);
openDirBtnSpr->getIcon()->setScale(openDirBtnSpr->getIcon()->getScale() * 1.4f);
auto openDirBtn = CCMenuItemSpriteExtra::create(
openDirBtnSpr, this, menu_selector(ModSettingsPopup::onOpenSaveDirectory)
);
m_buttonMenu->addChildAtPosition(openDirBtn, Anchor::BottomRight, ccp(-20, 20));
auto createFolderButton = [&, this](auto subSprName, auto callback) {
auto folderSpr = CCSprite::createWithSpriteFrameName("folderIcon_001.png");
auto folderSprSub = CCSprite::createWithSpriteFrameName(subSprName);
folderSprSub->setColor(ccBLACK);
folderSprSub->setOpacity(155);
folderSprSub->setScale(.55f);
folderSpr->addChildAtPosition(folderSprSub, Anchor::Center, ccp(0, -3));
auto buttonSpr = createGeodeButton(folderSpr, "", GeodeButtonSprite::Default, m_forceDisableTheme);
buttonSpr->setScale(.6f);
buttonSpr->getIcon()->setScale(buttonSpr->getIcon()->getScale() * 1.4f);
auto folderBtn = CCMenuItemSpriteExtra::create(
buttonSpr, this, static_cast<SEL_MenuHandler>(callback)
);
foldersMenu->addChild(folderBtn);
return folderBtn;
};
createFolderButton("save.png"_spr, &ModSettingsPopup::onOpenSaveDirectory);
createFolderButton("settings.png"_spr, &ModSettingsPopup::onOpenConfigDirectory);
auto persistentBtn = createFolderButton("persistent.png"_spr, &ModSettingsPopup::onOpenPersistentDirectory);
if (!std::filesystem::exists(mod->getPersistentDir(false))) {
persistentBtn->setEnabled(false);
auto spr = static_cast<CCSprite*>(persistentBtn->getNormalImage());
spr->setCascadeColorEnabled(true);
spr->setCascadeOpacityEnabled(true);
spr->setColor(ccGRAY);
spr->setOpacity(155);
}
foldersMenu->updateLayout();
m_changeListener.bind([this](auto* ev) {
this->updateState(ev->getNode());
@ -223,7 +240,9 @@ void ModSettingsPopup::onOpenSaveDirectory(CCObject*) {
}
void ModSettingsPopup::onOpenConfigDirectory(CCObject*) {
file::openFolder(m_mod->getConfigDir());
this->updateState();
}
void ModSettingsPopup::onOpenPersistentDirectory(CCObject*) {
file::openFolder(m_mod->getPersistentDir());
}
void ModSettingsPopup::onClearSearch(CCObject*) {
m_searchInput->setString("");
@ -238,12 +257,6 @@ void ModSettingsPopup::updateState(SettingNodeV3* invoker) {
m_restartBtn->setVisible(ModSettingsManager::from(m_mod)->restartRequired());
m_applyMenu->updateLayout();
auto configDirExists = std::filesystem::exists(m_mod->getConfigDir(false));
m_openConfigDirBtnSpr->setCascadeColorEnabled(true);
m_openConfigDirBtnSpr->setCascadeOpacityEnabled(true);
m_openConfigDirBtnSpr->setColor(configDirExists ? ccWHITE : ccGRAY);
m_openConfigDirBtnSpr->setOpacity(configDirExists ? 255 : 155);
auto listPosBefore = m_list->m_contentLayer->getPositionY();
auto listHeightBefore = m_list->m_contentLayer->getContentHeight();
@ -330,9 +343,9 @@ void ModSettingsPopup::onClose(CCObject* sender) {
GeodePopup::onClose(sender);
}
ModSettingsPopup* ModSettingsPopup::create(Mod* mod) {
ModSettingsPopup* ModSettingsPopup::create(Mod* mod, bool forceDisableTheme) {
auto ret = new ModSettingsPopup();
if (ret->init(440, 280, mod)) {
if (ret->init(440, 280, mod, GeodePopupStyle::Default, forceDisableTheme)) {
ret->autorelease();
return ret;
}

View file

@ -3,6 +3,9 @@
#include <Geode/loader/SettingV3.hpp>
#include <Geode/ui/Popup.hpp>
#include <Geode/utils/cocos.hpp>
#include <Geode/ui/ScrollLayer.hpp>
#include <Geode/ui/TextInput.hpp>
#include "../GeodeStyle.hpp"
using namespace geode::prelude;
@ -16,7 +19,6 @@ protected:
CCMenuItemSpriteExtra* m_applyBtn;
CCMenuItemSpriteExtra* m_restartBtn;
ButtonSprite* m_applyBtnSpr;
IconButtonSprite* m_openConfigDirBtnSpr;
TextInput* m_searchInput;
CCMenuItemSpriteExtra* m_searchClearBtn;
EventListener<EventFilter<SettingNodeValueChangeEventV3>> m_changeListener;
@ -30,8 +32,9 @@ protected:
void onResetAll(CCObject*);
void onOpenSaveDirectory(CCObject*);
void onOpenConfigDirectory(CCObject*);
void onOpenPersistentDirectory(CCObject*);
void onClearSearch(CCObject*);
public:
static ModSettingsPopup* create(Mod* mod);
static ModSettingsPopup* create(Mod* mod, bool forceDisableTheme = false);
};

View file

@ -1,6 +1,7 @@
#pragma once
#include <Geode/utils/cocos.hpp>
#include <Geode/utils/string.hpp>
#include <server/Server.hpp>
#include "../list/ModItem.hpp"
@ -143,6 +144,7 @@ enum class ServerModListType {
Featured,
Trending,
Recent,
Modtober24,
};
class ServerModListSource : public ModListSource {
@ -165,6 +167,8 @@ public:
server::ModsQuery const& getQuery() const;
InvalidateQueryAfter<server::ModsQuery> getQueryMut();
bool isDefaultQuery() const override;
server::ModsQuery createDefaultQuery() const;
ServerModListType getType() const;
};
class ModPackListSource : public ModListSource {
@ -191,6 +195,7 @@ void filterModsWithLocalQuery(ModListSource::ProvidedMods& mods, Query const& qu
std::vector<std::pair<ModSource, double>> filtered;
// Filter installed mods based on query
// TODO: maybe skip fuzzy matching altogether if query is empty?
for (auto& src : mods.mods) {
double weighted = 0;
bool addToList = true;
@ -223,7 +228,10 @@ void filterModsWithLocalQuery(ModListSource::ProvidedMods& mods, Query const& qu
return a.second > b.second;
}
// Sort secondarily alphabetically
return a.first.getMetadata().getName() < b.first.getMetadata().getName();
return utils::string::caseInsensitiveCompare(
a.first.getMetadata().getName(),
b.first.getMetadata().getName()
) == std::strong_ordering::less;
});
mods.mods.clear();

View file

@ -1,29 +1,7 @@
#include "ModListSource.hpp"
void ServerModListSource::resetQuery() {
switch (m_type) {
case ServerModListType::Download: {
m_query = server::ModsQuery {};
} break;
case ServerModListType::Featured: {
m_query = server::ModsQuery {
.featured = true,
};
} break;
case ServerModListType::Trending: {
m_query = server::ModsQuery {
.sorting = server::ModsSort::RecentlyUpdated,
};
} break;
case ServerModListType::Recent: {
m_query = server::ModsQuery {
.sorting = server::ModsSort::RecentlyPublished,
};
} break;
}
m_query = this->createDefaultQuery();
}
ServerModListSource::ProviderTask ServerModListSource::fetchPage(size_t page, size_t pageSize, bool forceUpdate) {
@ -56,7 +34,7 @@ ServerModListSource::ServerModListSource(ServerModListType type)
ServerModListSource* ServerModListSource::get(ServerModListType type) {
switch (type) {
default:
default: [[fallthrough]];
case ServerModListType::Download: {
static auto inst = new ServerModListSource(ServerModListType::Download);
return inst;
@ -76,6 +54,11 @@ ServerModListSource* ServerModListSource::get(ServerModListType type) {
static auto inst = new ServerModListSource(ServerModListType::Recent);
return inst;
} break;
case ServerModListType::Modtober24: {
static auto inst = new ServerModListSource(ServerModListType::Modtober24);
return inst;
} break;
}
}
@ -104,7 +87,31 @@ InvalidateQueryAfter<server::ModsQuery> ServerModListSource::getQueryMut() {
return InvalidateQueryAfter(m_query, this);
}
bool ServerModListSource::isDefaultQuery() const {
return !m_query.query.has_value() &&
m_query.tags.empty() &&
!m_query.developer.has_value();
return m_query == this->createDefaultQuery();
}
server::ModsQuery ServerModListSource::createDefaultQuery() const {
switch (m_type) {
case ServerModListType::Download: return server::ModsQuery {};
case ServerModListType::Featured: return server::ModsQuery {
.featured = true,
};
case ServerModListType::Trending: return server::ModsQuery {
.sorting = server::ModsSort::RecentlyUpdated,
};
case ServerModListType::Recent: return server::ModsQuery {
.sorting = server::ModsSort::RecentlyPublished,
};
case ServerModListType::Modtober24: return server::ModsQuery {
.tags = { "modtober24" },
};
}
}
ServerModListType ServerModListSource::getType() const {
return m_type;
}

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