1 #ifndef MUTATORS_BASE_H
2 #define MUTATORS_BASE_H
4 const int CBC_ORDER_FIRST = 1;
5 const int CBC_ORDER_LAST = 2;
6 const int CBC_ORDER_EXCLUSIVE = 3;
7 const int CBC_ORDER_ANY = 4;
9 bool CallbackChain_ReturnValue; // read-only field of the current return value
12 * Callbacks may be added to zero or more callback chains.
14 CLASS(Callback, Object)
16 * a callback function is like this:
23 ATTRIB(Callback, cbc_func, bool());
24 CONSTRUCTOR(Callback, bool() func) {
31 * Callback chains contain zero or more callbacks.
33 CLASS(CallbackChain, Object)
34 CLASS(CallbackNode, Object)
35 ATTRIB(CallbackNode, cbc, Callback);
36 ATTRIB(CallbackNode, cbc_next, CallbackNode);
37 ATTRIB(CallbackNode, cbc_order, int, 0);
38 CONSTRUCTOR(CallbackNode, Callback it, int order) {
39 CONSTRUCT(CallbackNode);
41 this.cbc_order = order;
43 ENDCLASS(CallbackNode)
45 ATTRIB(CallbackChain, cbc_next, CallbackNode);
46 ATTRIB(CallbackChain, cbc_order, int, 0);
47 CONSTRUCTOR(CallbackChain, string _name) {
48 CONSTRUCT(CallbackChain);
52 bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
54 if (order & CBC_ORDER_FIRST) {
55 if (order & CBC_ORDER_LAST)
56 if (this.cbc_order & CBC_ORDER_ANY)
58 if (this.cbc_order & CBC_ORDER_FIRST)
60 } else if (order & CBC_ORDER_LAST) {
61 if (this.cbc_order & CBC_ORDER_LAST)
64 entity node = NEW(CallbackNode, cb, order);
65 if (order & CBC_ORDER_FIRST) {
66 node.cbc_next = this.cbc_next;
68 } else if (order & CBC_ORDER_LAST) {
69 CallbackNode prev = NULL, it = this.cbc_next;
70 while (it) { prev = it, it = it.cbc_next; }
71 if (prev) prev.cbc_next = node;
72 else this.cbc_next = node;
74 // by default we execute last, but before a possible CBC_ORDER_LAST callback
75 CallbackNode prev = NULL, it = this.cbc_next;
76 while (it && !(it.cbc_order & CBC_ORDER_LAST)) { prev = it, it = it.cbc_next; }
78 if (prev) prev.cbc_next = node;
79 else this.cbc_next = node;
81 this.cbc_order |= (order | CBC_ORDER_ANY);
84 int CallbackChain_Remove(CallbackChain this, Callback cb)
87 for (Callback prev = NULL, it = this.cbc_next; it; prev = it, it = it.cbc_next) {
89 // remove it from the chain
90 Callback next = it.cbc_next;
91 if (prev) prev.cbc_next = next;
92 else this.cbc_next = next;
95 // it is now something we want to keep
96 order |= (it.cbc_order & CBC_ORDER_ANY);
98 this.cbc_order = order;
101 bool CallbackChain_Call(CallbackChain this)
104 for (Callback it = this.cbc_next; it; it = it.cbc_next) {
105 CallbackChain_ReturnValue = r;
106 r |= it.cbc.cbc_func();
108 return r; // callbacks return an error status, so 0 is default return value
110 ENDCLASS(CallbackChain)
112 #define _MUTATOR_HANDLE_NOP(type, id)
113 #define _MUTATOR_HANDLE_PARAMS(type, id) , type in_##id
114 #define _MUTATOR_HANDLE_PREPARE(type, id) id = in_##id;
115 #define _MUTATOR_HANDLE_PUSHTMP(type, id) TC(type, id); type tmp_##id = id;
116 #define _MUTATOR_HANDLE_PUSHOUT(type, id) type out_##id = id;
117 #define _MUTATOR_HANDLE_POPTMP(type, id) id = tmp_##id;
118 #define _MUTATOR_HANDLE_POPOUT(type, id) id = out_##id;
120 void RegisterHooks() {};
121 void RegisterCallbacks() {};
123 #define MUTATOR_HOOKABLE(id, params) _MUTATOR_HOOKABLE(id, params)
124 #define _MUTATOR_HOOKABLE(id, params) \
125 CallbackChain HOOK_##id; \
126 bool __Mutator_Send_##id(int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) { \
127 params(_MUTATOR_HANDLE_PUSHTMP, _MUTATOR_HANDLE_NOP) \
128 params(_MUTATOR_HANDLE_PREPARE, _MUTATOR_HANDLE_NOP) \
129 bool ret = CallbackChain_Call(HOOK_##id); \
130 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_PUSHOUT) \
131 params(_MUTATOR_HANDLE_POPTMP, _MUTATOR_HANDLE_NOP) \
132 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_POPOUT) \
135 [[accumulate]] void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); }
137 #define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__)
139 #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
141 #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
150 USING(mutatorfunc_t, bool(int));
152 CLASS(Mutator, Object)
153 ATTRIB(Mutator, m_id, int, 0);
154 ATTRIB(Mutator, m_name, string);
155 ATTRIB(Mutator, mutatorfunc, mutatorfunc_t);
156 ATTRIB(Mutator, mutatorcheck, bool());
157 CONSTRUCTOR(Mutator, string _name, mutatorfunc_t func) {
160 this.mutatorfunc = func;
164 REGISTRY(Mutators, BITS(7))
165 #define Mutators_from(i) _Mutators_from(i, NULL)
166 Mutator loaded_mutators[Mutators_MAX];
167 bool Mutator_Add(Mutator mut);
168 void Mutator_Remove(Mutator mut);
169 bool mutator_log = false;
172 /** server mutators activate corresponding client mutators for all clients */
173 REGISTER_NET_LINKED(Mutator)
176 bool Mutator_SendEntity(entity this, entity to, int sf)
178 int chan = MSG_ENTITY;
179 WriteHeader(chan, Mutator);
180 WriteString(chan, this.registered_id);
186 void NET_Mutator_Remove(entity this)
188 string s = this.netname;
189 WITH(bool, mutator_log, true, LAMBDA(
190 FOREACH(Mutators, it.registered_id == s, Mutator_Remove(it));
193 NET_HANDLE(Mutator, bool isNew)
196 string s = this.netname = ReadString();
201 this.entremove = NET_Mutator_Remove;
203 WITH(bool, mutator_log, true, LAMBDA(
204 FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; });
206 if (added > 1) LOG_WARNF("Added more than one mutator for %s", s);
213 bool Mutator_Add(Mutator mut)
216 for (int i = 0; i < Mutators_MAX; ++i) {
217 if (loaded_mutators[i] == mut)
218 return true; // already added
219 if (!(loaded_mutators[i]))
223 backtrace("WARNING: too many mutators, cannot add any more\n");
226 loaded_mutators[j] = mut;
227 mutatorfunc_t func = mut.mutatorfunc;
228 if (!func(MUTATOR_ADDING)) {
230 if (mutator_log) LOG_TRACEF("Mutator: added %s", mut.m_name);
232 Net_LinkEntity(mut, false, 0, Mutator_SendEntity);
236 backtrace("WARNING: when adding mutator: adding failed, rolling back\n");
237 if (func(MUTATOR_ROLLING_BACK)) {
239 error("WARNING: when adding mutator: rolling back failed");
244 void Mutator_Remove(Mutator mut)
247 for (i = 0; i < Mutators_MAX; ++i)
248 if (loaded_mutators[i] == mut)
250 if (i >= Mutators_MAX) {
251 backtrace("WARNING: removing not-added mutator\n");
254 loaded_mutators[i] = NULL;
255 mutatorfunc_t func = mut.mutatorfunc;
256 if (func(MUTATOR_REMOVING)) {
258 error("Mutator_Remove: removing mutator failed");
260 if (mutator_log) LOG_TRACEF("Mutator: removed %s", mut.m_name);
262 Net_UnlinkEntity(mut);
266 #define REGISTER_MUTATOR(id, dependence) \
267 bool MUTATORFUNCTION_##id##_hooks(int mode) { return = false; } \
268 bool MUTATORFUNCTION_##id(int mode) { \
270 bool ret = MUTATORFUNCTION_##id##_hooks(mode); if (ret) return ret; \
272 bool MUTATOR_##id##_check() { return dependence; } \
273 REGISTER(Mutators, MUTATOR, id, m_id, NEW(Mutator, #id, MUTATORFUNCTION_##id)) \
274 { this.mutatorcheck = MUTATOR_##id##_check; } \
275 [[accumulate]] bool MUTATORFUNCTION_##id(int mode)
277 STATIC_INIT(Mutators) {
283 STATIC_INIT_LATE(Mutators) {
284 FOREACH(Mutators, it.mutatorcheck(), Mutator_Add(it));
287 #define MUTATOR_ONADD if (mode == MUTATOR_ADDING)
288 #define MUTATOR_ONREMOVE if (mode == MUTATOR_REMOVING)
289 #define MUTATOR_ONROLLBACK_OR_REMOVE if (mode == MUTATOR_REMOVING || mode == MUTATOR_ROLLING_BACK)
290 #define MUTATOR_ADD(name) Mutator_Add(MUTATOR_##name)
291 #define MUTATOR_REMOVE(name) Mutator_Remove(MUTATOR_##name)
292 #define MUTATOR_RETURNVALUE CallbackChain_ReturnValue
294 #define _MUTATOR_CALLBACK(name, func) \
295 Callback CALLBACK_##name; \
297 [[accumulate]] void RegisterCallbacks() { CALLBACK_##name = NEW(Callback, func); }
299 #define MUTATOR_HOOKFUNCTION(...) \
300 EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__))
301 #define EVAL_MUTATOR_HOOKFUNCTION(...) __VA_ARGS__
303 #define MUTATOR_HOOKFUNCTION_2(mut, cb) \
304 MUTATOR_HOOKFUNCTION_3(mut, cb, CBC_ORDER_ANY)
306 #define MUTATOR_HOOKFUNCTION_3(mut, cb, order) \
307 _MUTATOR_CALLBACK(mut##_##cb, mut##_##cb) \
308 [[accumulate]] bool MUTATORFUNCTION_##mut##_hooks(int mode) { MUTATOR_HOOK(cb, mut##_##cb, order); } \
309 bool mut##_##cb() { return = false; } \
310 [[accumulate]] bool mut##_##cb()
312 #define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN { \
314 if (!CallbackChain_Add(HOOK_##cb, CALLBACK_##func, order)) { \
315 LOG_INFO("HOOK FAILED: ", #cb, ":", #func, "\n"); \
319 MUTATOR_ONROLLBACK_OR_REMOVE { \
320 CallbackChain_Remove(HOOK_##cb, CALLBACK_##func); \