Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
deglobalization.qh
Go to the documentation of this file.
1#include "lib/accumulate.qh"
2#include "lib/float.qh"
3#include "lib/log.qh"
4#include "lib/misc.qh"
5#include "lib/static.qh"
6#include "lib/vector.qh"
7
8// These macros wrap functions which use globals so mutation of global state only occurs inside them and is not visible from outside.
9// This helps prevent bugs where refactoring accidentally breaks implicit assumptions about global state ("pls call makevectors before calling this").
10// Currently only functions that use v_forward/v_right/v_up are wrapped since those are most common.
11// Some functions don't have wrappers because they're not used anywhere.
12
13// Steps (slightly inspired by steps in self.qh):
14// 1) (done) Create alternative names for the builtins (e.g. _makevectors_hidden) to be used inside wrappers.
15// Shadow the originals with macros that tell the user to use the wrappers instead. These are in the *defs.qh files.
16// 2) Create wrapper macros with the same name (e.g. makevectors) that still use globals but log their usage.
17// - Would be nice to also log reads and writes to the globals themselves. Probably possible with macros, comma expressions and [[alias]].
18// 3) Create wrapper macros that use locals (e.g. MAKE_VECTORS).
19// TODO stuff in the engine that uses the v_forward/v_right/v_up globals and is not wrapped yet:
20// - RF_USEAXIS, addentities, predraw,
21// - CL_GetEntityMatrix (in engine but is called from other functions so transitively any of them can use the globals - e.g. V_CalcRefdef, maybe others)
22// - however RF_USEAXIS is only used if MF_ROTATE is used which is only set in one place
23// - e.camera_transform / CL_VM_TransformView (in engine)
24// - this is the only used function that both sets and gets the globals (aim does too but isn't used in our code)
25// 4) Gradually replace uses of each function with its wrapper.
26// 5) When a function is no longer used, remove the wrapper with the same name to cause compile errors that will prevent accidental use in the future.
27
28// Final checking:
29// When all functions which use a global have been replaced with the wrappers,
30// the wrappers can check that the global contains NaN before its use and set it to NaN after its use.
31// This should detect if there is any remaining global mutation (even in the engine).
32// NaN is the most likely value to expose remaining usages - e.g. functions like traceline crash on it.
33
34#ifdef GAMEQC // menu doesn't use any globals
35
36// compile time switches in case perf is an issue
37#define DEGLOB_LOGGING 1
38#define DEGLOB_CLEAR 1
39
40const int DEGLOB_ORIGINAL = 1;
41const int DEGLOB_WRAPPED = 2;
42#if DEGLOB_LOGGING
43int autocvar_debug_deglobalization_logging = 0;
44// Varargs to this should already be stringized, otherwise they're expanded first which makes them less readable.
45// The downside is redundant quotes, fortunately none of these functions take strings.
46#define DEGLOB_LOG(kind, name, ...) deglob_log(kind, name, __FILE__, __LINE__, __FUNC__, #__VA_ARGS__)
47// This needs to be a function, not a macro,
48// because some wrappers of the old functions need to use comma expressions
49// because they return values.
50void deglob_log(int kind, string name, string file, int line, string func, string more_text) {
51 if (autocvar_debug_deglobalization_logging & kind) {
52 LOG_INFOF("%s %f %s %s:%d:%s args: %s", PROGNAME, time, name, file, line, func, more_text);
53 }
54}
55#else
56#define DEGLOB_LOG(kind, name, ...)
57void deglob_log(int kind, string name, string file, int line, string func, string more_text) {}
58#endif
59
60// convenience for deglobalization code - don't use these just to hide that globals are still used
61#define GET_V_GLOBALS(forward, right, up) MACRO_BEGIN forward = v_forward; right = v_right; up = v_up; MACRO_END
62#define SET_V_GLOBALS(forward, right, up) MACRO_BEGIN v_forward = forward; v_right = right; v_up = up; MACRO_END
63#if DEGLOB_CLEAR
64bool autocvar_debug_deglobalization_clear = true;
65#define CLEAR_V_GLOBALS() MACRO_BEGIN \
66 if (autocvar_debug_deglobalization_clear) { \
67 v_forward = VEC_NAN; v_right = VEC_NAN; v_up = VEC_NAN \
68 } \
69MACRO_END
70STATIC_INIT(globals) {
71 // set to NaN to more easily detect uninitialized use
73}
74#else
75#define CLEAR_V_GLOBALS()
76#endif
77
81#define MAKE_VECTORS(angles, forward, right, up) MACRO_BEGIN \
82 DEGLOB_LOG(DEGLOB_WRAPPED, "MAKE_VECTORS", #angles); \
83 _makevectors_hidden(angles); \
84 GET_V_GLOBALS(forward, right, up); \
85 CLEAR_V_GLOBALS(); \
86MACRO_END
87
89#define SKEL_GET_BONE_ABS(skel, bonenum, forward, right, up, origin) MACRO_BEGIN \
90 DEGLOB_LOG(DEGLOB_WRAPPED, "SKEL_GET_BONE_ABS", #skel, #bonenum); \
91 origin = _skel_get_boneabs_hidden(skel, bonenum) \
92 GET_V_GLOBALS(forward, right, up); \
93 CLEAR_V_GLOBALS(); \
94MACRO_END
95
96#define SKEL_SET_BONE(skel, bonenum, org, forward, right, up) MACRO_BEGIN \
97 DEGLOB_LOG(DEGLOB_WRAPPED, "SKEL_SET_BONE", #skel, #bonenum, #org); \
98 SET_V_GLOBALS(forward, right, up); \
99 _skel_set_bone_hidden(skel, bonenum, org); \
100 CLEAR_V_GLOBALS(); \
101MACRO_END
102
103#define ADD_DYNAMIC_LIGHT(org, radius, lightcolours, forward, right, up) MACRO_BEGIN \
104 DEGLOB_LOG(DEGLOB_WRAPPED, "ADD_DYNAMIC_LIGHT", #org, #radius, #lightcolours); \
105 SET_V_GLOBALS(forward, right, up); \
106 _adddynamiclight_hidden(org, radius, lightcolours); \
107 CLEAR_V_GLOBALS(); \
108MACRO_END
109
110#define ADD_DYNAMIC_LIGHT2(org, radius, lightcolours, style, cubemapname, pflags, forward, right, up) MACRO_BEGIN \
111 DEGLOB_LOG(DEGLOB_WRAPPED, "ADD_DYNAMIC_LIGHT2", #org, #radius, #lightcolours, #style, #cubemapname, #pflags); \
112 SET_V_GLOBALS(forward, right, up); \
113 _adddynamiclight2_hidden(org, radius, lightcolours, style, cubemapname, pflags); \
114 CLEAR_V_GLOBALS(); \
115MACRO_END
116
117#define VECTOR_VECTORS(forward_in, forward, right, up) MACRO_BEGIN \
118 DEGLOB_LOG(DEGLOB_WRAPPED, "VECTOR_VECTORS", #forward_in); \
119 _vectorvectors_hidden(forward_in); \
120 GET_V_GLOBALS(forward, right, up); \
121 CLEAR_V_GLOBALS(); \
122MACRO_END
123
125#define GET_TAG_INFO(ent, tagindex, forward, right, up, origin) MACRO_BEGIN \
126 DEGLOB_LOG(DEGLOB_WRAPPED, "GET_TAG_INFO", #ent, #tagindex); \
127 origin = _gettaginfo_hidden(ent, tagindex); \
128 GET_V_GLOBALS(forward, right, up); \
129 CLEAR_V_GLOBALS(); \
130MACRO_END
131
132#undef makevectors
133#define makevectors(angles) MACRO_BEGIN \
134 DEGLOB_LOG(DEGLOB_ORIGINAL, "makevectors", #angles); \
135 _makevectors_hidden(angles); \
136MACRO_END
137
138#undef skel_get_boneabs
139#define skel_get_boneabs(skel, bonenum) ( \
140 deglob_log(DEGLOB_ORIGINAL, "skel_get_boneabs", __FILE__, __LINE__, __FUNC__, #skel ", " #bonenum), \
141 _skel_get_boneabs_hidden(skel, bonenum) \
142)
143
144#undef skel_set_bone
145#define skel_set_bone(skel, bonenum, org) MACRO_BEGIN \
146 DEGLOB_LOG(DEGLOB_ORIGINAL, "skel_set_bone", #skel, #bonenum, #org); \
147 _skel_set_bone_hidden(skel, bonenum, org); \
148MACRO_END
149
150#undef adddynamiclight
151#define adddynamiclight(org, radius, lightcolours) MACRO_BEGIN \
152 DEGLOB_LOG(DEGLOB_ORIGINAL, "adddynamiclight", #org, #radius, #lightcolours); \
153 _adddynamiclight_hidden(org, radius, lightcolours); \
154MACRO_END
155
156#undef adddynamiclight2
157#define adddynamiclight2(org, radius, lightcolours, style, cubemapname, pflags) MACRO_BEGIN \
158 DEGLOB_LOG(DEGLOB_ORIGINAL, "adddynamiclight2", #org, #radius, #lightcolours, #style, #cubemapname, #pflags); \
159 _adddynamiclight2_hidden(org, radius, lightcolours, style, cubemapname, pflags); \
160MACRO_END
161
162#undef gettaginfo
163#define gettaginfo(ent, tagindex) ( \
164 deglob_log(DEGLOB_ORIGINAL, "gettaginfo", __FILE__, __LINE__, __FUNC__, #ent ", " #tagindex), \
165 _gettaginfo_hidden(ent, tagindex) \
166)
167
168#endif // GAMEQC
float time
#define CLEAR_V_GLOBALS()
const int DEGLOB_WRAPPED
void deglob_log(int kind, string name, string file, int line, string func, string more_text)
const int DEGLOB_ORIGINAL
#define LOG_INFOF(...)
Definition log.qh:66
string name
Definition menu.qh:30
#define PROGNAME
Definition progname.qh:4
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:32