Skip to content

Commit 8224aa9

Browse files
committed
CCObject fields support
1 parent e841f44 commit 8224aa9

File tree

10 files changed

+354
-42
lines changed

10 files changed

+354
-42
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
Miscellaneous Geode utils to make development easier:
44

5-
## Modifying any node
5+
## Modifying any CCObject
66

7-
Familiar modify syntax as Geode, supporting fields. `class $nodeModify(SomeNode)`
8-
You can name the modified node the same way as well `class $nodeModify(MySomeNode, SomeNode)`
7+
Familiar modify syntax as Geode, supporting fields. `class $objectModify(SomeObject)` or `class $nodeModify(SomeNode)`
8+
You can name the modified node/object the same way as well `class $objectModify(MySomeObject, SomeObject)` or `class $nodeModify(MySomeNode, SomeNode)`
9+
10+
$nodeModify and $objectModify do the same thing, but both names are available for organization purposes.
911

1012
To use this, you will create a `void modify()` method within that class and inside of there you can change the node to your hearts content. You can use the fields struct just like in Geode to add fields if needed.
1113

about.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
Miscellaneous Geode utils to make development easier:
44

5-
## Modifying any node
5+
## Modifying any CCObject
66

7-
Familiar modify syntax as Geode, supporting fields. `class $nodeModify(SomeNode)`
8-
You can name the modified node the same way as well `class $nodeModify(MySomeNode, SomeNode)`
7+
Familiar modify syntax as Geode, supporting fields. `class $objectModify(SomeObject)` or `class $nodeModify(SomeNode)`
8+
You can name the modified node/object the same way as well `class $objectModify(MySomeObject, SomeObject)` or `class $nodeModify(MySomeNode, SomeNode)`
9+
10+
$nodeModify and $objectModify do the same thing, but both names are available for organization purposes.
911

1012
To use this, you will create a `void modify()` method within that class and inside of there you can change the node to your hearts content. You can use the fields struct just like in Geode to add fields if needed.
1113

changelog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# 1.1.0
2+
- Fields and User Objects can now be used with CCObjects in the same way as CCNodes
3+
14
# 1.0.5
25
- iOS support
36

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,20 @@
44
#include <Geode/modify/CCObject.hpp>
55
#include <Geode/modify/CCScriptEngineManager.hpp>
66
#include <Geode/cocos/cocoa/CCObject.h>
7+
#include "Fields.h"
8+
9+
#ifdef GEODE_IS_WINDOWS
10+
#ifdef ALPHALANEOUS_UTILS_API_EXPORTING
11+
#define ALPHA_UTILS_API_DLL __declspec(dllexport)
12+
#else
13+
#define ALPHA_UTILS_API_DLL __declspec(dllimport)
14+
#endif
15+
#else
16+
#define ALPHA_UTILS_API_DLL __attribute__((visibility("default")))
17+
#endif
718

819
struct ObjectData : public cocos2d::CCObject {
920
geode::Ref<cocos2d::CCObject> m_object;
10-
int m_tag;
1121
};
1222

1323
class DummyScriptEngineProtocol : public cocos2d::CCScriptEngineProtocol {
@@ -33,14 +43,18 @@ class DummyScriptEngineProtocol : public cocos2d::CCScriptEngineProtocol {
3343
bool parseConfig(ConfigType type, const gd::string& str) { return false; };
3444
};
3545

36-
class $modify(MyCCScriptEngineManager, cocos2d::CCScriptEngineManager) {
46+
struct ALPHA_UTILS_API_DLL MyCCScriptEngineManager : public geode::Modify<MyCCScriptEngineManager, cocos2d::CCScriptEngineManager> {
3747
static cocos2d::CCScriptEngineManager* sharedManager();
3848
};
3949

40-
class $modify(FieldCCObject, cocos2d::CCObject) {
41-
cocos2d::CCObject* autorelease();
50+
class ObjectFieldContainer;
51+
52+
struct ALPHA_UTILS_API_DLL FieldCCObject : public cocos2d::CCObject {
4253
ObjectData* getObjectData();
4354
cocos2d::CCObject* getUserObject();
4455
void setUserObject(cocos2d::CCObject* object);
4556
void tryCreateData();
57+
ObjectFieldContainer* getFieldContainer(char const* forClass);
58+
void setUserObject(std::string const& id, CCObject* value);
59+
CCObject* getUserObject(std::string const& id);
4660
};

include/Fields.h

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#pragma once
2+
3+
#include <unordered_map>
4+
#include <string>
5+
#include <functional>
6+
#include <Geode/cocos/base_nodes/CCNode.h>
7+
#include "CCObject.h"
8+
#include "NodeModding.h"
9+
10+
#ifdef GEODE_IS_WINDOWS
11+
#ifdef ALPHALANEOUS_UTILS_API_EXPORTING
12+
#define ALPHA_UTILS_API_DLL __declspec(dllexport)
13+
#else
14+
#define ALPHA_UTILS_API_DLL __declspec(dllimport)
15+
#endif
16+
#else
17+
#define ALPHA_UTILS_API_DLL __attribute__((visibility("default")))
18+
#endif
19+
20+
//all of this is geode's work, just retrofitted to work with CCObjects
21+
22+
constexpr auto METADATA_TAG = 0xB324ABC;
23+
24+
static uint64_t fnv1aHash(char const* str) {
25+
uint64_t hash = 0xcbf29ce484222325;
26+
while (*str) {
27+
hash ^= *str++;
28+
hash *= 0x100000001b3;
29+
}
30+
return hash;
31+
}
32+
33+
template <typename T>
34+
class NoHashHasher;
35+
36+
template <>
37+
class NoHashHasher<uint64_t> {
38+
public:
39+
size_t operator()(uint64_t key) const {
40+
return key;
41+
}
42+
};
43+
44+
class FieldCCObject;
45+
46+
class ALPHA_UTILS_API_DLL ObjectFieldContainer {
47+
private:
48+
std::vector<void*> m_containedFields;
49+
std::vector<std::function<void(void*)>> m_destructorFunctions;
50+
51+
public:
52+
~ObjectFieldContainer();
53+
54+
void* getField(size_t index);
55+
void* setField(size_t index, size_t size, std::function<void(void*)> destructor);
56+
static ObjectFieldContainer* from(FieldCCObject* object, char const* forClass);
57+
};
58+
59+
size_t getFieldIndexForClass(char const* name);
60+
61+
class ALPHA_UTILS_API_DLL ObjectMetadata final : public cocos2d::CCObject {
62+
public:
63+
// for performance reasons, this key is the hash of the class name
64+
std::unordered_map<uint64_t, ObjectFieldContainer*, NoHashHasher<uint64_t>> m_classFieldContainers;
65+
std::unordered_map<std::string, geode::Ref<cocos2d::CCObject>> m_userObjects;
66+
67+
ObjectMetadata();
68+
69+
virtual ~ObjectMetadata();
70+
71+
static ObjectMetadata* set(FieldCCObject* target);
72+
73+
ObjectFieldContainer* getFieldContainer(char const* forClass);
74+
};
75+
76+
namespace AlphaUtils {
77+
template <class Derived>
78+
struct ObjectWrapper;
79+
}
80+
81+
template <class Parent>
82+
class ObjectFieldIntermediate {
83+
using Intermediate = AlphaUtils::ObjectWrapper<Parent>;
84+
// Padding used for guaranteeing any member of parents
85+
// will be in between sizeof(Intermediate) and sizeof(Parent)
86+
std::aligned_storage_t<std::alignment_of_v<cocos2d::CCObject>, std::alignment_of_v<cocos2d::CCObject>> m_padding;
87+
88+
public:
89+
// the constructor that constructs the fields.
90+
// we construct the Parent first,
91+
static void fieldConstructor(void* offsetField) {
92+
(void) new (offsetField) typename Parent::Fields();
93+
}
94+
95+
static void fieldDestructor(void* offsetField) {
96+
static_cast<typename Parent::Fields*>(offsetField)->~Fields();
97+
}
98+
99+
auto self() {
100+
// get the this pointer of the base
101+
// field intermediate is the first member of Modify
102+
// meaning we can get the base from ourself
103+
auto node = reinterpret_cast<Parent*>(reinterpret_cast<std::byte*>(this) - sizeof(cocos2d::CCObject));
104+
// static_assert(sizeof(Base) + sizeof() == sizeof(Intermediate), "offsetof not correct");
105+
106+
// generating the container if it doesn't exist
107+
auto container = ObjectFieldContainer::from(node, typeid(cocos2d::CCObject).name());
108+
109+
// the index is global across all mods, so the
110+
// function is defined in the loader source
111+
static size_t index = geode::modifier::getFieldIndexForClass(typeid(cocos2d::CCObject).name());
112+
113+
// the fields are actually offset from their original
114+
// offset, this is done to save on allocation and space
115+
auto offsetField = container->getField(index);
116+
if (!offsetField) {
117+
offsetField = container->setField(
118+
index, sizeof(typename Parent::Fields), &ObjectFieldIntermediate::fieldDestructor
119+
);
120+
121+
ObjectFieldIntermediate::fieldConstructor(offsetField);
122+
}
123+
124+
return reinterpret_cast<typename Parent::Fields*>(offsetField);
125+
}
126+
127+
auto operator->() {
128+
return this->self();
129+
}
130+
};

include/NodeModding.h

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <string>
55
#include <functional>
66
#include <Geode/cocos/base_nodes/CCNode.h>
7+
#include "CCObject.h"
8+
#include "Fields.h"
79

810
#ifdef GEODE_IS_WINDOWS
911
#ifdef ALPHALANEOUS_UTILS_API_EXPORTING
@@ -15,30 +17,64 @@
1517
#define ALPHA_UTILS_API_DLL __attribute__((visibility("default")))
1618
#endif
1719

20+
template <class Parent>
21+
class ObjectFieldIntermediate;
22+
1823
namespace AlphaUtils {
1924

25+
struct ModifyObjectInfo {
26+
int priority;
27+
std::function<void(FieldCCObject*)> method;
28+
};
29+
30+
template <class Derived>
31+
struct ObjectWrapper : public FieldCCObject {
32+
ObjectFieldIntermediate<Derived> m_fields;
33+
static int modifyPrio() { return 0; }
34+
};
35+
36+
class ALPHA_UTILS_API_DLL ObjectModding {
37+
protected:
38+
std::unordered_map<std::string, std::vector<ModifyObjectInfo>> m_objectsToModify;
39+
public:
40+
static ObjectModding* get();
41+
std::unordered_map<std::string, std::vector<ModifyObjectInfo>> getObjectsToModify();
42+
void addObjectToModify(std::string className, int prio, std::function<void(FieldCCObject*)> func);
43+
void handleObject(FieldCCObject* object);
44+
};
45+
46+
template <class T>
47+
class ModifyObjectLoad {
48+
public:
49+
ModifyObjectLoad(std::string str) {
50+
ObjectModding::get()->addObjectToModify(str, T::modifyPrio(), [](FieldCCObject* self) {
51+
reinterpret_cast<T*>(self)->T::modify();
52+
});
53+
}
54+
};
55+
56+
//remnants for compat
2057
struct ModifyInfo {
2158
int priority;
2259
std::function<void(cocos2d::CCNode*)> method;
2360
};
2461

62+
template <class Derived>
63+
struct NodeWrapper : public cocos2d::CCNode {
64+
geode::modifier::FieldIntermediate<Derived, cocos2d::CCNode> m_fields;
65+
static int modifyPrio() { return 0; }
66+
};
67+
2568
class ALPHA_UTILS_API_DLL NodeModding {
2669
protected:
2770
std::unordered_map<std::string, std::vector<ModifyInfo>> m_nodesToModify;
28-
2971
public:
3072
static NodeModding* get();
3173
std::unordered_map<std::string, std::vector<ModifyInfo>> getNodesToModify();
3274
void addNodeToModify(std::string className, int prio, std::function<void(cocos2d::CCNode*)> func);
3375
void handleNode(cocos2d::CCNode* node);
3476
};
3577

36-
template <class Derived>
37-
struct NodeWrapper : public cocos2d::CCNode {
38-
geode::modifier::FieldIntermediate<Derived, cocos2d::CCNode> m_fields;
39-
static int modifyPrio() { return 0; }
40-
};
41-
4278
template <class T>
4379
class ModifyLoad {
4480
public:
@@ -54,14 +90,16 @@ namespace AlphaUtils {
5490
#define ALPHA_MODIFY_DECLARE(base, derived) \
5591
GEODE_CONCAT(GEODE_CONCAT(derived, __LINE__), Dummy);\
5692
struct derived;\
57-
class GEODE_CONCAT(GEODE_CONCAT(derived, Hook), __LINE__) : AlphaUtils::NodeWrapper<derived> {\
93+
class GEODE_CONCAT(GEODE_CONCAT(derived, Hook), __LINE__) : AlphaUtils::ObjectWrapper<derived> {\
5894
private:\
59-
static inline AlphaUtils::ModifyLoad<derived> s_apply{#base};\
95+
static inline AlphaUtils::ModifyObjectLoad<derived> s_apply{#base};\
6096
};\
61-
struct derived : AlphaUtils::NodeWrapper<derived>
97+
struct derived : AlphaUtils::ObjectWrapper<derived>
6298

6399
#define MODIFY1(base) ALPHA_MODIFY_DECLARE(base, GEODE_CONCAT(hook, __LINE__))
64100
#define MODIFY2(derived, base) ALPHA_MODIFY_DECLARE(base, derived)
65101

66102
#define $nodeModify(...) \
67-
GEODE_INVOKE(GEODE_CONCAT(MODIFY, GEODE_NUMBER_OF_ARGS(__VA_ARGS__)), __VA_ARGS__)
103+
GEODE_INVOKE(GEODE_CONCAT(MODIFY, GEODE_NUMBER_OF_ARGS(__VA_ARGS__)), __VA_ARGS__)
104+
105+
#define $objectModify(...) $nodeModify(__VA_ARGS__)

mod.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
},
99
"id": "alphalaneous.alphas_geode_utils",
1010
"name": "Alpha's Geode Utils",
11-
"version": "v1.0.5",
11+
"version": "v1.1.0",
1212
"developer": "Alphalaneous",
1313
"description": "Miscellaneous utilities for Geode Modding",
1414
"api": {

0 commit comments

Comments
 (0)