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{
52 if (autocvar_debug_deglobalization_logging & kind)
53 LOG_INFOF("%s %f %s %s:%d:%s args: %s", PROGNAME, time, name, file, line, func, more_text);
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 { \
68 v_forward = VEC_NAN; \
69 v_right = VEC_NAN; \
70 v_up = VEC_NAN; \
71 } \
72MACRO_END
73STATIC_INIT(globals)
74{
75 // set to NaN to more easily detect uninitialized use
77}
78#else
79#define CLEAR_V_GLOBALS()
80#endif
81
85#define MAKE_VECTORS(angles, forward, right, up) MACRO_BEGIN \
86 DEGLOB_LOG(DEGLOB_WRAPPED, "MAKE_VECTORS", #angles); \
87 _makevectors_hidden(angles); \
88 GET_V_GLOBALS(forward, right, up); \
89 CLEAR_V_GLOBALS(); \
90MACRO_END
91
93#define SKEL_GET_BONE_ABS(skel, bonenum, forward, right, up, origin) MACRO_BEGIN \
94 DEGLOB_LOG(DEGLOB_WRAPPED, "SKEL_GET_BONE_ABS", #skel, #bonenum); \
95 origin = _skel_get_boneabs_hidden(skel, bonenum) \
96 GET_V_GLOBALS(forward, right, up); \
97 CLEAR_V_GLOBALS(); \
98MACRO_END
99
100#define SKEL_SET_BONE(skel, bonenum, org, forward, right, up) MACRO_BEGIN \
101 DEGLOB_LOG(DEGLOB_WRAPPED, "SKEL_SET_BONE", #skel, #bonenum, #org); \
102 SET_V_GLOBALS(forward, right, up); \
103 _skel_set_bone_hidden(skel, bonenum, org); \
104 CLEAR_V_GLOBALS(); \
105MACRO_END
106
107#define ADD_DYNAMIC_LIGHT(org, radius, lightcolours, forward, right, up) MACRO_BEGIN \
108 DEGLOB_LOG(DEGLOB_WRAPPED, "ADD_DYNAMIC_LIGHT", #org, #radius, #lightcolours); \
109 SET_V_GLOBALS(forward, right, up); \
110 _adddynamiclight_hidden(org, radius, lightcolours); \
111 CLEAR_V_GLOBALS(); \
112MACRO_END
113
114#define ADD_DYNAMIC_LIGHT2(org, radius, lightcolours, style, cubemapname, pflags, forward, right, up) MACRO_BEGIN \
115 DEGLOB_LOG(DEGLOB_WRAPPED, "ADD_DYNAMIC_LIGHT2", #org, #radius, #lightcolours, #style, #cubemapname, #pflags); \
116 SET_V_GLOBALS(forward, right, up); \
117 _adddynamiclight2_hidden(org, radius, lightcolours, style, cubemapname, pflags); \
118 CLEAR_V_GLOBALS(); \
119MACRO_END
120
121#define VECTOR_VECTORS(forward_in, forward, right, up) MACRO_BEGIN \
122 DEGLOB_LOG(DEGLOB_WRAPPED, "VECTOR_VECTORS", #forward_in); \
123 _vectorvectors_hidden(forward_in); \
124 GET_V_GLOBALS(forward, right, up); \
125 CLEAR_V_GLOBALS(); \
126MACRO_END
127
129#define GET_TAG_INFO(ent, tagindex, forward, right, up, origin) MACRO_BEGIN \
130 DEGLOB_LOG(DEGLOB_WRAPPED, "GET_TAG_INFO", #ent, #tagindex); \
131 origin = _gettaginfo_hidden(ent, tagindex); \
132 GET_V_GLOBALS(forward, right, up); \
133 CLEAR_V_GLOBALS(); \
134MACRO_END
135
136#undef makevectors
137#define makevectors(angles) MACRO_BEGIN \
138 DEGLOB_LOG(DEGLOB_ORIGINAL, "makevectors", #angles); \
139 _makevectors_hidden(angles); \
140MACRO_END
141
142#undef skel_get_boneabs
143#define skel_get_boneabs(skel, bonenum) ( \
144 deglob_log(DEGLOB_ORIGINAL, "skel_get_boneabs", __FILE__, __LINE__, __FUNC__, #skel ", " #bonenum), \
145 _skel_get_boneabs_hidden(skel, bonenum) \
146)
147
148#undef skel_set_bone
149#define skel_set_bone(skel, bonenum, org) MACRO_BEGIN \
150 DEGLOB_LOG(DEGLOB_ORIGINAL, "skel_set_bone", #skel, #bonenum, #org); \
151 _skel_set_bone_hidden(skel, bonenum, org); \
152MACRO_END
153
154#undef adddynamiclight
155#define adddynamiclight(org, radius, lightcolours) MACRO_BEGIN \
156 DEGLOB_LOG(DEGLOB_ORIGINAL, "adddynamiclight", #org, #radius, #lightcolours); \
157 _adddynamiclight_hidden(org, radius, lightcolours); \
158MACRO_END
159
160#undef adddynamiclight2
161#define adddynamiclight2(org, radius, lightcolours, style, cubemapname, pflags) MACRO_BEGIN \
162 DEGLOB_LOG(DEGLOB_ORIGINAL, "adddynamiclight2", #org, #radius, #lightcolours, #style, #cubemapname, #pflags); \
163 _adddynamiclight2_hidden(org, radius, lightcolours, style, cubemapname, pflags); \
164MACRO_END
165
166#undef gettaginfo
167#define gettaginfo(ent, tagindex) ( \
168 deglob_log(DEGLOB_ORIGINAL, "gettaginfo", __FILE__, __LINE__, __FUNC__, #ent ", " #tagindex), \
169 _gettaginfo_hidden(ent, tagindex) \
170)
171
172#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:63
string name
Definition menu.qh:30
#define PROGNAME
Definition progname.qh:4
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:33