]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mutators/base.qh
Mutators: extra pass for macro expansion
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / base.qh
1 #ifndef MUTATORS_BASE_H
2 #define MUTATORS_BASE_H
3
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;
8
9 bool CallbackChain_ReturnValue; // read-only field of the current return value
10
11 /**
12  * Callbacks may be added to zero or more callback chains.
13  */
14 CLASS(Callback, Object)
15     /**
16      * a callback function is like this:
17      * bool mycallback()
18      * {
19      *     do something
20      *     return false;
21      * }
22      */
23     ATTRIB(Callback, cbc_func, bool(), func_null)
24     CONSTRUCTOR(Callback, bool() func) {
25         CONSTRUCT(Callback);
26         this.cbc_func = func;
27     }
28 ENDCLASS(Callback)
29
30 /**
31  * Callback chains contain zero or more callbacks.
32  */
33 CLASS(CallbackChain, Object)
34     CLASS(CallbackNode, Object)
35         ATTRIB(CallbackNode, cbc, Callback, NULL)
36         ATTRIB(CallbackNode, cbc_next, CallbackNode, NULL)
37         ATTRIB(CallbackNode, cbc_order, int, 0)
38         CONSTRUCTOR(CallbackNode, Callback it, int order) {
39             CONSTRUCT(CallbackNode);
40             this.cbc = it;
41             this.cbc_order = order;
42         }
43     ENDCLASS(CallbackNode)
44
45     ATTRIB(CallbackChain, cbc_next, CallbackNode, NULL)
46     ATTRIB(CallbackChain, cbc_order, int, 0)
47     CONSTRUCTOR(CallbackChain, string _name) {
48         CONSTRUCT(CallbackChain);
49         this.netname = _name;
50     }
51
52     bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
53     {
54         if (order & CBC_ORDER_FIRST) {
55             if (order & CBC_ORDER_LAST)
56                 if (this.cbc_order & CBC_ORDER_ANY)
57                     return false;
58             if (this.cbc_order & CBC_ORDER_FIRST)
59                 return false;
60         } else if (order & CBC_ORDER_LAST) {
61             if (this.cbc_order & CBC_ORDER_LAST)
62                 return false;
63         }
64         entity node = NEW(CallbackNode, cb, order);
65         if (order & CBC_ORDER_FIRST) {
66             node.cbc_next = this.cbc_next;
67             this.cbc_next = node;
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;
73         } else {
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; }
77             node.cbc_next = it;
78             if (prev) prev.cbc_next = node;
79             else this.cbc_next = node;
80         }
81         this.cbc_order |= (order | CBC_ORDER_ANY);
82         return true;
83     }
84     int CallbackChain_Remove(CallbackChain this, Callback cb)
85     {
86         int n = 0, order = 0;
87         for (Callback prev = NULL, it = this.cbc_next; it; prev = it, it = it.cbc_next) {
88             if (it.cbc == cb) {
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;
93                 ++n;
94             }
95             // it is now something we want to keep
96             order |= (it.cbc_order & CBC_ORDER_ANY);
97         }
98         this.cbc_order = order;
99         return n;
100     }
101     bool CallbackChain_Call(CallbackChain this)
102     {
103         bool r = false;
104         for (Callback it = this.cbc_next; it; it = it.cbc_next) {
105             CallbackChain_ReturnValue = r;
106             r |= it.cbc.cbc_func();
107         }
108         return r; // callbacks return an error status, so 0 is default return value
109     }
110 ENDCLASS(CallbackChain)
111
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;
119
120 void RegisterHooks() {};
121 void RegisterCallbacks() {};
122
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) \
133         return ret; \
134     } \
135     [[accumulate]] void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); }
136
137 #define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__)
138 #ifdef __STDC__
139     #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
140 #else
141     #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
142 #endif
143
144 enum {
145     MUTATOR_REMOVING,
146     MUTATOR_ADDING,
147     MUTATOR_ROLLING_BACK
148 };
149
150 USING(mutatorfunc_t, bool(int));
151
152 CLASS(Mutator, Object)
153     ATTRIB(Mutator, m_id, int, 0)
154     ATTRIB(Mutator, m_name, string, string_null)
155     ATTRIB(Mutator, mutatorfunc, mutatorfunc_t, func_null)
156     ATTRIB(Mutator, mutatorcheck, bool(), func_null)
157     CONSTRUCTOR(Mutator, string _name, mutatorfunc_t func) {
158         CONSTRUCT(Mutator);
159         this.m_name = _name;
160         this.mutatorfunc = func;
161     }
162 ENDCLASS(Mutator)
163
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;
170
171 #ifndef MENUQC
172 /** server mutators activate corresponding client mutators for all clients */
173 REGISTER_NET_LINKED(Mutator)
174
175 #ifdef SVQC
176 bool Mutator_SendEntity(entity this, entity to, int sf)
177 {
178     int chan = MSG_ENTITY;
179     WriteHeader(chan, Mutator);
180     WriteString(chan, this.registered_id);
181     return true;
182 }
183 #endif
184
185 #ifdef CSQC
186 void NET_Mutator_Remove(entity this)
187 {
188     string s = this.netname;
189     WITH(bool, mutator_log, true, LAMBDA(
190         FOREACH(Mutators, it.registered_id == s, Mutator_Remove(it));
191     ));
192 }
193 NET_HANDLE(Mutator, bool isNew)
194 {
195     make_pure(this);
196     string s = this.netname = ReadString();
197     return = true;
198     if (isNew)
199     {
200         make_pure(this);
201         this.entremove = NET_Mutator_Remove;
202         int added = 0;
203         WITH(bool, mutator_log, true, LAMBDA(
204             FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; });
205         ));
206         if (added > 1) LOG_WARNINGF("Added more than one mutator for %s\n", s);
207     }
208 }
209 #endif
210
211 #endif
212
213 bool Mutator_Add(Mutator mut)
214 {
215     int j = -1;
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]))
220             j = i;
221     }
222     if (j < 0) {
223         backtrace("WARNING: too many mutators, cannot add any more\n");
224         return false;
225     }
226     loaded_mutators[j] = mut;
227     mutatorfunc_t func = mut.mutatorfunc;
228     if (!func(MUTATOR_ADDING)) {
229         // good
230         if (mutator_log) LOG_TRACEF("Mutator: added %s\n", mut.m_name);
231 #ifdef SVQC
232         Net_LinkEntity(mut, false, 0, Mutator_SendEntity);
233 #endif
234         return true;
235     }
236     backtrace("WARNING: when adding mutator: adding failed, rolling back\n");
237     if (func(MUTATOR_ROLLING_BACK)) {
238         // baaaaad
239         error("WARNING: when adding mutator: rolling back failed");
240     }
241     return false;
242 }
243
244 void Mutator_Remove(Mutator mut)
245 {
246     int i;
247     for (i = 0; i < Mutators_MAX; ++i)
248         if (loaded_mutators[i] == mut)
249             break;
250     if (i >= Mutators_MAX) {
251         backtrace("WARNING: removing not-added mutator\n");
252         return;
253     }
254     loaded_mutators[i] = NULL;
255     mutatorfunc_t func = mut.mutatorfunc;
256     if (func(MUTATOR_REMOVING)) {
257         // baaaaad
258         error("Mutator_Remove: removing mutator failed");
259     }
260     if (mutator_log) LOG_TRACEF("Mutator: removed %s\n", mut.m_name);
261 #ifdef SVQC
262     Net_UnlinkEntity(mut);
263 #endif
264 }
265
266 #define REGISTER_MUTATOR(id, dependence) \
267     bool MUTATORFUNCTION_##id##_hooks(int mode) { return = false; } \
268     bool MUTATORFUNCTION_##id(int mode) { \
269         return = false; \
270         bool ret = MUTATORFUNCTION_##id##_hooks(mode); if (ret) return ret; \
271     } \
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)
276
277 STATIC_INIT(Mutators) {
278     RegisterHooks();
279     RegisterCallbacks();
280     RegisterMutators();
281 }
282
283 STATIC_INIT_LATE(Mutators) {
284     FOREACH(Mutators, it.mutatorcheck(), Mutator_Add(it));
285 }
286
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
293
294 #define _MUTATOR_CALLBACK(name, func) \
295     Callback CALLBACK_##name; \
296     bool func(); \
297     [[accumulate]] void RegisterCallbacks() { CALLBACK_##name = NEW(Callback, func); }
298
299 #define MUTATOR_HOOKFUNCTION(...) \
300     EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__))
301 #define EVAL_MUTATOR_HOOKFUNCTION(...) __VA_ARGS__
302
303 #define MUTATOR_HOOKFUNCTION_2(mut, cb) \
304     MUTATOR_HOOKFUNCTION_3(mut, cb, CBC_ORDER_ANY)
305
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()
311
312 #define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN {                     \
313     MUTATOR_ONADD {                                                     \
314         if (!CallbackChain_Add(HOOK_##cb, CALLBACK_##func, order)) {    \
315             LOG_INFO("HOOK FAILED: ", #cb, ":", #func, "\n");           \
316             return true;                                                \
317         }                                                               \
318     }                                                                   \
319     MUTATOR_ONROLLBACK_OR_REMOVE {                                      \
320         CallbackChain_Remove(HOOK_##cb, CALLBACK_##func);               \
321     }                                                                   \
322 } MACRO_END
323
324 #include "events.qh"
325
326 #endif