Skip to content

Commit f89b421

Browse files
committed
🚧 Scoping out some modding utils
1 parent 87452b2 commit f89b421

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed

src/util/bbmodding.ts

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
type Targetable = Record<string, any>
2+
type TargetKey<Target extends Targetable> = keyof Target & string
3+
// type ModID = string
4+
type ModInjectionMode = 'override' | 'append' | 'prepend'
5+
6+
// interface RegisteredFunctionMod<Target extends Targetable, Key extends TargetKey<Target>> {
7+
// id: string
8+
// mode: ModInjectionMode
9+
// cb: (
10+
// this: Target,
11+
// ctx: ModContext<Target, Key>,
12+
// ...args: Parameters<Target[Key]>
13+
// ) => ModFunctionReturnResult<ReturnType<Target[Key]>>
14+
// }
15+
16+
interface ModContext<Target extends Targetable, Key extends TargetKey<Target>> {
17+
/**
18+
* The original function that was modified.
19+
*/
20+
originalFunction: Target[Key]
21+
/**
22+
* The key of the function that was modified.
23+
*/
24+
key: Key
25+
/**
26+
* The id of the mod that created this function.
27+
*/
28+
modId: string
29+
/**
30+
* The injection mode of the mod that created this function.
31+
*/
32+
modMode: ModInjectionMode
33+
}
34+
35+
interface ModFunctionReturnResult<T> {
36+
returnValue: T
37+
/**
38+
* If injection mode is 'append', this will prevent the original function from running.
39+
*/
40+
cancelled?: boolean
41+
}
42+
43+
const REGISTERED_MODS = new Map<Targetable, Map<string, any>>()
44+
45+
function assertEntryExists<Target extends Targetable, Key extends TargetKey<Target>>(
46+
target: Target,
47+
key: Key
48+
) {
49+
if (!REGISTERED_MODS.has(target)) {
50+
REGISTERED_MODS.set(target, new Map())
51+
}
52+
if (!REGISTERED_MODS.get(target)!.has(key)) {
53+
REGISTERED_MODS.get(target)!.set(key, new Map())
54+
}
55+
}
56+
57+
/**
58+
* Registers a function mod to a target object / class.
59+
* @param target The target object / class to modify.
60+
* @param key The key of the function to modify.
61+
* @param id The ID of the mod. Used to identify errors and conflicts.
62+
* @param mode The mode of the mod. Can be `override`, `append`, or `prepend`.
63+
* - `override` will replace the original function with the new one.
64+
* - `append` will run the original function first, then the new one.
65+
* - `prepend` will run the new function first, then the original one.
66+
* @param cb The callback function to run when the target function is called.
67+
*/
68+
export function registerFunctionMod<Target extends Targetable, Key extends TargetKey<Target>>(
69+
target: Target,
70+
key: Key,
71+
id: string,
72+
mode: ModInjectionMode,
73+
cb: (
74+
this: Target,
75+
ctx: ModContext<Target, Key>,
76+
...args: Parameters<Target[Key]>
77+
) => ModFunctionReturnResult<ReturnType<Target[Key]>>
78+
) {
79+
console.log(target, key, id, mode, cb)
80+
assertEntryExists(target, key)
81+
const mods = REGISTERED_MODS.get(target)!.get(key)!
82+
if (mods.has(id)) {
83+
throw new Error(`Mod with ID "${id}" already exists for function "${key}"`)
84+
}
85+
mods.set(id, { id, mode, cb })
86+
}
87+
88+
registerFunctionMod(Group.prototype, 'updateElement', 'my_epic_mod', 'override', function (ctx) {
89+
console.log('Hello World!', ctx)
90+
return { returnValue: this }
91+
})
92+
93+
/**
94+
* Gets all registered mods for a target object / class.
95+
* @param target The target object / class to get mods for.
96+
* @param key (Optional) The key of the function to get mods for.
97+
*/
98+
export function getRegisteredMods<Target extends Targetable, Key extends TargetKey<Target>>(
99+
target: Target,
100+
key?: Key
101+
) {
102+
//
103+
}
104+
105+
getRegisteredMods(Group.prototype)

0 commit comments

Comments
 (0)