2022-07-30 12:24:03 -04:00
# pragma once
2022-10-06 13:46:07 -04:00
2023-10-15 12:20:47 -04:00
# include "../utils/MiniFunction.hpp"
2022-07-30 12:24:03 -04:00
# include "Traits.hpp"
2022-12-24 13:13:53 -05:00
2022-10-08 05:59:06 -04:00
# include <Geode/loader/Loader.hpp>
2022-12-24 13:13:53 -05:00
# include <cocos2d.h>
2022-07-30 12:24:03 -04:00
# include <vector>
2022-10-13 05:56:23 -04:00
namespace cocos2d {
2022-10-30 14:59:20 -04:00
class CCNode ;
2022-10-13 05:56:23 -04:00
}
2022-12-24 13:13:53 -05:00
namespace geode {
template < class , class >
class Modify ;
}
2022-07-30 12:24:03 -04:00
namespace geode : : modifier {
2022-10-30 14:59:20 -04:00
class FieldContainer {
private :
std : : vector < void * > m_containedFields ;
2023-02-08 10:25:07 -05:00
std : : vector < utils : : MiniFunction < void ( void * ) > > m_destructorFunctions ;
2022-10-30 14:59:20 -04:00
public :
~ FieldContainer ( ) {
for ( auto i = 0u ; i < m_containedFields . size ( ) ; i + + ) {
2024-05-15 16:59:42 -04:00
if ( m_destructorFunctions [ i ] & & m_containedFields [ i ] ) {
m_destructorFunctions [ i ] ( m_containedFields [ i ] ) ;
operator delete ( m_containedFields [ i ] ) ;
}
2022-10-30 14:59:20 -04:00
}
}
void * getField ( size_t index ) {
2023-07-30 15:21:17 -04:00
while ( m_containedFields . size ( ) < = index ) {
2023-03-01 17:02:09 -05:00
m_containedFields . push_back ( nullptr ) ;
m_destructorFunctions . push_back ( nullptr ) ;
2022-10-30 14:59:20 -04:00
}
return m_containedFields . at ( index ) ;
}
2023-02-08 10:25:07 -05:00
void * setField ( size_t index , size_t size , utils : : MiniFunction < void ( void * ) > destructor ) {
2022-10-30 14:59:20 -04:00
m_containedFields . at ( index ) = operator new ( size ) ;
m_destructorFunctions . at ( index ) = destructor ;
return m_containedFields . at ( index ) ;
}
2024-04-12 15:56:05 -04:00
static FieldContainer * from ( cocos2d : : CCNode * node , char const * forClass ) {
return node - > getFieldContainer ( forClass ) ;
2022-10-30 14:59:20 -04:00
}
} ;
2022-12-24 13:13:53 -05:00
2023-03-01 17:02:09 -05:00
GEODE_DLL size_t getFieldIndexForClass ( char const * name ) ;
2022-10-30 14:59:20 -04:00
2024-04-21 17:09:49 -04:00
template < class Parent >
concept HasFields = requires {
typename Parent : : Fields ;
} ;
2022-11-09 12:11:50 -05:00
template < class Parent , class Base >
2022-10-30 14:59:20 -04:00
class FieldIntermediate {
2022-12-24 13:13:53 -05:00
using Intermediate = Modify < Parent , Base > ;
2022-10-30 14:59:20 -04:00
// Padding used for guaranteeing any member of parents
// will be in between sizeof(Intermediate) and sizeof(Parent)
2023-10-15 12:20:47 -04:00
alignas ( std : : max ( alignof ( Base ) , alignof ( uintptr_t ) ) ) uintptr_t m_padding ;
2022-10-30 14:59:20 -04:00
public :
2022-12-24 13:13:53 -05:00
// the constructor that constructs the fields.
// we construct the Parent first,
2024-04-22 08:34:57 -04:00
static void fieldConstructor ( void * offsetField ) {
if constexpr ( HasFields < Parent > ) {
( void ) new ( offsetField ) typename Parent : : Fields ( ) ;
}
else {
std : : array < std : : byte , sizeof ( Parent ) > parentContainer ;
2022-10-30 14:59:20 -04:00
2024-04-22 08:34:57 -04:00
auto parent = new ( parentContainer . data ( ) ) Parent ( ) ;
2022-10-30 14:59:20 -04:00
2024-04-22 08:34:57 -04:00
parent - > Intermediate : : ~ Intermediate ( ) ;
2022-10-30 14:59:20 -04:00
2024-04-22 08:34:57 -04:00
std : : memcpy (
offsetField ,
std : : launder ( & parentContainer [ sizeof ( Intermediate ) ] ) ,
sizeof ( Parent ) - sizeof ( Intermediate )
) ;
}
2022-10-30 14:59:20 -04:00
}
2024-04-22 08:34:57 -04:00
static void fieldDestructor ( void * offsetField ) {
if constexpr ( HasFields < Parent > ) {
static_cast < typename Parent : : Fields * > ( offsetField ) - > ~ Fields ( ) ;
}
else {
std : : array < std : : byte , sizeof ( Parent ) > parentContainer ;
2022-10-30 14:59:20 -04:00
2024-04-22 08:34:57 -04:00
auto parent = new ( parentContainer . data ( ) ) Intermediate ( ) ;
2022-10-30 14:59:20 -04:00
2024-04-22 08:34:57 -04:00
std : : memcpy (
std : : launder ( & parentContainer [ sizeof ( Intermediate ) ] ) ,
offsetField ,
sizeof ( Parent ) - sizeof ( Intermediate )
2022-10-30 14:59:20 -04:00
) ;
2024-04-22 08:34:57 -04:00
static_cast < Parent * > ( parent ) - > Parent : : ~ Parent ( ) ;
2022-10-30 14:59:20 -04:00
}
2024-04-21 17:09:49 -04:00
}
2024-04-22 08:53:23 -04:00
[[deprecated("Fields are now done using an explicit `Fields` struct. Please refer to https://docs.geode-sdk.org/tutorials/fields/ for more information.")]]
auto deprecatedSelf ( ) {
// get the this pointer of the base
// field intermediate is the first member of Modify
// meaning we canget the base from ourself
auto node = reinterpret_cast < Parent * > ( reinterpret_cast < std : : byte * > ( this ) - sizeof ( Base ) ) ;
static_assert ( sizeof ( Base ) = = offsetof ( Parent , m_fields ) , " offsetof not correct " ) ;
// generating the container if it doesn't exist
auto container = FieldContainer : : from ( node , typeid ( Base ) . name ( ) ) ;
// the index is global across all mods, so the
// function is defined in the loader source
static size_t index = getFieldIndexForClass ( typeid ( Base ) . name ( ) ) ;
// the fields are actually offset from their original
// offset, this is done to save on allocation and space
auto offsetField = container - > getField ( index ) ;
if ( ! offsetField ) {
offsetField = container - > setField (
index , sizeof ( Parent ) - sizeof ( Intermediate ) , & FieldIntermediate : : fieldDestructor
) ;
FieldIntermediate : : fieldConstructor ( offsetField ) ;
}
return reinterpret_cast < Parent * > (
reinterpret_cast < std : : byte * > ( offsetField ) - sizeof ( Intermediate )
) ;
}
2024-04-22 08:34:57 -04:00
auto self ( ) {
if constexpr ( HasFields < Parent > ) {
// get the this pointer of the base
// field intermediate is the first member of Modify
// meaning we canget the base from ourself
auto node = reinterpret_cast < Parent * > ( reinterpret_cast < std : : byte * > ( this ) - sizeof ( Base ) ) ;
static_assert ( sizeof ( Base ) = = offsetof ( Parent , m_fields ) , " offsetof not correct " ) ;
// generating the container if it doesn't exist
auto container = FieldContainer : : from ( node , typeid ( Base ) . name ( ) ) ;
// the index is global across all mods, so the
// function is defined in the loader source
static size_t index = getFieldIndexForClass ( typeid ( Base ) . name ( ) ) ;
// the fields are actually offset from their original
// offset, this is done to save on allocation and space
auto offsetField = container - > getField ( index ) ;
if ( ! offsetField ) {
offsetField = container - > setField (
index , sizeof ( typename Parent : : Fields ) , & FieldIntermediate : : fieldDestructor
) ;
FieldIntermediate : : fieldConstructor ( offsetField ) ;
}
return reinterpret_cast < typename Parent : : Fields * > ( offsetField ) ;
}
else {
2024-04-22 08:53:23 -04:00
return this - > deprecatedSelf ( ) ;
2024-04-21 17:09:49 -04:00
}
}
2024-04-22 08:34:57 -04:00
auto operator - > ( ) {
2024-04-21 17:09:49 -04:00
// workaround for "static assertion is not an integral constant expression" in CLion
// while the solution in https://github.com/microsoft/STL/issues/3311 works, you can't provide
// cli args to clang-tidy in clion, so we use this workaround instead
// https://youtrack.jetbrains.com/issue/CPP-27446/spurious-offsetof-in-staticassert-error-from-clangd#focus=Comments-27-8172811.0-0
// update: that workaround didn't work,
// undefining and re-defining offsetof caused another error further down
// so we're doing this now
# ifdef __CLION_IDE__
2024-04-21 17:22:40 -04:00
return reinterpret_cast < typename Parent : : Fields * > ( 69420 ) ;
2024-04-21 17:09:49 -04:00
# else
return this - > self ( ) ;
2024-01-13 14:12:20 -05:00
# endif
2023-03-01 17:02:09 -05:00
}
2022-10-30 14:59:20 -04:00
} ;
2022-07-30 12:24:03 -04:00
}