Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
net.qh
Go to the documentation of this file.
1#pragma once
2
3#include "registry.qh"
4#include "sort.qh"
5#include "yenc.qh"
6
7// netcode mismatch and not sure what caused it? developer_csqcentities 1
8
9.string netname;
10
11#ifdef GAMEQC
12
13.int m_id;
14.bool(entity this, entity sender, bool isNew) m_read;
15#define NET_HANDLE(id, param) bool \
16 Net_Handle_##id(entity this, entity sender, param)
17
18#define NET_GUARD(id) \
19 bool Net_Handle_##id##_guard(entity this, entity sender, bool isNew) \
20 { \
21 bool valid = false; \
22 net_guard_marker(to, valid); \
23 if (!valid) \
24 LOG_FATALF("Last message not fully parsed: %s", _net_prevmsgstr); \
25 _net_prevmsgstr = #id; \
26 return Net_Handle_##id(this, sender, isNew); \
27 }
28
29#ifdef CSQC
31 #define REGISTER_NET_TEMP(id) \
32 NET_HANDLE(id, bool); \
33 NET_GUARD(id); \
34 REGISTER(TempEntities, NET, id, m_id, new_pure(net_temp_packet)) \
35 { \
36 this.netname = #id; \
37 this.m_read = Net_Handle_##id##_guard; \
38 }
39#else
40 #define REGISTER_NET_TEMP(id) \
41 const bool NET_##id##_istemp = true; \
42 REGISTER(TempEntities, NET, id, m_id, new_pure(net_temp_packet)) \
43 { \
44 this.netname = #id; \
45 }
46#endif
47
48// k9er: why is this needed? this is the engine's job
49#ifdef CSQC
50 #define net_guard_marker(stream, this) MACRO_BEGIN \
51 if (NDEBUG) \
52 this = true; \
53 else \
54 { \
55 int _de = ReadByte(); \
56 int _ad = ReadByte(); \
57 int _be = ReadByte(); \
58 int _ef = ReadByte(); \
59 this = (_de == 0xDE && _ad == 0xAD && _be == 0xBE && _ef == 0xEF); \
60 } \
61 MACRO_END
62#else
63 #define net_guard_marker(stream, this) MACRO_BEGIN \
64 if (NDEBUG) \
65 this = true; \
66 else \
67 { \
68 WriteByte(stream, 0xDE); \
69 WriteByte(stream, 0xAD); \
70 WriteByte(stream, 0xBE); \
71 WriteByte(stream, 0xEF); \
72 this = true; \
73 } \
74 MACRO_END
75#endif
76
77REGISTRY(TempEntities, BITS(8) - 80)
78REGISTER_REGISTRY(TempEntities)
79REGISTRY_SORT(TempEntities)
80REGISTRY_CHECK(TempEntities)
81
82REGISTRY_DEFINE_GET(TempEntities, NULL)
83STATIC_INIT(TempEntities_renumber)
84{
85 FOREACH(TempEntities, true, it.m_id = 80 + i);
86}
87
88
89
90#ifdef CSQC
91 #define REGISTER_NET_LINKED(id) \
92 ACCUMULATE NET_HANDLE(id, bool isNew) \
93 { \
94 this = __self; \
95 this.sourceLoc = __FILE__":"STR(__LINE__); \
96 if (!this) \
97 isNew = true; \
98 } \
99 NET_GUARD(id); \
100 REGISTER(LinkedEntities, NET, id, m_id, new_pure(net_linked_packet)) \
101 { \
102 this.netname = #id; \
103 this.m_read = Net_Handle_##id##_guard; \
104 }
105#else
106 #define REGISTER_NET_LINKED(id) \
107 const bool NET_##id##_istemp = false; \
108 REGISTER(LinkedEntities, NET, id, m_id, new_pure(net_linked_packet)) \
109 { \
110 this.netname = #id; \
111 }
112#endif
113
114REGISTRY(LinkedEntities, BITS(8) - 1)
115REGISTER_REGISTRY(LinkedEntities)
116REGISTRY_SORT(LinkedEntities)
117REGISTRY_CHECK(LinkedEntities)
118
119REGISTRY_DEFINE_GET(LinkedEntities, NULL)
120STATIC_INIT(LinkedEntities_renumber)
121{
122 FOREACH(LinkedEntities, true, it.m_id = 1 + i);
123}
124
125
126
127#ifdef SVQC
128 #define REGISTER_NET_C2S(id) \
129 NET_HANDLE(id, bool); \
130 REGISTER(C2S_Protocol, NET, id, m_id, new_pure(net_c2s_packet)) \
131 { \
132 this.netname = #id; \
133 this.m_read = Net_Handle_##id; \
134 }
135#else
136 #define REGISTER_NET_C2S(id) \
137 const bool NET_##id##_istemp = true; \
138 REGISTER(C2S_Protocol, NET, id, m_id, new_pure(net_c2s_packet)) \
139 { \
140 this.netname = #id; \
141 }
142#endif
143
144REGISTRY(C2S_Protocol, BITS(8) - 1)
145REGISTER_REGISTRY(C2S_Protocol)
146REGISTRY_SORT(C2S_Protocol)
147REGISTRY_CHECK(C2S_Protocol)
148
149REGISTRY_DEFINE_GET(C2S_Protocol, NULL)
150STATIC_INIT(C2S_Protocol_renumber)
151{
152 FOREACH(C2S_Protocol, true, it.m_id = i);
153}
154
155#ifdef SVQC
156 const int MSG_ENTITY = 5;
157
158 // If SendFlags is set to non-0 it is networked
160
166
167 void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
168 {
169 if (e.classname == "")
170 {
171 LOG_WARN("Net_LinkEntity called on an entity without a classname, assigning default");
172 e.classname = "net_linked";
173 }
174
175 if (e.modelindex == 0)
176 {
177 vector mi = e.mins;
178 vector ma = e.maxs;
179 _setmodel(e, "null");
180 setsize(e, mi, ma);
181 }
182
183 setSendEntity(e, sendfunc);
184 e.SendFlags = 0xFFFFFF;
185
186 if (!docull)
187 e.effects |= EF_NODEPTHTEST;
188
189 if (dt)
190 {
191 e.nextthink = time + dt;
193 }
194 }
195
197 {
199 }
200
201 .void(entity this) uncustomizeentityforclient;
203
204 void SetCustomizer(entity e, bool(entity this, entity client) customizer, void(entity this) uncustomizer)
205 {
206 setcefc(e, customizer);
207 e.uncustomizeentityforclient = uncustomizer;
208 e.uncustomizeentityforclient_set = !!uncustomizer;
209 if (uncustomizer)
211 }
212
214 {
215 IL_EACH(g_uncustomizables, it.uncustomizeentityforclient_set, it.uncustomizeentityforclient(it));
216 }
217
219
220 int ReadByte();
221
222 void Net_ClientCommand(entity sender, string command)
223 {
224 // command matches `c2s "(.+)"`
225 string buf = substring(command, argv_start_index(1) + 1, -2);
226 if (buf == "")
227 return;
228 STRING_ITERATOR_SET(g_buf, buf, 0);
229 for (int C2S; (C2S = ReadByte()) >= 0; )
230 {
231 entity reader = REGISTRY_GET(C2S_Protocol, C2S);
232 if (reader && reader.m_read && reader.m_read(NULL, sender, true))
233 continue;
234 LOG_SEVEREF("Net_ClientCommand() with malformed C2S=%d", C2S);
235 return;
236 }
237 --g_buf_i;
238 int expected = strlen(buf);
239 if (g_buf_i > expected) LOG_WARNF("Underflow: %d", g_buf_i - expected);
240 if (g_buf_i < expected) LOG_WARNF("Overrflow: %d", expected - g_buf_i);
241 }
242#else
243 const int MSG_C2S = 0;
244
245 #define Net_Accept(classname) MACRO_BEGIN \
246 if (!this) \
247 this = new(classname); \
248 MACRO_END
249 #define Net_Reject() MACRO_BEGIN \
250 if (this) \
251 delete(this); \
252 MACRO_END
253
254 string g_buf;
255 void Net_Flush()
256 {
257 if (g_buf == "")
258 return;
259 localcmd("\ncmd c2s \"", strreplace("$", "$$", g_buf), "\"\n");
260 strfree(g_buf);
261 }
262#endif
263
264#ifdef CSQC
265 #define WriteHeader(to, id) \
266 WriteByte(to, NET_##id.m_id)
267#else
268 #define WriteHeader(to, id) MACRO_BEGIN \
269 if (NET_##id##_istemp) \
270 WriteByte(to, SVC_TEMPENTITY); \
271 WriteByte(to, NET_##id.m_id); \
272 bool _net_valid = false; \
273 net_guard_marker(to, _net_valid); \
274 MACRO_END
275#endif
276
277// serialization: new style
278
280#ifdef SVQC
281 #define stream_reading(stream) false
282 #define stream_writing(stream) true
283#else
284 #define stream_reading(stream) true
285 #define stream_writing(stream) false
286#endif
287
288// serialization: old
289
290#define ReadRegistered(r) \
291 REGISTRY_GET(r, (REGISTRY_COUNT(r) >= 255 ? ReadShort() : ReadByte()))
292#define WriteRegistered(r, to, it) \
293 (REGISTRY_COUNT(r) >= 255 ? WriteShort(to, it.m_id) : WriteByte(to, it.m_id))
294
295#ifdef CSQC
296 int ReadByte();
297 void WriteByte(int to, int b)
298 {
299 assert(to == MSG_C2S);
300 string s = string_null;
301 yenc_single(b, s);
302 string tmp = strcat(g_buf, s);
303 strcpy(g_buf, tmp);
304 }
305 void WriteShort(int to, int b)
306 {
307 WriteByte(to, (b >> 8) & 0xFF);
308 WriteByte(to, b & 0xFF);
309 }
310#else
311 int ReadByte()
312 {
313 int ret = -1;
314 ydec_single(g_buf, ret);
315 return ret;
316 }
317 int ReadShort()
318 {
319 return (ReadByte() << 8) | ReadByte();
320 }
321 void WriteByte(int to, int b);
322#endif
323
325#define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
326#define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
327
328#ifdef CSQC
331 {
332 int f = ReadShort();
333 if (f == 0)
334 return NULL;
335 return findfloat(NULL, entnum, f);
336 }
338 {
339 int v = ReadShort() << 8; // note: this is signed
340 v += ReadByte(); // note: this is unsigned
341 return v;
342 }
343 #define ReadInt48_t() vec2(ReadInt24_t(), ReadInt24_t())
344 #define ReadInt72_t() vec3(ReadInt24_t(), ReadInt24_t(), ReadInt24_t())
345
346 noref int _ReadSByte;
347 #define ReadSByte() (_ReadSByte = ReadByte(), (_ReadSByte & BIT(7) ? -128 : 0) + (_ReadSByte & BITS(7)))
348 #define ReadFloat() ReadCoord()
349 #define ReadVector() vec3(ReadFloat(), ReadFloat(), ReadFloat())
350 #define ReadVector2D() vec2(ReadFloat(), ReadFloat())
351 #define ReadAngleVector() vec3(ReadAngle(), ReadAngle(), ReadAngle())
352 #define ReadAngleVector2D() vec2(ReadAngle(), ReadAngle())
353
354 int Readbits(int num)
355 {
356 if (num > 16) return ReadInt24_t();
357 if (num > 8) return ReadShort();
358 return ReadByte();
359 }
360
362 {
363 float dt = ReadByte();
364 // map from range...PPROXPASTTIME_MAX / 256
365 dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt));
366
367 return servertime - dt;
368 }
369#else
370 void WriteInt24_t(float dst, float val)
371 {
372 float v;
373 WriteShort(dst, (v = floor(val >> 8)));
374 WriteByte(dst, val - (v << 8)); // 0..255
375 }
376 void WriteInt48_t(float dst, vector val)
377 {
378 WriteInt24_t(dst, val.x);
379 WriteInt24_t(dst, val.y);
380 }
381 void WriteInt72_t(float dst, vector val)
382 {
383 WriteInt24_t(dst, val.x);
384 WriteInt24_t(dst, val.y);
385 WriteInt24_t(dst, val.z);
386 }
387
388 #define WriteFloat(to, f) \
389 WriteCoord(to, f)
390 #define WriteVector(to, v) MACRO_BEGIN \
391 WriteFloat(to, v.x); \
392 WriteFloat(to, v.y); \
393 WriteFloat(to, v.z); \
394 MACRO_END
395 #define WriteVector2D(to, v) MACRO_BEGIN \
396 WriteFloat(to, v.x); \
397 WriteFloat(to, v.y); \
398 MACRO_END
399 #define WriteAngleVector(to, v) MACRO_BEGIN \
400 WriteAngle(to, v.x); \
401 WriteAngle(to, v.y); \
402 WriteAngle(to, v.z); \
403 MACRO_END
404 #define WriteAngleVector2D(to, v) MACRO_BEGIN \
405 WriteAngle(to, v.x); \
406 WriteAngle(to, v.y); \
407 MACRO_END
408
409 void Writebits(float dst, float val, int num)
410 {
411 if (num > 16) { WriteInt24_t(dst, val); return; }
412 if (num > 8) { WriteShort(dst, val); return; }
413 WriteByte(dst, val);
414 }
415
416 // this will use the value:
417 // 128
418 // accuracy near zero is APPROXPASTTIME_MAX/(256*255)
419 // accuracy at x is 1/derivative, i.e.
420 // APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536
421 void WriteApproxPastTime(float dst, float t)
422 {
423 float dt = time - t;
424 // warning: this is approximate; do not resend when you don't have to!
425 // be careful with sendflags here!
426 // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75
427
428 // map to range...
429 dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt));
430
431 // round...
432 dt = rint(bound(0, dt, 255));
433
434 WriteByte(dst, dt);
435 }
436
437 // allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example)
438 #define WRITESPECTATABLE_MSG_ONE(to, statement) MACRO_BEGIN \
439 entity prev = msg_entity; \
440 entity dst = to; \
441 FOREACH_CLIENT(IS_REAL_CLIENT(it), \
442 { \
443 if (it == dst || (it.classname == STR_SPECTATOR && it.enemy == dst)) \
444 { \
445 msg_entity = it; \
446 LAMBDA(statement) \
447 } \
448 }); \
449 msg_entity = prev; \
450 MACRO_END
451#endif
452
453#endif // GAMEQC
#define BITS(n)
Definition bits.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
limitations: NULL cannot be present elements can only be present once a maximum of IL_MAX lists can e...
string netname
Definition powerups.qc:20
float time
float entnum
const float EF_NODEPTHTEST
void SUB_Remove(entity this)
Remove entity.
Definition defer.qh:12
#define strlen
#define argv_start_index
int m_id
Definition effect.qh:19
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define IL_NEW()
#define IL_EACH(this, cond, body)
#define STRING_ITERATOR(this, s, i)
Definition iter.qh:45
#define STRING_ITERATOR_SET(this, s, i)
Definition iter.qh:49
#define FOREACH(list, cond, body)
Definition iter.qh:19
#define USING(name, T)
Definition _all.inc:72
int SendFlags
Definition net.qh:159
int Stream
Definition net.qh:279
noref int _ReadSByte
Definition net.qh:346
void WriteByte(int to, int b)
Definition net.qh:297
void SetCustomizer(entity e, bool(entity this, entity client) customizer, void(entity this) uncustomizer)
Definition net.qh:204
float servertime
Definition net.qh:329
const int MSG_ENTITY
Definition net.qh:156
float ReadApproxPastTime()
Definition net.qh:361
int ReadInt24_t()
Definition net.qh:337
void Net_ClientCommand(entity sender, string command)
Definition net.qh:222
void Net_UnlinkEntity(entity e)
Definition net.qh:196
#define APPROXPASTTIME_MAX
Definition net.qh:325
string _net_prevmsgstr
Definition net.qh:30
void UncustomizeEntitiesRun()
Definition net.qh:213
entity ReadCSQCEntity()
Definition net.qh:330
IntrusiveList g_uncustomizables
Definition net.qh:161
float uncustomizeentityforclient_set
Definition net.qh:202
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
void WriteShort(int to, int b)
Definition net.qh:305
int ReadByte()
const float APPROXPASTTIME_ACCURACY_REQUIREMENT
Definition net.qh:324
int Readbits(int num)
Definition net.qh:354
#define LOG_WARNF(...)
Definition log.qh:59
#define LOG_SEVEREF(...)
Definition log.qh:55
#define assert(expr,...)
Definition log.qh:8
#define LOG_WARN(...)
Definition log.qh:58
void localcmd(string command,...)
float bound(float min, float value, float max)
string substring(string s, float start, float length)
float rint(float f)
float floor(float f)
entity findfloat(entity start,.float field, float match)
var void func_null()
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NULL
Definition post.qh:14
#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 REGISTRY_GET(id, i)
Definition registry.qh:62
#define setSendEntity(e, f)
#define setthink(e, f)
vector
Definition self.qh:96
#define setcefc(e, f)
#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
#define yenc_single(c, ret)
Definition yenc.qh:5
#define ydec_single(stringiter, ret)
Definition yenc.qh:31