Skip to content

Commit b5e2bcb

Browse files
committed
Base Modding support
1 parent 2ca315e commit b5e2bcb

File tree

7 files changed

+200
-1
lines changed

7 files changed

+200
-1
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,34 @@ class $nodeModify(MyCustomCreatorLayer, CustomCreatorLayer) {
4545
};
4646
```
4747
48+
## Modifying Base Classes of CCNodes
49+
50+
Using $baseModify, you can modify all nodes that inheret some class. In the following example, we modify all FLAlertLayers, not just FLAlertLayer itself. These are different than the above $nodeModify and $cobjectModify, as it requires a valid type to be passed in, thus it does not natively support modifying other mod's nodes without declaring the class beforehand.
51+
52+
```c++
53+
class $baseModify(MyFLAlertLayer, FLAlertLayer) {
54+
55+
void modify() {
56+
setScale(2.f);
57+
}
58+
59+
}
60+
```
61+
62+
If you still wish to modify another mod's nodes by their base class, you can do the following.
63+
Let's say we had a node called AlphasEpicNode, which in itself inherets CCNode, that a lot of nodes from AlphasEpicMod inherit. Because we are defining it beforehand (make sure the definition matches that of how it is in said mod), the mod can deduce it and it's own base class names and it will work as expected.
64+
65+
```c++
66+
class AlphasEpicNode : public CCNode {};
67+
class $baseModify(MyAlphasEpicNode, AlphasEpicNode) {
68+
69+
void modify() {
70+
setScale(2.f);
71+
}
72+
73+
}
74+
```
75+
4876
## General Utils
4977

5078
### AlphaUtils::Cocos namespace:

about.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,34 @@ class $nodeModify(MyCustomCreatorLayer, CustomCreatorLayer) {
4545
};
4646
```
4747
48+
## Modifying Base Classes of CCNodes
49+
50+
Using $baseModify, you can modify all nodes that inheret some class. In the following example, we modify all FLAlertLayers, not just FLAlertLayer itself. These are different than the above $nodeModify and $cobjectModify, as it requires a valid type to be passed in, thus it does not natively support modifying other mod's nodes without declaring the class beforehand.
51+
52+
```c++
53+
class $baseModify(MyFLAlertLayer, FLAlertLayer) {
54+
55+
void modify() {
56+
setScale(2.f);
57+
}
58+
59+
}
60+
```
61+
62+
If you still wish to modify another mod's nodes by their base class, you can do the following.
63+
Let's say we had a node called AlphasEpicNode, which in itself inherets CCNode, that a lot of nodes from AlphasEpicMod inherit. Because we are defining it beforehand (make sure the definition matches that of how it is in said mod), the mod can deduce it and it's own base class names and it will work as expected.
64+
65+
```c++
66+
class AlphasEpicNode : public CCNode {};
67+
class $baseModify(MyAlphasEpicNode, AlphasEpicNode) {
68+
69+
void modify() {
70+
setScale(2.f);
71+
}
72+
73+
}
74+
```
75+
4876
## General Utils
4977

5078
### AlphaUtils::Cocos namespace:

include/NodeModding.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <Geode/cocos/base_nodes/CCNode.h>
77
#include "CCObject.h"
88
#include "Fields.h"
9+
#include "Utils.h"
910

1011
#ifdef GEODE_IS_WINDOWS
1112
#ifdef ALPHALANEOUS_UTILS_API_EXPORTING
@@ -53,6 +54,40 @@ namespace AlphaUtils {
5354
}
5455
};
5556

57+
struct ModifyBaseInfo {
58+
int priority;
59+
std::function<void(FieldCCObject*)> method;
60+
};
61+
62+
template <class Derived, class Type>
63+
struct BaseWrapper : public Type {
64+
geode::modifier::FieldIntermediate<Derived, Type> m_fields;
65+
static int modifyPrio() { return 0; }
66+
};
67+
68+
class ALPHA_UTILS_API_DLL BaseModding {
69+
protected:
70+
std::unordered_map<std::string, std::vector<ModifyBaseInfo>> m_basesToModify;
71+
public:
72+
bool m_lock;
73+
static BaseModding* get();
74+
std::unordered_map<std::string, std::vector<ModifyBaseInfo>> getBasesToModify();
75+
void addBaseToModify(std::string className, int prio, std::function<void(FieldCCObject*)> func);
76+
void handleBase(FieldCCObject* object);
77+
};
78+
79+
template <class T, class Type>
80+
class ModifyBaseLoad {
81+
public:
82+
ModifyBaseLoad() {
83+
BaseModding::get()->addBaseToModify(AlphaUtils::Cocos::getClassNameByType<Type>(), T::modifyPrio(), [](FieldCCObject* self) {
84+
BaseModding::get()->m_lock = true;
85+
reinterpret_cast<T*>(self)->T::modify();
86+
BaseModding::get()->m_lock = false;
87+
});
88+
}
89+
};
90+
5691
//remnants for compat
5792
struct ModifyInfo {
5893
int priority;
@@ -105,14 +140,29 @@ class GEODE_CONCAT(GEODE_CONCAT(derived, Hook), __LINE__) : AlphaUtils::NodeWrap
105140
};\
106141
struct derived : AlphaUtils::NodeWrapper<derived>
107142

143+
#define ALPHA_BASE_MODIFY_DECLARE(base, derived) \
144+
GEODE_CONCAT(GEODE_CONCAT(derived, __LINE__), Dummy);\
145+
struct derived;\
146+
class GEODE_CONCAT(GEODE_CONCAT(derived, Hook), __LINE__) : AlphaUtils::BaseWrapper<derived, base> {\
147+
private:\
148+
static inline AlphaUtils::ModifyBaseLoad<derived, base> s_apply{};\
149+
};\
150+
struct derived : AlphaUtils::BaseWrapper<derived, base>
151+
108152
#define MODIFY1(base) ALPHA_MODIFY_DECLARE(base, GEODE_CONCAT(hook, __LINE__))
109153
#define MODIFY2(derived, base) ALPHA_MODIFY_DECLARE(base, derived)
110154

111155
#define MODIFYNODE1(base) ALPHA_NODE_MODIFY_DECLARE(base, GEODE_CONCAT(hook, __LINE__))
112156
#define MODIFYNODE2(derived, base) ALPHA_NODE_MODIFY_DECLARE(base, derived)
113157

158+
#define MODIFYBASE1(base) ALPHA_BASE_MODIFY_DECLARE(base, GEODE_CONCAT(hook, __LINE__))
159+
#define MODIFYBASE2(derived, base) ALPHA_BASE_MODIFY_DECLARE(base, derived)
160+
114161
#define $nodeModify(...) \
115162
GEODE_INVOKE(GEODE_CONCAT(MODIFYNODE, GEODE_NUMBER_OF_ARGS(__VA_ARGS__)), __VA_ARGS__)
116163

117164
#define $objectModify(...) \
118165
GEODE_INVOKE(GEODE_CONCAT(MODIFY, GEODE_NUMBER_OF_ARGS(__VA_ARGS__)), __VA_ARGS__)
166+
167+
#define $baseModify(...) \
168+
GEODE_INVOKE(GEODE_CONCAT(MODIFYBASE, GEODE_NUMBER_OF_ARGS(__VA_ARGS__)), __VA_ARGS__)

include/Utils.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <Geode/cocos/base_nodes/CCNode.h>
66
#include <Geode/cocos/include/CCProtocols.h>
77
#include <Geode/utils/cocos.hpp>
8+
#include <Geode/utils/casts.hpp>
89
#include <random>
910

1011
#define public_cast(value, member) [](auto* v) { \
@@ -48,6 +49,67 @@ namespace AlphaUtils {
4849
return ret;
4950
}
5051

52+
static inline bool checkBaseClassNames(cocos2d::CCObject* obj, const std::string& name) {
53+
if (!obj) return false;
54+
55+
auto basePtr = dynamic_cast<void*>(obj);
56+
auto vftable = *reinterpret_cast<geode::cast::VftableType**>(basePtr);
57+
58+
auto metaPtr = static_cast<geode::cast::MetaPointerType*>(static_cast<geode::cast::CompleteVftableType*>(vftable));
59+
60+
#ifdef GEODE_IS_X64
61+
auto locatorOffset = metaPtr->m_completeLocator->m_locatorOffset;
62+
auto base = reinterpret_cast<uintptr_t>(metaPtr->m_completeLocator) - locatorOffset;
63+
#else
64+
auto base = 0;
65+
#endif
66+
67+
auto classDesc = metaPtr->m_completeLocator->m_classDescriptor.into(base);
68+
auto baseClassArray = classDesc->m_baseClassArray.into(base);
69+
for (int32_t i = 0; i < classDesc->m_numBaseClasses; ++i) {
70+
auto descriptorEntry = baseClassArray->m_descriptorEntries[i].into(base);
71+
auto typeDesc = descriptorEntry->m_typeDescriptor.into(base);
72+
auto optionIdent = static_cast<char const*>(
73+
typeDesc->m_typeDescriptorName
74+
);
75+
76+
if (std::strcmp(name.c_str(), optionIdent) == 0) {
77+
return true;
78+
}
79+
}
80+
81+
return false;
82+
}
83+
84+
template <class T>
85+
static inline T getObject() {
86+
static T obj;
87+
return obj;
88+
}
89+
90+
template <class T>
91+
static inline std::string getClassNameByType() {
92+
auto basePtr = dynamic_cast<void*>(getObject<T>());
93+
auto vftable = *reinterpret_cast<geode::cast::VftableType**>(basePtr);
94+
95+
auto metaPtr = static_cast<geode::cast::MetaPointerType*>(static_cast<geode::cast::CompleteVftableType*>(vftable));
96+
97+
#ifdef GEODE_IS_X64
98+
auto locatorOffset = metaPtr->m_completeLocator->m_locatorOffset;
99+
auto base = reinterpret_cast<uintptr_t>(metaPtr->m_completeLocator) - locatorOffset;
100+
#else
101+
auto base = 0;
102+
#endif
103+
104+
auto classDesc = metaPtr->m_completeLocator->m_classDescriptor.into(base);
105+
auto entry = classDesc->m_baseClassArray.into(base)->m_descriptorEntries[0].into(base);
106+
auto optionIdent = static_cast<char const*>(
107+
entry->m_typeDescriptor.into(base)->m_typeDescriptorName
108+
);
109+
110+
return optionIdent;
111+
}
112+
51113
//getChildByType but using a string instead for dynamic use.
52114
static inline std::optional<cocos2d::CCNode*> getChildByClassName(cocos2d::CCNode* node, std::string name, int index = 0) {
53115
size_t indexCounter = 0;

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.1.5",
11+
"version": "v1.1.6",
1212
"developer": "Alphalaneous",
1313
"description": "Miscellaneous utilities for Geode Modding",
1414
"api": {

src/NodeModding.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,36 @@ ObjectModding* ObjectModding::get() {
3535
return instance;
3636
}
3737

38+
std::unordered_map<std::string, std::vector<ModifyBaseInfo>> BaseModding::getBasesToModify() {
39+
return m_basesToModify;
40+
}
41+
42+
void BaseModding::addBaseToModify(std::string className, int prio, std::function<void(FieldCCObject*)> func) {
43+
m_basesToModify[className].push_back({prio, func});
44+
}
45+
46+
void BaseModding::handleBase(FieldCCObject* object) {
47+
if (m_lock) return;
48+
for (auto& [key, methods] : m_basesToModify) {
49+
if (AlphaUtils::Cocos::checkBaseClassNames(object, key)) {
50+
std::sort(methods.begin(), methods.end(), [](auto& left, auto& right) {
51+
return left.priority < right.priority;
52+
});
53+
for (auto& pair : methods) {
54+
pair.method(object);
55+
}
56+
}
57+
}
58+
}
59+
60+
BaseModding* BaseModding::get() {
61+
static BaseModding* instance = nullptr;
62+
if (!instance) {
63+
instance = new BaseModding();
64+
}
65+
return instance;
66+
}
67+
3868
std::unordered_map<std::string, std::vector<ModifyInfo>> NodeModding::getNodesToModify() {
3969
return m_nodesToModify;
4070
}

src/hooks/CCObject.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class $modify(CCObject) {
5050
FieldCCObject* obj = reinterpret_cast<FieldCCObject*>(this);
5151
obj->tryCreateData();
5252
ObjectModding::get()->handleObject(obj);
53+
BaseModding::get()->handleBase(obj);
5354
return CCObject::autorelease();
5455
}
5556
};

0 commit comments

Comments
 (0)