]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mutators/base.qh
Merge branch 'master' into terencehill/hud_updates
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mutators / base.qh
1 #pragma once
2
3 const int CBC_ORDER_FIRST = 1;
4 const int CBC_ORDER_LAST = 2;
5 const int CBC_ORDER_EXCLUSIVE = 3;
6 const int CBC_ORDER_ANY = 4;
7
8 bool CallbackChain_ReturnValue; // read-only field of the current return value
9
10 /**
11  * Callbacks may be added to zero or more callback chains.
12  */
13 CLASS(Callback, Object)
14     /**
15      * a callback function is like this:
16      * bool mycallback()
17      * {
18      *     do something
19      *     return false;
20      * }
21      */
22     ATTRIB(Callback, cbc_func, bool());
23     CONSTRUCTOR(Callback, bool() func) {
24         CONSTRUCT(Callback);
25         this.cbc_func = func;
26     }
27 ENDCLASS(Callback)
28
29 /**
30  * Callback chains contain zero or more callbacks.
31  */
32 CLASS(CallbackChain, Object)
33     CLASS(CallbackNode, Object)
34         ATTRIB(CallbackNode, cbc, Callback);
35         ATTRIB(CallbackNode, cbc_next, CallbackNode);
36         ATTRIB(CallbackNode, cbc_order, int, 0);
37         CONSTRUCTOR(CallbackNode, Callback it, int order) {
38             CONSTRUCT(CallbackNode);
39             this.cbc = it;
40             this.cbc_order = order;
41         }
42     ENDCLASS(CallbackNode)
43
44     ATTRIB(CallbackChain, cbc_next, CallbackNode);
45     ATTRIB(CallbackChain, cbc_order, int, 0);
46     CONSTRUCTOR(CallbackChain, string _name) {
47         CONSTRUCT(CallbackChain);
48         this.netname = _name;
49     }
50
51     bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
52     {
53         if (order & CBC_ORDER_FIRST) {
54             if (order & CBC_ORDER_LAST)
55                 if (this.cbc_order & CBC_ORDER_ANY)
56                     return false;
57             if (this.cbc_order & CBC_ORDER_FIRST)
58                 return false;
59         } else if (order & CBC_ORDER_LAST) {
60             if (this.cbc_order & CBC_ORDER_LAST)
61                 return false;
62         }
63         entity node = NEW(CallbackNode, cb, order);
64         if (order & CBC_ORDER_FIRST) {
65             node.cbc_next = this.cbc_next;
66             this.cbc_next = node;
67         } else if (order & CBC_ORDER_LAST) {
68             CallbackNode prev = NULL, it = this.cbc_next;
69             while (it) { prev = it, it = it.cbc_next; }
70             if (prev) prev.cbc_next = node;
71             else this.cbc_next = node;
72         } else {
73             // by default we execute last, but before a possible CBC_ORDER_LAST callback
74             CallbackNode prev = NULL, it = this.cbc_next;
75             while (it && !(it.cbc_order & CBC_ORDER_LAST)) { prev = it, it = it.cbc_next; }
76             node.cbc_next = it;
77             if (prev) prev.cbc_next = node;
78             else this.cbc_next = node;
79         }
80         this.cbc_order |= (order | CBC_ORDER_ANY);
81         return true;
82     }
83     int CallbackChain_Remove(CallbackChain this, Callback cb)
84     {
85         int n = 0, order = 0;
86         for (Callback prev = NULL, it = this.cbc_next; it; prev = it, it = it.cbc_next) {
87             if (it.cbc == cb) {
88                 // remove it from the chain
89                 Callback next = it.cbc_next;
90                 if (prev) prev.cbc_next = next;
91                 else this.cbc_next = next;
92                 ++n;
93             }
94             // it is now something we want to keep
95             order |= (it.cbc_order & CBC_ORDER_ANY);
96         }
97         this.cbc_order = order;
98         return n;
99     }
100     bool CallbackChain_Call(CallbackChain this)
101     {
102         bool r = false;
103         for (Callback it = this.cbc_next; it; it = it.cbc_next) {
104             CallbackChain_ReturnValue = r;
105             r |= it.cbc.cbc_func();
106         }
107         return r; // callbacks return an error status, so 0 is default return value
108     }
109 ENDCLASS(CallbackChain)
110
111 #define _MUTATOR_HANDLE_NOP(type, id)
112 #define _MUTATOR_HANDLE_PARAMS(type, id) , type in_##id
113 #define _MUTATOR_HANDLE_PREPARE(type, id) id = in_##id;
114 #define _MUTATOR_HANDLE_PUSHTMP(type, id) TC(type, id); type tmp_##id = id;
115 #define _MUTATOR_HANDLE_PUSHOUT(type, id) type out_##id = id;
116 #define _MUTATOR_HANDLE_POPTMP(type, id) id = tmp_##id;
117 #define _MUTATOR_HANDLE_POPOUT(type, id) id = out_##id;
118
119 void RegisterHooks() {};
120 void RegisterCallbacks() {};
121
122 #define MUTATOR_HOOKABLE(id, params) _MUTATOR_HOOKABLE(id, params)
123 #define _MUTATOR_HOOKABLE(id, params) \
124     CallbackChain HOOK_##id;  \
125     bool __Mutator_Send_##id(int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) { \
126         params(_MUTATOR_HANDLE_PUSHTMP, _MUTATOR_HANDLE_NOP) \
127         params(_MUTATOR_HANDLE_PREPARE, _MUTATOR_HANDLE_NOP) \
128         bool ret = CallbackChain_Call(HOOK_##id); \
129         params(_MUTATOR_HANDLE_NOP,     _MUTATOR_HANDLE_PUSHOUT) \
130         params(_MUTATOR_HANDLE_POPTMP,  _MUTATOR_HANDLE_NOP) \
131         params(_MUTATOR_HANDLE_NOP,     _MUTATOR_HANDLE_POPOUT) \
132         return ret; \
133     } \
134     [[accumulate]] void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); }
135
136 #define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__)
137 #ifdef __STDC__
138     #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
139 #else
140     #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
141 #endif
142
143 enum {
144     MUTATOR_REMOVING,
145     MUTATOR_ADDING,
146     MUTATOR_ROLLING_BACK
147 };
148
149 USING(mutatorfunc_t, bool(int));
150
151 CLASS(Mutator, Object)
152     ATTRIB(Mutator, m_id, int, 0);
153     ATTRIB(Mutator, m_name, string);
154     ATTRIB(Mutator, mutatorfunc, mutatorfunc_t);
155     ATTRIB(Mutator, mutatorcheck, bool());
156     CONSTRUCTOR(Mutator, string _name, mutatorfunc_t func) {
157         CONSTRUCT(Mutator);
158         this.m_name = _name;
159         this.mutatorfunc = func;
160     }
161 ENDCLASS(Mutator)
162
163 REGISTRY(Mutators, BITS(7))
164 #define Mutators_from(i) _Mutators_from(i, NULL)
165 Mutator loaded_mutators[Mutators_MAX];
166 bool Mutator_Add(Mutator mut);
167 void Mutator_Remove(Mutator mut);
168 bool mutator_log = false;
169
170 #ifdef GAMEQC
171 /** server mutators activate corresponding client mutators for all clients */
172 REGISTER_NET_LINKED(Mutator)
173
174 #ifdef SVQC
175 bool Mutator_SendEntity(entity this, entity to, int sf)
176 {
177     int chan = MSG_ENTITY;
178     WriteHeader(chan, Mutator);
179     WriteString(chan, this.registered_id);
180     return true;
181 }
182 #endif
183
184 #ifdef CSQC
185 void NET_Mutator_Remove(entity this)
186 {
187     string s = this.netname;
188     WITH(bool, mutator_log, true, LAMBDA(
189         FOREACH(Mutators, it.registered_id == s, Mutator_Remove(it));
190     ));
191 }
192 NET_HANDLE(Mutator, bool isNew)
193 {
194     make_pure(this);
195     string s = this.netname = ReadString();
196     return = true;
197     if (isNew)
198     {
199         make_pure(this);
200         this.entremove = NET_Mutator_Remove;
201         int added = 0;
202         WITH(bool, mutator_log, true, LAMBDA(
203             FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; });
204         ));
205         if (added > 1) LOG_WARNF("Added more than one mutator for %s", s);
206     }
207 }
208 #endif
209
210 #endif
211
212 bool Mutator_Add(Mutator mut)
213 {
214     int j = -1;
215     for (int i = 0; i < Mutators_MAX; ++i) {
216         if (loaded_mutators[i] == mut)
217             return true; // already added
218         if (!(loaded_mutators[i]))
219             j = i;
220     }
221     if (j < 0) {
222         backtrace("WARNING: too many mutators, cannot add any more\n");
223         return false;
224     }
225     loaded_mutators[j] = mut;
226     mutatorfunc_t func = mut.mutatorfunc;
227     if (!func(MUTATOR_ADDING)) {
228         // good
229         if (mutator_log) LOG_TRACEF("Mutator: added %s", mut.m_name);
230 #ifdef SVQC
231         Net_LinkEntity(mut, false, 0, Mutator_SendEntity);
232 #endif
233         return true;
234     }
235     backtrace("WARNING: when adding mutator: adding failed, rolling back\n");
236     if (func(MUTATOR_ROLLING_BACK)) {
237         // baaaaad
238         error("WARNING: when adding mutator: rolling back failed");
239     }
240     return false;
241 }
242
243 void Mutator_Remove(Mutator mut)
244 {
245     int i;
246     for (i = 0; i < Mutators_MAX; ++i)
247         if (loaded_mutators[i] == mut)
248             break;
249     if (i >= Mutators_MAX) {
250         backtrace("WARNING: removing not-added mutator\n");
251         return;
252     }
253     loaded_mutators[i] = NULL;
254     mutatorfunc_t func = mut.mutatorfunc;
255     if (func(MUTATOR_REMOVING)) {
256         // baaaaad
257         error("Mutator_Remove: removing mutator failed");
258     }
259     if (mutator_log) LOG_TRACEF("Mutator: removed %s", mut.m_name);
260 #ifdef SVQC
261     Net_UnlinkEntity(mut);
262 #endif
263 }
264
265 #define REGISTER_MUTATOR(id, dependence) \
266     bool MUTATORFUNCTION_##id##_hooks(int mode) { return = false; } \
267     bool MUTATORFUNCTION_##id(int mode) { \
268         return = false; \
269         bool ret = MUTATORFUNCTION_##id##_hooks(mode); if (ret) return ret; \
270     } \
271     bool MUTATOR_##id##_check() { return dependence; } \
272     REGISTER(Mutators, MUTATOR, id, m_id, NEW(Mutator, #id, MUTATORFUNCTION_##id)) \
273     { this.mutatorcheck = MUTATOR_##id##_check; } \
274     [[accumulate]] bool MUTATORFUNCTION_##id(int mode)
275
276 STATIC_INIT(Mutators) {
277     RegisterHooks();
278     RegisterCallbacks();
279     RegisterMutators();
280 }
281
282 STATIC_INIT_LATE(Mutators) {
283     FOREACH(Mutators, it.mutatorcheck(), Mutator_Add(it));
284 }
285
286 #define MUTATOR_ONADD                   if (mode == MUTATOR_ADDING)
287 #define MUTATOR_ONREMOVE                if (mode == MUTATOR_REMOVING)
288 #define MUTATOR_ONROLLBACK_OR_REMOVE    if (mode == MUTATOR_REMOVING || mode == MUTATOR_ROLLING_BACK)
289 #define MUTATOR_ADD(name)               Mutator_Add(MUTATOR_##name)
290 #define MUTATOR_REMOVE(name)            Mutator_Remove(MUTATOR_##name)
291 #define MUTATOR_RETURNVALUE             CallbackChain_ReturnValue
292
293 #define _MUTATOR_CALLBACK(name, func) \
294     Callback CALLBACK_##name; \
295     bool func(); \
296     [[accumulate]] void RegisterCallbacks() { CALLBACK_##name = NEW(Callback, func); }
297
298 #define MUTATOR_HOOKFUNCTION(...) \
299     EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__))
300 #define EVAL_MUTATOR_HOOKFUNCTION(...) __VA_ARGS__
301
302 #define MUTATOR_HOOKFUNCTION_2(mut, cb) \
303     MUTATOR_HOOKFUNCTION_3(mut, cb, CBC_ORDER_ANY)
304
305 #define MUTATOR_HOOKFUNCTION_3(mut, cb, order) \
306     _MUTATOR_CALLBACK(mut##_##cb, mut##_##cb) \
307     [[accumulate]] bool MUTATORFUNCTION_##mut##_hooks(int mode) { MUTATOR_HOOK(cb, mut##_##cb, order); } \
308     bool mut##_##cb() { return = false; } \
309     [[accumulate]] bool mut##_##cb()
310
311 #define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN {                     \
312     MUTATOR_ONADD {                                                     \
313         if (!CallbackChain_Add(HOOK_##cb, CALLBACK_##func, order)) {    \
314             LOG_INFO("HOOK FAILED: ", #cb, ":", #func, "\n");           \
315             return true;                                                \
316         }                                                               \
317     }                                                                   \
318     MUTATOR_ONROLLBACK_OR_REMOVE {                                      \
319         CallbackChain_Remove(HOOK_##cb, CALLBACK_##func);               \
320     }                                                                   \
321 } MACRO_END
322
323 #include "events.qh"