]> git.xonotic.org Git - xonotic/darkplaces.git/blob - sv_ents_csqc.c
Merge pull request #19 from LegendaryGuard/patch-1: Improving Doxyfile
[xonotic/darkplaces.git] / sv_ents_csqc.c
1 #include "quakedef.h"
2 #include "protocol.h"
3
4 // NOTE: this only works with DP5 protocol and upwards. For lower protocols
5 // (including QUAKE), no packet loss handling for CSQC is done, which makes
6 // CSQC basically useless.
7 // Always use the DP5 protocol, or a higher one, when using CSQC entities.
8 static void EntityFrameCSQC_LostAllFrames(client_t *client)
9 {
10         prvm_prog_t *prog = SVVM_prog;
11         // mark ALL csqc entities as requiring a FULL resend!
12         // I know this is a bad workaround, but better than nothing.
13         int i, n;
14         prvm_edict_t *ed;
15
16         n = client->csqcnumedicts;
17         for(i = 0; i < n; ++i)
18         {
19                 if(client->csqcentityscope[i] & SCOPE_EXISTED_ONCE)
20                 {
21                         ed = prog->edicts + i;
22                         client->csqcentitysendflags[i] |= 0xFFFFFF;  // FULL RESEND. We can't clear SCOPE_ASSUMED_EXISTING yet as this would cancel removes on a rejected send attempt.
23                         if (!PRVM_serveredictfunction(ed, SendEntity))  // If it was ever sent to that client as a CSQC entity...
24                                 client->csqcentityscope[i] |= SCOPE_ASSUMED_EXISTING;  // FORCE REMOVE.
25                 }
26         }
27 }
28 void EntityFrameCSQC_LostFrame(client_t *client, int framenum)
29 {
30         // marks a frame as lost
31         int i, j;
32         qbool valid;
33         int ringfirst, ringlast;
34         static int recoversendflags[MAX_EDICTS]; // client only
35         csqcentityframedb_t *d;
36
37         if(client->csqcentityframe_lastreset < 0)
38                 return;
39         if(framenum < client->csqcentityframe_lastreset)
40                 return; // no action required, as we resent that data anyway
41
42         // is our frame out of history?
43         ringfirst = client->csqcentityframehistory_next; // oldest entry
44         ringlast = (ringfirst + NUM_CSQCENTITYDB_FRAMES - 1) % NUM_CSQCENTITYDB_FRAMES; // most recently added entry
45
46         valid = false;
47         
48         for(j = 0; j < NUM_CSQCENTITYDB_FRAMES; ++j)
49         {
50                 d = &client->csqcentityframehistory[(ringfirst + j) % NUM_CSQCENTITYDB_FRAMES];
51                 if(d->framenum < 0)
52                         continue;
53                 if(d->framenum == framenum)
54                         break;
55                 else if(d->framenum < framenum)
56                         valid = true;
57         }
58         if(j == NUM_CSQCENTITYDB_FRAMES)
59         {
60                 if(valid) // got beaten, i.e. there is a frame < framenum
61                 {
62                         // a non-csqc frame got lost... great
63                         return;
64                 }
65                 else
66                 {
67                         // a too old frame got lost... sorry, cannot handle this
68                         Con_DPrintf("CSQC entity DB: lost a frame too early to do any handling (resending ALL)...\n");
69                         Con_DPrintf("Lost frame = %d\n", framenum);
70                         Con_DPrintf("Entity DB = %d to %d\n", client->csqcentityframehistory[ringfirst].framenum, client->csqcentityframehistory[ringlast].framenum);
71                         EntityFrameCSQC_LostAllFrames(client);
72                         client->csqcentityframe_lastreset = -1;
73                 }
74                 return;
75         }
76
77         // so j is the frame that got lost
78         // ringlast is the frame that we have to go to
79         ringfirst = (ringfirst + j) % NUM_CSQCENTITYDB_FRAMES;
80         if(ringlast < ringfirst)
81                 ringlast += NUM_CSQCENTITYDB_FRAMES;
82         
83         memset(recoversendflags, 0, sizeof(recoversendflags));
84
85         for(j = ringfirst; j <= ringlast; ++j)
86         {
87                 d = &client->csqcentityframehistory[j % NUM_CSQCENTITYDB_FRAMES];
88                 if(d->framenum < 0)
89                 {
90                         // deleted frame
91                 }
92                 else if(d->framenum < framenum)
93                 {
94                         // a frame in the past... should never happen
95                         Con_Printf("CSQC entity DB encountered a frame from the past when recovering from PL...?\n");
96                 }
97                 else if(d->framenum == framenum)
98                 {
99                         // handling the actually lost frame now
100                         for(i = 0; i < d->num; ++i)
101                         {
102                                 int sf = d->sendflags[i];
103                                 int ent = d->entno[i];
104                                 if(sf < 0) // remove
105                                         recoversendflags[ent] |= -1; // all bits, including sign
106                                 else if(sf > 0)
107                                         recoversendflags[ent] |= sf;
108                         }
109                 }
110                 else
111                 {
112                         // handling the frames that followed it now
113                         for(i = 0; i < d->num; ++i)
114                         {
115                                 int sf = d->sendflags[i];
116                                 int ent = d->entno[i];
117                                 if(sf < 0) // remove
118                                 {
119                                         recoversendflags[ent] = 0; // no need to update, we got a more recent remove (and will fix it THEN)
120                                         break; // no flags left to remove...
121                                 }
122                                 else if(sf > 0)
123                                         recoversendflags[ent] &= ~sf; // no need to update these bits, we already got them later
124                         }
125                 }
126         }
127
128         for(i = 0; i < client->csqcnumedicts; ++i)
129         {
130                 if(recoversendflags[i] < 0)
131                         client->csqcentityscope[i] |= SCOPE_ASSUMED_EXISTING;  // FORCE REMOVE.
132                 else
133                         client->csqcentitysendflags[i] |= recoversendflags[i];
134         }
135 }
136 static int EntityFrameCSQC_AllocFrame(client_t *client, int framenum)
137 {
138         int ringfirst = client->csqcentityframehistory_next; // oldest entry
139         client->csqcentityframehistory_next += 1;
140         client->csqcentityframehistory_next %= NUM_CSQCENTITYDB_FRAMES;
141         client->csqcentityframehistory[ringfirst].framenum = framenum;
142         client->csqcentityframehistory[ringfirst].num = 0;
143         return ringfirst;
144 }
145 static void EntityFrameCSQC_DeallocFrame(client_t *client, int framenum)
146 {
147         int ringfirst = client->csqcentityframehistory_next; // oldest entry
148         int ringlast = (ringfirst + NUM_CSQCENTITYDB_FRAMES - 1) % NUM_CSQCENTITYDB_FRAMES; // most recently added entry
149         if(framenum == client->csqcentityframehistory[ringlast].framenum)
150         {
151                 client->csqcentityframehistory[ringlast].framenum = -1;
152                 client->csqcentityframehistory[ringlast].num = 0;
153                 client->csqcentityframehistory_next = ringlast;
154         }
155         else
156                 Con_Printf("Trying to dealloc the wrong entity frame\n");
157 }
158
159 //[515]: we use only one array per-client for SendEntity feature
160 // TODO: add some handling for entity send priorities, to better deal with huge
161 // amounts of csqc networked entities
162 qbool EntityFrameCSQC_WriteFrame (sizebuf_t *msg, int maxsize, int numnumbers, const unsigned short *numbers, int framenum)
163 {
164         prvm_prog_t *prog = SVVM_prog;
165         int num, number, end, sendflags;
166         qbool sectionstarted = false;
167         const unsigned short *n;
168         prvm_edict_t *ed;
169         client_t *client = svs.clients + sv.writeentitiestoclient_clientnumber;
170         int dbframe = EntityFrameCSQC_AllocFrame(client, framenum);
171         csqcentityframedb_t *db = &client->csqcentityframehistory[dbframe];
172
173         if(client->csqcentityframe_lastreset < 0)
174                 client->csqcentityframe_lastreset = framenum;
175
176         maxsize -= 24; // always fit in an empty svc_entities message (for packet loss detection!)
177
178         // make sure there is enough room to store the svc_csqcentities byte,
179         // the terminator (0x0000) and at least one entity update
180         if (msg->cursize + 32 >= maxsize)
181                 return false;
182
183         if (client->csqcnumedicts < prog->num_edicts)
184                 client->csqcnumedicts = prog->num_edicts;
185
186         number = 1;
187         for (num = 0, n = numbers;num < numnumbers;num++, n++)
188         {
189                 end = *n;
190                 for (;number < end;number++)
191                 {
192                         client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
193                         if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
194                                 client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
195                         client->csqcentitysendflags[number] = 0xFFFFFF;
196                 }
197                 ed = prog->edicts + number;
198                 client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
199                 if (PRVM_serveredictfunction(ed, SendEntity))
200                         client->csqcentityscope[number] |= SCOPE_WANTUPDATE;
201                 else
202                 {
203                         if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
204                                 client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
205                         client->csqcentitysendflags[number] = 0xFFFFFF;
206                 }
207                 number++;
208         }
209         end = client->csqcnumedicts;
210         for (;number < end;number++)
211         {
212                 client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
213                 if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
214                         client->csqcentityscope[number] |= SCOPE_WANTREMOVE;
215                 client->csqcentitysendflags[number] = 0xFFFFFF;
216         }
217
218         // now try to emit the entity updates
219         // (FIXME: prioritize by distance?)
220         end = client->csqcnumedicts;
221         for (number = 1;number < end;number++)
222         {
223                 if (!(client->csqcentityscope[number] & SCOPE_WANTSEND))
224                         continue;
225                 if(db->num >= NUM_CSQCENTITIES_PER_FRAME)
226                         break;
227                 ed = prog->edicts + number;
228                 if (client->csqcentityscope[number] & SCOPE_WANTREMOVE)  // Also implies ASSUMED_EXISTING.
229                 {
230                         // A removal. SendFlags have no power here.
231                         // write a remove message
232                         // first write the message identifier if needed
233                         if(!sectionstarted)
234                         {
235                                 sectionstarted = 1;
236                                 MSG_WriteByte(msg, svc_csqcentities);
237                         }
238                         // write the remove message
239                         {
240                                 ENTITYSIZEPROFILING_START(msg, number, 0);
241                                 MSG_WriteShort(msg, (unsigned short)number | 0x8000);
242                                 client->csqcentityscope[number] &= ~(SCOPE_WANTSEND | SCOPE_ASSUMED_EXISTING);
243                                 client->csqcentitysendflags[number] = 0xFFFFFF; // resend completely if it becomes active again
244                                 db->entno[db->num] = number;
245                                 db->sendflags[db->num] = -1;
246                                 db->num += 1;
247                                 ENTITYSIZEPROFILING_END(msg, number, 0);
248                         }
249                         if (msg->cursize + 17 >= maxsize)
250                                 break;
251                 }
252                 else
253                 {
254                         // save the cursize value in case we overflow and have to rollback
255                         int oldcursize = msg->cursize;
256
257                         // An update.
258                         sendflags = client->csqcentitysendflags[number];
259                         // Nothing to send? FINE.
260                         if (!sendflags)
261                                 continue;
262                         // If it's a new entity, always assume sendflags 0xFFFFFF.
263                         if (!(client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING))
264                                 sendflags = 0xFFFFFF;
265
266                         // write an update
267                         if (PRVM_serveredictfunction(ed, SendEntity))
268                         {
269                                 if(!sectionstarted)
270                                         MSG_WriteByte(msg, svc_csqcentities);
271                                 {
272                                         int oldcursize2 = msg->cursize;
273                                         ENTITYSIZEPROFILING_START(msg, number, sendflags);
274                                         MSG_WriteShort(msg, number);
275                                         msg->allowoverflow = true;
276                                         PRVM_G_INT(OFS_PARM0) = sv.writeentitiestoclient_cliententitynumber;
277                                         PRVM_G_FLOAT(OFS_PARM1) = sendflags;
278                                         PRVM_serverglobaledict(self) = number;
279                                         prog->ExecuteProgram(prog, PRVM_serveredictfunction(ed, SendEntity), "Null SendEntity\n");
280                                         msg->allowoverflow = false;
281                                         if(!PRVM_G_FLOAT(OFS_RETURN))
282                                         {
283                                                 // Send rejected by CSQC. This means we want to remove it.
284                                                 // CSQC requests we remove this one.
285                                                 if (client->csqcentityscope[number] & SCOPE_ASSUMED_EXISTING)
286                                                 {
287                                                         msg->cursize = oldcursize2;
288                                                         msg->overflowed = false;
289                                                         MSG_WriteShort(msg, (unsigned short)number | 0x8000);
290                                                         client->csqcentityscope[number] &= ~(SCOPE_WANTSEND | SCOPE_ASSUMED_EXISTING);
291                                                         client->csqcentitysendflags[number] = 0;
292                                                         db->entno[db->num] = number;
293                                                         db->sendflags[db->num] = -1;
294                                                         db->num += 1;
295                                                         // and take note that we have begun the svc_csqcentities
296                                                         // section of the packet
297                                                         sectionstarted = 1;
298                                                         ENTITYSIZEPROFILING_END(msg, number, 0);
299                                                         if (msg->cursize + 17 >= maxsize)
300                                                                 break;
301                                                 }
302                                                 else
303                                                 {
304                                                         // Nothing to do. Just don't do it again.
305                                                         msg->cursize = oldcursize;
306                                                         msg->overflowed = false;
307                                                         client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
308                                                         client->csqcentitysendflags[number] = 0;
309                                                 }
310                                                 continue;
311                                         }
312                                         else if(PRVM_G_FLOAT(OFS_RETURN) && msg->cursize + 2 <= maxsize)
313                                         {
314                                                 // an update has been successfully written
315                                                 client->csqcentitysendflags[number] = 0;
316                                                 db->entno[db->num] = number;
317                                                 db->sendflags[db->num] = sendflags;
318                                                 db->num += 1;
319                                                 client->csqcentityscope[number] &= ~SCOPE_WANTSEND;
320                                                 client->csqcentityscope[number] |= SCOPE_EXISTED_ONCE | SCOPE_ASSUMED_EXISTING;
321                                                 // and take note that we have begun the svc_csqcentities
322                                                 // section of the packet
323                                                 sectionstarted = 1;
324                                                 ENTITYSIZEPROFILING_END(msg, number, sendflags);
325                                                 if (msg->cursize + 17 >= maxsize)
326                                                         break;
327                                                 continue;
328                                         }
329                                 }
330                         }
331                         // self.SendEntity returned false (or does not exist) or the
332                         // update was too big for this packet - rollback the buffer to its
333                         // state before the writes occurred, we'll try again next frame
334                         msg->cursize = oldcursize;
335                         msg->overflowed = false;
336                 }
337         }
338         if (sectionstarted)
339         {
340                 // write index 0 to end the update (0 is never used by real entities)
341                 MSG_WriteShort(msg, 0);
342         }
343
344         if(db->num == 0)
345                 // if no single ent got added, remove the frame from the DB again, to allow
346                 // for a larger history
347                 EntityFrameCSQC_DeallocFrame(client, framenum);
348         
349         return sectionstarted;
350 }