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) \
269 MACRO_BEGIN \
270 if (NET_##id##_istemp) \
271 WriteByte(to, SVC_TEMPENTITY); \
272 WriteByte(to, NET_##id.m_id); \
273 bool _net_valid = false; \
274 net_guard_marker(to, _net_valid); \
275 MACRO_END
276#endif
277
278// serialization: new style
279
281#ifdef SVQC
282 #define stream_reading(stream) false
283 #define stream_writing(stream) true
284#else
285 #define stream_reading(stream) true
286 #define stream_writing(stream) false
287#endif
288
289// serialization: old
290
291#define ReadRegistered(r) \
292 REGISTRY_GET(r, (REGISTRY_COUNT(r) >= 255 ? ReadShort() : ReadByte()))
293#define WriteRegistered(r, to, it) \
294 (REGISTRY_COUNT(r) >= 255 ? WriteShort(to, it.m_id) : WriteByte(to, it.m_id))
295
296#ifdef CSQC
297 int ReadByte();
298 void WriteByte(int to, int b)
299 {
300 assert(to == MSG_C2S);
301 string s = string_null;
302 yenc_single(b, s);
303 string tmp = strcat(g_buf, s);
304 strcpy(g_buf, tmp);
305 }
306 void WriteShort(int to, int b)
307 {
308 WriteByte(to, (b >> 8) & 0xFF);
309 WriteByte(to, b & 0xFF);
310 }
311#else
312 int ReadByte()
313 {
314 int ret = -1;
315 ydec_single(g_buf, ret);
316 return ret;
317 }
318 int ReadShort()
319 {
320 return (ReadByte() << 8) | ReadByte();
321 }
322 void WriteByte(int to, int b);
323#endif
324
326#define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
327#define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
328
329#ifdef CSQC
332 {
333 int f = ReadShort();
334 if (f == 0)
335 return NULL;
336 return findfloat(NULL, entnum, f);
337 }
339 {
340 int v = ReadShort() << 8; // note: this is signed
341 v += ReadByte(); // note: this is unsigned
342 return v;
343 }
344 #define ReadInt48_t() vec2(ReadInt24_t(), ReadInt24_t())
345 #define ReadInt72_t() vec3(ReadInt24_t(), ReadInt24_t(), ReadInt24_t())
346
347 noref int _ReadSByte;
348 #define ReadSByte() (_ReadSByte = ReadByte(), (_ReadSByte & BIT(7) ? -128 : 0) + (_ReadSByte & BITS(7)))
349 #define ReadFloat() ReadCoord()
350 #define ReadVector() vec3(ReadFloat(), ReadFloat(), ReadFloat())
351 #define ReadVector2D() vec2(ReadFloat(), ReadFloat())
352 #define ReadAngleVector() vec3(ReadAngle(), ReadAngle(), ReadAngle())
353 #define ReadAngleVector2D() vec2(ReadAngle(), ReadAngle())
354
355 int Readbits(int num)
356 {
357 if (num > 16) return ReadInt24_t();
358 if (num > 8) return ReadShort();
359 return ReadByte();
360 }
361
363 {
364 float dt = ReadByte();
365 // map from range...PPROXPASTTIME_MAX / 256
366 dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt));
367
368 return servertime - dt;
369 }
370#else
371 void WriteInt24_t(float dst, float val)
372 {
373 float v;
374 WriteShort(dst, (v = floor(val >> 8)));
375 WriteByte(dst, val - (v << 8)); // 0..255
376 }
377 void WriteInt48_t(float dst, vector val)
378 {
379 WriteInt24_t(dst, val.x);
380 WriteInt24_t(dst, val.y);
381 }
382 void WriteInt72_t(float dst, vector val)
383 {
384 WriteInt24_t(dst, val.x);
385 WriteInt24_t(dst, val.y);
386 WriteInt24_t(dst, val.z);
387 }
388
389 #define WriteFloat(to, f) \
390 WriteCoord(to, f)
391 #define WriteVector(to, v) MACRO_BEGIN \
392 WriteFloat(to, v.x); \
393 WriteFloat(to, v.y); \
394 WriteFloat(to, v.z); \
395 MACRO_END
396 #define WriteVector2D(to, v) MACRO_BEGIN \
397 WriteFloat(to, v.x); \
398 WriteFloat(to, v.y); \
399 MACRO_END
400 #define WriteAngleVector(to, v) MACRO_BEGIN \
401 WriteAngle(to, v.x); \
402 WriteAngle(to, v.y); \
403 WriteAngle(to, v.z); \
404 MACRO_END
405 #define WriteAngleVector2D(to, v) MACRO_BEGIN \
406 WriteAngle(to, v.x); \
407 WriteAngle(to, v.y); \
408 MACRO_END
409
410 void Writebits(float dst, float val, int num)
411 {
412 if (num > 16) { WriteInt24_t(dst, val); return; }
413 if (num > 8) { WriteShort(dst, val); return; }
414 WriteByte(dst, val);
415 }
416
417 // this will use the value:
418 // 128
419 // accuracy near zero is APPROXPASTTIME_MAX/(256*255)
420 // accuracy at x is 1/derivative, i.e.
421 // APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536
422 void WriteApproxPastTime(float dst, float t)
423 {
424 float dt = time - t;
425 // warning: this is approximate; do not resend when you don't have to!
426 // be careful with sendflags here!
427 // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75
428
429 // map to range...
430 dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt));
431
432 // round...
433 dt = rint(bound(0, dt, 255));
434
435 WriteByte(dst, dt);
436 }
437
438 // allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example)
439 #define WRITESPECTATABLE_MSG_ONE(to, statement) MACRO_BEGIN \
440 entity prev = msg_entity; \
441 entity dst = to; \
442 FOREACH_CLIENT(IS_REAL_CLIENT(it), \
443 { \
444 if (it == dst || (it.classname == STR_SPECTATOR && it.enemy == dst)) \
445 { \
446 msg_entity = it; \
447 LAMBDA(statement); \
448 } \
449 }); \
450 msg_entity = prev; \
451 MACRO_END
452#endif
453
454#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: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:280
int SendFlags
Definition net.qh:159
noref int _ReadSByte
Definition net.qh:347
void WriteByte(int to, int b)
Definition net.qh:298
void SetCustomizer(entity e, bool(entity this, entity client) customizer, void(entity this) uncustomizer)
Definition net.qh:204
float servertime
Definition net.qh:330
const int MSG_ENTITY
Definition net.qh:156
float ReadApproxPastTime()
Definition net.qh:362
int ReadInt24_t()
Definition net.qh:338
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:326
string _net_prevmsgstr
Definition net.qh:30
void UncustomizeEntitiesRun()
Definition net.qh:213
entity ReadCSQCEntity()
Definition net.qh:331
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:306
int ReadByte()
const float APPROXPASTTIME_ACCURACY_REQUIREMENT
Definition net.qh:325
int Readbits(int num)
Definition net.qh:355
#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