Xonotic
base.qh
Go to the documentation of this file.
1 #pragma once
2 
3 #ifdef CSQC
4 #include <client/main.qh>
5 #endif
6 
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;
11 
12 bool CallbackChain_ReturnValue; // read-only field of the current return value
13 
26  ATTRIB(Callback, cbc_func, bool());
27  CONSTRUCTOR(Callback, bool() func) {
29  this.cbc_func = func;
30  }
32 
33 
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);
43  this.cbc = it;
44  this.cbc_order = order;
45  }
47 
48  ATTRIB(CallbackChain, cbc_next, CallbackNode);
49  ATTRIB(CallbackChain, cbc_order, int, 0);
50  CONSTRUCTOR(CallbackChain, string _name) {
52  this.netname = _name;
53  }
54 
55  bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
56  {
57  if (order & CBC_ORDER_FIRST) {
58  if (order & CBC_ORDER_LAST)
59  if (this.cbc_order & CBC_ORDER_ANY)
60  return false;
61  if (this.cbc_order & CBC_ORDER_FIRST)
62  return false;
63  } else if (order & CBC_ORDER_LAST) {
64  if (this.cbc_order & CBC_ORDER_LAST)
65  return false;
66  }
67  entity node = NEW(CallbackNode, cb, order);
68  if (order & CBC_ORDER_FIRST) {
69  node.cbc_next = this.cbc_next;
70  this.cbc_next = node;
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;
76  } else {
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; }
80  node.cbc_next = it;
81  if (prev) prev.cbc_next = node;
82  else this.cbc_next = node;
83  }
84  this.cbc_order |= (order | CBC_ORDER_ANY);
85  return true;
86  }
88  {
89  int n = 0, order = 0;
90  for (Callback prev = NULL, it = this.cbc_next; it; prev = it, it = it.cbc_next) {
91  if (it.cbc == cb) {
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;
96  ++n;
97  }
98  // it is now something we want to keep
99  order |= (it.cbc_order & CBC_ORDER_ANY);
100  }
101  this.cbc_order = order;
102  return n;
103  }
105  {
106  bool r = false;
107  for (Callback it = this.cbc_next; it; it = it.cbc_next) {
109  r |= it.cbc.cbc_func();
110  }
111  return r; // callbacks return an error status, so 0 is default return value
112  }
114 
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;
122 
123 void RegisterHooks() {};
125 
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) \
136  return ret; \
137  } \
138  ACCUMULATE void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); }
139 
140 #define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__)
141 #ifdef __STDC__
142  #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__))
143 #else
144  #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
145 #endif
146 
147 enum {
151 };
152 
153 USING(mutatorfunc_t, bool(int));
154 
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) {
161  CONSTRUCT(Mutator);
162  this.m_name = _name;
163  this.mutatorfunc = func;
164  }
166 
167 REGISTRY(Mutators, BITS(7))
168 
169 REGISTRY_DEFINE_GET(Mutators, NULL)
170 bool Mutator_Add(Mutator mut);
171 void Mutator_Remove(Mutator mut);
172 bool mutator_log = false;
173 .bool m_added;
174 
175 #define _MUTATOR_IS_ENABLED(this) this.mutatorcheck()
176 #define MUTATOR_IS_ENABLED(this) _MUTATOR_IS_ENABLED(MUTATOR_##this)
177 
178 #ifdef GAMEQC
179 
181 
182 #ifdef SVQC
183 bool Mutator_SendEntity(entity this, entity to, int sf)
184 {
185  int chan = MSG_ENTITY;
186  WriteHeader(chan, Mutator);
187  WriteString(chan, this.registered_id);
188  return true;
189 }
190 #endif
191 
192 #ifdef CSQC
193 void NET_Mutator_Remove(entity this)
194 {
195  string s = this.netname;
196  WITH(bool, mutator_log, true, {
197  FOREACH(Mutators, it.registered_id == s, Mutator_Remove(it));
198  });
199 }
200 NET_HANDLE(Mutator, bool isNew)
201 {
202  make_pure(this);
203  string s = this.netname = ReadString();
204  return = true;
205  if (isNew)
206  {
207  make_pure(this);
208  this.entremove = NET_Mutator_Remove;
209  int added = 0;
210  WITH(bool, mutator_log, true, {
211  FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; });
212  });
213  if (added > 1) LOG_WARNF("Added more than one mutator for %s", s);
214  }
215 }
216 #endif
217 
218 #endif
219 
221 {
222  if(mut.m_added)
223  return true; // already added
224 
225  mut.m_added = true;
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 
244 {
245  if(!mut.m_added)
246  {
247  backtrace("WARNING: removing not-added mutator\n");
248  return;
249  }
250 
251  mut.m_added = false;
252  mutatorfunc_t func = mut.mutatorfunc;
253  if (func(MUTATOR_REMOVING)) {
254  // baaaaad
255  error("Mutator_Remove: removing mutator failed");
256  }
257  if (mutator_log) LOG_TRACEF("Mutator: removed %s", mut.m_name);
258 #ifdef SVQC
259  Net_UnlinkEntity(mut);
260 #endif
261 }
262 
263 #define REGISTER_MUTATOR(id, dependence) \
264  bool MUTATORFUNC_##id##_hooks(int mode) { return = false; } \
265  bool MUTATORFUNC_##id(int mode) { \
266  return = false; \
267  bool ret = MUTATORFUNC_##id##_hooks(mode); if (ret) return ret; \
268  } \
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)
273 
274 STATIC_INIT(Mutators) {
275  RegisterHooks();
277  RegisterMutators();
278 }
279 
280 STATIC_INIT_LATE(Mutators) {
281  FOREACH(Mutators, _MUTATOR_IS_ENABLED(it), Mutator_Add(it));
282 }
283 
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)
287 
288 #define MUTATOR_STATIC() MACRO_BEGIN \
289  MUTATOR_ONADD { \
290  /* game loads at time 1 */ \
291  if (time > 1) { \
292  error("This is a game type and it cannot be added at runtime."); \
293  } \
294  } \
295  MUTATOR_ONREMOVE { \
296  LOG_INFO("This is a game type and it cannot be removed at runtime."); \
297  return -1; \
298  } \
299 MACRO_END
300 
301 #define MUTATOR_ADD(name) Mutator_Add(MUTATOR_##name)
302 #define MUTATOR_REMOVE(name) Mutator_Remove(MUTATOR_##name)
303 #define MUTATOR_RETURNVALUE CallbackChain_ReturnValue
304 
305 #define _MUTATOR_CALLBACK(name, func) \
306  Callback CB_##name; \
307  bool func(); \
308  ACCUMULATE void RegisterCallbacks() { CB_##name = NEW(Callback, func); }
309 
310 #define MUTATOR_HOOKFUNCTION(...) \
311  EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__))
312 #define EVAL_MUTATOR_HOOKFUNCTION(...) __VA_ARGS__
313 
314 #define MUTATOR_HOOKFUNCTION_2(mut, cb) \
315  MUTATOR_HOOKFUNCTION_3(mut, cb, CBC_ORDER_ANY)
316 
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()
322 
323 void _mutPrintFail(string cb, string func)
324 {
325  // this is inside a function to avoid expanding it on compilation everytime
326  LOG_INFO("HOOK FAILED: ", cb, ":", func);
327 }
328 
329 #define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN \
330  MUTATOR_ONADD { \
331  if (!CallbackChain_Add(HOOK_##cb, CB_##func, order)) { \
332  _mutPrintFail(#cb, #func); \
333  return true; \
334  } \
335  } \
336  MUTATOR_ONROLLBACK_OR_REMOVE { \
337  CallbackChain_Remove(HOOK_##cb, CB_##func); \
338  } \
339 MACRO_END
340 
341 #include "events.qh"
STATIC_INIT(Mutators)
Definition: base.qh:274
string registered_id
registered item identifier
Definition: registry.qh:60
const int CBC_ORDER_EXCLUSIVE
Definition: base.qh:9
#define REGISTER_NET_LINKED(id)
Definition: net.qh:67
void _mutPrintFail(string cb, string func)
Definition: base.qh:323
Callbacks may be added to zero or more callback chains.
Definition: base.qh:17
#define NEW(cname,...)
Definition: oo.qh:105
void Mutator_Remove(Mutator mut)
Definition: base.qh:243
CLASS(Object) Object
Definition: oo.qh:318
#define ReadString
Callback chains contain zero or more callbacks.
Definition: base.qh:36
bool CallbackChain_Call(CallbackChain this)
Definition: base.qh:104
Definition: base.qh:155
entity() spawn
prev
Definition: all.qh:66
#define _MUTATOR_IS_ENABLED(this)
Definition: base.qh:175
string netname
Definition: powerups.qc:20
#define NET_HANDLE(id, param)
Definition: net.qh:12
entity to
Definition: self.qh:96
void RegisterCallbacks()
Definition: base.qh:124
#define CONSTRUCT(cname,...)
Definition: oo.qh:111
bool Mutator_Add(Mutator mut)
Definition: base.qh:220
#define ATTRIB(...)
Definition: oo.qh:136
#define LOG_WARNF(...)
Definition: log.qh:67
const int CBC_ORDER_LAST
Definition: base.qh:8
#define REGISTRY_DEFINE_GET(id, null)
Definition: registry.qh:40
int m_id
Definition: effect.qh:19
bool m_added
Definition: base.qh:173
int CallbackChain_Remove(CallbackChain this, Callback cb)
Definition: base.qh:87
#define REGISTRY(id, max)
Declare a new registry.
Definition: registry.qh:26
const int CBC_ORDER_FIRST
Definition: base.qh:7
#define NULL
Definition: post.qh:17
#define LOG_INFO(...)
Definition: log.qh:70
#define backtrace(msg)
Definition: log.qh:105
#define LOG_TRACEF(...)
Definition: log.qh:82
#define make_pure(e)
Definition: oo.qh:12
void RegisterHooks()
Definition: base.qh:123
next
Definition: all.qh:88
bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
Definition: base.qh:55
#define ENDCLASS(cname)
Definition: oo.qh:269
#define WITH(type, name, value, block)
Definition: misc.qh:40
bool(int) mutatorfunc_t
Definition: base.qh:153
const int CBC_ORDER_ANY
Definition: base.qh:10
#define USING(name, T)
Definition: _all.inc:72
bool mutator_log
Definition: base.qh:172
bool CallbackChain_ReturnValue
Definition: base.qh:12
#define FOREACH(list, cond, body)
Definition: iter.qh:19
#define CONSTRUCTOR(cname,...)
Definition: oo.qh:201
#define BITS(n)
Definition: bits.qh:9
string m_name
Definition: scores.qh:135
STATIC_INIT_LATE(Mutators)
Definition: base.qh:280