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;
8 bool CallbackChain_ReturnValue; // read-only field of the current return value
11 * Callbacks may be added to zero or more callback chains.
13 CLASS(Callback, Object)
15 * a callback function is like this:
22 ATTRIB(Callback, cbc_func, bool());
23 CONSTRUCTOR(Callback, bool() func) {
30 * Callback chains contain zero or more callbacks.
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);
40 this.cbc_order = order;
42 ENDCLASS(CallbackNode)
44 ATTRIB(CallbackChain, cbc_next, CallbackNode);
45 ATTRIB(CallbackChain, cbc_order, int, 0);
46 CONSTRUCTOR(CallbackChain, string _name) {
47 CONSTRUCT(CallbackChain);
51 bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
53 if (order & CBC_ORDER_FIRST) {
54 if (order & CBC_ORDER_LAST)
55 if (this.cbc_order & CBC_ORDER_ANY)
57 if (this.cbc_order & CBC_ORDER_FIRST)
59 } else if (order & CBC_ORDER_LAST) {
60 if (this.cbc_order & CBC_ORDER_LAST)
63 entity node = NEW(CallbackNode, cb, order);
64 if (order & CBC_ORDER_FIRST) {
65 node.cbc_next = this.cbc_next;
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;
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; }
77 if (prev) prev.cbc_next = node;
78 else this.cbc_next = node;
80 this.cbc_order |= (order | CBC_ORDER_ANY);
83 int CallbackChain_Remove(CallbackChain this, Callback cb)
86 for (Callback prev = NULL, it = this.cbc_next; it; prev = it, it = it.cbc_next) {
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;
94 // it is now something we want to keep
95 order |= (it.cbc_order & CBC_ORDER_ANY);
97 this.cbc_order = order;
100 bool CallbackChain_Call(CallbackChain this)
103 for (Callback it = this.cbc_next; it; it = it.cbc_next) {
104 CallbackChain_ReturnValue = r;
105 r |= it.cbc.cbc_func();
107 return r; // callbacks return an error status, so 0 is default return value
109 ENDCLASS(CallbackChain)
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;
119 void RegisterHooks() {};
120 void RegisterCallbacks() {};
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) \
134 [[accumulate]] void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); }
136 #define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__)
138 #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
140 #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
149 USING(mutatorfunc_t, bool(int));
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) {
159 this.mutatorfunc = func;
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;
171 /** server mutators activate corresponding client mutators for all clients */
172 REGISTER_NET_LINKED(Mutator)
175 bool Mutator_SendEntity(entity this, entity to, int sf)
177 int chan = MSG_ENTITY;
178 WriteHeader(chan, Mutator);
179 WriteString(chan, this.registered_id);
185 void NET_Mutator_Remove(entity this)
187 string s = this.netname;
188 WITH(bool, mutator_log, true, LAMBDA(
189 FOREACH(Mutators, it.registered_id == s, Mutator_Remove(it));
192 NET_HANDLE(Mutator, bool isNew)
195 string s = this.netname = ReadString();
200 this.entremove = NET_Mutator_Remove;
202 WITH(bool, mutator_log, true, LAMBDA(
203 FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; });
205 if (added > 1) LOG_WARNF("Added more than one mutator for %s", s);
212 bool Mutator_Add(Mutator mut)
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]))
222 backtrace("WARNING: too many mutators, cannot add any more\n");
225 loaded_mutators[j] = mut;
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)
246 for (i = 0; i < Mutators_MAX; ++i)
247 if (loaded_mutators[i] == mut)
249 if (i >= Mutators_MAX) {
250 backtrace("WARNING: removing not-added mutator\n");
253 loaded_mutators[i] = NULL;
254 mutatorfunc_t func = mut.mutatorfunc;
255 if (func(MUTATOR_REMOVING)) {
257 error("Mutator_Remove: removing mutator failed");
259 if (mutator_log) LOG_TRACEF("Mutator: removed %s", mut.m_name);
261 Net_UnlinkEntity(mut);
265 #define REGISTER_MUTATOR(id, dependence) \
266 bool MUTATORFUNCTION_##id##_hooks(int mode) { return = false; } \
267 bool MUTATORFUNCTION_##id(int mode) { \
269 bool ret = MUTATORFUNCTION_##id##_hooks(mode); if (ret) return ret; \
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)
276 STATIC_INIT(Mutators) {
282 STATIC_INIT_LATE(Mutators) {
283 FOREACH(Mutators, it.mutatorcheck(), Mutator_Add(it));
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
293 #define _MUTATOR_CALLBACK(name, func) \
294 Callback CALLBACK_##name; \
296 [[accumulate]] void RegisterCallbacks() { CALLBACK_##name = NEW(Callback, func); }
298 #define MUTATOR_HOOKFUNCTION(...) \
299 EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__))
300 #define EVAL_MUTATOR_HOOKFUNCTION(...) __VA_ARGS__
302 #define MUTATOR_HOOKFUNCTION_2(mut, cb) \
303 MUTATOR_HOOKFUNCTION_3(mut, cb, CBC_ORDER_ANY)
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()
311 #define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN { \
313 if (!CallbackChain_Add(HOOK_##cb, CALLBACK_##func, order)) { \
314 LOG_INFO("HOOK FAILED: ", #cb, ":", #func, "\n"); \
318 MUTATOR_ONROLLBACK_OR_REMOVE { \
319 CallbackChain_Remove(HOOK_##cb, CALLBACK_##func); \