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 bool Mutator_Add(Mutator mut);
166 void Mutator_Remove(Mutator mut);
167 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, {
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, {
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 return true; // already added
218 mutatorfunc_t func = mut.mutatorfunc;
219 if (!func(MUTATOR_ADDING)) {
221 if (mutator_log) LOG_TRACEF("Mutator: added %s", mut.m_name);
223 Net_LinkEntity(mut, false, 0, Mutator_SendEntity);
227 backtrace("WARNING: when adding mutator: adding failed, rolling back\n");
228 if (func(MUTATOR_ROLLING_BACK)) {
230 error("WARNING: when adding mutator: rolling back failed");
235 void Mutator_Remove(Mutator mut)
239 backtrace("WARNING: removing not-added mutator\n");
244 mutatorfunc_t func = mut.mutatorfunc;
245 if (func(MUTATOR_REMOVING)) {
247 error("Mutator_Remove: removing mutator failed");
249 if (mutator_log) LOG_TRACEF("Mutator: removed %s", mut.m_name);
251 Net_UnlinkEntity(mut);
255 #define REGISTER_MUTATOR(id, dependence) \
256 bool MUTATORFUNCTION_##id##_hooks(int mode) { return = false; } \
257 bool MUTATORFUNCTION_##id(int mode) { \
259 bool ret = MUTATORFUNCTION_##id##_hooks(mode); if (ret) return ret; \
261 bool MUTATOR_##id##_check() { return dependence; } \
262 REGISTER(Mutators, MUTATOR, id, m_id, NEW(Mutator, #id, MUTATORFUNCTION_##id)) \
263 { this.mutatorcheck = MUTATOR_##id##_check; } \
264 [[accumulate]] bool MUTATORFUNCTION_##id(int mode)
266 STATIC_INIT(Mutators) {
272 STATIC_INIT_LATE(Mutators) {
273 FOREACH(Mutators, it.mutatorcheck(), Mutator_Add(it));
276 #define MUTATOR_ONADD if (mode == MUTATOR_ADDING)
277 #define MUTATOR_ONREMOVE if (mode == MUTATOR_REMOVING)
278 #define MUTATOR_ONROLLBACK_OR_REMOVE if (mode == MUTATOR_REMOVING || mode == MUTATOR_ROLLING_BACK)
279 #define MUTATOR_ADD(name) Mutator_Add(MUTATOR_##name)
280 #define MUTATOR_REMOVE(name) Mutator_Remove(MUTATOR_##name)
281 #define MUTATOR_RETURNVALUE CallbackChain_ReturnValue
283 #define _MUTATOR_CALLBACK(name, func) \
284 Callback CALLBACK_##name; \
286 [[accumulate]] void RegisterCallbacks() { CALLBACK_##name = NEW(Callback, func); }
288 #define MUTATOR_HOOKFUNCTION(...) \
289 EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__))
290 #define EVAL_MUTATOR_HOOKFUNCTION(...) __VA_ARGS__
292 #define MUTATOR_HOOKFUNCTION_2(mut, cb) \
293 MUTATOR_HOOKFUNCTION_3(mut, cb, CBC_ORDER_ANY)
295 #define MUTATOR_HOOKFUNCTION_3(mut, cb, order) \
296 _MUTATOR_CALLBACK(mut##_##cb, mut##_##cb) \
297 [[accumulate]] bool MUTATORFUNCTION_##mut##_hooks(int mode) { MUTATOR_HOOK(cb, mut##_##cb, order); } \
298 bool mut##_##cb() { return = false; } \
299 [[accumulate]] bool mut##_##cb()
301 #define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN { \
303 if (!CallbackChain_Add(HOOK_##cb, CALLBACK_##func, order)) { \
304 LOG_INFO("HOOK FAILED: ", #cb, ":", #func, "\n"); \
308 MUTATOR_ONROLLBACK_OR_REMOVE { \
309 CallbackChain_Remove(HOOK_##cb, CALLBACK_##func); \