4 #include <client/main.qh>
7 const int CBC_ORDER_FIRST = 1;
8 const int CBC_ORDER_LAST = 2;
9 const int CBC_ORDER_EXCLUSIVE = 3;
10 const int CBC_ORDER_ANY = 4;
12 bool CallbackChain_ReturnValue; // read-only field of the current return value
15 * Callbacks may be added to zero or more callback chains.
17 CLASS(Callback, Object)
19 * a callback function is like this:
26 ATTRIB(Callback, cbc_func, bool());
27 CONSTRUCTOR(Callback, bool() func) {
34 * Callback chains contain zero or more callbacks.
36 CLASS(CallbackChain, Object)
37 CLASS(CallbackNode, Object)
38 ATTRIB(CallbackNode, cbc, Callback);
39 ATTRIB(CallbackNode, cbc_next, CallbackNode);
40 ATTRIB(CallbackNode, cbc_order, int, 0);
41 CONSTRUCTOR(CallbackNode, Callback it, int order) {
42 CONSTRUCT(CallbackNode);
44 this.cbc_order = order;
46 ENDCLASS(CallbackNode)
48 ATTRIB(CallbackChain, cbc_next, CallbackNode);
49 ATTRIB(CallbackChain, cbc_order, int, 0);
50 CONSTRUCTOR(CallbackChain, string _name) {
51 CONSTRUCT(CallbackChain);
55 bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
57 if (order & CBC_ORDER_FIRST) {
58 if (order & CBC_ORDER_LAST)
59 if (this.cbc_order & CBC_ORDER_ANY)
61 if (this.cbc_order & CBC_ORDER_FIRST)
63 } else if (order & CBC_ORDER_LAST) {
64 if (this.cbc_order & CBC_ORDER_LAST)
67 entity node = NEW(CallbackNode, cb, order);
68 if (order & CBC_ORDER_FIRST) {
69 node.cbc_next = this.cbc_next;
71 } else if (order & CBC_ORDER_LAST) {
72 CallbackNode prev = NULL, it = this.cbc_next;
73 while (it) { prev = it, it = it.cbc_next; }
74 if (prev) prev.cbc_next = node;
75 else this.cbc_next = node;
77 // by default we execute last, but before a possible CBC_ORDER_LAST callback
78 CallbackNode prev = NULL, it = this.cbc_next;
79 while (it && !(it.cbc_order & CBC_ORDER_LAST)) { prev = it, it = it.cbc_next; }
81 if (prev) prev.cbc_next = node;
82 else this.cbc_next = node;
84 this.cbc_order |= (order | CBC_ORDER_ANY);
87 int CallbackChain_Remove(CallbackChain this, Callback cb)
90 for (Callback prev = NULL, it = this.cbc_next; it; prev = it, it = it.cbc_next) {
92 // remove it from the chain
93 Callback next = it.cbc_next;
94 if (prev) prev.cbc_next = next;
95 else this.cbc_next = next;
98 // it is now something we want to keep
99 order |= (it.cbc_order & CBC_ORDER_ANY);
101 this.cbc_order = order;
104 bool CallbackChain_Call(CallbackChain this)
107 for (Callback it = this.cbc_next; it; it = it.cbc_next) {
108 CallbackChain_ReturnValue = r;
109 r |= it.cbc.cbc_func();
111 return r; // callbacks return an error status, so 0 is default return value
113 ENDCLASS(CallbackChain)
115 #define _MUTATOR_HANDLE_NOP(type, id)
116 #define _MUTATOR_HANDLE_PARAMS(type, id) , type in_##id
117 #define _MUTATOR_HANDLE_PREPARE(type, id) id = in_##id;
118 #define _MUTATOR_HANDLE_PUSHTMP(type, id) TC(type, id); type tmp_##id = id;
119 #define _MUTATOR_HANDLE_PUSHOUT(type, id) type out_##id = id;
120 #define _MUTATOR_HANDLE_POPTMP(type, id) id = tmp_##id;
121 #define _MUTATOR_HANDLE_POPOUT(type, id) id = out_##id;
123 void RegisterHooks() {};
124 void RegisterCallbacks() {};
126 #define MUTATOR_HOOKABLE(id, params) _MUTATOR_HOOKABLE(id, params)
127 #define _MUTATOR_HOOKABLE(id, params) \
128 CallbackChain HOOK_##id; \
129 bool __Mutator_Send_##id(int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) { \
130 params(_MUTATOR_HANDLE_PUSHTMP, _MUTATOR_HANDLE_NOP) \
131 params(_MUTATOR_HANDLE_PREPARE, _MUTATOR_HANDLE_NOP) \
132 bool ret = CallbackChain_Call(HOOK_##id); \
133 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_PUSHOUT) \
134 params(_MUTATOR_HANDLE_POPTMP, _MUTATOR_HANDLE_NOP) \
135 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_POPOUT) \
138 ACCUMULATE void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); }
140 #define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__)
142 #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
144 #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
153 USING(mutatorfunc_t, bool(int));
155 CLASS(Mutator, Object)
156 ATTRIB(Mutator, m_id, int, 0);
157 ATTRIB(Mutator, m_name, string);
158 ATTRIB(Mutator, mutatorfunc, mutatorfunc_t);
159 ATTRIB(Mutator, mutatorcheck, bool());
160 CONSTRUCTOR(Mutator, string _name, mutatorfunc_t func) {
163 this.mutatorfunc = func;
167 REGISTRY(Mutators, BITS(7))
169 REGISTRY_DEFINE_GET(Mutators, NULL)
170 bool Mutator_Add(Mutator mut);
171 void Mutator_Remove(Mutator mut);
172 bool mutator_log = false;
175 #define _MUTATOR_IS_ENABLED(this) this.mutatorcheck()
176 #define MUTATOR_IS_ENABLED(this) _MUTATOR_IS_ENABLED(MUTATOR_##this)
179 /** server mutators activate corresponding client mutators for all clients */
180 REGISTER_NET_LINKED(Mutator)
183 bool Mutator_SendEntity(entity this, entity to, int sf)
185 int chan = MSG_ENTITY;
186 WriteHeader(chan, Mutator);
187 WriteString(chan, this.registered_id);
193 void NET_Mutator_Remove(entity this)
195 string s = this.netname;
196 WITH(bool, mutator_log, true, {
197 FOREACH(Mutators, it.registered_id == s, Mutator_Remove(it));
200 NET_HANDLE(Mutator, bool isNew)
203 string s = this.netname = ReadString();
208 this.entremove = NET_Mutator_Remove;
210 WITH(bool, mutator_log, true, {
211 FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; });
213 if (added > 1) LOG_WARNF("Added more than one mutator for %s", s);
220 bool Mutator_Add(Mutator mut)
223 return true; // already added
226 mutatorfunc_t func = mut.mutatorfunc;
227 if (!func(MUTATOR_ADDING)) {
229 if (mutator_log) LOG_TRACEF("Mutator: added %s", mut.m_name);
231 Net_LinkEntity(mut, false, 0, Mutator_SendEntity);
235 backtrace("WARNING: when adding mutator: adding failed, rolling back\n");
236 if (func(MUTATOR_ROLLING_BACK)) {
238 error("WARNING: when adding mutator: rolling back failed");
243 void Mutator_Remove(Mutator mut)
247 backtrace("WARNING: removing not-added mutator\n");
252 mutatorfunc_t func = mut.mutatorfunc;
253 if (func(MUTATOR_REMOVING)) {
255 error("Mutator_Remove: removing mutator failed");
257 if (mutator_log) LOG_TRACEF("Mutator: removed %s", mut.m_name);
259 Net_UnlinkEntity(mut);
263 #define REGISTER_MUTATOR(id, dependence) \
264 bool MUTATORFUNC_##id##_hooks(int mode) { return = false; } \
265 bool MUTATORFUNC_##id(int mode) { \
267 bool ret = MUTATORFUNC_##id##_hooks(mode); if (ret) return ret; \
269 bool MUTATOR_##id##_check() { return dependence; } \
270 REGISTER(Mutators, MUTATOR, id, m_id, NEW(Mutator, #id, MUTATORFUNC_##id)) \
271 { this.mutatorcheck = MUTATOR_##id##_check; } \
272 ACCUMULATE bool MUTATORFUNC_##id(int mode)
274 STATIC_INIT(Mutators) {
280 STATIC_INIT_LATE(Mutators) {
281 FOREACH(Mutators, _MUTATOR_IS_ENABLED(it), Mutator_Add(it));
284 #define MUTATOR_ONADD if (mode == MUTATOR_ADDING)
285 #define MUTATOR_ONREMOVE if (mode == MUTATOR_REMOVING)
286 #define MUTATOR_ONROLLBACK_OR_REMOVE if (mode == MUTATOR_REMOVING || mode == MUTATOR_ROLLING_BACK)
288 #define MUTATOR_STATIC() MACRO_BEGIN \
290 /* game loads at time 1 */ \
292 error("This is a game type and it cannot be added at runtime."); \
296 LOG_INFO("This is a game type and it cannot be removed at runtime."); \
301 #define MUTATOR_ADD(name) Mutator_Add(MUTATOR_##name)
302 #define MUTATOR_REMOVE(name) Mutator_Remove(MUTATOR_##name)
303 #define MUTATOR_RETURNVALUE CallbackChain_ReturnValue
305 #define _MUTATOR_CALLBACK(name, func) \
306 Callback CB_##name; \
308 ACCUMULATE void RegisterCallbacks() { CB_##name = NEW(Callback, func); }
310 #define MUTATOR_HOOKFUNCTION(...) \
311 EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__))
312 #define EVAL_MUTATOR_HOOKFUNCTION(...) __VA_ARGS__
314 #define MUTATOR_HOOKFUNCTION_2(mut, cb) \
315 MUTATOR_HOOKFUNCTION_3(mut, cb, CBC_ORDER_ANY)
317 #define MUTATOR_HOOKFUNCTION_3(mut, cb, order) \
318 _MUTATOR_CALLBACK(mut##_##cb, mut##_##cb) \
319 ACCUMULATE bool MUTATORFUNC_##mut##_hooks(int mode) { MUTATOR_HOOK(cb, mut##_##cb, order); } \
320 bool mut##_##cb() { return = false; } \
321 ACCUMULATE bool mut##_##cb()
323 void _mutPrintFail(string cb, string func)
325 // this is inside a function to avoid expanding it on compilation everytime
326 LOG_INFO("HOOK FAILED: ", cb, ":", func);
329 #define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN \
331 if (!CallbackChain_Add(HOOK_##cb, CB_##func, order)) { \
332 _mutPrintFail(#cb, #func); \
336 MUTATOR_ONROLLBACK_OR_REMOVE { \
337 CallbackChain_Remove(HOOK_##cb, CB_##func); \