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