diff options
Diffstat (limited to 'apps/plugins/sdl/progs/quake/sv_main.c')
-rw-r--r-- | apps/plugins/sdl/progs/quake/sv_main.c | 1209 |
1 files changed, 1209 insertions, 0 deletions
diff --git a/apps/plugins/sdl/progs/quake/sv_main.c b/apps/plugins/sdl/progs/quake/sv_main.c new file mode 100644 index 0000000000..5ea62f1efa --- /dev/null +++ b/apps/plugins/sdl/progs/quake/sv_main.c | |||
@@ -0,0 +1,1209 @@ | |||
1 | /* | ||
2 | Copyright (C) 1996-1997 Id Software, Inc. | ||
3 | |||
4 | This program is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public License | ||
6 | as published by the Free Software Foundation; either version 2 | ||
7 | of the License, or (at your option) any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
12 | |||
13 | See the GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program; if not, write to the Free Software | ||
17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | |||
19 | */ | ||
20 | // sv_main.c -- server main program | ||
21 | |||
22 | #include "quakedef.h" | ||
23 | |||
24 | server_t sv; | ||
25 | server_static_t svs; | ||
26 | |||
27 | char localmodels[MAX_MODELS][5]; // inline model names for precache | ||
28 | |||
29 | //============================================================================ | ||
30 | |||
31 | /* | ||
32 | =============== | ||
33 | SV_Init | ||
34 | =============== | ||
35 | */ | ||
36 | void SV_Init (void) | ||
37 | { | ||
38 | int i; | ||
39 | extern cvar_t sv_maxvelocity; | ||
40 | extern cvar_t sv_gravity; | ||
41 | extern cvar_t sv_nostep; | ||
42 | extern cvar_t sv_friction; | ||
43 | extern cvar_t sv_edgefriction; | ||
44 | extern cvar_t sv_stopspeed; | ||
45 | extern cvar_t sv_maxspeed; | ||
46 | extern cvar_t sv_accelerate; | ||
47 | extern cvar_t sv_idealpitchscale; | ||
48 | extern cvar_t sv_aim; | ||
49 | |||
50 | Cvar_RegisterVariable (&sv_maxvelocity); | ||
51 | Cvar_RegisterVariable (&sv_gravity); | ||
52 | Cvar_RegisterVariable (&sv_friction); | ||
53 | Cvar_RegisterVariable (&sv_edgefriction); | ||
54 | Cvar_RegisterVariable (&sv_stopspeed); | ||
55 | Cvar_RegisterVariable (&sv_maxspeed); | ||
56 | Cvar_RegisterVariable (&sv_accelerate); | ||
57 | Cvar_RegisterVariable (&sv_idealpitchscale); | ||
58 | Cvar_RegisterVariable (&sv_aim); | ||
59 | Cvar_RegisterVariable (&sv_nostep); | ||
60 | |||
61 | for (i=0 ; i<MAX_MODELS ; i++) | ||
62 | sprintf (localmodels[i], "*%i", i); | ||
63 | } | ||
64 | |||
65 | /* | ||
66 | ============================================================================= | ||
67 | |||
68 | EVENT MESSAGES | ||
69 | |||
70 | ============================================================================= | ||
71 | */ | ||
72 | |||
73 | /* | ||
74 | ================== | ||
75 | SV_StartParticle | ||
76 | |||
77 | Make sure the event gets sent to all clients | ||
78 | ================== | ||
79 | */ | ||
80 | void SV_StartParticle (vec3_t org, vec3_t dir, int color, int count) | ||
81 | { | ||
82 | int i, v; | ||
83 | |||
84 | if (sv.datagram.cursize > MAX_DATAGRAM-16) | ||
85 | return; | ||
86 | MSG_WriteByte (&sv.datagram, svc_particle); | ||
87 | MSG_WriteCoord (&sv.datagram, org[0]); | ||
88 | MSG_WriteCoord (&sv.datagram, org[1]); | ||
89 | MSG_WriteCoord (&sv.datagram, org[2]); | ||
90 | for (i=0 ; i<3 ; i++) | ||
91 | { | ||
92 | v = dir[i]*16; | ||
93 | if (v > 127) | ||
94 | v = 127; | ||
95 | else if (v < -128) | ||
96 | v = -128; | ||
97 | MSG_WriteChar (&sv.datagram, v); | ||
98 | } | ||
99 | MSG_WriteByte (&sv.datagram, count); | ||
100 | MSG_WriteByte (&sv.datagram, color); | ||
101 | } | ||
102 | |||
103 | /* | ||
104 | ================== | ||
105 | SV_StartSound | ||
106 | |||
107 | Each entity can have eight independant sound sources, like voice, | ||
108 | weapon, feet, etc. | ||
109 | |||
110 | Channel 0 is an auto-allocate channel, the others override anything | ||
111 | allready running on that entity/channel pair. | ||
112 | |||
113 | An attenuation of 0 will play full volume everywhere in the level. | ||
114 | Larger attenuations will drop off. (max 4 attenuation) | ||
115 | |||
116 | ================== | ||
117 | */ | ||
118 | void SV_StartSound (edict_t *entity, int channel, char *sample, int volume, | ||
119 | float attenuation) | ||
120 | { | ||
121 | int sound_num; | ||
122 | int field_mask; | ||
123 | int i; | ||
124 | int ent; | ||
125 | |||
126 | if (volume < 0 || volume > 255) | ||
127 | Sys_Error ("SV_StartSound: volume = %i", volume); | ||
128 | |||
129 | if (attenuation < 0 || attenuation > 4) | ||
130 | Sys_Error ("SV_StartSound: attenuation = %f", attenuation); | ||
131 | |||
132 | if (channel < 0 || channel > 7) | ||
133 | Sys_Error ("SV_StartSound: channel = %i", channel); | ||
134 | |||
135 | if (sv.datagram.cursize > MAX_DATAGRAM-16) | ||
136 | return; | ||
137 | |||
138 | // find precache number for sound | ||
139 | for (sound_num=1 ; sound_num<MAX_SOUNDS | ||
140 | && sv.sound_precache[sound_num] ; sound_num++) | ||
141 | if (!strcmp(sample, sv.sound_precache[sound_num])) | ||
142 | break; | ||
143 | |||
144 | if ( sound_num == MAX_SOUNDS || !sv.sound_precache[sound_num] ) | ||
145 | { | ||
146 | Con_Printf ("SV_StartSound: %s not precacheed\n", sample); | ||
147 | return; | ||
148 | } | ||
149 | |||
150 | ent = NUM_FOR_EDICT(entity); | ||
151 | |||
152 | channel = (ent<<3) | channel; | ||
153 | |||
154 | field_mask = 0; | ||
155 | if (volume != DEFAULT_SOUND_PACKET_VOLUME) | ||
156 | field_mask |= SND_VOLUME; | ||
157 | if (attenuation != DEFAULT_SOUND_PACKET_ATTENUATION) | ||
158 | field_mask |= SND_ATTENUATION; | ||
159 | |||
160 | // directed messages go only to the entity the are targeted on | ||
161 | MSG_WriteByte (&sv.datagram, svc_sound); | ||
162 | MSG_WriteByte (&sv.datagram, field_mask); | ||
163 | if (field_mask & SND_VOLUME) | ||
164 | MSG_WriteByte (&sv.datagram, volume); | ||
165 | if (field_mask & SND_ATTENUATION) | ||
166 | MSG_WriteByte (&sv.datagram, attenuation*64); | ||
167 | MSG_WriteShort (&sv.datagram, channel); | ||
168 | MSG_WriteByte (&sv.datagram, sound_num); | ||
169 | for (i=0 ; i<3 ; i++) | ||
170 | MSG_WriteCoord (&sv.datagram, entity->v.origin[i]+0.5*(entity->v.mins[i]+entity->v.maxs[i])); | ||
171 | } | ||
172 | |||
173 | /* | ||
174 | ============================================================================== | ||
175 | |||
176 | CLIENT SPAWNING | ||
177 | |||
178 | ============================================================================== | ||
179 | */ | ||
180 | |||
181 | /* | ||
182 | ================ | ||
183 | SV_SendServerinfo | ||
184 | |||
185 | Sends the first message from the server to a connected client. | ||
186 | This will be sent on the initial connection and upon each server load. | ||
187 | ================ | ||
188 | */ | ||
189 | void SV_SendServerinfo (client_t *client) | ||
190 | { | ||
191 | char **s; | ||
192 | char message[2048]; | ||
193 | |||
194 | MSG_WriteByte (&client->message, svc_print); | ||
195 | sprintf (message, "%c\nVERSION %4.2f SERVER (%i CRC)", 2, VERSION, pr_crc); | ||
196 | MSG_WriteString (&client->message,message); | ||
197 | |||
198 | MSG_WriteByte (&client->message, svc_serverinfo); | ||
199 | MSG_WriteLong (&client->message, PROTOCOL_VERSION); | ||
200 | MSG_WriteByte (&client->message, svs.maxclients); | ||
201 | |||
202 | if (!coop.value && deathmatch.value) | ||
203 | MSG_WriteByte (&client->message, GAME_DEATHMATCH); | ||
204 | else | ||
205 | MSG_WriteByte (&client->message, GAME_COOP); | ||
206 | |||
207 | sprintf (message, pr_strings+sv.edicts->v.message); | ||
208 | |||
209 | MSG_WriteString (&client->message,message); | ||
210 | |||
211 | for (s = sv.model_precache+1 ; *s ; s++) | ||
212 | MSG_WriteString (&client->message, *s); | ||
213 | MSG_WriteByte (&client->message, 0); | ||
214 | |||
215 | for (s = sv.sound_precache+1 ; *s ; s++) | ||
216 | MSG_WriteString (&client->message, *s); | ||
217 | MSG_WriteByte (&client->message, 0); | ||
218 | |||
219 | // send music | ||
220 | MSG_WriteByte (&client->message, svc_cdtrack); | ||
221 | MSG_WriteByte (&client->message, sv.edicts->v.sounds); | ||
222 | MSG_WriteByte (&client->message, sv.edicts->v.sounds); | ||
223 | |||
224 | // set view | ||
225 | MSG_WriteByte (&client->message, svc_setview); | ||
226 | MSG_WriteShort (&client->message, NUM_FOR_EDICT(client->edict)); | ||
227 | |||
228 | MSG_WriteByte (&client->message, svc_signonnum); | ||
229 | MSG_WriteByte (&client->message, 1); | ||
230 | |||
231 | client->sendsignon = true; | ||
232 | client->spawned = false; // need prespawn, spawn, etc | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | ================ | ||
237 | SV_ConnectClient | ||
238 | |||
239 | Initializes a client_t for a new net connection. This will only be called | ||
240 | once for a player each game, not once for each level change. | ||
241 | ================ | ||
242 | */ | ||
243 | void SV_ConnectClient (int clientnum) | ||
244 | { | ||
245 | edict_t *ent; | ||
246 | client_t *client; | ||
247 | int edictnum; | ||
248 | struct qsocket_s *netconnection; | ||
249 | int i; | ||
250 | float spawn_parms[NUM_SPAWN_PARMS]; | ||
251 | |||
252 | client = svs.clients + clientnum; | ||
253 | |||
254 | Con_DPrintf ("Client %s connected\n", client->netconnection->address); | ||
255 | |||
256 | edictnum = clientnum+1; | ||
257 | |||
258 | ent = EDICT_NUM(edictnum); | ||
259 | |||
260 | // set up the client_t | ||
261 | netconnection = client->netconnection; | ||
262 | |||
263 | if (sv.loadgame) | ||
264 | memcpy (spawn_parms, client->spawn_parms, sizeof(spawn_parms)); | ||
265 | memset (client, 0, sizeof(*client)); | ||
266 | client->netconnection = netconnection; | ||
267 | |||
268 | strcpy (client->name, "unconnected"); | ||
269 | client->active = true; | ||
270 | client->spawned = false; | ||
271 | client->edict = ent; | ||
272 | client->message.data = client->msgbuf; | ||
273 | client->message.maxsize = sizeof(client->msgbuf); | ||
274 | client->message.allowoverflow = true; // we can catch it | ||
275 | |||
276 | #ifdef IDGODS | ||
277 | client->privileged = IsID(&client->netconnection->addr); | ||
278 | #else | ||
279 | client->privileged = false; | ||
280 | #endif | ||
281 | |||
282 | if (sv.loadgame) | ||
283 | memcpy (client->spawn_parms, spawn_parms, sizeof(spawn_parms)); | ||
284 | else | ||
285 | { | ||
286 | // call the progs to get default spawn parms for the new client | ||
287 | PR_ExecuteProgram (pr_global_struct->SetNewParms); | ||
288 | for (i=0 ; i<NUM_SPAWN_PARMS ; i++) | ||
289 | client->spawn_parms[i] = (&pr_global_struct->parm1)[i]; | ||
290 | } | ||
291 | |||
292 | SV_SendServerinfo (client); | ||
293 | } | ||
294 | |||
295 | |||
296 | /* | ||
297 | =================== | ||
298 | SV_CheckForNewClients | ||
299 | |||
300 | =================== | ||
301 | */ | ||
302 | void SV_CheckForNewClients (void) | ||
303 | { | ||
304 | struct qsocket_s *ret; | ||
305 | int i; | ||
306 | |||
307 | // | ||
308 | // check for new connections | ||
309 | // | ||
310 | while (1) | ||
311 | { | ||
312 | ret = NET_CheckNewConnections (); | ||
313 | if (!ret) | ||
314 | break; | ||
315 | |||
316 | // | ||
317 | // init a new client structure | ||
318 | // | ||
319 | for (i=0 ; i<svs.maxclients ; i++) | ||
320 | if (!svs.clients[i].active) | ||
321 | break; | ||
322 | if (i == svs.maxclients) | ||
323 | Sys_Error ("Host_CheckForNewClients: no free clients"); | ||
324 | |||
325 | svs.clients[i].netconnection = ret; | ||
326 | SV_ConnectClient (i); | ||
327 | |||
328 | net_activeconnections++; | ||
329 | } | ||
330 | } | ||
331 | |||
332 | |||
333 | |||
334 | /* | ||
335 | =============================================================================== | ||
336 | |||
337 | FRAME UPDATES | ||
338 | |||
339 | =============================================================================== | ||
340 | */ | ||
341 | |||
342 | /* | ||
343 | ================== | ||
344 | SV_ClearDatagram | ||
345 | |||
346 | ================== | ||
347 | */ | ||
348 | void SV_ClearDatagram (void) | ||
349 | { | ||
350 | SZ_Clear (&sv.datagram); | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | ============================================================================= | ||
355 | |||
356 | The PVS must include a small area around the client to allow head bobbing | ||
357 | or other small motion on the client side. Otherwise, a bob might cause an | ||
358 | entity that should be visible to not show up, especially when the bob | ||
359 | crosses a waterline. | ||
360 | |||
361 | ============================================================================= | ||
362 | */ | ||
363 | |||
364 | int fatbytes; | ||
365 | byte fatpvs[MAX_MAP_LEAFS/8]; | ||
366 | |||
367 | void SV_AddToFatPVS (vec3_t org, mnode_t *node) | ||
368 | { | ||
369 | int i; | ||
370 | byte *pvs; | ||
371 | mplane_t *plane; | ||
372 | float d; | ||
373 | |||
374 | while (1) | ||
375 | { | ||
376 | // if this is a leaf, accumulate the pvs bits | ||
377 | if (node->contents < 0) | ||
378 | { | ||
379 | if (node->contents != CONTENTS_SOLID) | ||
380 | { | ||
381 | pvs = Mod_LeafPVS ( (mleaf_t *)node, sv.worldmodel); | ||
382 | for (i=0 ; i<fatbytes ; i++) | ||
383 | fatpvs[i] |= pvs[i]; | ||
384 | } | ||
385 | return; | ||
386 | } | ||
387 | |||
388 | plane = node->plane; | ||
389 | d = DotProduct (org, plane->normal) - plane->dist; | ||
390 | if (d > 8) | ||
391 | node = node->children[0]; | ||
392 | else if (d < -8) | ||
393 | node = node->children[1]; | ||
394 | else | ||
395 | { // go down both | ||
396 | SV_AddToFatPVS (org, node->children[0]); | ||
397 | node = node->children[1]; | ||
398 | } | ||
399 | } | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | ============= | ||
404 | SV_FatPVS | ||
405 | |||
406 | Calculates a PVS that is the inclusive or of all leafs within 8 pixels of the | ||
407 | given point. | ||
408 | ============= | ||
409 | */ | ||
410 | byte *SV_FatPVS (vec3_t org) | ||
411 | { | ||
412 | fatbytes = (sv.worldmodel->numleafs+31)>>3; | ||
413 | Q_memset (fatpvs, 0, fatbytes); | ||
414 | SV_AddToFatPVS (org, sv.worldmodel->nodes); | ||
415 | return fatpvs; | ||
416 | } | ||
417 | |||
418 | //============================================================================= | ||
419 | |||
420 | |||
421 | /* | ||
422 | ============= | ||
423 | SV_WriteEntitiesToClient | ||
424 | |||
425 | ============= | ||
426 | */ | ||
427 | void SV_WriteEntitiesToClient (edict_t *clent, sizebuf_t *msg) | ||
428 | { | ||
429 | int e, i; | ||
430 | int bits; | ||
431 | byte *pvs; | ||
432 | vec3_t org; | ||
433 | float miss; | ||
434 | edict_t *ent; | ||
435 | |||
436 | // find the client's PVS | ||
437 | VectorAdd (clent->v.origin, clent->v.view_ofs, org); | ||
438 | pvs = SV_FatPVS (org); | ||
439 | |||
440 | // send over all entities (excpet the client) that touch the pvs | ||
441 | ent = NEXT_EDICT(sv.edicts); | ||
442 | for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent)) | ||
443 | { | ||
444 | #ifdef QUAKE2 | ||
445 | // don't send if flagged for NODRAW and there are no lighting effects | ||
446 | if (ent->v.effects == EF_NODRAW) | ||
447 | continue; | ||
448 | #endif | ||
449 | |||
450 | // ignore if not touching a PV leaf | ||
451 | if (ent != clent) // clent is ALLWAYS sent | ||
452 | { | ||
453 | // ignore ents without visible models | ||
454 | if (!ent->v.modelindex || !pr_strings[ent->v.model]) | ||
455 | continue; | ||
456 | |||
457 | for (i=0 ; i < ent->num_leafs ; i++) | ||
458 | if (pvs[ent->leafnums[i] >> 3] & (1 << (ent->leafnums[i]&7) )) | ||
459 | break; | ||
460 | |||
461 | if (i == ent->num_leafs) | ||
462 | continue; // not visible | ||
463 | } | ||
464 | |||
465 | if (msg->maxsize - msg->cursize < 16) | ||
466 | { | ||
467 | Con_Printf ("packet overflow\n"); | ||
468 | return; | ||
469 | } | ||
470 | |||
471 | // send an update | ||
472 | bits = 0; | ||
473 | |||
474 | for (i=0 ; i<3 ; i++) | ||
475 | { | ||
476 | miss = ent->v.origin[i] - ent->baseline.origin[i]; | ||
477 | if ( miss < -0.1 || miss > 0.1 ) | ||
478 | bits |= U_ORIGIN1<<i; | ||
479 | } | ||
480 | |||
481 | if ( ent->v.angles[0] != ent->baseline.angles[0] ) | ||
482 | bits |= U_ANGLE1; | ||
483 | |||
484 | if ( ent->v.angles[1] != ent->baseline.angles[1] ) | ||
485 | bits |= U_ANGLE2; | ||
486 | |||
487 | if ( ent->v.angles[2] != ent->baseline.angles[2] ) | ||
488 | bits |= U_ANGLE3; | ||
489 | |||
490 | if (ent->v.movetype == MOVETYPE_STEP) | ||
491 | bits |= U_NOLERP; // don't mess up the step animation | ||
492 | |||
493 | if (ent->baseline.colormap != ent->v.colormap) | ||
494 | bits |= U_COLORMAP; | ||
495 | |||
496 | if (ent->baseline.skin != ent->v.skin) | ||
497 | bits |= U_SKIN; | ||
498 | |||
499 | if (ent->baseline.frame != ent->v.frame) | ||
500 | bits |= U_FRAME; | ||
501 | |||
502 | if (ent->baseline.effects != ent->v.effects) | ||
503 | bits |= U_EFFECTS; | ||
504 | |||
505 | if (ent->baseline.modelindex != ent->v.modelindex) | ||
506 | bits |= U_MODEL; | ||
507 | |||
508 | if (e >= 256) | ||
509 | bits |= U_LONGENTITY; | ||
510 | |||
511 | if (bits >= 256) | ||
512 | bits |= U_MOREBITS; | ||
513 | |||
514 | // | ||
515 | // write the message | ||
516 | // | ||
517 | MSG_WriteByte (msg,bits | U_SIGNAL); | ||
518 | |||
519 | if (bits & U_MOREBITS) | ||
520 | MSG_WriteByte (msg, bits>>8); | ||
521 | if (bits & U_LONGENTITY) | ||
522 | MSG_WriteShort (msg,e); | ||
523 | else | ||
524 | MSG_WriteByte (msg,e); | ||
525 | |||
526 | if (bits & U_MODEL) | ||
527 | MSG_WriteByte (msg, ent->v.modelindex); | ||
528 | if (bits & U_FRAME) | ||
529 | MSG_WriteByte (msg, ent->v.frame); | ||
530 | if (bits & U_COLORMAP) | ||
531 | MSG_WriteByte (msg, ent->v.colormap); | ||
532 | if (bits & U_SKIN) | ||
533 | MSG_WriteByte (msg, ent->v.skin); | ||
534 | if (bits & U_EFFECTS) | ||
535 | MSG_WriteByte (msg, ent->v.effects); | ||
536 | if (bits & U_ORIGIN1) | ||
537 | MSG_WriteCoord (msg, ent->v.origin[0]); | ||
538 | if (bits & U_ANGLE1) | ||
539 | MSG_WriteAngle(msg, ent->v.angles[0]); | ||
540 | if (bits & U_ORIGIN2) | ||
541 | MSG_WriteCoord (msg, ent->v.origin[1]); | ||
542 | if (bits & U_ANGLE2) | ||
543 | MSG_WriteAngle(msg, ent->v.angles[1]); | ||
544 | if (bits & U_ORIGIN3) | ||
545 | MSG_WriteCoord (msg, ent->v.origin[2]); | ||
546 | if (bits & U_ANGLE3) | ||
547 | MSG_WriteAngle(msg, ent->v.angles[2]); | ||
548 | } | ||
549 | } | ||
550 | |||
551 | /* | ||
552 | ============= | ||
553 | SV_CleanupEnts | ||
554 | |||
555 | ============= | ||
556 | */ | ||
557 | void SV_CleanupEnts (void) | ||
558 | { | ||
559 | int e; | ||
560 | edict_t *ent; | ||
561 | |||
562 | ent = NEXT_EDICT(sv.edicts); | ||
563 | for (e=1 ; e<sv.num_edicts ; e++, ent = NEXT_EDICT(ent)) | ||
564 | { | ||
565 | ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH; | ||
566 | } | ||
567 | |||
568 | } | ||
569 | |||
570 | /* | ||
571 | ================== | ||
572 | SV_WriteClientdataToMessage | ||
573 | |||
574 | ================== | ||
575 | */ | ||
576 | void SV_WriteClientdataToMessage (edict_t *ent, sizebuf_t *msg) | ||
577 | { | ||
578 | int bits; | ||
579 | int i; | ||
580 | edict_t *other; | ||
581 | int items; | ||
582 | #ifndef QUAKE2 | ||
583 | eval_t *val; | ||
584 | #endif | ||
585 | |||
586 | // | ||
587 | // send a damage message | ||
588 | // | ||
589 | if (ent->v.dmg_take || ent->v.dmg_save) | ||
590 | { | ||
591 | other = PROG_TO_EDICT(ent->v.dmg_inflictor); | ||
592 | MSG_WriteByte (msg, svc_damage); | ||
593 | MSG_WriteByte (msg, ent->v.dmg_save); | ||
594 | MSG_WriteByte (msg, ent->v.dmg_take); | ||
595 | for (i=0 ; i<3 ; i++) | ||
596 | MSG_WriteCoord (msg, other->v.origin[i] + 0.5*(other->v.mins[i] + other->v.maxs[i])); | ||
597 | |||
598 | ent->v.dmg_take = 0; | ||
599 | ent->v.dmg_save = 0; | ||
600 | } | ||
601 | |||
602 | // | ||
603 | // send the current viewpos offset from the view entity | ||
604 | // | ||
605 | SV_SetIdealPitch (); // how much to look up / down ideally | ||
606 | |||
607 | // a fixangle might get lost in a dropped packet. Oh well. | ||
608 | if ( ent->v.fixangle ) | ||
609 | { | ||
610 | MSG_WriteByte (msg, svc_setangle); | ||
611 | for (i=0 ; i < 3 ; i++) | ||
612 | MSG_WriteAngle (msg, ent->v.angles[i] ); | ||
613 | ent->v.fixangle = 0; | ||
614 | } | ||
615 | |||
616 | bits = 0; | ||
617 | |||
618 | if (ent->v.view_ofs[2] != DEFAULT_VIEWHEIGHT) | ||
619 | bits |= SU_VIEWHEIGHT; | ||
620 | |||
621 | if (ent->v.idealpitch) | ||
622 | bits |= SU_IDEALPITCH; | ||
623 | |||
624 | // stuff the sigil bits into the high bits of items for sbar, or else | ||
625 | // mix in items2 | ||
626 | #ifdef QUAKE2 | ||
627 | items = (int)ent->v.items | ((int)ent->v.items2 << 23); | ||
628 | #else | ||
629 | val = GetEdictFieldValue(ent, "items2"); | ||
630 | |||
631 | if (val) | ||
632 | items = (int)ent->v.items | ((int)val->_float << 23); | ||
633 | else | ||
634 | items = (int)ent->v.items | ((int)pr_global_struct->serverflags << 28); | ||
635 | #endif | ||
636 | |||
637 | bits |= SU_ITEMS; | ||
638 | |||
639 | if ( (int)ent->v.flags & FL_ONGROUND) | ||
640 | bits |= SU_ONGROUND; | ||
641 | |||
642 | if ( ent->v.waterlevel >= 2) | ||
643 | bits |= SU_INWATER; | ||
644 | |||
645 | for (i=0 ; i<3 ; i++) | ||
646 | { | ||
647 | if (ent->v.punchangle[i]) | ||
648 | bits |= (SU_PUNCH1<<i); | ||
649 | if (ent->v.velocity[i]) | ||
650 | bits |= (SU_VELOCITY1<<i); | ||
651 | } | ||
652 | |||
653 | if (ent->v.weaponframe) | ||
654 | bits |= SU_WEAPONFRAME; | ||
655 | |||
656 | if (ent->v.armorvalue) | ||
657 | bits |= SU_ARMOR; | ||
658 | |||
659 | // if (ent->v.weapon) | ||
660 | bits |= SU_WEAPON; | ||
661 | |||
662 | // send the data | ||
663 | |||
664 | MSG_WriteByte (msg, svc_clientdata); | ||
665 | MSG_WriteShort (msg, bits); | ||
666 | |||
667 | if (bits & SU_VIEWHEIGHT) | ||
668 | MSG_WriteChar (msg, ent->v.view_ofs[2]); | ||
669 | |||
670 | if (bits & SU_IDEALPITCH) | ||
671 | MSG_WriteChar (msg, ent->v.idealpitch); | ||
672 | |||
673 | for (i=0 ; i<3 ; i++) | ||
674 | { | ||
675 | if (bits & (SU_PUNCH1<<i)) | ||
676 | MSG_WriteChar (msg, ent->v.punchangle[i]); | ||
677 | if (bits & (SU_VELOCITY1<<i)) | ||
678 | MSG_WriteChar (msg, ent->v.velocity[i]/16); | ||
679 | } | ||
680 | |||
681 | // [always sent] if (bits & SU_ITEMS) | ||
682 | MSG_WriteLong (msg, items); | ||
683 | |||
684 | if (bits & SU_WEAPONFRAME) | ||
685 | MSG_WriteByte (msg, ent->v.weaponframe); | ||
686 | if (bits & SU_ARMOR) | ||
687 | MSG_WriteByte (msg, ent->v.armorvalue); | ||
688 | if (bits & SU_WEAPON) | ||
689 | MSG_WriteByte (msg, SV_ModelIndex(pr_strings+ent->v.weaponmodel)); | ||
690 | |||
691 | MSG_WriteShort (msg, ent->v.health); | ||
692 | MSG_WriteByte (msg, ent->v.currentammo); | ||
693 | MSG_WriteByte (msg, ent->v.ammo_shells); | ||
694 | MSG_WriteByte (msg, ent->v.ammo_nails); | ||
695 | MSG_WriteByte (msg, ent->v.ammo_rockets); | ||
696 | MSG_WriteByte (msg, ent->v.ammo_cells); | ||
697 | |||
698 | if (standard_quake) | ||
699 | { | ||
700 | MSG_WriteByte (msg, ent->v.weapon); | ||
701 | } | ||
702 | else | ||
703 | { | ||
704 | for(i=0;i<32;i++) | ||
705 | { | ||
706 | if ( ((int)ent->v.weapon) & (1<<i) ) | ||
707 | { | ||
708 | MSG_WriteByte (msg, i); | ||
709 | break; | ||
710 | } | ||
711 | } | ||
712 | } | ||
713 | } | ||
714 | |||
715 | /* | ||
716 | ======================= | ||
717 | SV_SendClientDatagram | ||
718 | ======================= | ||
719 | */ | ||
720 | qboolean SV_SendClientDatagram (client_t *client) | ||
721 | { | ||
722 | byte buf[MAX_DATAGRAM]; | ||
723 | sizebuf_t msg; | ||
724 | |||
725 | msg.data = buf; | ||
726 | msg.maxsize = sizeof(buf); | ||
727 | msg.cursize = 0; | ||
728 | |||
729 | MSG_WriteByte (&msg, svc_time); | ||
730 | MSG_WriteFloat (&msg, sv.time); | ||
731 | |||
732 | // add the client specific data to the datagram | ||
733 | SV_WriteClientdataToMessage (client->edict, &msg); | ||
734 | |||
735 | SV_WriteEntitiesToClient (client->edict, &msg); | ||
736 | |||
737 | // copy the server datagram if there is space | ||
738 | if (msg.cursize + sv.datagram.cursize < msg.maxsize) | ||
739 | SZ_Write (&msg, sv.datagram.data, sv.datagram.cursize); | ||
740 | |||
741 | // send the datagram | ||
742 | if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) | ||
743 | { | ||
744 | SV_DropClient (true);// if the message couldn't send, kick off | ||
745 | return false; | ||
746 | } | ||
747 | |||
748 | return true; | ||
749 | } | ||
750 | |||
751 | /* | ||
752 | ======================= | ||
753 | SV_UpdateToReliableMessages | ||
754 | ======================= | ||
755 | */ | ||
756 | void SV_UpdateToReliableMessages (void) | ||
757 | { | ||
758 | int i, j; | ||
759 | client_t *client; | ||
760 | |||
761 | // check for changes to be sent over the reliable streams | ||
762 | for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) | ||
763 | { | ||
764 | if (host_client->old_frags != host_client->edict->v.frags) | ||
765 | { | ||
766 | for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++) | ||
767 | { | ||
768 | if (!client->active) | ||
769 | continue; | ||
770 | MSG_WriteByte (&client->message, svc_updatefrags); | ||
771 | MSG_WriteByte (&client->message, i); | ||
772 | MSG_WriteShort (&client->message, host_client->edict->v.frags); | ||
773 | } | ||
774 | |||
775 | host_client->old_frags = host_client->edict->v.frags; | ||
776 | } | ||
777 | } | ||
778 | |||
779 | for (j=0, client = svs.clients ; j<svs.maxclients ; j++, client++) | ||
780 | { | ||
781 | if (!client->active) | ||
782 | continue; | ||
783 | SZ_Write (&client->message, sv.reliable_datagram.data, sv.reliable_datagram.cursize); | ||
784 | } | ||
785 | |||
786 | SZ_Clear (&sv.reliable_datagram); | ||
787 | } | ||
788 | |||
789 | |||
790 | /* | ||
791 | ======================= | ||
792 | SV_SendNop | ||
793 | |||
794 | Send a nop message without trashing or sending the accumulated client | ||
795 | message buffer | ||
796 | ======================= | ||
797 | */ | ||
798 | void SV_SendNop (client_t *client) | ||
799 | { | ||
800 | sizebuf_t msg; | ||
801 | byte buf[4]; | ||
802 | |||
803 | msg.data = buf; | ||
804 | msg.maxsize = sizeof(buf); | ||
805 | msg.cursize = 0; | ||
806 | |||
807 | MSG_WriteChar (&msg, svc_nop); | ||
808 | |||
809 | if (NET_SendUnreliableMessage (client->netconnection, &msg) == -1) | ||
810 | SV_DropClient (true); // if the message couldn't send, kick off | ||
811 | client->last_message = realtime; | ||
812 | } | ||
813 | |||
814 | /* | ||
815 | ======================= | ||
816 | SV_SendClientMessages | ||
817 | ======================= | ||
818 | */ | ||
819 | void SV_SendClientMessages (void) | ||
820 | { | ||
821 | int i; | ||
822 | |||
823 | // update frags, names, etc | ||
824 | SV_UpdateToReliableMessages (); | ||
825 | |||
826 | // build individual updates | ||
827 | for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) | ||
828 | { | ||
829 | if (!host_client->active) | ||
830 | continue; | ||
831 | |||
832 | if (host_client->spawned) | ||
833 | { | ||
834 | if (!SV_SendClientDatagram (host_client)) | ||
835 | continue; | ||
836 | } | ||
837 | else | ||
838 | { | ||
839 | // the player isn't totally in the game yet | ||
840 | // send small keepalive messages if too much time has passed | ||
841 | // send a full message when the next signon stage has been requested | ||
842 | // some other message data (name changes, etc) may accumulate | ||
843 | // between signon stages | ||
844 | if (!host_client->sendsignon) | ||
845 | { | ||
846 | if (realtime - host_client->last_message > 5) | ||
847 | SV_SendNop (host_client); | ||
848 | continue; // don't send out non-signon messages | ||
849 | } | ||
850 | } | ||
851 | |||
852 | // check for an overflowed message. Should only happen | ||
853 | // on a very fucked up connection that backs up a lot, then | ||
854 | // changes level | ||
855 | if (host_client->message.overflowed) | ||
856 | { | ||
857 | SV_DropClient (true); | ||
858 | host_client->message.overflowed = false; | ||
859 | continue; | ||
860 | } | ||
861 | |||
862 | if (host_client->message.cursize || host_client->dropasap) | ||
863 | { | ||
864 | if (!NET_CanSendMessage (host_client->netconnection)) | ||
865 | { | ||
866 | // I_Printf ("can't write\n"); | ||
867 | continue; | ||
868 | } | ||
869 | |||
870 | if (host_client->dropasap) | ||
871 | SV_DropClient (false); // went to another level | ||
872 | else | ||
873 | { | ||
874 | if (NET_SendMessage (host_client->netconnection | ||
875 | , &host_client->message) == -1) | ||
876 | SV_DropClient (true); // if the message couldn't send, kick off | ||
877 | SZ_Clear (&host_client->message); | ||
878 | host_client->last_message = realtime; | ||
879 | host_client->sendsignon = false; | ||
880 | } | ||
881 | } | ||
882 | } | ||
883 | |||
884 | |||
885 | // clear muzzle flashes | ||
886 | SV_CleanupEnts (); | ||
887 | } | ||
888 | |||
889 | |||
890 | /* | ||
891 | ============================================================================== | ||
892 | |||
893 | SERVER SPAWNING | ||
894 | |||
895 | ============================================================================== | ||
896 | */ | ||
897 | |||
898 | /* | ||
899 | ================ | ||
900 | SV_ModelIndex | ||
901 | |||
902 | ================ | ||
903 | */ | ||
904 | int SV_ModelIndex (char *name) | ||
905 | { | ||
906 | int i; | ||
907 | |||
908 | if (!name || !name[0]) | ||
909 | return 0; | ||
910 | |||
911 | for (i=0 ; i<MAX_MODELS && sv.model_precache[i] ; i++) | ||
912 | if (!strcmp(sv.model_precache[i], name)) | ||
913 | return i; | ||
914 | if (i==MAX_MODELS || !sv.model_precache[i]) | ||
915 | Sys_Error ("SV_ModelIndex: model %s not precached", name); | ||
916 | return i; | ||
917 | } | ||
918 | |||
919 | /* | ||
920 | ================ | ||
921 | SV_CreateBaseline | ||
922 | |||
923 | ================ | ||
924 | */ | ||
925 | void SV_CreateBaseline (void) | ||
926 | { | ||
927 | int i; | ||
928 | edict_t *svent; | ||
929 | int entnum; | ||
930 | |||
931 | for (entnum = 0; entnum < sv.num_edicts ; entnum++) | ||
932 | { | ||
933 | // get the current server version | ||
934 | svent = EDICT_NUM(entnum); | ||
935 | if (svent->free) | ||
936 | continue; | ||
937 | if (entnum > svs.maxclients && !svent->v.modelindex) | ||
938 | continue; | ||
939 | |||
940 | // | ||
941 | // create entity baseline | ||
942 | // | ||
943 | VectorCopy (svent->v.origin, svent->baseline.origin); | ||
944 | VectorCopy (svent->v.angles, svent->baseline.angles); | ||
945 | svent->baseline.frame = svent->v.frame; | ||
946 | svent->baseline.skin = svent->v.skin; | ||
947 | if (entnum > 0 && entnum <= svs.maxclients) | ||
948 | { | ||
949 | svent->baseline.colormap = entnum; | ||
950 | svent->baseline.modelindex = SV_ModelIndex("progs/player.mdl"); | ||
951 | } | ||
952 | else | ||
953 | { | ||
954 | svent->baseline.colormap = 0; | ||
955 | svent->baseline.modelindex = | ||
956 | SV_ModelIndex(pr_strings + svent->v.model); | ||
957 | } | ||
958 | |||
959 | // | ||
960 | // add to the message | ||
961 | // | ||
962 | MSG_WriteByte (&sv.signon,svc_spawnbaseline); | ||
963 | MSG_WriteShort (&sv.signon,entnum); | ||
964 | |||
965 | MSG_WriteByte (&sv.signon, svent->baseline.modelindex); | ||
966 | MSG_WriteByte (&sv.signon, svent->baseline.frame); | ||
967 | MSG_WriteByte (&sv.signon, svent->baseline.colormap); | ||
968 | MSG_WriteByte (&sv.signon, svent->baseline.skin); | ||
969 | for (i=0 ; i<3 ; i++) | ||
970 | { | ||
971 | MSG_WriteCoord(&sv.signon, svent->baseline.origin[i]); | ||
972 | MSG_WriteAngle(&sv.signon, svent->baseline.angles[i]); | ||
973 | } | ||
974 | } | ||
975 | } | ||
976 | |||
977 | |||
978 | /* | ||
979 | ================ | ||
980 | SV_SendReconnect | ||
981 | |||
982 | Tell all the clients that the server is changing levels | ||
983 | ================ | ||
984 | */ | ||
985 | void SV_SendReconnect (void) | ||
986 | { | ||
987 | char data[128]; | ||
988 | sizebuf_t msg; | ||
989 | |||
990 | msg.data = data; | ||
991 | msg.cursize = 0; | ||
992 | msg.maxsize = sizeof(data); | ||
993 | |||
994 | MSG_WriteChar (&msg, svc_stufftext); | ||
995 | MSG_WriteString (&msg, "reconnect\n"); | ||
996 | NET_SendToAll (&msg, 5); | ||
997 | |||
998 | if (cls.state != ca_dedicated) | ||
999 | #ifdef QUAKE2 | ||
1000 | Cbuf_InsertText ("reconnect\n"); | ||
1001 | #else | ||
1002 | Cmd_ExecuteString ("reconnect\n", src_command); | ||
1003 | #endif | ||
1004 | } | ||
1005 | |||
1006 | |||
1007 | /* | ||
1008 | ================ | ||
1009 | SV_SaveSpawnparms | ||
1010 | |||
1011 | Grabs the current state of each client for saving across the | ||
1012 | transition to another level | ||
1013 | ================ | ||
1014 | */ | ||
1015 | void SV_SaveSpawnparms (void) | ||
1016 | { | ||
1017 | int i, j; | ||
1018 | |||
1019 | svs.serverflags = pr_global_struct->serverflags; | ||
1020 | |||
1021 | for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) | ||
1022 | { | ||
1023 | if (!host_client->active) | ||
1024 | continue; | ||
1025 | |||
1026 | // call the progs to get default spawn parms for the new client | ||
1027 | pr_global_struct->self = EDICT_TO_PROG(host_client->edict); | ||
1028 | PR_ExecuteProgram (pr_global_struct->SetChangeParms); | ||
1029 | for (j=0 ; j<NUM_SPAWN_PARMS ; j++) | ||
1030 | host_client->spawn_parms[j] = (&pr_global_struct->parm1)[j]; | ||
1031 | } | ||
1032 | } | ||
1033 | |||
1034 | |||
1035 | /* | ||
1036 | ================ | ||
1037 | SV_SpawnServer | ||
1038 | |||
1039 | This is called at the start of each level | ||
1040 | ================ | ||
1041 | */ | ||
1042 | extern float scr_centertime_off; | ||
1043 | |||
1044 | #ifdef QUAKE2 | ||
1045 | void SV_SpawnServer (char *server, char *startspot) | ||
1046 | #else | ||
1047 | void SV_SpawnServer (char *server) | ||
1048 | #endif | ||
1049 | { | ||
1050 | edict_t *ent; | ||
1051 | int i; | ||
1052 | |||
1053 | // let's not have any servers with no name | ||
1054 | if (hostname.string[0] == 0) | ||
1055 | Cvar_Set ("hostname", "UNNAMED"); | ||
1056 | scr_centertime_off = 0; | ||
1057 | |||
1058 | Con_DPrintf ("SpawnServer: %s\n",server); | ||
1059 | svs.changelevel_issued = false; // now safe to issue another | ||
1060 | |||
1061 | // | ||
1062 | // tell all connected clients that we are going to a new level | ||
1063 | // | ||
1064 | if (sv.active) | ||
1065 | { | ||
1066 | SV_SendReconnect (); | ||
1067 | } | ||
1068 | |||
1069 | // | ||
1070 | // make cvars consistant | ||
1071 | // | ||
1072 | if (coop.value) | ||
1073 | Cvar_SetValue ("deathmatch", 0); | ||
1074 | current_skill = (int)(skill.value + 0.5); | ||
1075 | if (current_skill < 0) | ||
1076 | current_skill = 0; | ||
1077 | if (current_skill > 3) | ||
1078 | current_skill = 3; | ||
1079 | |||
1080 | Cvar_SetValue ("skill", (float)current_skill); | ||
1081 | |||
1082 | // | ||
1083 | // set up the new server | ||
1084 | // | ||
1085 | Host_ClearMemory (); | ||
1086 | |||
1087 | memset (&sv, 0, sizeof(sv)); | ||
1088 | |||
1089 | strcpy (sv.name, server); | ||
1090 | #ifdef QUAKE2 | ||
1091 | if (startspot) | ||
1092 | strcpy(sv.startspot, startspot); | ||
1093 | #endif | ||
1094 | |||
1095 | // load progs to get entity field count | ||
1096 | //printf("progs 1"); | ||
1097 | PR_LoadProgs (); | ||
1098 | //printf("progs 2"); | ||
1099 | |||
1100 | // allocate server memory | ||
1101 | sv.max_edicts = MAX_EDICTS; | ||
1102 | |||
1103 | sv.edicts = Hunk_AllocName (sv.max_edicts*pr_edict_size, "edicts"); | ||
1104 | |||
1105 | sv.datagram.maxsize = sizeof(sv.datagram_buf); | ||
1106 | sv.datagram.cursize = 0; | ||
1107 | sv.datagram.data = sv.datagram_buf; | ||
1108 | |||
1109 | sv.reliable_datagram.maxsize = sizeof(sv.reliable_datagram_buf); | ||
1110 | sv.reliable_datagram.cursize = 0; | ||
1111 | sv.reliable_datagram.data = sv.reliable_datagram_buf; | ||
1112 | |||
1113 | sv.signon.maxsize = sizeof(sv.signon_buf); | ||
1114 | sv.signon.cursize = 0; | ||
1115 | sv.signon.data = sv.signon_buf; | ||
1116 | |||
1117 | //printf("progs 3"); | ||
1118 | // leave slots at start for clients only | ||
1119 | sv.num_edicts = svs.maxclients+1; | ||
1120 | for (i=0 ; i<svs.maxclients ; i++) | ||
1121 | { | ||
1122 | ent = EDICT_NUM(i+1); | ||
1123 | svs.clients[i].edict = ent; | ||
1124 | } | ||
1125 | |||
1126 | sv.state = ss_loading; | ||
1127 | sv.paused = false; | ||
1128 | |||
1129 | sv.time = 1.0; | ||
1130 | |||
1131 | strcpy (sv.name, server); | ||
1132 | sprintf (sv.modelname,"maps/%s.bsp", server); | ||
1133 | sv.worldmodel = Mod_ForName (sv.modelname, false); | ||
1134 | if (!sv.worldmodel) | ||
1135 | { | ||
1136 | Con_Printf ("Couldn't spawn server %s\n", sv.modelname); | ||
1137 | sv.active = false; | ||
1138 | return; | ||
1139 | } | ||
1140 | sv.models[1] = sv.worldmodel; | ||
1141 | |||
1142 | // | ||
1143 | // clear world interaction links | ||
1144 | // | ||
1145 | //printf("progs 4"); | ||
1146 | SV_ClearWorld (); | ||
1147 | //printf("progs 5"); | ||
1148 | |||
1149 | sv.sound_precache[0] = pr_strings; | ||
1150 | |||
1151 | sv.model_precache[0] = pr_strings; | ||
1152 | sv.model_precache[1] = sv.modelname; | ||
1153 | for (i=1 ; i<sv.worldmodel->numsubmodels ; i++) | ||
1154 | { | ||
1155 | sv.model_precache[1+i] = localmodels[i]; | ||
1156 | sv.models[i+1] = Mod_ForName (localmodels[i], false); | ||
1157 | } | ||
1158 | |||
1159 | // | ||
1160 | // load the rest of the entities | ||
1161 | // | ||
1162 | //printf("progs 6"); | ||
1163 | ent = EDICT_NUM(0); | ||
1164 | memset (&ent->v, 0, progs->entityfields * 4); | ||
1165 | //printf("progs 7"); | ||
1166 | ent->free = false; | ||
1167 | ent->v.model = sv.worldmodel->name - pr_strings; | ||
1168 | ent->v.modelindex = 1; // world model | ||
1169 | ent->v.solid = SOLID_BSP; | ||
1170 | ent->v.movetype = MOVETYPE_PUSH; | ||
1171 | |||
1172 | if (coop.value) | ||
1173 | pr_global_struct->coop = coop.value; | ||
1174 | else | ||
1175 | pr_global_struct->deathmatch = deathmatch.value; | ||
1176 | |||
1177 | pr_global_struct->mapname = sv.name - pr_strings; | ||
1178 | #ifdef QUAKE2 | ||
1179 | pr_global_struct->startspot = sv.startspot - pr_strings; | ||
1180 | #endif | ||
1181 | |||
1182 | // serverflags are for cross level information (sigils) | ||
1183 | pr_global_struct->serverflags = svs.serverflags; | ||
1184 | |||
1185 | //printf("progs 8"); | ||
1186 | ED_LoadFromFile (sv.worldmodel->entities); | ||
1187 | |||
1188 | //printf("progs 9"); | ||
1189 | sv.active = true; | ||
1190 | |||
1191 | // all setup is completed, any further precache statements are errors | ||
1192 | sv.state = ss_active; | ||
1193 | |||
1194 | // run two frames to allow everything to settle | ||
1195 | host_frametime = 0.1; | ||
1196 | SV_Physics (); | ||
1197 | SV_Physics (); | ||
1198 | |||
1199 | // create a baseline for more efficient communications | ||
1200 | SV_CreateBaseline (); | ||
1201 | |||
1202 | // send serverinfo to all connected clients | ||
1203 | for (i=0,host_client = svs.clients ; i<svs.maxclients ; i++, host_client++) | ||
1204 | if (host_client->active) | ||
1205 | SV_SendServerinfo (host_client); | ||
1206 | |||
1207 | Con_DPrintf ("Server spawned.\n"); | ||
1208 | } | ||
1209 | |||