]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/lib/net.qh
Merge branch 'master' into terencehill/menu_hudskin_selector
[xonotic/xonotic-data.pk3dir.git] / qcsrc / lib / net.qh
1 #ifndef NET_H
2 #define NET_H
3
4 #ifdef SVQC
5         .int Version;  // deprecated, use SendFlags
6         .int SendFlags;
7         .bool(entity to, int sendflags) SendEntity;
8         /** return false to remove from the client */
9         .bool(entity this, entity to, int sendflags) SendEntity3;
10
11         bool SendEntity_self(entity to, int sendflags) { return self.SendEntity3(self, to, sendflags); }
12
13         void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
14         {
15                 if (e.classname == "") e.classname = "net_linked";
16
17                 if (e.model == "" || self.modelindex == 0)
18                 {
19                         vector mi = e.mins;
20                         vector ma = e.maxs;
21                         _setmodel(e, "null");
22                         setsize(e, mi, ma);
23                 }
24
25                 e.SendEntity = SendEntity_self;
26                 e.SendEntity3 = sendfunc;
27                 e.SendFlags = 0xFFFFFF;
28
29                 if (!docull) e.effects |= EF_NODEPTHTEST;
30
31                 if (dt)
32                 {
33                         e.nextthink = time + dt;
34                         e.think = SUB_Remove_self;
35                 }
36         }
37
38         void Net_UnlinkEntity(entity e)
39         {
40                 e.SendEntity = func_null;
41         }
42
43         .void() uncustomizeentityforclient;
44         .float uncustomizeentityforclient_set;
45
46         void SetCustomizer(entity e, float() customizer, void() uncustomizer)
47         {
48                 e.customizeentityforclient = customizer;
49                 e.uncustomizeentityforclient = uncustomizer;
50                 e.uncustomizeentityforclient_set = !!uncustomizer;
51         }
52
53         void UncustomizeEntitiesRun()
54         {
55                 for (entity e = NULL; (e = findfloat(e, uncustomizeentityforclient_set, 1)); )
56                         WITH(entity, self, e, e.uncustomizeentityforclient());
57         }
58
59 #endif
60
61 #include "registry.qh"
62 #include "sort.qh"
63
64 .string netname;
65 .int m_id;
66 .bool(entity this, bool isNew) m_read;
67
68 #ifdef CSQC
69         #define Net_Accept(classname) \
70                 do \
71                 { \
72                         if (!this)    this = new(classname); \
73                 } \
74                 while (0)
75         #define Net_Reject() \
76                 do \
77                 { \
78                         if (this)     remove(this); \
79                 } \
80                 while (0)
81         #define NET_HANDLE(id, param) \
82                 bool Net_Handle_##id(entity this, param)
83 #else
84         #define WriteHeader(to, id) \
85                 do \
86                 { \
87                         if (NET_##id##_istemp) WriteByte(to, SVC_TEMPENTITY); \
88                         WriteByte(to, NET_##id.m_id); \
89                 } \
90                 while (0)
91 #endif
92
93 #ifdef CSQC
94         #define REGISTER_NET_LINKED(id) \
95                 [[accumulate]] NET_HANDLE(id, bool isnew) \
96                 { \
97                         this = self; \
98                         this.sourceLocFile = __FILE__; \
99                         this.sourceLocLine = __LINE__; \
100                         if (!this) isnew = true; \
101                 } \
102                 REGISTER(LinkedEntities, NET, id, m_id, new(net_linked_packet)) \
103                 { \
104                         make_pure(this); \
105                         this.netname = #id; \
106                         this.m_read = Net_Handle_##id; \
107                 }
108 #else
109         #define REGISTER_NET_LINKED(id) \
110                 const bool NET_##id##_istemp = false; \
111                 REGISTER(LinkedEntities, NET, id, m_id, new(net_linked_packet)) \
112                 { \
113                         make_pure(this); \
114                         this.netname = #id; \
115                 }
116 #endif
117
118 REGISTRY(LinkedEntities, BITS(8) - 1)
119 #define LinkedEntities_from(i) _LinkedEntities_from(i, NULL)
120 REGISTER_REGISTRY(LinkedEntities)
121 REGISTRY_SORT(LinkedEntities)
122 REGISTRY_CHECK(LinkedEntities)
123 STATIC_INIT(RegisterLinkedEntities_renumber)
124 {
125         for (int i = 0; i < LinkedEntities_COUNT; ++i)
126                 LinkedEntities_from(i).m_id = 1 + i;
127 }
128
129 #ifdef CSQC
130         #define REGISTER_NET_TEMP(id) \
131                 NET_HANDLE(id, bool); \
132                 REGISTER(TempEntities, NET, id, m_id, new(net_temp_packet)) \
133                 { \
134                         make_pure(this); \
135                         this.netname = #id; \
136                         this.m_read = Net_Handle_##id; \
137                 }
138 #else
139         #define REGISTER_NET_TEMP(id) \
140                 const bool NET_##id##_istemp = true; \
141                 REGISTER(TempEntities, NET, id, m_id, new(net_temp_packet)) \
142                 { \
143                         make_pure(this); \
144                         this.netname = #id; \
145                 }
146 #endif
147
148 REGISTRY(TempEntities, BITS(8) - 80)
149 #define TempEntities_from(i) _TempEntities_from(i, NULL)
150 REGISTER_REGISTRY(TempEntities)
151 REGISTRY_SORT(TempEntities)
152 REGISTRY_CHECK(TempEntities)
153 STATIC_INIT(RegisterTempEntities_renumber)
154 {
155         for (int i = 0; i < TempEntities_COUNT; ++i)
156                 TempEntities_from(i).m_id = 80 + i;
157 }
158
159 #ifndef MENUQC
160         const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05;
161         #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
162         #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
163
164         #ifdef CSQC
165                 entity ReadCSQCEntity()
166                 {
167                         int f = ReadShort();
168                         if (f == 0) return world;
169                         return findfloat(world, entnum, f);
170                 }
171                 int ReadInt24_t()
172                 {
173                         int v = ReadShort() << 8; // note: this is signed
174                         v += ReadByte();          // note: this is unsigned
175                         return v;
176                 }
177                 vector ReadInt48_t()
178                 {
179                         vector v;
180                         v.x = ReadInt24_t();
181                         v.y = ReadInt24_t();
182                         v.z = 0;
183                         return v;
184                 }
185                 vector ReadInt72_t()
186                 {
187                         vector v;
188                         v.x = ReadInt24_t();
189                         v.y = ReadInt24_t();
190                         v.z = ReadInt24_t();
191                         return v;
192                 }
193
194                 int _ReadSByte;
195                 #define ReadSByte() (_ReadSByte = ReadByte(), (_ReadSByte & BIT(7) ? -128 : 0) + (_ReadSByte & BITS(7)))
196                 #define ReadFloat() ReadCoord()
197         vector ReadVector() { vector v; v.x = ReadFloat(); v.y = ReadFloat(); v.z = ReadFloat(); return v; }
198                 vector ReadVector2D() { vector v; v.x = ReadFloat(); v.y = ReadFloat(); v.z = 0; return v; }
199
200                 float ReadApproxPastTime()
201                 {
202                         float dt = ReadByte();
203
204                         // map from range...PPROXPASTTIME_MAX / 256
205                         dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt));
206
207                         return servertime - dt;
208                 }
209
210         #else
211                 const int MSG_ENTITY = 5;
212
213                 void WriteInt24_t(float dst, float val)
214                 {
215                         float v;
216                         WriteShort(dst, (v = floor(val >> 8)));
217                         WriteByte(dst, val - (v << 8));  // 0..255
218                 }
219                 void WriteInt48_t(float dst, vector val)
220                 {
221                         WriteInt24_t(dst, val.x);
222                         WriteInt24_t(dst, val.y);
223                 }
224                 void WriteInt72_t(float dst, vector val)
225                 {
226                         WriteInt24_t(dst, val.x);
227                         WriteInt24_t(dst, val.y);
228                         WriteInt24_t(dst, val.z);
229                 }
230
231         #define WriteFloat(to, f) WriteCoord(to, f)
232                 #define WriteVector(to, v) do { WriteFloat(to, v.x); WriteFloat(to, v.y); WriteFloat(to, v.z); } while (0)
233         #define WriteVector2D(to, v) do { WriteFloat(to, v.x); WriteFloat(to, v.y); } while (0)
234
235                 // this will use the value:
236                 //   128
237                 // accuracy near zero is APPROXPASTTIME_MAX/(256*255)
238                 // accuracy at x is 1/derivative, i.e.
239                 //   APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536
240                 void WriteApproxPastTime(float dst, float t)
241                 {
242                         float dt = time - t;
243
244                         // warning: this is approximate; do not resend when you don't have to!
245                         // be careful with sendflags here!
246                         // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75
247
248                         // map to range...
249                         dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt));
250
251                         // round...
252                         dt = rint(bound(0, dt, 255));
253
254                         WriteByte(dst, dt);
255                 }
256
257                 // allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example)
258                 #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname, statement) \
259                         entity varname; varname = msg_entity; \
260                         FOR_EACH_REALCLIENT(msg_entity) \
261                         if (msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) \
262                                 statement msg_entity = varname
263                 #define WRITESPECTATABLE_MSG_ONE(statement) \
264                         do \
265                         { \
266                                 WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement); \
267                         } \
268                         while (0)
269                 #define WRITESPECTATABLE(msg, statement) \
270                         if (msg == MSG_ONE) WRITESPECTATABLE_MSG_ONE(statement); \
271                         else \
272                                 statement float WRITESPECTATABLE_workaround = 0
273         #endif
274 #endif
275
276 #endif