Xonotic
spawnfunc.qh
Go to the documentation of this file.
1 #pragma once
2 
3 // remove this ifdef when client or menu will actually make use of this stuff
4 #ifdef SVQC
5 
7 noref bool require_spawnfunc_prefix;
8 .bool spawnfunc_checked;
10 noref string __fullspawndata;
11 .string fullspawndata;
12 
13 // Optional type checking; increases compile time too much to be enabled by default
14 #if 0
15  bool entityfieldassignablefromeditor(int i)
16  {
17  switch (entityfieldtype(i))
18  {
19  case FIELD_STRING:
20  case FIELD_FLOAT:
21  case FIELD_VECTOR:
22  return true;
23  }
24  return false;
25  }
26 
27  #define _spawnfunc_checktypes(fld) \
28  if (s == #fld) \
29  if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", s);
30 #else
31  #define _spawnfunc_checktypes(fld)
32 #endif
33  #define _spawnfunc_check(fld) \
34  if (s == #fld) continue;
35 
36  noref int __spawnfunc_expecting;
37  noref entity __spawnfunc_expect;
38  noref bool __spawnfunc_unreachable_workaround = true;
39 
40  .void(entity) __spawnfunc_constructor;
41  noref IntrusiveList g_spawn_queue;
42 
43  #define SPAWNFUNC_INTERNAL_FIELDS(X) \
44  X(string, classname, "spawnfunc") \
45  X(string, target, string_null) \
46  X(string, target2, string_null) \
47  X(string, target3, string_null) \
48  X(string, target4, string_null) \
49  X(string, targetname, string_null) \
50 
51 
52  #define X(T, fld, def) .T fld, __spawnfunc_##fld;
53  SPAWNFUNC_INTERNAL_FIELDS(X)
54  #undef X
55 
56  void __spawnfunc_defer(entity prototype, void(entity) constructor)
57  {
58  IL_PUSH(g_spawn_queue, prototype);
59  #define X(T, fld, def) { prototype.__spawnfunc_##fld = prototype.fld; prototype.fld = def; }
60  SPAWNFUNC_INTERNAL_FIELDS(X);
61  #undef X
62  prototype.__spawnfunc_constructor = constructor;
63  }
64 
65  noref IntrusiveList g_map_entities;
66  #define __spawnfunc_spawn_all() MACRO_BEGIN \
67  g_map_entities = IL_NEW(); \
68  IL_EACH(g_spawn_queue, true, __spawnfunc_spawn(it)); \
69  MACRO_END
70 #ifdef SVQC
71  void _SV_OnEntityPreSpawnFunction(entity this);
72 #endif
73  void __spawnfunc_spawn(entity prototype)
74  {
75  entity e = new(clone);
76  copyentity(prototype, e);
77  IL_PUSH(g_map_entities, e);
78  #define X(T, fld, def) { e.fld = e.__spawnfunc_##fld; e.__spawnfunc_##fld = def; }
79  SPAWNFUNC_INTERNAL_FIELDS(X);
80  #undef X
81 #ifdef SVQC
82  _SV_OnEntityPreSpawnFunction(e);
83  if (wasfreed(e)) {
84  return;
85  }
86 #endif
87  e.__spawnfunc_constructor(e);
88  }
89 
90  #define FIELD_SCALAR(fld, n) \
91  fld(n)
92  #define FIELD_VEC(fld, n) \
93  fld(n) \
94  fld(n##_x) \
95  fld(n##_y) \
96  fld(n##_z)
97 
98  #define FIELDS_NONE(fld)
99  #define FIELDS_ALL(fld) if (false)
100 
101  #define FIELDS_COMMON(fld) \
102  FIELD_SCALAR(fld, classname) \
103  FIELD_SCALAR(fld, sourceLoc) \
104  FIELD_SCALAR(fld, spawnfunc_checked) \
105  FIELD_VEC(fld, origin) \
106 
107 
108  #define FIELDS_UNION(fld) \
109  FIELD_SCALAR(fld, Version) \
110  FIELD_SCALAR(fld, ammo_cells) \
111  FIELD_SCALAR(fld, ammo_nails) \
112  FIELD_SCALAR(fld, ammo_rockets) \
113  FIELD_SCALAR(fld, antiwall_flag) \
114  FIELD_SCALAR(fld, armorvalue) \
115  FIELD_SCALAR(fld, atten) \
116  FIELD_SCALAR(fld, bgmscriptdecay) \
117  FIELD_SCALAR(fld, bgmscriptsustain) \
118  FIELD_SCALAR(fld, bgmscript) \
119  FIELD_SCALAR(fld, button0) \
120  FIELD_SCALAR(fld, chmap) \
121  FIELD_SCALAR(fld, cnt) \
122  FIELD_SCALAR(fld, colormap) \
123  FIELD_SCALAR(fld, count) \
124  FIELD_SCALAR(fld, curvetarget) \
125  FIELD_SCALAR(fld, cvarfilter) \
126  FIELD_SCALAR(fld, debrisdamageforcescale) \
127  FIELD_SCALAR(fld, debrisfadetime) \
128  FIELD_SCALAR(fld, debrismovetype) \
129  FIELD_SCALAR(fld, debrisskin) \
130  FIELD_SCALAR(fld, debristimejitter) \
131  FIELD_SCALAR(fld, debristime) \
132  FIELD_SCALAR(fld, debris) \
133  FIELD_SCALAR(fld, delay) \
134  FIELD_SCALAR(fld, dmgtime) \
135  FIELD_SCALAR(fld, dmg) \
136  FIELD_SCALAR(fld, dmg_edge) \
137  FIELD_SCALAR(fld, dmg_force) \
138  FIELD_SCALAR(fld, dmg_radius) \
139  FIELD_SCALAR(fld, effects) \
140  FIELD_SCALAR(fld, falloff) \
141  FIELD_SCALAR(fld, flags) \
142  FIELD_SCALAR(fld, fog) \
143  FIELD_SCALAR(fld, frags) \
144  FIELD_SCALAR(fld, frame) \
145  FIELD_SCALAR(fld, gametype) \
146  FIELD_SCALAR(fld, gametypefilter) \
147  FIELD_SCALAR(fld, geomtype) \
148  FIELD_SCALAR(fld, gravity) \
149  FIELD_SCALAR(fld, health) \
150  FIELD_SCALAR(fld, height) \
151  FIELD_SCALAR(fld, impulse) \
152  FIELD_SCALAR(fld, invincible_finished) \
153  FIELD_SCALAR(fld, invisibility_finished) \
154  FIELD_SCALAR(fld, item_pickupsound) \
155  FIELD_SCALAR(fld, killtarget) \
156  FIELD_SCALAR(fld, lerpfrac) \
157  FIELD_SCALAR(fld, light_lev) \
158  FIELD_SCALAR(fld, lip) \
159  FIELD_SCALAR(fld, loddistance1) \
160  FIELD_SCALAR(fld, lodmodel1) \
161  FIELD_SCALAR(fld, lodmodel2) \
162  FIELD_SCALAR(fld, ltime) \
163  FIELD_SCALAR(fld, map) \
164  FIELD_SCALAR(fld, max_health) \
165  FIELD_SCALAR(fld, mdl) \
166  FIELD_SCALAR(fld, message2) \
167  FIELD_SCALAR(fld, message) \
168  FIELD_SCALAR(fld, modelindex) \
169  FIELD_SCALAR(fld, modelscale) \
170  FIELD_SCALAR(fld, model) \
171  FIELD_SCALAR(fld, monsterid) \
172  FIELD_SCALAR(fld, monster_moveflags) \
173  FIELD_SCALAR(fld, monster_name) \
174  FIELD_SCALAR(fld, movetype) \
175  FIELD_SCALAR(fld, move_movetype) \
176  FIELD_SCALAR(fld, netname) \
177  FIELD_SCALAR(fld, nextthink) \
178  FIELD_SCALAR(fld, noalign) \
179  FIELD_SCALAR(fld, noise1) \
180  FIELD_SCALAR(fld, noise2) \
181  FIELD_SCALAR(fld, noise3) \
182  FIELD_SCALAR(fld, noise) \
183  FIELD_SCALAR(fld, notcpm) \
184  FIELD_SCALAR(fld, notfree) \
185  FIELD_SCALAR(fld, notsingle) \
186  FIELD_SCALAR(fld, notta) \
187  FIELD_SCALAR(fld, notteam) \
188  FIELD_SCALAR(fld, notvq3) \
189  FIELD_SCALAR(fld, phase) \
190  FIELD_SCALAR(fld, platmovetype) \
191  FIELD_SCALAR(fld, race_place) \
192  FIELD_SCALAR(fld, speed_finished) \
193  FIELD_SCALAR(fld, strength_finished) \
194  FIELD_SCALAR(fld, radius) \
195  FIELD_SCALAR(fld, respawntimestart) \
196  FIELD_SCALAR(fld, respawntimejitter) \
197  FIELD_SCALAR(fld, respawntime) \
198  FIELD_SCALAR(fld, restriction) \
199  FIELD_SCALAR(fld, scale) \
200  FIELD_SCALAR(fld, skin) \
201  FIELD_SCALAR(fld, solid) \
202  FIELD_SCALAR(fld, sound1) \
203  FIELD_SCALAR(fld, sounds) \
204  FIELD_SCALAR(fld, spawnflags) \
205  FIELD_SCALAR(fld, spawnmob) \
206  FIELD_SCALAR(fld, speed) \
207  FIELD_SCALAR(fld, strength) \
208  FIELD_SCALAR(fld, style) \
209  FIELD_SCALAR(fld, target2) \
210  FIELD_SCALAR(fld, target3) \
211  FIELD_SCALAR(fld, target4) \
212  FIELD_SCALAR(fld, targetname) \
213  FIELD_SCALAR(fld, target) \
214  FIELD_SCALAR(fld, target_random) \
215  FIELD_SCALAR(fld, target_range) \
216  FIELD_SCALAR(fld, team) \
217  FIELD_SCALAR(fld, trigger_reverse) \
218  FIELD_SCALAR(fld, turret_scale_aim) \
219  FIELD_SCALAR(fld, turret_scale_ammo) \
220  FIELD_SCALAR(fld, turret_scale_damage) \
221  FIELD_SCALAR(fld, turret_scale_health) \
222  FIELD_SCALAR(fld, turret_scale_range) \
223  FIELD_SCALAR(fld, turret_scale_refire) \
224  FIELD_SCALAR(fld, turret_scale_respawn) \
225  FIELD_SCALAR(fld, volume) \
226  FIELD_SCALAR(fld, wait) \
227  FIELD_SCALAR(fld, warpzone_fadeend) \
228  FIELD_SCALAR(fld, warpzone_fadestart) \
229  FIELD_SCALAR(fld, weapon) \
230  FIELD_SCALAR(fld, worldtype) \
231  FIELD_VEC(fld, absmax) \
232  FIELD_VEC(fld, absmin) \
233  FIELD_VEC(fld, angles) \
234  FIELD_VEC(fld, avelocity) \
235  FIELD_VEC(fld, beam_color)\
236  FIELD_VEC(fld, debrisavelocityjitter) \
237  FIELD_VEC(fld, debrisvelocity) \
238  FIELD_VEC(fld, debrisvelocityjitter) \
239  FIELD_VEC(fld, color) \
240  FIELD_VEC(fld, mangle) \
241  FIELD_VEC(fld, maxs) \
242  FIELD_VEC(fld, mins) \
243  FIELD_VEC(fld, modelscale_vec) \
244  FIELD_VEC(fld, velocity) \
245 
246 
247 ERASEABLE
248 void _checkWhitelisted(entity this, string id)
249 {
250  for (int i = 0, n = numentityfields(); i < n; ++i)
251  {
252  string value = getentityfieldstring(i, this);
253  string s = entityfieldname(i);
254  FIELDS_UNION(_spawnfunc_checktypes)
255  if (value == "") continue;
256  if (s == "") continue;
257  FIELDS_COMMON(_spawnfunc_check)
258  FIELDS_UNION(_spawnfunc_check)
259  LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue."), id, s, value);
260  }
261 }
262 
263 // this function simply avoids expanding IL_NEW during compilation
264 // for each spawning entity
265 void g_spawn_queue_spawn() { g_spawn_queue = IL_NEW(); }
266 
267 noref bool __spawnfunc_first;
268 
269 #define spawnfunc(id) \
270  void __spawnfunc_##id(entity this); \
271  ACCUMULATE void spawnfunc_##id(entity this) \
272  { \
273  if (!__spawnfunc_first) { \
274  __spawnfunc_first = true; \
275  static_init_early(); \
276  } \
277  bool dospawn = true; \
278  if (__spawnfunc_expecting > 1) { __spawnfunc_expecting = 0; } \
279  else if (__spawnfunc_expecting) { \
280  /* engine call */ \
281  if (!g_spawn_queue) g_spawn_queue_spawn(); \
282  __spawnfunc_expecting = 0; \
283  this = __spawnfunc_expect; \
284  __spawnfunc_expect = NULL; \
285  dospawn = false; \
286  } else { \
287  /* userland call */ \
288  assert(this); \
289  } \
290  if (!this.sourceLoc) { \
291  this.sourceLoc = __FILE__":"STR(__LINE__); \
292  } \
293  this.classname = #id; \
294  if (!this.spawnfunc_checked) { \
295  _checkWhitelisted(this, #id); \
296  if (__fullspawndata) { \
297  /* not supported in old DP */ \
298  /* must be read inside the real spawnfunc */ \
299  this.fullspawndata = __fullspawndata; \
300  } \
301  this.spawnfunc_checked = true; \
302  if (this) { \
303  /* not worldspawn, delay spawn */ \
304  /* clear some dangerous fields (TODO: properly support these in the map!) */ \
305  this.think = func_null; \
306  this.nextthink = 0; \
307  __spawnfunc_defer(this, __spawnfunc_##id); \
308  } else { \
309  /* world might not be "worldspawn" */ \
310  this.__spawnfunc_constructor = __spawnfunc_##id; \
311  } \
312  } \
313  if (dospawn) { __spawnfunc_##id(this); } \
314  if (__spawnfunc_unreachable_workaround) return; \
315  } \
316  void __spawnfunc_##id(entity this)
317 
318 #endif
#define X(team)
#define IL_NEW()
entity() spawn
limitations: NULL cannot be present elements can only be present once a maximum of IL_MAX lists can e...
#define ERASEABLE
Definition: _all.inc:35
float FIELD_VECTOR
#define LOG_WARNF(...)
Definition: log.qh:67
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
float FIELD_FLOAT
float FIELD_STRING