Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
ent_cs.qc
Go to the documentation of this file.
1#include "ent_cs.qh"
2
3#if defined(CSQC)
6#elif defined(MENUQC)
7#elif defined(SVQC)
11#endif
12
13REGISTRY(EntCSProps, BITS(16) - 1)
14REGISTER_REGISTRY(EntCSProps)
15REGISTRY_SORT(EntCSProps)
16REGISTRY_CHECK(EntCSProps)
17
18REGISTRY_DEFINE_GET(EntCSProps, NULL)
19STATIC_INIT(EntCSProps_renumber) { FOREACH(EntCSProps, true, it.m_id = i); }
20
21// these entcs_props ids need to be referenced directly
25STATIC_INIT(EntCSProps_setglobalids)
26{
27 FOREACH(EntCSProps, true, {
28 if (it.registered_id == "ENTCS_PROP_ENTNUM") ENTCS_PROP_ENTNUM_id = it.m_id;
29 if (it.registered_id == "ENTCS_PROP_ORIGIN") ENTCS_PROP_ORIGIN_id = it.m_id;
30 if (it.registered_id == "ENTCS_PROP_HEALTH") ENTCS_PROP_HEALTH_id = it.m_id;
31 });
32}
33
34#ifdef SVQC
35// Force an origin update, for player sounds
37{
38 CS(player).entcs.m_forceupdate = BIT(ENTCS_PROP_ORIGIN_id);
39}
40#endif
41
43.bool(entity ent, entity player) m_check;
44.void(entity ent, entity player) m_set;
45.void(int chan, entity ent) m_send;
46.void(entity ent) m_receive;
47
48#ifdef SVQC
49#define _ENTCS_PROP(id, ispublic, checkprop, checkprop_pl, setprop, svsend, clreceive) \
50 void id##_set(entity ent, entity player) { setprop(ent.(checkprop), player.(checkprop_pl)); } \
51 void id##_send(int chan, entity ent) { LAMBDA(svsend); } \
52 REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \
53 this.m_public = ispublic; \
54 this.m_check = id##_check; \
55 this.m_set = id##_set; \
56 this.m_send = id##_send; \
57 }
58
59#define ENTCS_PROP(id, ispublic, checkprop, checkprop_pl, setprop, svsend, clreceive) \
60 bool id##_check(entity ent, entity player) { return (ent.(checkprop) != player.(checkprop_pl)); } \
61 _ENTCS_PROP(id, ispublic, checkprop, checkprop_pl, setprop, svsend, clreceive)
62
63#define ENTCS_PROP_CODED(id, ispublic, checkprop, checkprop_pl, setprop, decfactor, svsend, clreceive) \
64 bool id##_check(entity ent, entity player) { \
65 return (floor(ent.(checkprop)) / decfactor != floor(player.(checkprop_pl)) / decfactor); \
66 } \
67 _ENTCS_PROP(id, ispublic, checkprop, checkprop_pl, setprop, svsend, clreceive)
68
69#elif defined(CSQC)
70#define ENTCS_PROP(id, ispublic, checkprop, checkprop_pl, setprop, svsend, clreceive) \
71 void id##_receive(entity ent) { LAMBDA(clreceive); } \
72 REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \
73 this.m_public = ispublic; \
74 this.m_receive = id##_receive; \
75 }
76
77#define ENTCS_PROP_CODED(id, ispublic, checkprop, checkprop_pl, setprop, decfactor, svsend, clreceive) \
78 ENTCS_PROP(id, ispublic, checkprop, checkprop_pl, setprop, svsend, clreceive)
79#endif
80
81#ifdef SVQC
82#define ENTCS_PROP_RESOURCE(id, ispublic, checkprop, setprop, decfactor, svsend, clreceive) \
83 bool id##_check(entity ent, entity player) { \
84 return (floor(GetResource(ent, checkprop) / decfactor) != floor(GetResource(player, checkprop) / decfactor)); \
85 } \
86 void id##_set(entity ent, entity player) { SetResourceExplicit(ent, checkprop, GetResource(player, checkprop)); } \
87 void id##_send(int chan, entity ent) { LAMBDA(svsend); } \
88 REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \
89 this.m_public = ispublic; \
90 this.m_check = id##_check; \
91 this.m_set = id##_set; \
92 this.m_send = id##_send; \
93 }
94#elif defined(CSQC)
95#define ENTCS_PROP_RESOURCE(id, ispublic, checkprop, setprop, decfactor, svsend, clreceive) \
96 void id##_receive(entity ent) { LAMBDA(clreceive); } \
97 REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \
98 this.m_public = ispublic; \
99 this.m_receive = id##_receive; \
100 }
101#endif
102
103#define ENTCS_SET_NORMAL(var, x) MACRO_BEGIN \
104 var = x; \
105MACRO_END
106
108#define ENTCS_SET_MUTABLE_STRING(var, x) MACRO_BEGIN \
109 strcpy(var, x); \
110MACRO_END
111
112ENTCS_PROP(ENTNUM, false, sv_entnum, sv_entnum, ENTCS_SET_NORMAL, {}, {}) /* sentinel */
113
115 { WriteVector(chan, ent.origin); },
116 { ent.has_sv_origin = true; vector v = ReadVector(); setorigin(ent, v); })
117
118#define DEC_FACTOR (360 / 64)
120 { WriteByte(chan, ent.angles.y / DEC_FACTOR); },
121 { vector v = '0 0 0'; v.y = ReadByte() * DEC_FACTOR; ent.angles = v; })
122#undef DEC_FACTOR
123
124// FIXME: use a better scale?
125#define DEC_FACTOR 10
127 { WriteByte(chan, bound(0, GetResource(ent, RES_HEALTH) / DEC_FACTOR, 255)); },
128 { ent.healthvalue = ReadByte() * DEC_FACTOR; })
129
131 { WriteByte(chan, bound(0, GetResource(ent, RES_ARMOR) / DEC_FACTOR, 255)); },
133#undef DEC_FACTOR
134
136 { WriteString(chan, ent.netname); },
137 { strcpy(ent.netname, ReadString()); })
138
140 { WriteString(chan, ent.model); },
141 { strcpy(ent.model, ReadString()); })
142
144 { WriteByte(chan, ent.skin); },
145 { ent.skin = ReadByte(); })
146
148 { WriteByte(chan, ent.clientcolors); },
149 { ent.colormap = ReadByte(); })
150
152 { WriteShort(chan, ent.frags); },
153 { ent.frags = ReadShort(); })
154
156 { WriteByte(chan, ent.handicap_level); },
157 { ent.handicap_level = ReadByte(); })
158
159// index of join queue team selection, max 127 because -1 means any available team
161 { WriteChar(chan, ent.wants_join); },
162 { ent.wants_join = ReadChar(); })
163
164// use sv_solid to avoid changing solidity state of entcs entities
166 { WriteByte(chan, ent.sv_solid); },
167 { ent.sv_solid = ReadByte(); })
168
169#ifdef SVQC
170
173 {
174 FOREACH(EntCSProps, true,
175 {
176 if (it.m_public)
177 ENTCS_PUBLICMASK |= BIT(it.m_id);
178 else
179 ENTCS_PRIVATEMASK |= BIT(it.m_id);
180 });
181 }
182
184 {
185 FOREACH_CLIENT(it != player && IS_PLAYER(it),
186 {
187 CS(it).entcs.SendFlags |= ENTCS_PRIVATEMASK;
188 });
189 }
190
191 bool _entcs_send(entity this, entity to, int sf, int chan)
192 {
193 entity player = this.owner;
194 sf |= BIT(ENTCS_PROP_ENTNUM_id); // assume private
195 do {
196 if (IS_PLAYER(player))
197 {
198 if (radar_showenemies) break;
199 if (SAME_TEAM(to, player)) break;
200 if (!(IS_PLAYER(to) || INGAME(to))) break;
201 }
202 sf &= ENTCS_PUBLICMASK; // no private updates
203 } while (0);
204
205 sf |= this.m_forceupdate;
206 this.m_forceupdate = 0;
207 if (chan == MSG_ENTITY)
208 WriteHeader(chan, ENT_CLIENT_ENTCS);
209 else
210 WriteHeader(chan, CLIENT_ENTCS);
211 WriteByte(chan, etof(player) - 1);
212 WriteShort(chan, sf);
213 FOREACH(EntCSProps, sf & BIT(it.m_id),
214 {
215 it.m_send(chan, this);
216 });
217 return true;
218 }
219
220 bool entcs_send(entity this, entity to, int sf)
221 {
222 return _entcs_send(this, to, sf, MSG_ENTITY);
223 }
224
226 {
227 this.nextthink = time + 0.015625; // TODO: increase this to like 0.125 once the client can do smoothing
228 entity player = this.owner;
229 FOREACH(EntCSProps, it.m_check(this, player),
230 {
231 it.m_set(this, player);
232 this.SendFlags |= BIT(it.m_id);
233 });
234
236 {
237 // health is set to special values after the game ends, ignore any change
239 }
240
241 // always send origin of players even if they stand still otherwise
242 // if a teammate isn't in my pvs and their health (or view angle or name
243 // etc...) changes then their tag disappears
244 if (IS_PLAYER(this.owner))
246
247 // not needed, origin is just data to be sent
248 //setorigin(this, this.origin); // relink
249 }
250
251 void entcs_attach(entity player)
252 {
253 entity e = CS(player).entcs = new_pure(entcs_sender);
254 e.owner = player;
256 e.nextthink = time;
257 Net_LinkEntity(e, false, 0, entcs_send);
258 // NOTE: the following code block has been disabled as a workaround for https://gitlab.com/xonotic/xonotic-data.pk3dir/-/issues/1824
259#if 0
260 if (!IS_REAL_CLIENT(player)) return;
261 FOREACH_CLIENT(true, {
262 assert(CS(it).entcs);
263 _entcs_send(CS(it).entcs, msg_entity = player, BITS(23), MSG_ONE);
264 });
265#endif
266 }
267
268 void entcs_detach(entity player)
269 {
270 if (!CS(player).entcs) return;
271 delete(CS(player).entcs);
272 CS(player).entcs = NULL;
273 }
274
275#endif
276
277#ifdef CSQC
278
280 {
281 int n = this.sv_entnum;
282 entity e = entcs_receiver(n);
284 strfree(e.netname);
285 strfree(e.model);
286 if (e != this) delete(e);
287 }
288
289 void entcs_think(entity this)
290 {
292 if (e == NULL)
293 {
294 // player model is NOT in client's PVS
296 this.has_origin = this.has_sv_origin;
297 return;
298 }
299 this.has_origin = true;
300 // when a player model is in client's PVS we use its origin directly
301 // (entcs networked origin is overriden)
302 this.origin = e.origin;
304 setorigin(this, this.origin);
305 // `cl_forceplayermodels 1` sounds will be wrong until the player has been in the PVS, but so be it
306 if (this.model != e.model)
307 {
308 strcpy(this.model, e.model);
309 }
310 }
311
312 bool ReadEntcs(entity this)
313 {
314 int n = ReadByte();
315 entity e = entcs_receiver(n);
316 if (e == NULL)
317 {
318 if (!this)
319 // initial = temp
320 e = new_pure(ENT_CLIENT_ENTCS);
321 else
322 // initial = linked
323 e = this;
325 entcs_receiver(n, e);
326 }
327 else if (e != this && this)
328 {
329 // upgrade to linked
330 delete(e);
331 e = this;
333 entcs_receiver(n, e);
334 }
335
337 e.sv_entnum = n;
338 int sf = ReadShort();
339 e.has_sv_origin = false;
340 e.m_entcs_private = boolean(sf & BIT(ENTCS_PROP_ENTNUM_id));
341 FOREACH(EntCSProps, sf & BIT(it.m_id),
342 {
343 it.m_receive(e);
344 });
345 e.iflags |= IFLAG_ORIGIN;
347 getthink(e)(e);
348 return true;
349 }
350
351 NET_HANDLE(ENT_CLIENT_ENTCS, bool isnew)
352 {
353 if (isnew)
354 {
355 make_pure(this);
356 this.entremove = Ent_RemoveEntCS;
357 }
358 return ReadEntcs(this);
359 }
360
361 NET_HANDLE(CLIENT_ENTCS, bool isnew)
362 {
363 return ReadEntcs(NULL);
364 }
365
366#endif
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
#define BITS(n)
Definition bits.qh:9
#define boolean(value)
Definition bool.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
#define ReadString
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
string netname
Definition powerups.qc:20
entity owner
Definition main.qh:87
int sv_entnum
Definition main.qh:186
#define IS_PLAYER(s)
Definition player.qh:243
float skin
float time
float nextthink
vector origin
#define ENTCS_SET_MUTABLE_STRING(var, x)
the engine player name strings are mutable!
Definition ent_cs.qc:108
int ENTCS_PUBLICMASK
Definition ent_cs.qc:171
RES_ARMOR
Definition ent_cs.qc:130
#define DEC_FACTOR
Definition ent_cs.qc:125
void entcs_detach(entity player)
Definition ent_cs.qc:268
int ENTCS_PRIVATEMASK
Definition ent_cs.qc:171
bool m_public
Definition ent_cs.qc:42
model
Definition ent_cs.qc:139
int ENTCS_PROP_HEALTH_id
Definition ent_cs.qc:24
#define ENTCS_SET_NORMAL(var, x)
Definition ent_cs.qc:103
int ENTCS_PROP_ENTNUM_id
Definition ent_cs.qc:22
bool ReadEntcs(entity this)
Definition ent_cs.qc:312
clientcolors
Definition ent_cs.qc:147
int ENTCS_PROP_ORIGIN_id
Definition ent_cs.qc:23
#define ENTCS_PROP(id, ispublic, checkprop, checkprop_pl, setprop, svsend, clreceive)
Definition ent_cs.qc:59
bool entcs_send(entity this, entity to, int sf)
Definition ent_cs.qc:220
angles_y
Definition ent_cs.qc:119
#define ENTCS_PROP_RESOURCE(id, ispublic, checkprop, setprop, decfactor, svsend, clreceive)
Definition ent_cs.qc:82
bool _entcs_send(entity this, entity to, int sf, int chan)
Definition ent_cs.qc:191
#define ENTCS_PROP_CODED(id, ispublic, checkprop, checkprop_pl, setprop, decfactor, svsend, clreceive)
Definition ent_cs.qc:63
void entcs_attach(entity player)
Definition ent_cs.qc:251
handicap_level
Definition ent_cs.qc:155
solid
Definition ent_cs.qc:165
void entcs_think(entity this)
Definition ent_cs.qc:225
void entcs_update_players(entity player)
Definition ent_cs.qc:183
void entcs_force_origin(entity player)
Definition ent_cs.qc:36
void Ent_RemoveEntCS(entity this)
Definition ent_cs.qc:279
sv_solid
Definition ent_cs.qc:165
bool radar_showenemies
Definition ent_cs.qh:50
bool has_sv_origin
True when a recent server sent origin has been received.
Definition ent_cs.qh:19
int frags
Definition ent_cs.qh:71
#define entcs_receiver(...)
Definition ent_cs.qh:65
int wants_join
Definition ent_cs.qh:73
entity CSQCModel_server2csqc(int i)
Definition cl_model.qc:314
int m_forceupdate
Definition ent_cs.qh:44
entity entcs
Definition ent_cs.qh:34
bool has_origin
True when origin is available.
Definition ent_cs.qh:16
bool intermission_running
void InterpolateOrigin_Undo(entity this)
snap origin to iorigin2 (actual origin)
void InterpolateOrigin_Reset(entity this)
void InterpolateOrigin_Note(entity this)
void InterpolateOrigin_Do(entity this)
set origin based on iorigin1 (old pos), iorigin2 (desired pos), and time
const int IFLAG_ORIGIN
#define FOREACH(list, cond, body)
Definition iter.qh:19
int SendFlags
Definition net.qh:118
#define NET_HANDLE(id, param)
Definition net.qh:15
const int MSG_ENTITY
Definition net.qh:115
#define ReadVector()
Definition net.qh:367
#define WriteHeader(to, id)
Definition net.qh:221
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
int ReadByte()
#define assert(expr,...)
Definition log.qh:8
float MSG_ONE
Definition menudefs.qc:56
float bound(float min, float value, float max)
void WriteString(string data, float dest, float desto)
void WriteChar(float data, float dest, float desto)
void WriteShort(float data, float dest, float desto)
void WriteByte(float data, float dest, float desto)
#define etof(e)
Definition misc.qh:25
#define MODEL(name, path)
Definition all.qh:8
#define make_pure(e)
direct use is
Definition oo.qh:13
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:67
#define NULL
Definition post.qh:14
entity msg_entity
Definition progsdefs.qc:63
#define REGISTRY_SORT(...)
Definition registry.qh:128
#define REGISTER_REGISTRY(id)
Definition registry.qh:229
#define REGISTRY(id, max)
Declare a new registry.
Definition registry.qh:26
#define REGISTRY_CHECK(id)
Definition registry.qh:175
#define REGISTRY_DEFINE_GET(id, null)
Definition registry.qh:40
#define setthink(e, f)
#define getthink(e)
vector
Definition self.qh:92
ClientState CS(Client this)
Definition state.qh:47
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:32
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
Header file that describes the resource system.
#define INGAME(it)
Definition sv_rules.qh:24
#define SAME_TEAM(a, b)
Definition teams.qh:241
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:50