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