1 /*
2 Copyright (C) 1997-2001 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 // cl_main.c -- client main loop
21
22 #include "client.h"
23
24 #ifndef __TIMESTAMP__
25 #define __TIMESTAMP__ __DATE__ " " __TIME__
26 #endif
27
28 int deferred_model_index;
29
30 extern cvar_t *qport;
31 extern cvar_t *vid_ref;
32
33 typedef struct incoming_s
34 {
35 netadr_t remote;
36 int type;
37 uint32 time;
38 } incoming_t;
39
40 #define CL_UNDEF 0
41 #define CL_RCON 1
42 #define CL_SERVER_INFO 2
43 #define CL_SERVER_STATUS 3
44 #define CL_SERVER_STATUS_FULL 4
45 #define CL_MASTER_QUERY 5
46
47 //list of addresses other than remote that can send us connectionless and for what purpose
48 static incoming_t incoming_allowed[16];
49 static int incoming_allowed_index;
50
51 typedef struct ignore_s
52 {
53 struct ignore_s *next;
54 char *text;
55 } ignore_t;
56
57 ignore_t cl_ignores;
58
59 cvar_t *freelook;
60
61 cvar_t *adr0;
62 cvar_t *adr1;
63 cvar_t *adr2;
64 cvar_t *adr3;
65 cvar_t *adr4;
66 cvar_t *adr5;
67 cvar_t *adr6;
68 cvar_t *adr7;
69 cvar_t *adr8;
70
71 #ifdef CL_STEREO_SUPPORT
72 cvar_t *cl_stereo_separation;
73 cvar_t *cl_stereo;
74 #endif
75
76 cvar_t *rcon_client_password;
77 cvar_t *rcon_address;
78
79 cvar_t *cl_noskins;
80 //cvar_t *cl_autoskins;
81 cvar_t *cl_footsteps;
82 cvar_t *cl_timeout;
83 cvar_t *cl_predict;
84 cvar_t *cl_backlerp;
85 cvar_t *r_maxfps;
86 cvar_t *cl_maxfps;
87 cvar_t *cl_async;
88 cvar_t *cl_smoothsteps;
89 cvar_t *cl_instantpacket;
90
91 cvar_t *cl_gun;
92
93 cvar_t *cl_add_particles;
94 cvar_t *cl_add_lights;
95 cvar_t *cl_add_entities;
96 cvar_t *cl_add_blend;
97
98 cvar_t *cl_shownet;
99 cvar_t *cl_showmiss;
100 cvar_t *cl_showclamp;
101
102 cvar_t *cl_paused;
103 cvar_t *cl_timedemo;
104
105 //r1: filter high bits
106 cvar_t *cl_filterchat;
107
108 cvar_t *lookspring;
109 cvar_t *lookstrafe;
110 cvar_t *sensitivity;
111
112 cvar_t *m_pitch;
113 cvar_t *m_yaw;
114 cvar_t *m_forward;
115 cvar_t *m_side;
116
117 cvar_t *cl_lightlevel;
118
119 //
120 // userinfo
121 //
122 cvar_t *info_password;
123 cvar_t *info_spectator;
124 cvar_t *name;
125 cvar_t *skin;
126 cvar_t *rate;
127 cvar_t *fov;
128 cvar_t *msg;
129 cvar_t *hand;
130 cvar_t *gender;
131 cvar_t *gender_auto;
132
133 cvar_t *cl_vwep;
134
135 //r1ch
136 //cvar_t *cl_defertimer;
137 cvar_t *cl_protocol;
138
139 cvar_t *dbg_framesleep;
140 //cvar_t *cl_snaps;
141
142 cvar_t *cl_nolerp;
143
144 cvar_t *cl_instantack;
145 cvar_t *cl_autorecord;
146
147 cvar_t *cl_railtrail;
148 cvar_t *cl_test = &uninitialized_cvar;
149 cvar_t *cl_test2;
150 cvar_t *cl_test3;
151
152 cvar_t *cl_original_dlights;
153 cvar_t *cl_default_location = &uninitialized_cvar;
154 cvar_t *cl_player_updates;
155 cvar_t *cl_updaterate;
156
157 cvar_t *cl_proxy;
158
159 #ifdef NO_SERVER
160 cvar_t *allow_download;
161 cvar_t *allow_download_players;
162 cvar_t *allow_download_models;
163 cvar_t *allow_download_sounds;
164 cvar_t *allow_download_maps;
165 #endif
166
167 client_static_t cls;
168 client_state_t cl;
169
170 centity_t cl_entities[MAX_EDICTS];
171
172 entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];
173
174 qboolean send_packet_now;
175
176 extern cvar_t *allow_download;
177 extern cvar_t *allow_download_players;
178 extern cvar_t *allow_download_models;
179 extern cvar_t *allow_download_sounds;
180 extern cvar_t *allow_download_maps;
181
182 //======================================================================
183
184 /*
185 ====================
186 CL_WriteDemoMessage
187
188 Dumps the current net message, prefixed by the length
189 ====================
190 */
CL_WriteDemoMessageFull(void)191 void CL_WriteDemoMessageFull (void)
192 {
193 int len, swlen;
194
195 // the first eight bytes are just packet sequencing stuff
196 len = net_message.cursize-8;
197 swlen = LittleLong(len);
198 if (swlen > 0)
199 {
200 fwrite (&swlen, 4, 1, cls.demofile);
201 fwrite (net_message_buffer+8, len, 1, cls.demofile);
202 }
203 }
204
CL_WriteDemoMessage(byte * buff,int len,qboolean forceFlush)205 void CL_WriteDemoMessage (byte *buff, int len, qboolean forceFlush)
206 {
207 if (!cls.demorecording || cls.serverProtocol == PROTOCOL_ORIGINAL)
208 return;
209
210 if (forceFlush)
211 {
212 if (!cls.demowaiting)
213 {
214 qboolean dropped_frame;
215 int swlen;
216
217 dropped_frame = false;
218
219 if (cl.demoBuff.overflowed)
220 {
221 Com_DPrintf ("Dropped a demo frame, maximum message size exceeded: %d > %d\n", cl.demoBuff.cursize, cl.demoBuff.maxsize);
222
223 //we write a message regardless to keep in sync time-wise.
224 SZ_Clear (&cl.demoBuff);
225 SZ_WriteByte (&cl.demoBuff, svc_nop);
226 dropped_frame = true;
227 }
228 else if (!cl.demoBuff.cursize)
229 {
230 Com_DPrintf ("Dropped a demo frame, no data to be written\n");
231 //never write zero length frames, they cause demo end
232 //we write a message regardless to keep in sync time-wise.
233 SZ_Clear (&cl.demoBuff);
234 SZ_WriteByte (&cl.demoBuff, svc_nop);
235 dropped_frame = true;
236 }
237
238 swlen = LittleLong(cl.demoBuff.cursize);
239 fwrite (&swlen, 4, 1, cls.demofile);
240 fwrite (cl.demoFrame, cl.demoBuff.cursize, 1, cls.demofile);
241
242 //fixme: this is ugly
243 if (noFrameFromServerPacket == 0 && !dropped_frame)
244 cl.demoLastFrame = &cl.frames[cl.frame.serverframe & UPDATE_MASK];
245 }
246 SZ_Clear (&cl.demoBuff);
247 }
248
249 if (len)
250 SZ_Write (&cl.demoBuff, buff, len);
251 }
252
253 void CL_DemoDeltaEntity (const entity_state_t *from, const entity_state_t *to, qboolean force, qboolean newentity);
CL_BeginRecording(char * name)254 qboolean CL_BeginRecording (char *name)
255 {
256 byte buf_data[1390];
257 sizebuf_t buf;
258 int i;
259 int len;
260 entity_state_t *ent;
261
262 FS_CreatePath (name);
263
264 cls.demofile = fopen (name, "wb");
265
266 if (!cls.demofile)
267 return false;
268
269 cls.demorecording = true;
270
271 // don't start saving messages until a non-delta compressed message is received
272 cls.demowaiting = true;
273
274 // inform server we need to receive more data
275 if (cls.serverProtocol == PROTOCOL_R1Q2)
276 {
277 MSG_BeginWriting (clc_setting);
278 MSG_WriteShort (CLSET_RECORDING);
279 MSG_WriteShort (1);
280 MSG_EndWriting (&cls.netchan.message);
281 }
282
283 //
284 // write out messages to hold the startup information
285 //
286 SZ_Init (&buf, buf_data, sizeof(buf_data));
287
288 //if (cls.serverProtocol == PROTOCOL_ORIGINAL)
289 // buf.maxsize = 1390;
290
291 // send the serverdata
292 MSG_BeginWriting (svc_serverdata);
293 MSG_WriteLong (PROTOCOL_ORIGINAL);
294 MSG_WriteLong (0x10000 + cl.servercount);
295 MSG_WriteByte (1); // demos are always attract loops
296 MSG_WriteString (cl.gamedir);
297 MSG_WriteShort (cl.playernum);
298 MSG_WriteString (cl.configstrings[CS_NAME]);
299 /*if (cls.serverProtocol == PROTOCOL_R1Q2)
300 {
301 MSG_WriteByte (cl.enhancedServer);
302 MSG_WriteShort (MINOR_VERSION_R1Q2);
303 MSG_WriteByte (cl.advancedDeltas);
304 }*/
305 MSG_EndWriting (&buf);
306
307 // configstrings
308 for (i=0 ; i<MAX_CONFIGSTRINGS ; i++)
309 {
310 if (cl.configstrings[i][0])
311 {
312 if (buf.cursize + strlen (cl.configstrings[i]) + 64 > buf.maxsize)
313 { // write it out
314 len = LittleLong (buf.cursize);
315 fwrite (&len, 4, 1, cls.demofile);
316 fwrite (buf.data, buf.cursize, 1, cls.demofile);
317 buf.cursize = 0;
318 }
319
320 MSG_BeginWriting (svc_configstring);
321 MSG_WriteShort (i);
322 MSG_WriteString (cl.configstrings[i]);
323 MSG_EndWriting (&buf);
324 }
325 }
326
327 // baselines
328
329 for (i=0; i<MAX_EDICTS ; i++)
330 {
331 ent = &cl_entities[i].baseline;
332 if (!ent->modelindex)
333 continue;
334
335 if (buf.cursize + 64 > buf.maxsize)
336 { // write it out
337 len = LittleLong (buf.cursize);
338 fwrite (&len, 4, 1, cls.demofile);
339 fwrite (buf.data, buf.cursize, 1, cls.demofile);
340 buf.cursize = 0;
341 }
342
343 MSG_BeginWriting (svc_spawnbaseline);
344 CL_DemoDeltaEntity (&null_entity_state, &cl_entities[i].baseline, true, true);
345 MSG_EndWriting (&buf);
346 }
347
348 MSG_BeginWriting (svc_stufftext);
349 MSG_WriteString ("precache\n");
350 MSG_EndWriting (&buf);
351
352 // write it to the demo file
353
354 len = LittleLong (buf.cursize);
355 fwrite (&len, 4, 1, cls.demofile);
356 fwrite (buf.data, buf.cursize, 1, cls.demofile);
357
358 // the rest of the demo file will be individual frames
359 return true;
360 }
361
CL_EndRecording(void)362 void CL_EndRecording(void)
363 {
364 int len;
365
366 // finish up
367 len = -1;
368 fwrite (&len, 4, 1, cls.demofile);
369 fclose (cls.demofile);
370
371 // inform server we are done with extra data
372 if (cls.serverProtocol == PROTOCOL_R1Q2)
373 {
374 MSG_BeginWriting (clc_setting);
375 MSG_WriteShort (CLSET_RECORDING);
376 MSG_WriteShort (0);
377 MSG_EndWriting (&cls.netchan.message);
378 }
379
380 cls.demofile = NULL;
381 cls.demorecording = false;
382
383 // reset delta demo state
384 SZ_Clear (&cl.demoBuff);
385 cl.demoLastFrame = NULL;
386 }
387
388 /*
389 ====================
390 CL_Stop_f
391
392 stop recording a demo
393 ====================
394 */
CL_Stop_f(void)395 void CL_Stop_f (void)
396 {
397 int len;
398
399 if (!cls.demorecording)
400 {
401 Com_Printf ("Not recording a demo.\n", LOG_CLIENT);
402 return;
403 }
404
405 len = ftell (cls.demofile);
406
407 CL_EndRecording();
408
409 Com_Printf ("Stopped demo, recorded %d bytes.\n", LOG_CLIENT, len);
410 }
411
CL_Ignore_f(void)412 void CL_Ignore_f (void)
413 {
414 ignore_t *list, *last, *newentry;
415
416 if (Cmd_Argc() < 2)
417 {
418 Com_Printf ("usage: ignore text\n", LOG_GENERAL);
419 return;
420 }
421
422 list = &cl_ignores;
423
424 last = list->next;
425
426 newentry = Z_TagMalloc (sizeof(*list), TAGMALLOC_CLIENT_IGNORE);
427 newentry->text = CopyString (Cmd_Args(), TAGMALLOC_CLIENT_IGNORE);
428 newentry->next = last;
429 list->next = newentry;
430
431 Com_Printf ("%s added to ignore list.\n", LOG_GENERAL, newentry->text);
432 }
433
CL_IgnoreMatch(const char * string)434 qboolean CL_IgnoreMatch (const char *string)
435 {
436 ignore_t *entry;
437
438 entry = &cl_ignores;
439
440 while (entry->next)
441 {
442 entry = entry->next;
443 if (strstr (string, entry->text))
444 return true;
445 }
446
447 return false;
448 }
449
CL_Unignore_f(void)450 void CL_Unignore_f (void)
451 {
452 ignore_t *entry, *last;
453 char *match;
454
455 if (Cmd_Argc() < 2)
456 {
457 Com_Printf ("usage: unignore text\n", LOG_GENERAL);
458 return;
459 }
460
461 match = Cmd_Args();
462 entry = last = &cl_ignores;
463
464 while (entry->next)
465 {
466 entry = entry->next;
467
468 if (!Q_stricmp (match, entry->text))
469 {
470 last->next = entry->next;
471 Z_Free (entry->text);
472 Z_Free (entry);
473
474 Com_Printf ("Ignore '%s' removed.\n", LOG_GENERAL, match);
475 return;
476 }
477 last = entry;
478 }
479
480 Com_Printf ("Ignore '%s' not found.\n", LOG_GENERAL, match);
481 }
482
483 /*
484 ====================
485 CL_Record_f
486
487 record <demoname>
488
489 Begins recording a demo from the current position
490 ====================
491 */
CL_Record_f(void)492 void CL_Record_f (void)
493 {
494 char name[MAX_OSPATH];
495
496 if (Cmd_Argc() != 2)
497 {
498 Com_Printf ("record <demoname>\n", LOG_CLIENT);
499 return;
500 }
501
502 if (cls.demorecording)
503 {
504 Com_Printf ("Already recording.\n", LOG_CLIENT);
505 return;
506 }
507
508 if (cls.state != ca_active)
509 {
510 Com_Printf ("You must be in a level to record.\n", LOG_CLIENT);
511 return;
512 }
513
514 if (cl.attractloop)
515 {
516 Com_Printf ("Unable to record from a demo stream due to insufficient deltas.\n", LOG_CLIENT);
517 return;
518 }
519
520 if (strstr (Cmd_Argv(1), "..") || strchr (Cmd_Argv(1), '/') || strchr (Cmd_Argv(1), '\\') )
521 {
522 Com_Printf ("Illegal filename.\n", LOG_CLIENT);
523 return;
524 }
525
526 //
527 // open the demo file
528 //
529 Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1));
530
531 FS_CreatePath (name);
532
533 if (!CL_BeginRecording (name))
534 {
535 Com_Printf ("ERROR: Couldn't open %s for writing.\n", LOG_CLIENT, name);
536 }
537 else
538 {
539 /*if (cls.serverProtocol == PROTOCOL_R1Q2)
540 {
541 //make it a bit more obvious for the bleeding idiots out there
542 //Com_Printf ("WARNING: Demos recorded at cl_protocol 35 are not guaranteed to replay properly!\n", LOG_CLIENT);
543 Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n", LOG_CLIENT);
544 S_StartLocalSound ("player/burn1.wav");
545 S_StartLocalSound ("player/burn2.wav");
546 con.ormask = 128;
547 Com_Printf ("Demos recorded at cl_protocol 35 are\nnot guaranteed to replay properly!\n", LOG_CLIENT);
548 con.ormask = 0;
549 Com_Printf("\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n", LOG_CLIENT);
550 }*/
551 Com_Printf ("recording to %s.\n", LOG_CLIENT, name);
552 }
553 }
554
555 //======================================================================
556
557 /*
558 ===================
559 Cmd_ForwardToServer
560
561 adds the current command line as a clc_stringcmd to the client message.
562 things like godmode, noclip, etc, are commands directed to the server,
563 so when they are typed in at the console, they will need to be forwarded.
564 ===================
565 */
Cmd_ForwardToServer(void)566 void Cmd_ForwardToServer (void)
567 {
568 char *cmd;
569
570 cmd = Cmd_Argv(0);
571 if (cls.state <= ca_connected || *cmd == '-' || *cmd == '+')
572 {
573 Com_Printf ("Unknown command \"%s\"\n", LOG_CLIENT, cmd);
574 return;
575 }
576
577 Com_DPrintf ("Cmd_ForwardToServer: forwarding '%s'\n", cmd);
578
579 //Com_Printf ("fwd: %s %s\n", cmd, Cmd_Args());
580
581 MSG_BeginWriting (clc_stringcmd);
582 MSG_Print (cmd);
583 if (Cmd_Argc() > 1)
584 {
585 MSG_Print (" ");
586 MSG_Print (Cmd_Args());
587 }
588 MSG_EndWriting (&cls.netchan.message);
589 send_packet_now = true;
590 }
591
592 /*void CL_Setenv_f( void )
593 {
594 int argc = Cmd_Argc();
595
596 if ( argc > 2 )
597 {
598 char buffer[1000];
599 int i;
600
601 strcpy( buffer, Cmd_Argv(1) );
602 strcat( buffer, "=" );
603
604 for ( i = 2; i < argc; i++ )
605 {
606 strcat( buffer, Cmd_Argv( i ) );
607 strcat( buffer, " " );
608 }
609
610 putenv( buffer );
611 }
612 else if ( argc == 2 )
613 {
614 char *env = getenv( Cmd_Argv(1) );
615
616 if ( env )
617 {
618 Com_Printf( "%s=%s\n", Cmd_Argv(1), env );
619 }
620 else
621 {
622 Com_Printf( "%s undefined\n", Cmd_Argv(1), env );
623 }
624 }
625 }*/
626
627 /*
628 ==================
629 CL_ForwardToServer_f
630 ==================
631 */
CL_ForwardToServer_f(void)632 void CL_ForwardToServer_f (void)
633 {
634 //if (cls.state != ca_connected && cls.state != ca_active)
635 if (cls.state < ca_connected)
636 {
637 Com_Printf ("Can't \"%s\", not connected\n", LOG_CLIENT, Cmd_Argv(0));
638 return;
639 }
640
641 //r1: support \xxx escape sequences for testing various evil hacks
642 #ifdef _DEBUG
643 {
644 char *rew = Cmd_Args();
645 char args[MAX_STRING_CHARS];
646 char tmp[4];
647 memset (args, 0, sizeof(args));
648 strcpy (args, rew);
649 memset (rew, 0, sizeof(args));
650 strcpy (rew, args);
651 while (*rew && *rew+3) {
652 if (*rew == '\\' && isdigit(*(rew+1)) && isdigit(*(rew+2)) && isdigit(*(rew+3))) {
653 Q_strncpy (tmp, rew+1, 3);
654 *rew = atoi(tmp);
655 memcpy (rew+1, rew+4, 1024 - (rew - Cmd_Args() + 3));
656 //*(rew + (rew - Cmd_Args() - 2)) = 0;
657 }
658 rew++;
659 }
660 }
661 #endif
662
663 //Com_Printf ("fwd: %s\n", Cmd_Args());
664
665 // don't forward the first argument
666 if (Cmd_Argc() > 1)
667 {
668 Com_DPrintf ("CL_ForwardToServer_f: Wrote '%s'\n", Cmd_Args());
669
670 MSG_WriteByte (clc_stringcmd);
671 MSG_Print (Cmd_Args());
672 MSG_EndWriting (&cls.netchan.message);
673
674 send_packet_now = true;
675 }
676 }
677
678
679 /*
680 ==================
681 CL_Pause_f
682 ==================
683 */
CL_Pause_f(void)684 void CL_Pause_f (void)
685 {
686 // never pause in multiplayer
687 if (!cl.attractloop && (Cvar_IntValue ("maxclients") > 1 || !Com_ServerState ()))
688 {
689 Cvar_Set ("paused", "0");
690 return;
691 }
692
693 Cvar_SetValue ("paused", (float)!cl_paused->intvalue);
694 }
695
696 /*
697 ==================
698 CL_Quit_f
699 ==================
700 */
CL_Quit_f(void)701 NORETURN void CL_Quit_f (void)
702 {
703 CL_Disconnect (false);
704 Com_Quit ();
705 }
706
707 /*
708 ================
709 CL_Drop
710
711 Called after an ERR_DROP was thrown
712 ================
713 */
CL_Drop(qboolean skipdisconnect,qboolean nonerror)714 void CL_Drop (qboolean skipdisconnect, qboolean nonerror)
715 {
716 if (cls.state == ca_uninitialized)
717 return;
718
719 if (cls.state == ca_disconnected)
720 return;
721
722 CL_Disconnect (skipdisconnect);
723
724 // drop loading plaque unless this is the initial game start
725 if (cls.disable_servercount != -1)
726 SCR_EndLoadingPlaque (); // get rid of loading plaque
727
728 //reset if we crashed a menu
729 M_ForceMenuOff ();
730
731 if (!nonerror)
732 cls.serverProtocol = 0;
733 }
734
735
736 /*
737 =======================
738 CL_SendConnectPacket
739
740 We have gotten a challenge from the server, so try and
741 connect.
742 ======================
743 */
CL_SendConnectPacket(int useProtocol)744 void CL_SendConnectPacket (int useProtocol)
745 {
746 netadr_t adr;
747 int port;
748 unsigned msglen;
749
750 #ifdef CLIENT_DLL
751 if (!cllib_active) {
752 //retry loading it now
753 CL_ClDLL_Restart_f ();
754 //if (!cllib_active)
755 //Com_Error (ERR_DROP, "no client_dll.dll loaded for current game.");
756 }
757 #endif
758
759 if (!NET_StringToAdr (cls.servername, &adr))
760 {
761 Com_Printf ("Bad server address: %s\n", LOG_CLIENT, cls.servername);
762 cls.connect_time = 0;
763 return;
764 }
765
766 if (adr.port == 0)
767 adr.port = ShortSwap (PORT_SERVER);
768
769 if (qport->intvalue == -1 || cls.proxyState == ps_active)
770 port = (int)(random() * 0xFFFF);
771 else
772 port = qport->intvalue;
773
774 userinfo_modified = false;
775
776 if (Com_ServerState() == ss_demo)
777 {
778 msglen = MAX_USABLEMSG;
779 cls.serverProtocol = 35;
780 }
781 else
782 {
783 msglen = Cvar_IntValue ("net_maxmsglen");
784
785 if (!cls.serverProtocol)
786 {
787 if (cl_protocol->intvalue)
788 cls.serverProtocol = cl_protocol->intvalue;
789 else if (useProtocol)
790 cls.serverProtocol = useProtocol;
791 else
792 cls.serverProtocol = PROTOCOL_R1Q2;
793 }
794 }
795
796 if (cls.serverProtocol == PROTOCOL_R1Q2)
797 port &= 0xFF;
798
799 cls.quakePort = port;
800
801 Com_DPrintf ("Cl_SendConnectPacket: protocol=%d, port=%d, challenge=%u\n", cls.serverProtocol, port, cls.challenge);
802
803 //r1: only send enhanced connect string on new protocol in order to avoid confusing
804 // other engine mods which may or may not extend this.
805
806
807 if (cls.serverProtocol == PROTOCOL_R1Q2)
808 Netchan_OutOfBandPrint (NS_CLIENT, &adr, "connect %i %i %i \"%s\" %u %u\n", cls.serverProtocol, port, cls.challenge, Cvar_Userinfo(), msglen, MINOR_VERSION_R1Q2);
809 else
810 Netchan_OutOfBandPrint (NS_CLIENT, &adr, "connect %i %i %i \"%s\"\n", cls.serverProtocol, port, cls.challenge, Cvar_Userinfo());
811 }
812
813 /*
814 =================
815 CL_Reconnect_f
816
817 The server is changing levels
818 =================
819 */
CL_Reconnect_f(void)820 void CL_Reconnect_f (void)
821 {
822 //ZOID
823 //if we are downloading, we don't change! This so we don't suddenly stop downloading a map
824 if (cls.download)
825 return;
826
827 S_StopAllSounds ();
828
829 if (cls.state == ca_connected)
830 {
831 Com_Printf ("reconnecting (soft)...\n", LOG_CLIENT);
832 //cls.state = ca_connected;
833 MSG_WriteByte (clc_stringcmd);
834 MSG_WriteString ("new");
835 MSG_EndWriting (&cls.netchan.message);
836 send_packet_now = true;
837 return;
838 }
839
840 if (cls.servername[0])
841 {
842 if (cls.state >= ca_connected) {
843 Com_Printf ("disconnecting\n", LOG_CLIENT);
844 strcpy (cls.lastservername, cls.servername);
845 CL_Disconnect(false);
846 cls.connect_time = cls.realtime - 1500;
847 } else
848 cls.connect_time = -99999; // fire immediately
849
850 cls.state = ca_connecting;
851 //Com_Printf ("reconnecting (hard)...\n");
852 }
853
854 if (cls.lastservername[0])
855 {
856 cls.connect_time = -99999;
857 cls.state = ca_connecting;
858 Com_Printf ("reconnecting (hard)...\n", LOG_CLIENT);
859 strcpy (cls.servername, cls.lastservername);
860 }
861 else
862 {
863 Com_Printf ("No server to reconnect to.\n", LOG_CLIENT);
864 }
865 }
866
867 /*
868 =================
869 CL_CheckForResend
870
871 Resend a connect message if the last one has timed out
872 =================
873 */
CL_CheckForResend(void)874 void CL_CheckForResend (void)
875 {
876 netadr_t adr;
877
878 // if the local server is running and we aren't
879 // then connect
880 if (cls.state == ca_disconnected && Com_ServerState() )
881 {
882 cls.state = ca_connecting;
883 strcpy (cls.servername, "localhost");
884 // we don't need a challenge on the localhost
885 CL_SendConnectPacket (0);
886 return;
887 // cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
888 }
889
890 // resend if we haven't gotten a reply yet
891 if (cls.state != ca_connecting)
892 return;
893
894 if (cls.realtime - cls.connect_time < 3000)
895 return;
896
897 if (cl_proxy->string[0] && cls.proxyState != ps_active)
898 {
899 if (!NET_StringToAdr (cl_proxy->string, &cls.proxyAddr))
900 {
901 Com_Printf ("Bad proxy address: %s\n", LOG_CLIENT, cl_proxy->string);
902 cls.state = ca_disconnected;
903 cls.proxyState = ps_none;
904 return;
905 }
906
907 if (cls.proxyAddr.port == 0)
908 cls.proxyAddr.port = ShortSwap (27999);
909
910 cls.proxyState = ps_pending;
911 }
912 else
913 {
914 if (!NET_StringToAdr (cls.servername, &adr))
915 {
916 Com_Printf ("Bad server address: %s\n", LOG_CLIENT, cls.servername);
917 cls.state = ca_disconnected;
918 cls.proxyState = ps_none;
919 return;
920 }
921
922 if (adr.port == 0)
923 adr.port = ShortSwap (PORT_SERVER);
924 }
925
926 cls.connect_time = cls.realtime; // for retransmit requests
927
928 //_asm int 3;
929 if (cls.proxyState == ps_pending)
930 {
931 Com_Printf ("Connecting to %s...\n", LOG_CLIENT, NET_AdrToString(&cls.proxyAddr));
932 Netchan_OutOfBandProxyPrint (NS_CLIENT, &cls.proxyAddr, "proxygetchallenge\n");
933 }
934 else
935 {
936 Com_Printf ("Connecting to %s...\n", LOG_CLIENT, cls.servername);
937 Netchan_OutOfBandPrint (NS_CLIENT, &adr, "getchallenge\n");
938 }
939 }
940
941
942 /*
943 ================
944 CL_Connect_f
945
946 ================
947 */
CL_Connect_f(void)948 void CL_Connect_f (void)
949 {
950 char *server, *p;
951 netadr_t adr;
952
953 if (Cmd_Argc() != 2)
954 {
955 Com_Printf ("usage: connect <server>\n", LOG_GENERAL);
956 return;
957 }
958
959 if (Com_ServerState ())
960 { // if running a local server, kill it and reissue
961 #ifndef NO_SERVER
962 SV_Shutdown ("Server has shut down", false, false);
963 #endif
964 }
965 else
966 {
967 //we do this anyway below
968 //CL_Disconnect (false);
969 }
970
971 server = Cmd_Argv (1);
972
973 // quake2://protocol support
974 if (!strncmp (server, "quake2://", 9))
975 server += 9;
976
977 p = strchr (server, '/');
978 if (p)
979 p[0] = 0;
980
981 NET_Config (NET_CLIENT); // allow remote
982
983 if (!NET_StringToAdr (server, &adr))
984 {
985 Com_Printf ("Bad server address: %s\n", LOG_CLIENT, server);
986 return;
987 }
988
989 if (adr.port == 0)
990 adr.port = ShortSwap (PORT_SERVER);
991
992 CL_Disconnect (false);
993
994 //reset protocol attempt if we're connecting to a different server
995 if (!NET_CompareAdr (&adr, &cls.netchan.remote_address))
996 {
997 Com_DPrintf ("Resetting protocol attempt since %s is not ", NET_AdrToString (&adr));
998 Com_DPrintf ("%s.\n", NET_AdrToString (&cls.netchan.remote_address));
999 cls.serverProtocol = 0;
1000 }
1001
1002 cls.state = ca_connecting;
1003 Q_strncpy (cls.servername, server, sizeof(cls.servername)-1);
1004 strcpy (cls.lastservername, cls.servername);
1005
1006 cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
1007 }
1008
1009 /*
1010 =====================
1011 CL_Rcon_f
1012
1013 Send the rest of the command line over as
1014 an unconnected command.
1015 =====================
1016 */
CL_Rcon_f(void)1017 void CL_Rcon_f (void)
1018 {
1019 char message[1024];
1020 netadr_t to;
1021
1022 //r1: buffer check ffs!
1023 if ((strlen(Cmd_Args()) + strlen(rcon_client_password->string) + 16) >= sizeof(message)) {
1024 Com_Printf ("Length of password + command exceeds maximum allowed length.\n", LOG_CLIENT);
1025 return;
1026 }
1027
1028 *(int *)message = -1;
1029 message[4] = 0;
1030
1031 NET_Config (NET_CLIENT); // allow remote
1032
1033 strcat (message, "rcon ");
1034
1035 if (rcon_client_password->string[0])
1036 {
1037 strcat (message, rcon_client_password->string);
1038 strcat (message, " ");
1039 }
1040
1041 strcat (message, Cmd_Args());
1042
1043 /*for (i=1 ; i<Cmd_Argc() ; i++)
1044 {
1045 strcat (message, Cmd_Argv(i));
1046 strcat (message, " ");
1047 }*/
1048
1049
1050 if (cls.state >= ca_connected)
1051 {
1052 to = cls.netchan.remote_address;
1053 }
1054 else
1055 {
1056 if (!strlen(rcon_address->string))
1057 {
1058 Com_Printf ("You must either be connected,\n"
1059 "or set the 'rcon_address' cvar\n"
1060 "to issue rcon commands\n", LOG_CLIENT);
1061
1062 return;
1063 }
1064 NET_StringToAdr (rcon_address->string, &to);
1065 if (to.port == 0)
1066 to.port = ShortSwap (PORT_SERVER);
1067 }
1068
1069 //allow remote response
1070 incoming_allowed[incoming_allowed_index & 15].remote = to;
1071 incoming_allowed[incoming_allowed_index & 15].type = CL_RCON;
1072 incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1073 incoming_allowed_index++;
1074
1075 NET_SendPacket (NS_CLIENT, (int)strlen(message)+1, message, &to);
1076 }
1077
CL_ServerStatus_f(void)1078 void CL_ServerStatus_f (void)
1079 {
1080 netadr_t adr;
1081 int i;
1082 int type;
1083
1084 i = 1;
1085
1086 if (!strcmp (Cmd_Argv(1), "/s"))
1087 {
1088 i++;
1089 type = CL_SERVER_STATUS_FULL;
1090 }
1091 else if (!strcmp (Cmd_Argv(1), "/i"))
1092 {
1093 i++;
1094 type = CL_SERVER_INFO;
1095 }
1096 else
1097 {
1098 type = CL_SERVER_STATUS;
1099 }
1100
1101 NET_Config (NET_CLIENT); // allow remote
1102
1103 if (i >= Cmd_Argc())
1104 {
1105 if (cls.state < ca_connected)
1106 {
1107 Com_Printf ("usage: serverstatus [/s|/i] [server]\n", LOG_GENERAL);
1108 return;
1109 }
1110 adr = cls.netchan.remote_address;
1111 }
1112 else
1113 {
1114 if (!NET_StringToAdr (Cmd_Argv(i), &adr))
1115 {
1116 Com_Printf ("Bad address %s\n", LOG_CLIENT, Cmd_Argv(i));
1117 return;
1118 }
1119 }
1120
1121 if (!adr.port)
1122 adr.port = ShortSwap (PORT_SERVER);
1123
1124 if (type == CL_SERVER_INFO)
1125 Netchan_OutOfBandPrint (NS_CLIENT, &adr, "info 34");
1126 else
1127 Netchan_OutOfBandPrint (NS_CLIENT, &adr, "status");
1128
1129 incoming_allowed[incoming_allowed_index & 15].remote = adr;
1130 incoming_allowed[incoming_allowed_index & 15].type = type;
1131 incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1132 incoming_allowed_index++;
1133 }
1134
1135 /*
1136 =====================
1137 CL_ClearState
1138
1139 =====================
1140 */
CL_ClearState(void)1141 void CL_ClearState (void)
1142 {
1143 cvar_t *gameDirHack;
1144
1145 S_StopAllSounds ();
1146 CL_ClearEffects ();
1147 CL_ClearTEnts ();
1148
1149 // wipe the entire cl structure
1150 memset (&cl, 0, sizeof(cl));
1151 memset (&cl_entities, 0, sizeof(cl_entities));
1152
1153 //r1 unprotect game
1154 gameDirHack = Cvar_FindVar ("game");
1155 gameDirHack->flags &= ~CVAR_NOSET;
1156
1157 //r1: local ents clear
1158 Le_Reset ();
1159
1160 //r1: reset
1161 cl.maxclients = MAX_CLIENTS;
1162 SZ_Clear (&cls.netchan.message);
1163
1164 SZ_Init (&cl.demoBuff, cl.demoFrame, sizeof(cl.demoFrame));
1165 cl.demoBuff.allowoverflow = true;
1166
1167 cl.settings[SVSET_FPS] = 10;
1168 }
1169
1170 /*
1171 =====================
1172 CL_Disconnect
1173
1174 Goes from a connected state to full screen console state
1175 Sends a disconnect message to the server
1176 This is also called on Com_Error, so it shouldn't cause any errors
1177 =====================
1178 */
CL_Disconnect(qboolean skipdisconnect)1179 void CL_Disconnect (qboolean skipdisconnect)
1180 {
1181 byte final[16];
1182
1183 if (cls.state == ca_disconnected)
1184 return;
1185
1186 if (cl_timedemo->intvalue)
1187 {
1188 unsigned int time;
1189
1190 time = Sys_Milliseconds () - cl.timedemo_start;
1191 if (time > 0)
1192 Com_Printf ("%i frames, %3.2f seconds: %3.1f fps\n", LOG_CLIENT, cl.timedemo_frames,
1193 time/1000.0f, cl.timedemo_frames*1000.0f / time);
1194 }
1195
1196 VectorClear (cl.refdef.blend);
1197 re.CinematicSetPalette(NULL);
1198
1199 //r1: why?
1200 //M_ForceMenuOff ();
1201
1202 cls.connect_time = 0;
1203
1204 #ifdef CINEMATICS
1205 SCR_StopCinematic ();
1206 #endif
1207
1208 if (cls.demorecording)
1209 CL_EndRecording();
1210 //CL_Stop_f ();
1211
1212 // send a disconnect message to the server
1213 if (!skipdisconnect)
1214 {
1215 final[0] = clc_stringcmd;
1216 strcpy ((char *)final+1, "disconnect");
1217
1218 Netchan_Transmit (&cls.netchan, 11, final);
1219 Netchan_Transmit (&cls.netchan, 11, final);
1220 Netchan_Transmit (&cls.netchan, 11, final);
1221 }
1222
1223 CL_ClearState ();
1224
1225 // stop download
1226 if (cls.download)
1227 {
1228 fclose(cls.download);
1229 cls.download = NULL;
1230 }
1231
1232 #ifdef USE_CURL
1233 CL_CancelHTTPDownloads (true);
1234 cls.downloadReferer[0] = 0;
1235 #endif
1236
1237 cls.downloadname[0] = 0;
1238 cls.downloadposition = 0;
1239
1240 cls.state = ca_disconnected;
1241 cls.proxyState = ps_none;
1242 cls.servername[0] = 0;
1243
1244 Cvar_ForceSet ("$mapname", "");
1245 Cvar_ForceSet ("$game", "");
1246 Cvar_ForceSet ("$serverip", "");
1247
1248 //r1: swap games if needed
1249 Cvar_GetLatchedVars();
1250
1251 NET_SetProxy (NULL);
1252 MSG_Clear();
1253 }
1254
CL_Disconnect_f(void)1255 void CL_Disconnect_f (void)
1256 {
1257 if (cls.state != ca_disconnected)
1258 {
1259 cls.serverProtocol = 0;
1260 cls.key_dest = key_console;
1261 Com_Error (ERR_HARD, "Disconnected from server");
1262 }
1263 }
1264
1265 //Spam repeated cmd for overflow checkings
1266 #ifdef _DEBUG
CL_Spam_f(void)1267 void CL_Spam_f (void)
1268 {
1269 char cmdBuff[1380] = {0};
1270 int count;
1271 int i;
1272 char *what;
1273
1274 count = atoi (Cmd_Argv(2));
1275
1276 if (!count)
1277 return;
1278
1279 what = Cmd_Argv(1);
1280
1281 Q_strncpy (cmdBuff, Cmd_Argv(3), sizeof(cmdBuff)-1);
1282 if (cmdBuff[0])
1283 strcat (cmdBuff, " ");
1284
1285 if (count * strlen(what) >= sizeof(cmdBuff)-strlen(cmdBuff)-2)
1286 {
1287 Com_Printf ("Too long an expanded string.\n", LOG_CLIENT);
1288 return;
1289 }
1290
1291 for (i = 0; i < count; i++)
1292 strcat (cmdBuff, what);
1293
1294 strcat (cmdBuff, "\n");
1295
1296 MSG_WriteByte (clc_stringcmd);
1297 MSG_WriteString (cmdBuff);
1298 MSG_EndWriting (&cls.netchan.message);
1299
1300 Com_Printf ("Wrote %d bytes stringcmd.\n", LOG_CLIENT, count * strlen(what));
1301 }
1302 #endif
1303
1304 /*
1305 ====================
1306 CL_Packet_f
1307
1308 packet <destination> <contents>
1309
1310 Contents allows \n escape character
1311 ====================
1312 */
1313 #ifdef _DEBUG
CL_Packet_f(void)1314 void CL_Packet_f (void)
1315 {
1316 char send[2048];
1317 int i, l;
1318 char *in, *out;
1319 netadr_t adr;
1320
1321 if (Cmd_Argc() < 3)
1322 {
1323 Com_Printf ("packet <destination> <contents>\n", LOG_CLIENT);
1324 return;
1325 }
1326
1327 NET_Config (NET_CLIENT); // allow remote
1328
1329 if (!NET_StringToAdr (Cmd_Argv(1), &adr))
1330 {
1331 Com_Printf ("Bad address\n", LOG_CLIENT);
1332 return;
1333 }
1334
1335 if (!adr.port)
1336 adr.port = ShortSwap (PORT_SERVER);
1337
1338 in = Cmd_Args2(2);
1339 out = send+4;
1340
1341 *(int *)send = -1;
1342
1343 l = (int)strlen (in);
1344 for (i=0 ; i<l ; i++)
1345 {
1346 if (out - send >= sizeof(send)-1)
1347 break;
1348
1349 if (in[i] == '\\' && in[i+1] == 'n')
1350 {
1351 *out++ = '\n';
1352 i++;
1353 }
1354 else
1355 *out++ = in[i];
1356 }
1357 *out = 0;
1358
1359 incoming_allowed[incoming_allowed_index & 15].remote = adr;
1360 incoming_allowed[incoming_allowed_index & 15].type = CL_UNDEF;
1361 incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1362 incoming_allowed_index++;
1363
1364 NET_SendPacket (NS_CLIENT, (int)(out-send), send, &adr);
1365 }
1366 #endif
1367
1368 /*
1369 =================
1370 CL_Changing_f
1371
1372 Just sent as a hint to the client that they should
1373 drop to full console
1374 =================
1375 */
CL_Changing_f(void)1376 void CL_Changing_f (void)
1377 {
1378 char *cmd;
1379
1380 //ZOID
1381 //if we are downloading, we don't change! This so we don't suddenly stop downloading a map
1382 if (cls.download)
1383 return;
1384
1385 if (cls.demofile)
1386 CL_EndRecording();
1387
1388 //force screen update in case user has screenshot etc they want doing
1389 SCR_UpdateScreen ();
1390
1391 cmd = Cmd_MacroExpandString("$cl_endmapcmd");
1392 if (cmd)
1393 {
1394 if (cmd[0])
1395 {
1396 char *p;
1397 //note, can't use Cmd_ExecuteString as it doesn't handle ;
1398 //but we must otherwise we don't run immediately!
1399 p = cmd;
1400
1401 for (;;)
1402 {
1403 p = strchr (p, ';');
1404 if (p)
1405 {
1406 p[0] = 0;
1407 Cmd_ExecuteString (cmd);
1408 p = cmd = p + 1;
1409 if (!cmd[0])
1410 break;
1411 }
1412 else
1413 {
1414 Cmd_ExecuteString (cmd);
1415 break;
1416 }
1417 }
1418 }
1419 }
1420 else
1421 Com_Printf ("WARNING: Error expanding $cl_endmapcmd, ignored.\n", LOG_CLIENT|LOG_WARNING);
1422
1423 //r1: prevent manual issuing crashing game
1424 if (cls.state < ca_connected)
1425 return;
1426
1427 //r1: stop loading models right now
1428 deferred_model_index = MAX_MODELS;
1429
1430 SCR_BeginLoadingPlaque ();
1431 cls.state = ca_connected; // not active anymore, but not disconnected
1432 Com_Printf ("\nChanging map...\n", LOG_CLIENT);
1433
1434 //shut up
1435 noFrameFromServerPacket = 0;
1436 }
1437
1438
1439 /*
1440 =================
1441 CL_ParseStatusMessage
1442
1443 Handle a reply from a ping
1444 =================
1445 */
CL_ParseStatusMessage(void)1446 void CL_ParseStatusMessage (void)
1447 {
1448 char *s;
1449
1450 s = MSG_ReadString (&net_message);
1451
1452 Com_Printf ("%s\n", LOG_CLIENT, s);
1453 M_AddToServerList (net_from, s);
1454 }
1455
1456 //FIXME: add this someday
1457 /*void CL_GetServers_f (void)
1458 {
1459 netadr_t adr;
1460
1461 if (Cmd_Argc() < 2)
1462 {
1463 Com_Printf ("Usage: getservers <master>\n", LOG_GENERAL);
1464 return;
1465 }
1466
1467 NET_StringToAdr (Cmd_Argv(1), &adr);
1468
1469 Netchan_OutOfBandPrint (NS_CLIENT, &adr, "getservers");
1470
1471 //allow remote response
1472 incoming_allowed[incoming_allowed_index & 15].remote = to;
1473 incoming_allowed[incoming_allowed_index & 15].type = CL_MASTER_QUERY;
1474 incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1475 incoming_allowed_index++;
1476 }*/
1477
1478 /*
1479 =================
1480 CL_PingServers_f
1481 =================
1482 */
CL_PingServers_f(void)1483 void CL_PingServers_f (void)
1484 {
1485 int i;
1486 netadr_t adr;
1487 char name[16];
1488 const char *adrstring;
1489 //cvar_t *noudp;
1490 //cvar_t *noipx;
1491
1492 NET_Config (NET_CLIENT); // allow remote
1493
1494 // send a broadcast packet
1495 Com_Printf ("pinging broadcast...\n", LOG_CLIENT|LOG_NOTICE);
1496
1497 adr.type = NA_BROADCAST;
1498 adr.port = ShortSwap(PORT_SERVER);
1499
1500 //r1: only ping original, r1q2 servers respond to either, but 3.20 server would
1501 //reply with errors on receiving enhanced info
1502 Netchan_OutOfBandPrint (NS_CLIENT, &adr, "info 34\n");
1503
1504 //also ping local server
1505 adr.type = NA_IP;;
1506 *(int *)&adr.ip = 0x100007F;
1507 adr.port = ShortSwap(PORT_SERVER);
1508 Netchan_OutOfBandPrint (NS_CLIENT, &adr, "info 34\n");
1509
1510 // send a packet to each address book entry
1511 for (i=0 ; i < 32; i++)
1512 {
1513 Com_sprintf (name, sizeof(name), "adr%i", i);
1514 adrstring = Cvar_VariableString (name);
1515 if (!adrstring || !adrstring[0])
1516 continue;
1517
1518 Com_Printf ("pinging %s...\n", LOG_CLIENT|LOG_NOTICE, adrstring);
1519 if (!NET_StringToAdr (adrstring, &adr))
1520 {
1521 Com_Printf ("Bad address: %s\n", LOG_CLIENT, adrstring);
1522 continue;
1523 }
1524 if (!adr.port)
1525 adr.port = ShortSwap(PORT_SERVER);
1526 //Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_R1Q2));
1527 Netchan_OutOfBandPrint (NS_CLIENT, &adr, "info %i\n", PROTOCOL_ORIGINAL);
1528 }
1529 }
1530
1531
1532 /*
1533 =================
1534 CL_Skins_f
1535
1536 Load or download any custom player skins and models
1537 =================
1538 */
1539
CL_Skins_f(void)1540 void CL_Skins_f (void)
1541 {
1542 int i;
1543
1544 for (i=0 ; i < MAX_CLIENTS ; i++)
1545 {
1546 if (!cl.configstrings[CS_PLAYERSKINS+i][0])
1547 continue;
1548 //if (developer->intvalue)
1549 Com_Printf ("client %i: %s\n", LOG_CLIENT, i, cl.configstrings[CS_PLAYERSKINS+i]);
1550 //SCR_UpdateScreen ();
1551 //Sys_SendKeyEvents (); // pump message loop
1552 CL_ParseClientinfo (i);
1553 }
1554 //Com_Printf ("Precached all skins.\n", LOG_CLIENT);
1555 }
1556
CL_ProxyPacket(void)1557 void CL_ProxyPacket (void)
1558 {
1559 char *s;
1560 char *c;
1561
1562 if (cls.proxyState != ps_pending)
1563 return;
1564
1565 MSG_BeginReading (&net_message);
1566 MSG_ReadLong (&net_message); // skip the -2
1567
1568 s = MSG_ReadStringLine (&net_message);
1569
1570 Cmd_TokenizeString (s, false);
1571
1572 c = Cmd_Argv(0);
1573
1574 if (!strcmp (c, "proxychallenge"))
1575 {
1576 netadr_t remote_addr;
1577 unsigned long challenge = strtoul(Cmd_Argv(1), NULL, 10);
1578
1579 if (!NET_StringToAdr (cls.servername, &remote_addr))
1580 return;
1581
1582 if (remote_addr.port == 0)
1583 remote_addr.port = ShortSwap (PORT_SERVER);
1584
1585 Netchan_OutOfBandProxyPrint (NS_CLIENT, &cls.proxyAddr, "proxyconnect %lu %s\n", challenge, NET_AdrToString (&remote_addr));
1586 }
1587 else if (!strcmp (c, "proxyactive"))
1588 {
1589 int port;
1590
1591 port = atoi (Cmd_Argv(1));
1592
1593 if (port >= 1024)
1594 net_from.port = ShortSwap(port);
1595
1596 NET_SetProxy (&net_from);
1597 cls.proxyAddr = net_from;
1598 cls.proxyState = ps_active;
1599 cls.connect_time = -99999; // fire immediately
1600 }
1601 }
1602
1603 /*
1604 =================
1605 CL_ConnectionlessPacket
1606
1607 Responses to broadcasts, etc
1608 =================
1609 */
CL_ConnectionlessPacket(void)1610 void CL_ConnectionlessPacket (void)
1611 {
1612 int type;
1613 int i;
1614
1615 char *s;
1616 char *c;
1617
1618 netadr_t remote_addr;
1619 netadr_t *remote;
1620
1621 MSG_BeginReading (&net_message);
1622 MSG_ReadLong (&net_message); // skip the -1
1623
1624 s = MSG_ReadStringLine (&net_message);
1625
1626 NET_StringToAdr (cls.servername, &remote_addr);
1627
1628 remote = &remote_addr;
1629
1630 Cmd_TokenizeString (s, false);
1631
1632 c = Cmd_Argv(0);
1633
1634 type = CL_UNDEF;
1635
1636 //r1: server responding to a status broadcast (ignores security check due
1637 //to broadcasts responding)
1638 if (!strcmp(c, "info"))
1639 {
1640 if (cls.state == ca_disconnected || cls.key_dest != key_game)
1641 CL_ParseStatusMessage ();
1642 return;
1643 }
1644 else if (!strcmp (c, "passive_connect"))
1645 {
1646 if (!cls.passivemode)
1647 {
1648 Com_DPrintf ("Illegal %s from %s. Ignored.\n", c, NET_AdrToString (&net_from));
1649 }
1650 else
1651 {
1652 Com_Printf ("Received %s from %s -- beginning connection.\n", LOG_CLIENT, c, NET_AdrToString (&net_from));
1653 strcpy (cls.servername, NET_AdrToString (&net_from));
1654 cls.state = ca_connecting;
1655 cls.connect_time = -99999;
1656 cls.passivemode = false;
1657 }
1658 return;
1659 }
1660
1661 //r1: security check. (only allow from current connected server
1662 //and last destinations client sent a packet to)
1663 for (i = 0; i < sizeof(incoming_allowed) / sizeof(incoming_allowed[0]); i++)
1664 {
1665 if (incoming_allowed[i].time > cls.realtime && NET_CompareBaseAdr (&net_from, &incoming_allowed[i].remote))
1666 {
1667 type = incoming_allowed[i].type;
1668 //invalidate
1669 incoming_allowed[i].time = 0;
1670 goto safe;
1671 }
1672 }
1673
1674 if (!NET_CompareBaseAdr (&net_from, remote) && !NET_CompareBaseAdr (&net_from, &cls.proxyAddr))
1675 {
1676 Com_DPrintf ("Illegal %s from %s. Ignored.\n", c, NET_AdrToString (&net_from));
1677 return;
1678 }
1679
1680 safe:
1681
1682 if (type == CL_UNDEF)
1683 {
1684 //r1: spamfix: don't echo packet type mid-game
1685 if (cls.state < ca_active)
1686 Com_Printf ("%s: %s\n", LOG_CLIENT, NET_AdrToString (&net_from), c);
1687 }
1688
1689 // server connection
1690 if (!strcmp(c, "client_connect"))
1691 {
1692 #ifdef ANTICHEAT
1693 qboolean try_to_use_anticheat;
1694 #endif
1695 int i;
1696 char *buff, *p;
1697
1698 if (cls.state == ca_connected)
1699 {
1700 Com_DPrintf ("Dup connect received. Ignored.\n");
1701 return;
1702
1703 //r1: note, next 2 should be redundant with the above global check.
1704 } else if (cls.state == ca_disconnected) {
1705 //FIXME: this should never happen (disconnecting nukes remote, no remote
1706 //= no packet)
1707 Com_DPrintf ("Received connect when disconnected. Ignored.\n");
1708 return;
1709 } else if (cls.state == ca_active) {
1710 Com_DPrintf ("Illegal connect when already connected !! (q2msgs?). Ignored.\n");
1711 return;
1712 }
1713
1714 Com_DPrintf ("client_connect: new\n");
1715
1716 Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, cls.serverProtocol, cls.quakePort, 0);
1717
1718 buff = NET_AdrToString(&cls.netchan.remote_address);
1719
1720 #ifdef ANTICHEAT
1721 try_to_use_anticheat = false;
1722 #endif
1723
1724 for (i = 1; i < Cmd_Argc(); i++)
1725 {
1726 p = Cmd_Argv(i);
1727 if (!strncmp (p, "dlserver=", 9))
1728 {
1729 #ifdef USE_CURL
1730 p += 9;
1731 Com_sprintf (cls.downloadReferer, sizeof(cls.downloadReferer), "quake2://%s", buff);
1732 CL_SetHTTPServer (p);
1733 if (cls.downloadServer[0])
1734 Com_Printf ("HTTP downloading enabled, URL: %s\n", LOG_CLIENT|LOG_NOTICE, cls.downloadServer);
1735 #else
1736 Com_Printf ("HTTP downloading supported by server but this client was built without USE_CURL, bad luck.\n", LOG_CLIENT);
1737 #endif
1738 }
1739 #ifdef ANTICHEAT
1740 else if (!strncmp (p, "ac=", 3))
1741 {
1742 p+= 3;
1743 if (!p[0])
1744 continue;
1745 if (atoi (p))
1746 try_to_use_anticheat = true;
1747 }
1748 #endif
1749 }
1750
1751 //note, not inside the loop as we could potentially clobber cmd_argc/cmd_argv
1752 #ifdef ANTICHEAT
1753 if (try_to_use_anticheat)
1754 {
1755 MSG_WriteByte (clc_nop);
1756 MSG_EndWriting (&cls.netchan.message);
1757 Netchan_Transmit (&cls.netchan, 0, NULL);
1758 S_StopAllSounds ();
1759 con.ormask = 128;
1760 Com_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n", LOG_CLIENT);
1761 Com_Printf ("Loading anticheat, this may take a few moments...\n", LOG_GENERAL);
1762 Com_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n", LOG_CLIENT);
1763 con.ormask = 0;
1764 SCR_UpdateScreen ();
1765 SCR_UpdateScreen ();
1766 SCR_UpdateScreen ();
1767 if (!Sys_GetAntiCheatAPI ())
1768 Com_Printf (" ERROR: anticheat.dll failed to load\n"
1769 "Either the file is missing, or something is preventing\n"
1770 "it from loading or connecting to the anticheat server.\n"
1771 "This is commonly caused by over-aggressive anti-virus\n"
1772 "software. Try disabling any anti-virus or other security\n"
1773 "software or add an exception for anticheat.dll. Make sure\n"
1774 "your system date/time is set correctly as this can prevent\n"
1775 "anticheat from working.\n"
1776 "\n"
1777 "Trying to connect without anticheat support.\n", LOG_GENERAL);
1778 }
1779 #endif
1780 p = strchr (buff, ':');
1781 if (p)
1782 p[0] = 0;
1783 Cvar_ForceSet ("$serverip", buff);
1784
1785 MSG_WriteByte (clc_stringcmd);
1786 MSG_WriteString ("new");
1787 MSG_EndWriting (&cls.netchan.message);
1788
1789 cls.key_dest = key_game;
1790
1791 //CL_FixCvarCheats();
1792 send_packet_now = true;
1793 cls.state = ca_connected;
1794 return;
1795 }
1796
1797 // remote command from gui front end
1798 // r1: removed, this is dangerous to leave in with new NET_IsLocalAddress code.
1799 /*if (!strcmp(c, "cmd"))
1800 {
1801 if (!NET_IsLocalAddress(&net_from))
1802 {
1803 Com_DPrintf ("Command packet from remote host. Ignored.\n");
1804 return;
1805 }
1806 Sys_AppActivate ();
1807 s = MSG_ReadString (&net_message);
1808 Cbuf_AddText (s);
1809 Cbuf_AddText ("\n");
1810 return;
1811 }*/
1812
1813 // print command from somewhere
1814 if (!strcmp(c, "print"))
1815 {
1816 s = MSG_ReadString (&net_message);
1817
1818 switch (type)
1819 {
1820 case CL_RCON:
1821 //rcon can come in multiple packets
1822 incoming_allowed[incoming_allowed_index & 15].remote = net_from;
1823 incoming_allowed[incoming_allowed_index & 15].type = CL_RCON;
1824 incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1825 incoming_allowed_index++;
1826
1827 //intentional fallthrough
1828 case CL_SERVER_INFO:
1829 Com_Printf ("%s", LOG_CLIENT, s);
1830 break;
1831 case CL_SERVER_STATUS:
1832 case CL_SERVER_STATUS_FULL:
1833 {
1834 char *p;
1835 char *player_name;
1836 char *player_score;
1837 char *player_ping;
1838
1839 //skip the serverinfo
1840 if (type == CL_SERVER_STATUS)
1841 {
1842 p = strstr (s, "\n");
1843 if (!p)
1844 return;
1845 else
1846 p++;
1847 }
1848 else
1849 {
1850 int flip;
1851
1852 Com_Printf ("Server Info\n"
1853 "-----------\n", LOG_CLIENT);
1854
1855 flip = 0;
1856 p = s + 1;
1857
1858 //make it more readable
1859 while (p[0])
1860 {
1861 if (p[0] == '\\')
1862 {
1863 if (flip)
1864 p[0] = '\n';
1865 else
1866 p[0] = '=';
1867 flip ^= 1;
1868 }
1869 else if (p[0] == '\n')
1870 {
1871 while (p[0] && p[0] == '\n')
1872 {
1873 p[0] = 0;
1874 p++;
1875 }
1876 break;
1877 }
1878 p++;
1879 }
1880 Com_Printf ("%s\n", LOG_CLIENT, s+1);
1881 }
1882
1883 Com_Printf ("Name Score Ping\n"
1884 "--------------- ----- ----\n", LOG_CLIENT);
1885
1886 /*
1887 0 0 "OCLD"
1888 86 86 "a vehicle"
1889 81 28 "1ST-Timer"
1890 27 51 "[DSH]Fella"
1891 86 24 "[MCB]Jonny"
1892 */
1893 //icky icky parser
1894 while (p[0])
1895 {
1896 if (isdigit (p[0]))
1897 {
1898 player_score = p;
1899 p = strchr (player_score, ' ');
1900 if (p)
1901 {
1902 p[0] = 0;
1903 p++;
1904 if (isdigit (p[0]))
1905 {
1906 player_ping = p;
1907 p = strchr (player_ping, ' ');
1908 if (p)
1909 {
1910 p[0] = 0;
1911 p++;
1912 if (p[0] == '"')
1913 {
1914 player_name = p + 1;
1915 p = strchr (player_name, '"');
1916 if (p)
1917 {
1918 p[0] = 0;
1919 p++;
1920 if (p[0] == '\n')
1921 {
1922 Com_Printf ("%-15s %5s %4s\n", LOG_CLIENT, player_name, player_score, player_ping);
1923 p++;
1924 } else return;
1925 } else return;
1926 } else return;
1927 } else return;
1928 } else return;
1929 } else return;
1930 } else return;
1931 }
1932 }
1933 break;
1934 default:
1935 //BIG HACK to allow new client on old server!
1936
1937 //r1: spoofed spam fix, only allow print during connection process, other legit cases should be handled above
1938 if (cls.state == ca_connecting || cls.state == ca_connected)
1939 Com_Printf ("%s", LOG_CLIENT, s);
1940
1941 //r1: security fix: only do protocol fallback when connecting.
1942 if (cls.state == ca_connecting)
1943 {
1944 if (!strstr (s, "full") &&
1945 !strstr (s, "locked") &&
1946 !strncmp (s, "Server is ", 10) &&
1947 cls.serverProtocol != PROTOCOL_ORIGINAL)
1948 {
1949 Com_Printf ("Retrying with protocol %d.\n", LOG_CLIENT, PROTOCOL_ORIGINAL);
1950 cls.serverProtocol = PROTOCOL_ORIGINAL;
1951 //force immediate retry
1952 cls.connect_time = -99999;
1953
1954 }
1955 }
1956 break;
1957 }
1958 return;
1959 }
1960
1961 // ping from somewhere
1962
1963 //r1: removed, could be used for flooding modemers
1964 //r1: tested, can even lag out a cable connect !
1965
1966 /*if (!strcmp(c, "ping"))
1967 {
1968 Netchan_OutOfBandPrint (NS_CLIENT, net_from, "ack");
1969 return;
1970 }*/
1971
1972 // challenge from the server we are connecting to
1973 if (!strcmp(c, "challenge"))
1974 {
1975 int protocol = PROTOCOL_ORIGINAL;
1976 int i;
1977 char *p;
1978
1979 if (cls.state != ca_connecting)
1980 {
1981 Com_DPrintf ("Illegal challenge from server whilst already connected, ignored.\n");
1982 return;
1983 }
1984
1985 cls.challenge = atoi(Cmd_Argv(1));
1986
1987 // available protocol versions now in challenge, until few months we still default to brute.
1988 for (i = 2; i < Cmd_Argc(); i++)
1989 {
1990 p = Cmd_Argv(i);
1991 if (!strncmp (p, "p=", 2))
1992 {
1993 p += 2;
1994 if (!p[0])
1995 continue;
1996 for (;;)
1997 {
1998 i = atoi (p);
1999 if (i == PROTOCOL_R1Q2)
2000 protocol = i;
2001 p = strchr(p, ',');
2002 if (!p)
2003 break;
2004 p++;
2005 if (!p[0])
2006 break;
2007 }
2008 break;
2009 }
2010 }
2011
2012 //r1: reset the timer so we don't send dup. getchallenges
2013 cls.connect_time = cls.realtime;
2014 CL_SendConnectPacket (protocol);
2015 return;
2016 }
2017
2018 // echo request from server
2019 //r1: removed, no real purpose and possible attack vector
2020 /*if (!strcmp(c, "echo"))
2021 {
2022 Netchan_OutOfBandPrint (NS_CLIENT, &net_from, "%s", Cmd_Argv(1) );
2023 return;
2024 }*/
2025
2026 Com_DPrintf ("Unknown connectionless packet command %s\n", c);
2027 }
2028
2029 /*
2030 =================
2031 CL_ReadPackets
2032 =================
2033 */
CL_ReadPackets(void)2034 void CL_ReadPackets (void)
2035 {
2036 int i;
2037
2038 #ifdef _DEBUG
2039 memset (net_message_buffer, 31, sizeof(net_message_buffer));
2040 #endif
2041
2042 while ((i = (NET_GetPacket (NS_CLIENT, &net_from, &net_message))))
2043 {
2044 //
2045 // remote command packet
2046 //
2047 if (*(int *)net_message_buffer == -1)
2048 {
2049 if (i == -1 && cls.key_dest != key_game)
2050 Com_Printf ("Port unreachable from %s\n", LOG_CLIENT|LOG_NOTICE, NET_AdrToString (&net_from));
2051 else
2052 CL_ConnectionlessPacket ();
2053 continue;
2054 }
2055 else if (*(int *)net_message_buffer == -2)
2056 {
2057 CL_ProxyPacket ();
2058 continue;
2059 }
2060
2061 if (cls.state <= ca_connecting)
2062 continue; // dump it if not connected
2063
2064 if (net_message.cursize < 8)
2065 {
2066 //r1: delegated to DPrintf (someone could spam your console with crap otherwise)
2067 Com_DPrintf ("%s: Runt packet\n",NET_AdrToString(&net_from));
2068 continue;
2069 }
2070
2071 //
2072 // packet from server
2073 //
2074 if (!NET_CompareAdr (&net_from, &cls.netchan.remote_address))
2075 {
2076 Com_DPrintf ("%s:sequenced packet without connection\n"
2077 ,NET_AdrToString(&net_from));
2078 continue;
2079 }
2080
2081 if (i == -1)
2082 Com_Error (ERR_HARD, "Connection reset by peer.");
2083
2084 if (!Netchan_Process(&cls.netchan, &net_message))
2085 continue; // wasn't accepted for some reason
2086
2087 if (cls.netchan.got_reliable)
2088 {
2089 if (cls.state == ca_connected)
2090 send_packet_now = true;
2091 else if (cl_instantack->intvalue)
2092 Netchan_Transmit (&cls.netchan, 0, NULL);
2093
2094 if (cl_shownet->intvalue == 1)
2095 Com_Printf ("r", LOG_CLIENT);
2096 }
2097
2098 if (CL_ParseServerMessage ())
2099 {
2100 CL_AddNetgraph ();
2101
2102 if (scr_sizegraph->intvalue)
2103 CL_AddSizegraph ();
2104 }
2105
2106 //
2107 // we don't know if it is ok to save a demo message until
2108 // after we have parsed the frame
2109 //
2110 if (cls.demorecording && !cls.demowaiting && cls.serverProtocol == PROTOCOL_ORIGINAL)
2111 CL_WriteDemoMessageFull ();
2112 }
2113
2114 //
2115 // check timeout
2116 //
2117 if (cls.state >= ca_connected
2118 && cls.realtime - cls.netchan.last_received > cl_timeout->intvalue*1000)
2119 {
2120 if (++cl.timeoutcount > 5) // timeoutcount saves debugger
2121 {
2122 Com_Printf ("\nServer connection timed out.\n", LOG_CLIENT);
2123 #ifdef _DEBUG
2124 CL_Reconnect_f ();
2125 #else
2126 CL_Disconnect (false);
2127 #endif
2128 return;
2129 }
2130 }
2131 else
2132 cl.timeoutcount = 0;
2133
2134 }
2135
2136
2137 //=============================================================================
2138
2139 /*
2140 ==============
2141 CL_FixUpGender_f
2142 ==============
2143 */
2144
2145 //r1: FIXME: this seems.. wrong. yet another excuse for a client dll.
CL_FixUpGender(void)2146 void CL_FixUpGender(void)
2147 {
2148 char *p;
2149 char sk[80];
2150
2151 if (gender_auto->intvalue) {
2152
2153 if (gender->modified) {
2154 // was set directly, don't override the user
2155 gender->modified = false;
2156 return;
2157 }
2158
2159 Q_strncpy(sk, skin->string, sizeof(sk) - 1);
2160 if ((p = strchr(sk, '/')) != NULL)
2161 p[0] = 0;
2162 if (Q_stricmp(sk, "male") == 0 || Q_stricmp(sk, "cyborg") == 0)
2163 Cvar_Set ("gender", "male");
2164 else if (Q_stricmp(sk, "female") == 0 || Q_stricmp(sk, "crackhor") == 0)
2165 Cvar_Set ("gender", "female");
2166 else
2167 Cvar_Set ("gender", "none");
2168 gender->modified = false;
2169 }
2170 }
2171
2172 /*
2173 ==============
2174 CL_Userinfo_f
2175 ==============
2176 */
CL_Userinfo_f(void)2177 void CL_Userinfo_f (void)
2178 {
2179 Com_Printf ("User info settings:\n", LOG_CLIENT);
2180 Info_Print (Cvar_Userinfo());
2181 }
2182
2183 /*
2184 =================
2185 CL_Snd_Restart_f
2186
2187 Restart the sound subsystem so it can pick up
2188 new parameters and flush all sounds
2189 =================
2190 */
CL_Snd_Restart_f(void)2191 void CL_Snd_Restart_f (void)
2192 {
2193 S_Shutdown ();
2194
2195 if (Cmd_Argc() == 1)
2196 S_Init (-1);
2197 else
2198 S_Init (atoi(Cmd_Argv(1)));
2199
2200 memset (cl.sound_precache, 0, sizeof(cl.sound_precache));
2201 CL_RegisterSounds ();
2202 }
2203
2204 int precache_check; // for autodownload of precache items
2205 int precache_spawncount;
2206 int precache_tex;
2207 int precache_model_skin;
2208 uint32 precache_start_time;
2209
2210 static byte *precache_model; // used for skin checking in alias models
2211
2212 //#define PLAYER_MULT 5
2213
2214 // ENV_CNT is map load, ENV_CNT+1 is first env map
2215 #define ENV_CNT (CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
2216 #define TEXTURE_CNT (ENV_CNT+13)
2217
2218 static const char *env_suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
2219
2220 typedef struct cl_location_s
2221 {
2222 struct cl_location_s *next;
2223 char *name;
2224 vec3_t location;
2225 } cl_location_t;
2226
2227 static cl_location_t cl_locations;
2228
CL_Loc_Init(void)2229 void CL_Loc_Init (void)
2230 {
2231 memset (&cl_locations, 0, sizeof(cl_locations));
2232 }
2233
CL_FreeLocs(void)2234 void CL_FreeLocs (void)
2235 {
2236 cl_location_t *loc = &cl_locations,
2237 *old = NULL;
2238
2239 while (loc->next)
2240 {
2241 loc = loc->next;
2242 if (old)
2243 {
2244 Z_Free (old->name);
2245 Z_Free (old);
2246 }
2247 old = loc;
2248 }
2249
2250 if (old)
2251 {
2252 Z_Free (old->name);
2253 Z_Free (old);
2254 }
2255
2256 cl_locations.next = NULL;
2257 }
2258
2259 //you wanted locs, you got em... dear god this is terrible code :)
CL_LoadLoc(const char * filename)2260 qboolean CL_LoadLoc (const char *filename)
2261 {
2262 char *locBuffer;
2263 cl_location_t *loc = &cl_locations;
2264 char *x, *y, *z, *name, *line;
2265 int linenum;
2266 int len;
2267 FILE *handle;
2268 qboolean closeFile;
2269
2270 CL_FreeLocs ();
2271
2272 len = FS_LoadFile ((char *)filename, NULL);
2273
2274 if (len == -1)
2275 {
2276 Com_DPrintf ("CL_LoadLoc: %s not found\n", filename);
2277 return false;
2278 }
2279
2280 FS_FOpenFile (filename, &handle, HANDLE_OPEN, &closeFile);
2281 if (!handle)
2282 {
2283 Com_Printf ("CL_LoadLoc: couldn't open %s\n", LOG_CLIENT|LOG_WARNING, filename);
2284 return false;
2285 }
2286
2287 locBuffer = Z_TagMalloc (len+2, TAGMALLOC_CLIENT_LOC);
2288 //locBuffer = alloca (len+2);
2289 FS_Read (locBuffer, len, handle);
2290
2291 if (closeFile)
2292 FS_FCloseFile (handle);
2293
2294 //terminate if no EOL
2295 locBuffer[len-1] = '\n';
2296 locBuffer[len] = 0;
2297
2298 linenum = 0;
2299
2300 line = locBuffer;
2301
2302 for (;;)
2303 {
2304 //eof
2305 if (line - locBuffer >= len)
2306 break;
2307
2308 //skip whitespace
2309 while (*line && (*line == ' ' || *line == '\t' || *line == '\r' || *line == '\n'))
2310 {
2311 line++;
2312 if (line - locBuffer >= len)
2313 break;
2314 }
2315
2316 //eof now?
2317 if (line - locBuffer >= len)
2318 break;
2319
2320 linenum++;
2321 x = line;
2322
2323 name = y = z = NULL;
2324
2325 while (*line)
2326 {
2327 if (*line == ' ' || *line == '\t') {
2328 *line++ = '\0';
2329 y = line;
2330 break;
2331 }
2332 line++;
2333 if (line - locBuffer >= len)
2334 break;
2335 }
2336
2337 while (*line)
2338 {
2339 if (*line == ' ' || *line == '\t') {
2340 *line++ = '\0';
2341 z = line;
2342 break;
2343 }
2344 line++;
2345 if (line - locBuffer >= len)
2346 break;
2347 }
2348
2349 if (line - locBuffer >= len)
2350 break;
2351
2352 while (*line)
2353 {
2354 if (*line == ' ' || *line == '\t')
2355 {
2356 *line++ = '\0';
2357 name = line;
2358 break;
2359 }
2360 line++;
2361 if (line - locBuffer >= len)
2362 break;
2363 }
2364
2365 if (line - locBuffer >= len)
2366 break;
2367
2368 while (*line)
2369 {
2370 if (*line == '\n' || *line == '\r') {
2371 *line = '\0';
2372 break;
2373 }
2374 line++;
2375 if (line - locBuffer >= len)
2376 break;
2377 }
2378
2379 if (!*x || !y || !*y || !z || !*z || !name || !*name)
2380 {
2381 Com_Printf ("CL_LoadLoc: Parse error on line %d of '%s'.\n", LOG_CLIENT, linenum, filename);
2382 break;
2383 }
2384
2385 loc->next = Z_TagMalloc (sizeof(cl_location_t), TAGMALLOC_CLIENT_LOC);
2386 loc = loc->next;
2387
2388 loc->next = NULL;
2389 loc->name = CopyString (name, TAGMALLOC_CLIENT_LOC);
2390 VectorSet (loc->location, (float)atoi(x)/8.0f, (float)atoi(y)/8.0f, (float)atoi(z)/8.0f);
2391
2392 //Com_DPrintf ("CL_AddLoc: adding location '%s'\n", name);
2393
2394 //advance past \0
2395 line++;
2396 }
2397
2398 Com_Printf ("Read %d map locations from '%s'\n", LOG_CLIENT, linenum, filename);
2399
2400 Z_Free (locBuffer);
2401 return true;
2402 }
2403
CL_Loc_Get(vec3_t org)2404 const char *CL_Loc_Get (vec3_t org)
2405 {
2406 vec3_t distance;
2407 uint32 length, bestlength = 0xFFFFFFFF;
2408 cl_location_t *loc = &cl_locations, *best = &cl_locations;
2409
2410 Q_assert (cl_locations.next);
2411
2412 while (loc->next)
2413 {
2414 loc = loc->next;
2415
2416 VectorSubtract (loc->location, org, distance);
2417 length = (int)VectorLength (distance);
2418
2419 if (length < bestlength)
2420 {
2421 best = loc;
2422 bestlength = length;
2423 }
2424 }
2425
2426 return best->name;
2427 }
2428
CL_AddLoc_f(void)2429 void CL_AddLoc_f (void)
2430 {
2431 cl_location_t *loc, *last, *newentry;
2432
2433 if (Cmd_Argc() < 2)
2434 {
2435 Com_Printf ("Purpose: Add a new location.\n"
2436 "Syntax : addloc <name>\n"
2437 "Example: addloc red base\n", LOG_CLIENT);
2438 return;
2439 }
2440
2441 if (cls.state < ca_active)
2442 {
2443 Com_Printf ("Not connected.\n", LOG_GENERAL);
2444 return;
2445 }
2446
2447 loc = &cl_locations;
2448
2449 last = loc->next;
2450
2451 newentry = Z_TagMalloc (sizeof(*loc), TAGMALLOC_CLIENT_LOC);
2452 newentry->name = CopyString (Cmd_Args(), TAGMALLOC_CLIENT_LOC);
2453 FastVectorCopy (cl.refdef.vieworg, newentry->location);
2454 newentry->next = last;
2455 loc->next = newentry;
2456
2457 Com_Printf ("Location '%s' added at (%d, %d, %d).\n", LOG_CLIENT, newentry->name, (int)cl.refdef.vieworg[0]*8, (int)cl.refdef.vieworg[1]*8, (int)cl.refdef.vieworg[2]*8);
2458 }
2459
CL_SaveLoc_f(void)2460 void CL_SaveLoc_f (void)
2461 {
2462 cl_location_t *loc = &cl_locations;
2463 FILE *out;
2464
2465 char locbuff[MAX_QPATH+4];
2466 char locfile[MAX_OSPATH];
2467
2468 if (!cl_locations.next)
2469 {
2470 Com_Printf ("No locations defined.\n", LOG_GENERAL);
2471 return;
2472 }
2473
2474 COM_StripExtension (cl.configstrings[CS_MODELS+1], locbuff);
2475 strcat (locbuff, ".loc");
2476
2477 Com_sprintf (locfile, sizeof(locfile), "%s/%s", FS_Gamedir(), locbuff);
2478
2479 out = fopen (locfile, "wb");
2480 if (!out)
2481 {
2482 Com_Printf ("CL_SaveLoc_f: Unable to open '%s' for writing.\n", LOG_CLIENT, locfile);
2483 return;
2484 }
2485
2486 while (loc->next)
2487 {
2488 loc = loc->next;
2489 fprintf (out, "%i %i %i %s\n", (int)loc->location[0]*8, (int)loc->location[1]*8, (int)loc->location[2]*8, loc->name);
2490 }
2491
2492 fclose (out);
2493 Com_Printf ("Locations saved to '%s'.\n", LOG_CLIENT, locbuff);
2494 }
2495
2496 void vectoangles2 (vec3_t value1, vec3_t angles);
2497
CL_Get_Loc_There(void)2498 const char *CL_Get_Loc_There (void)
2499 {
2500 trace_t tr;
2501 vec3_t end;
2502
2503 if (!cl_locations.next)
2504 return cl_default_location->string;
2505
2506 end[0] = cl.refdef.vieworg[0] + cl.v_forward[0] * 65556 + cl.v_right[0];
2507 end[1] = cl.refdef.vieworg[1] + cl.v_forward[1] * 65556 + cl.v_right[1];
2508 end[2] = cl.refdef.vieworg[2] + cl.v_forward[2] * 65556 + cl.v_right[2];
2509
2510 tr = CM_BoxTrace (cl.refdef.vieworg, end, vec3_origin, vec3_origin, 0, MASK_SOLID);
2511
2512 if (tr.fraction != 1.0f)
2513 return CL_Loc_Get (tr.endpos);
2514
2515 return CL_Loc_Get (end);
2516 }
2517
CL_Get_Loc_Here(void)2518 const char *CL_Get_Loc_Here (void)
2519 {
2520 if (!cl_locations.next)
2521 return cl_default_location->string;
2522
2523 return CL_Loc_Get (cl.refdef.vieworg);
2524 }
2525
2526 /*void CL_Say_Preprocessor (void)
2527 {
2528 char *location_name, *p;
2529 char *say_text;
2530
2531 say_text = p = Cmd_Args();
2532
2533 if (cl_locations.next)
2534 {
2535 while (say_text[0] && say_text[1])
2536 {
2537 if ((say_text[0] == '%' && say_text[1] == 'l') || (say_text[0] == '@' && say_text[1] == 't'))
2538 {
2539 int location_len, cmd_len;
2540
2541 if (say_text[1] == 'l')
2542 {
2543 location_name = CL_Loc_Get (cl.refdef.vieworg);
2544 }
2545 else
2546 {
2547 trace_t tr;
2548 vec3_t end;
2549
2550 end[0] = cl.refdef.vieworg[0] + cl.v_forward[0] * 65556 + cl.v_right[0];
2551 end[1] = cl.refdef.vieworg[1] + cl.v_forward[1] * 65556 + cl.v_right[1];
2552 end[2] = cl.refdef.vieworg[2] + cl.v_forward[2] * 65556 + cl.v_right[2];
2553
2554 tr = CM_BoxTrace (cl.refdef.vieworg, end, vec3_origin, vec3_origin, 0, MASK_SOLID);
2555
2556 if (tr.fraction != 1.0f)
2557 location_name = CL_Loc_Get (tr.endpos);
2558 else
2559 location_name = CL_Loc_Get (end);
2560 }
2561
2562 cmd_len = (int)strlen(Cmd_Args());
2563 location_len = (int)strlen(location_name);
2564
2565 if (cmd_len + location_len >= MAX_STRING_CHARS-1)
2566 {
2567 Com_DPrintf ("CL_Say_Preprocessor: location expansion aborted, not enough space\n");
2568 break;
2569 }
2570
2571 memmove (say_text + location_len, say_text + 2, cmd_len - (say_text - p) - 1);
2572 memcpy (say_text, location_name, location_len);
2573 say_text += location_len;
2574 }
2575 say_text++;
2576 }
2577 }
2578
2579 Cmd_ForwardToServer ();
2580 }*/
2581
colortext(const char * text)2582 const char *colortext(const char *text)
2583 {
2584 static char ctext[2][80];
2585 static int c=0;
2586 const char *p;
2587 char *cp;
2588 c^=1;
2589 if (!text[0])
2590 return text;
2591
2592 for (p=text, cp=ctext[c];p[0]!=0;p++, cp++){
2593 cp[0]=p[0]|128;
2594 }
2595 cp[0]=0;
2596 return ctext[c];
2597 }
2598
CL_ResetPrecacheCheck(void)2599 void CL_ResetPrecacheCheck (void)
2600 {
2601 precache_start_time = Sys_Milliseconds();
2602
2603 precache_check = CS_MODELS;
2604 precache_model = 0;
2605 precache_model_skin = -1;
2606 }
2607
CL_RequestNextDownload(void)2608 void CL_RequestNextDownload (void)
2609 {
2610 int PLAYER_MULT;
2611 const char *sexedSounds[MAX_SOUNDS];
2612 uint32 map_checksum; // for detecting cheater maps
2613 char fn[MAX_OSPATH];
2614
2615 char *cmd;
2616 dmdl_t *pheader;
2617 dsprite_t *spriteheader;
2618
2619 int i;
2620
2621 if (cls.state != ca_connected)
2622 return;
2623
2624 PLAYER_MULT = 0;
2625
2626 for (i = CS_SOUNDS; i < CS_SOUNDS + MAX_SOUNDS; i++)
2627 {
2628 if (cl.configstrings[i][0] == '*')// && !strstr (cl.configstrings[i], ".."))
2629 sexedSounds[PLAYER_MULT++] = cl.configstrings[i] + 1;
2630 }
2631
2632 PLAYER_MULT += 5;
2633
2634 if (!allow_download->intvalue && precache_check < ENV_CNT)
2635 precache_check = ENV_CNT;
2636
2637 //ZOID
2638 if (precache_check == CS_MODELS)
2639 { // confirm map
2640 precache_check = CS_MODELS+2; // 0 isn't used
2641 if (allow_download_maps->intvalue)
2642 {
2643 if (strlen(cl.configstrings[CS_MODELS+1]) >= MAX_QPATH-1)
2644 Com_Error (ERR_DROP, "Bad map configstring '%s'", cl.configstrings[CS_MODELS+1]);
2645
2646 if (!CL_CheckOrDownloadFile(cl.configstrings[CS_MODELS+1]))
2647 return; // started a download
2648 }
2649 }
2650
2651
2652 redoSkins:;
2653
2654 if (precache_check >= CS_MODELS && precache_check < CS_MODELS+MAX_MODELS)
2655 {
2656 if (allow_download_models->intvalue)
2657 {
2658 char *skinname;
2659
2660 while (precache_check < CS_MODELS+MAX_MODELS &&
2661 cl.configstrings[precache_check][0])
2662 {
2663 //its a brush/alias model, we don't do those
2664 if (cl.configstrings[precache_check][0] == '*' || cl.configstrings[precache_check][0] == '#')
2665 {
2666 precache_check++;
2667 continue;
2668 }
2669
2670 //new model, try downloading it
2671 if (precache_model_skin == -1)
2672 {
2673 if (!CL_CheckOrDownloadFile(cl.configstrings[precache_check]))
2674 {
2675 precache_check++;
2676 return; // started a download
2677 }
2678 precache_check++;
2679 }
2680 else
2681 {
2682 //model is ok, now we are checking for skins in the model
2683 if (!precache_model)
2684 {
2685 //load model into buffer
2686 precache_model_skin = 1;
2687 FS_LoadFile (cl.configstrings[precache_check], (void **)&precache_model);
2688 if (!precache_model)
2689 {
2690 //shouldn't happen?
2691 precache_model_skin = 0;
2692 precache_check++;
2693 continue; // couldn't load it
2694 }
2695
2696 //is it an alias model
2697 if (LittleLong(*(uint32 *)precache_model) != IDALIASHEADER)
2698 {
2699 //is it a sprite
2700 if (LittleLong(*(uint32 *)precache_model) != IDSPRITEHEADER)
2701 {
2702 //no, free and move onto next model
2703 FS_FreeFile(precache_model);
2704 precache_model = NULL;
2705 precache_model_skin = 0;
2706 precache_check++;
2707 continue;
2708 }
2709 else
2710 {
2711 //get sprite header
2712 spriteheader = (dsprite_t *)precache_model;
2713 if (LittleLong (spriteheader->version != SPRITE_VERSION))
2714 {
2715 //this is unknown version! free and move onto next.
2716 FS_FreeFile(precache_model);
2717 precache_model = NULL;
2718 precache_check++;
2719 precache_model_skin = 0;
2720 continue; // couldn't load it
2721 }
2722 }
2723 }
2724 else
2725 {
2726 //get model header
2727 pheader = (dmdl_t *)precache_model;
2728 if (LittleLong (pheader->version) != ALIAS_VERSION)
2729 {
2730 //unknown version! free and move onto next
2731 FS_FreeFile(precache_model);
2732 precache_model = NULL;
2733 precache_check++;
2734 precache_model_skin = 0;
2735 continue; // couldn't load it
2736 }
2737 }
2738 }
2739
2740 //if its an alias model
2741 if (LittleLong(*(uint32 *)precache_model) == IDALIASHEADER)
2742 {
2743 pheader = (dmdl_t *)precache_model;
2744
2745 //iterate through number of skins
2746 while (precache_model_skin - 1 < LittleLong(pheader->num_skins))
2747 {
2748 skinname = (char *)precache_model +
2749 LittleLong(pheader->ofs_skins) +
2750 (precache_model_skin - 1)*MAX_SKINNAME;
2751
2752 //r1: spam warning for models that are broken
2753 if (strchr (skinname, '\\'))
2754 {
2755 Com_Printf ("Warning, model %s with incorrectly linked skin: %s\n", LOG_CLIENT|LOG_WARNING, cl.configstrings[precache_check], skinname);
2756 }
2757 else if (strlen(skinname) > MAX_SKINNAME-1)
2758 {
2759 Com_Error (ERR_DROP, "Model %s has too long a skin path: %s", cl.configstrings[precache_check], skinname);
2760 }
2761
2762 //check if this skin exists
2763 if (!CL_CheckOrDownloadFile(skinname))
2764 {
2765 precache_model_skin++;
2766 return; // started a download
2767 }
2768 precache_model_skin++;
2769 }
2770 }
2771 else
2772 {
2773 //its a sprite
2774 spriteheader = (dsprite_t *)precache_model;
2775
2776 //iterate through skins
2777 while (precache_model_skin - 1 < LittleLong(spriteheader->numframes))
2778 {
2779 skinname = spriteheader->frames[(precache_model_skin - 1)].name;
2780
2781 //r1: spam warning for models that are broken
2782 if (strchr (skinname, '\\'))
2783 {
2784 Com_Printf ("Warning, sprite %s with incorrectly linked skin: %s\n", LOG_CLIENT|LOG_WARNING, cl.configstrings[precache_check], skinname);
2785 }
2786 else if (strlen(skinname) > MAX_SKINNAME-1)
2787 {
2788 Com_Error (ERR_DROP, "Sprite %s has too long a skin path: %s", cl.configstrings[precache_check], skinname);
2789 }
2790
2791 //check if this skin exists
2792 if (!CL_CheckOrDownloadFile(skinname))
2793 {
2794 precache_model_skin++;
2795 return; // started a download
2796 }
2797 precache_model_skin++;
2798 }
2799 }
2800
2801 //we're done checking the model and all skins, free
2802 if (precache_model)
2803 {
2804 FS_FreeFile(precache_model);
2805 precache_model = NULL;
2806 }
2807
2808 precache_model_skin = 0;
2809 precache_check++;
2810 }
2811 }
2812 }
2813 if (precache_model_skin == -1)
2814 {
2815 precache_check = CS_MODELS + 2;
2816 precache_model_skin = 0;
2817
2818 //pending downloads (models), let's wait here before we can check skins.
2819 #ifdef USE_CURL
2820 if (CL_PendingHTTPDownloads ())
2821 return;
2822 #endif
2823
2824 goto redoSkins;
2825 }
2826 precache_check = CS_SOUNDS;
2827 }
2828
2829 if (precache_check >= CS_SOUNDS && precache_check < CS_SOUNDS+MAX_SOUNDS)
2830 {
2831 if (allow_download_sounds->intvalue)
2832 {
2833 if (precache_check == CS_SOUNDS)
2834 precache_check++; // zero is blank
2835
2836 while (precache_check < CS_SOUNDS+MAX_SOUNDS &&
2837 cl.configstrings[precache_check][0])
2838 {
2839 if (cl.configstrings[precache_check][0] == '*')
2840 {
2841 precache_check++;
2842 continue;
2843 }
2844
2845 if (cl.configstrings[precache_check][0] == '#')
2846 Com_sprintf(fn, sizeof(fn), "%s", cl.configstrings[precache_check++] + 1);
2847 else
2848 Com_sprintf(fn, sizeof(fn), "sound/%s", cl.configstrings[precache_check++]);
2849
2850 if (!CL_CheckOrDownloadFile(fn))
2851 return; // started a download
2852 }
2853 }
2854 precache_check = CS_IMAGES;
2855 }
2856
2857 if (precache_check >= CS_IMAGES && precache_check < CS_IMAGES+MAX_IMAGES)
2858 {
2859 if (precache_check == CS_IMAGES)
2860 precache_check++; // zero is blank
2861
2862 while (precache_check < CS_IMAGES+MAX_IMAGES && cl.configstrings[precache_check][0])
2863 {
2864 Com_sprintf(fn, sizeof(fn), "pics/%s.pcx", cl.configstrings[precache_check]);
2865 if (FS_LoadFile (fn, NULL) == -1)
2866 {
2867 //Com_sprintf(fn, sizeof(fn), "pics/%s.pcx", cl.configstrings[precache_check]);
2868 if (!CL_CheckOrDownloadFile(fn))
2869 {
2870 precache_check++;
2871 return; // started a download
2872 }
2873 }
2874 precache_check++;
2875 }
2876 precache_check = CS_PLAYERSKINS;
2877 }
2878
2879 // skins are special, since a player has three things to download:
2880 // model, weapon model and skin
2881 // so precache_check is now *3
2882 if (precache_check >= CS_PLAYERSKINS && precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
2883 {
2884 if (allow_download_players->intvalue)
2885 {
2886 while (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
2887 {
2888 int i, n, j, length;
2889 char model[MAX_QPATH], skin[MAX_QPATH], *p;
2890
2891 i = (precache_check - CS_PLAYERSKINS)/PLAYER_MULT;
2892 n = (precache_check - CS_PLAYERSKINS)%PLAYER_MULT;
2893
2894 if (i >= cl.maxclients)
2895 {
2896 precache_check = ENV_CNT;
2897 continue;
2898 }
2899
2900 if (!cl.configstrings[CS_PLAYERSKINS+i][0])
2901 {
2902 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2903 continue;
2904 }
2905
2906 if (n && cls.failed_download)
2907 {
2908 cls.failed_download = false;
2909 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2910 continue;
2911 }
2912
2913 if ((p = strchr(cl.configstrings[CS_PLAYERSKINS+i], '\\')) != NULL)
2914 {
2915 p++;
2916 }
2917 else
2918 {
2919 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2920 continue;
2921 }
2922
2923 Q_strncpy(model, p, sizeof(model)-1);
2924
2925 p = strchr(model, '/');
2926
2927 if (!p)
2928 p = strchr(model, '\\');
2929
2930 if (p)
2931 {
2932 *p++ = 0;
2933 if (!p[0] || !model[0])
2934 {
2935 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2936 continue;
2937 }
2938 else
2939 {
2940 Q_strncpy (skin, p, sizeof(skin)-1);
2941 }
2942 }
2943 else
2944 {
2945 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2946 continue;
2947 }
2948 //*skin = 0;
2949
2950 length = (int)strlen (model);
2951 for (j = 0; j < length; j++)
2952 {
2953 if (!isvalidchar(model[j]))
2954 {
2955 Com_Printf ("Bad character '%c' in playermodel '%s'\n", LOG_CLIENT|LOG_WARNING, model[j], model);
2956 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2957 goto skipplayer;
2958 }
2959 }
2960
2961 length = (int)strlen (skin);
2962 for (j = 0; j < length; j++)
2963 {
2964 if (!isvalidchar(skin[j]))
2965 {
2966 Com_Printf ("Bad character '%c' in playerskin '%s'\n", LOG_CLIENT|LOG_WARNING, skin[j], skin);
2967 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2968 goto skipplayer;
2969 }
2970 }
2971
2972 switch (n)
2973 {
2974 case -1:
2975 continue;
2976 case 0: // model
2977 cls.failed_download = false;
2978 Com_sprintf(fn, sizeof(fn), "players/%s/tris.md2", model);
2979 if (!CL_CheckOrDownloadFile(fn)) {
2980 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 1;
2981 return; // started a download
2982 }
2983 //n++;
2984 /*FALL THROUGH*/
2985
2986 case 1: // weapon model
2987 Com_sprintf(fn, sizeof(fn), "players/%s/weapon.md2", model);
2988 if (!CL_CheckOrDownloadFile(fn)) {
2989 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 2;
2990 return; // started a download
2991 }
2992 //n++;
2993 /*FALL THROUGH*/
2994
2995 case 2: // weapon skin
2996 Com_sprintf(fn, sizeof(fn), "players/%s/weapon.pcx", model);
2997 if (!CL_CheckOrDownloadFile(fn)) {
2998 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 3;
2999 return; // started a download
3000 }
3001 //n++;
3002 /*FALL THROUGH*/
3003
3004 case 3: // skin
3005 Com_sprintf(fn, sizeof(fn), "players/%s/%s.pcx", model, skin);
3006 if (!CL_CheckOrDownloadFile(fn)) {
3007 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 4;
3008 return; // started a download
3009 }
3010 //n++;
3011 /*FALL THROUGH*/
3012
3013 case 4: // skin_i
3014 Com_sprintf(fn, sizeof(fn), "players/%s/%s_i.pcx", model, skin);
3015 if (!CL_CheckOrDownloadFile(fn)) {
3016 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 5;
3017 return; // started a download
3018 }
3019 n = 5;
3020
3021 default:
3022 while (n < PLAYER_MULT)
3023 {
3024 Com_sprintf(fn, sizeof(fn), "players/%s/%s", model, sexedSounds[n-5]);
3025 n++;
3026 if (!CL_CheckOrDownloadFile(fn)) {
3027 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + n;
3028 return; // started a download
3029 }
3030 }
3031 }
3032
3033 // move on to next model
3034 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
3035
3036 skipplayer:;
3037 }
3038 }
3039 // precache phase completed
3040 precache_check = ENV_CNT;
3041 }
3042
3043 #ifdef USE_CURL
3044 //map might still be downloading?
3045 if (CL_PendingHTTPDownloads ())
3046 return;
3047 #endif
3048
3049 if (precache_check == ENV_CNT)
3050 {
3051 char locbuff[MAX_QPATH+4];
3052 precache_check = ENV_CNT + 1;
3053
3054 COM_StripExtension (cl.configstrings[CS_MODELS+1], locbuff);
3055 strcat (locbuff, ".loc");
3056
3057 //try basedir/moddir/maps/mapname.loc and basedir/locs/mapname.loc
3058 if (!CL_LoadLoc (locbuff))
3059 {
3060 locbuff[0] = 'l';
3061 locbuff[1] = 'o';
3062 locbuff[2] = 'c';
3063 locbuff[3] = 's';
3064 CL_LoadLoc (locbuff);
3065 }
3066
3067 CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
3068
3069 if (map_checksum && map_checksum != strtoul(cl.configstrings[CS_MAPCHECKSUM], NULL, 10)) {
3070 Com_Error (ERR_DROP, "Local map version differs from server: 0x%.8x != 0x%.8x",
3071 map_checksum, atoi(cl.configstrings[CS_MAPCHECKSUM]));
3072 return;
3073 }
3074 }
3075
3076 if (precache_check > ENV_CNT && precache_check < TEXTURE_CNT) {
3077 if (allow_download->intvalue && allow_download_maps->intvalue) {
3078 while (precache_check < TEXTURE_CNT) {
3079 int n = precache_check++ - ENV_CNT - 1;
3080
3081 if (n & 1)
3082 Com_sprintf(fn, sizeof(fn), "env/%s%s.pcx",
3083 cl.configstrings[CS_SKY], env_suf[n/2]);
3084 else
3085 Com_sprintf(fn, sizeof(fn), "env/%s%s.tga",
3086 cl.configstrings[CS_SKY], env_suf[n/2]);
3087 if (!CL_CheckOrDownloadFile(fn))
3088 return; // started a download
3089 }
3090 }
3091 precache_check = TEXTURE_CNT;
3092 }
3093
3094 if (precache_check == TEXTURE_CNT) {
3095 precache_check = TEXTURE_CNT+1;
3096 precache_tex = 0;
3097 }
3098
3099 // confirm existance of textures, download any that don't exist
3100 if (precache_check == TEXTURE_CNT+1) {
3101 // from qcommon/cmodel.c
3102 extern int numtexinfo;
3103 extern mapsurface_t map_surfaces[];
3104
3105 if (allow_download->intvalue && allow_download_maps->intvalue) {
3106 while (precache_tex < numtexinfo) {
3107 //char fn[MAX_OSPATH];
3108
3109 //r1: spam warning about texture
3110 if (strchr (map_surfaces[precache_tex].rname, '\\'))
3111 Com_Printf ("Warning, incorrectly referenced texture '%s'\n", LOG_CLIENT|LOG_WARNING, map_surfaces[precache_tex].rname);
3112 else if (!strchr (map_surfaces[precache_tex].rname, '/'))
3113 Com_Printf ("Warning, texture '%s' not in a sub-directory\n", LOG_CLIENT|LOG_WARNING, map_surfaces[precache_tex].rname);
3114
3115 Com_sprintf(fn, sizeof(fn), "textures/%s.wal", map_surfaces[precache_tex++].rname);
3116 if (!CL_CheckOrDownloadFile(fn))
3117 return; // started a download
3118 }
3119 }
3120 precache_check = TEXTURE_CNT+999;
3121 }
3122
3123 //pending downloads (possibly textures), let's wait here.
3124 #ifdef USE_CURL
3125 if (CL_PendingHTTPDownloads ())
3126 return;
3127 #endif
3128
3129 //ZOID
3130 CL_RegisterSounds ();
3131 CL_PrepRefresh ();
3132
3133 Com_DPrintf ("Precache completed in %u msec.\n", Sys_Milliseconds() - precache_start_time);
3134
3135 if (cl_async->intvalue && cl_maxfps->intvalue > 100)
3136 Com_Printf ("\n%s\nR1Q2 separates network and rendering so a cl_maxfps value > 30\nis rarely needed. Use r_maxfps to control maximum rendered FPS\n\n", LOG_CLIENT, colortext ("WARNING: A cl_maxfps value of over 100 is strongly discouraged"));
3137
3138 if (cl_autorecord->intvalue)
3139 {
3140 char autorecord_name[MAX_QPATH];
3141 char mapname[MAX_QPATH];
3142 char time_buff[32];
3143
3144 time_t tm;
3145
3146 time(&tm);
3147 strftime(time_buff, sizeof(time_buff)-1, "%Y-%m-%d-%H%M", localtime(&tm));
3148
3149 COM_StripExtension (cl.configstrings[CS_MODELS+1], mapname);
3150
3151 Com_sprintf (autorecord_name, sizeof(autorecord_name), "%s/demos/%s-%s.dm2", FS_Gamedir(), time_buff, mapname + 5);
3152
3153 if (CL_BeginRecording (autorecord_name))
3154 Com_Printf ("Auto-recording to %s.\n", LOG_CLIENT, autorecord_name);
3155 else
3156 Com_Printf ("Couldn't auto-record to %s.\n", LOG_CLIENT, autorecord_name);
3157 }
3158
3159 CL_FixCvarCheats();
3160
3161 if (cls.serverProtocol == PROTOCOL_R1Q2)
3162 {
3163 MSG_BeginWriting (clc_setting);
3164 MSG_WriteShort (CLSET_NOGUN);
3165 MSG_WriteShort (cl_gun->intvalue ? 0 : 1);
3166 MSG_EndWriting (&cls.netchan.message);
3167
3168 MSG_BeginWriting (clc_setting);
3169 MSG_WriteShort (CLSET_PLAYERUPDATE_REQUESTS);
3170 MSG_WriteShort (cl_player_updates->intvalue);
3171 MSG_EndWriting (&cls.netchan.message);
3172
3173 MSG_BeginWriting (clc_setting);
3174 MSG_WriteShort (CLSET_FPS);
3175 MSG_WriteShort (cl_updaterate->intvalue);
3176 MSG_EndWriting (&cls.netchan.message);
3177 }
3178
3179 MSG_WriteByte (clc_stringcmd);
3180 MSG_WriteString (va("begin %i\n", precache_spawncount) );
3181 MSG_EndWriting (&cls.netchan.message);
3182
3183 cmd = Cmd_MacroExpandString("$cl_beginmapcmd");
3184 if (cmd)
3185 {
3186 Cbuf_AddText (cmd);
3187 Cbuf_AddText ("\n");
3188 Cbuf_Execute ();
3189 }
3190 else
3191 Com_Printf ("WARNING: Error expanding $cl_beginmapcmd, ignored.\n", LOG_CLIENT|LOG_WARNING);
3192
3193 send_packet_now = true;
3194 }
3195
3196 /*
3197 =================
3198 CL_Precache_f
3199
3200 The server will send this command right
3201 before allowing the client into the server
3202 =================
3203 */
CL_Precache_f(void)3204 void CL_Precache_f (void)
3205 {
3206 //Yet another hack to let old demos work
3207 //the old precache sequence
3208 if (Cmd_Argc() < 2)
3209 {
3210 uint32 map_checksum; // for detecting cheater maps
3211 CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
3212 CL_RegisterSounds ();
3213 CL_PrepRefresh ();
3214 return;
3215 }
3216
3217 precache_spawncount = atoi(Cmd_Argv(1));
3218 CL_ResetPrecacheCheck ();
3219 CL_RequestNextDownload();
3220 }
3221
CL_Toggle_f(void)3222 void CL_Toggle_f (void)
3223 {
3224 cvar_t *tvar;
3225
3226 if (Cmd_Argc() < 2)
3227 {
3228 Com_Printf ("Usage: toggle cvar [option1 option2 option3 ...]\n", LOG_CLIENT);
3229 return;
3230 }
3231
3232 tvar = Cvar_FindVar (Cmd_Argv(1));
3233
3234 if (!tvar)
3235 {
3236 Com_Printf ("%s: no such variable\n", LOG_CLIENT, Cmd_Argv(1));
3237 return;
3238 }
3239
3240 if (Cmd_Argc() == 2)
3241 {
3242 if (tvar->value != 0 && tvar->value != 1)
3243 {
3244 Com_Printf ("%s: not a binary variable\n", LOG_CLIENT, Cmd_Argv(1));
3245 return;
3246 }
3247 Cvar_SetValue (Cmd_Argv(1), (float)((int)tvar->value ^ 1));
3248 }
3249 else
3250 {
3251 int i;
3252
3253 for (i = 2; i < Cmd_Argc(); i++)
3254 {
3255 if (!strcmp (tvar->string, Cmd_Argv(i)))
3256 {
3257 if (i == Cmd_Argc() -1)
3258 {
3259 Cvar_Set (Cmd_Argv(1), Cmd_Argv(2));
3260 break;
3261 }
3262 else
3263 {
3264 Cvar_Set (Cmd_Argv(1), Cmd_Argv(i+1));
3265 break;
3266 }
3267 }
3268 }
3269 }
3270 }
3271
version_update(cvar_t * self,char * old,char * newValue)3272 void version_update (cvar_t *self, char *old, char *newValue)
3273 {
3274 Cvar_Set ("cl_version", va(PRODUCTNAME " " VERSION "; %s", newValue[0] ? newValue : "unknown renderer"));
3275 }
3276
_name_changed(cvar_t * var,char * oldValue,char * newValue)3277 void _name_changed (cvar_t *var, char *oldValue, char *newValue)
3278 {
3279 if (strlen(newValue) >= 16)
3280 {
3281 newValue[15] = 0;
3282 }
3283 else if (!*newValue)
3284 {
3285 Cvar_Set ("name", "unnamed");
3286 }
3287 }
3288
_maxfps_changed(cvar_t * var,char * oldValue,char * newValue)3289 void _maxfps_changed (cvar_t *var, char *oldValue, char *newValue)
3290 {
3291 if (var->intvalue < 5)
3292 {
3293 Cvar_Set (var->name, "5");
3294 }
3295
3296 if (cl_async->intvalue == 0)
3297 {
3298 if (var == cl_maxfps)
3299 Cvar_SetValue ("r_maxfps", var->value);
3300 else if (var == r_maxfps)
3301 Cvar_SetValue ("cl_maxfps", var->value);
3302 }
3303 }
3304
_async_changed(cvar_t * var,char * oldValue,char * newValue)3305 void _async_changed (cvar_t *var, char *oldValue, char *newValue)
3306 {
3307 if (var->intvalue == 0)
3308 {
3309 Cvar_SetValue ("r_maxfps", cl_maxfps->value);
3310 }
3311 }
3312
_railtrail_changed(cvar_t * var,char * oldValue,char * newValue)3313 void _railtrail_changed (cvar_t *var, char *oldValue, char *newValue)
3314 {
3315 if (var->intvalue > 254)
3316 Cvar_Set (var->name, "254");
3317 else if (var->intvalue < 0)
3318 Cvar_Set (var->name, "0");
3319 }
3320
_protocol_changed(cvar_t * var,char * oldValue,char * newValue)3321 void _protocol_changed (cvar_t *var, char *oldValue, char *newValue)
3322 {
3323 //force reparsing of cl_protocol
3324 if (cls.state == ca_disconnected)
3325 cls.serverProtocol = 0;
3326 }
3327
CL_FollowIP_f(void)3328 void CL_FollowIP_f (void)
3329 {
3330 if (!cls.followHost[0])
3331 {
3332 Com_Printf ("No IP to follow.\n", LOG_GENERAL);
3333 return;
3334 }
3335
3336 Cbuf_AddText (va("connect \"%s\"\n", cls.followHost));
3337 }
3338
CL_IndexStats_f(void)3339 void CL_IndexStats_f (void)
3340 {
3341 int i;
3342 int count;
3343 /*
3344 #define CS_SOUNDS (CS_MODELS+MAX_MODELS) //288
3345 #define CS_IMAGES (CS_SOUNDS+MAX_SOUNDS) //544
3346 #define CS_LIGHTS (CS_IMAGES+MAX_IMAGES) //800
3347 #define CS_ITEMS (CS_LIGHTS+MAX_LIGHTSTYLES) //1056
3348 #define CS_PLAYERSKINS (CS_ITEMS+MAX_ITEMS) //1312
3349 #define CS_GENERAL (CS_PLAYERSKINS+MAX_CLIENTS) //1568
3350 #define MAX_CONFIGSTRINGS (CS_GENERAL+MAX_GENERAL) //2080
3351 */
3352 if (Cmd_Argc() != 1)
3353 {
3354 unsigned offset;
3355 unsigned count = atoi(Cmd_Argv(2));
3356
3357 if (!Q_stricmp (Cmd_Argv(1), "cs_models"))
3358 offset = CS_MODELS;
3359 else if (!Q_stricmp (Cmd_Argv(1), "cs_sounds"))
3360 offset = CS_SOUNDS;
3361 else if (!Q_stricmp (Cmd_Argv(1), "cs_images"))
3362 offset = CS_IMAGES;
3363 else if (!Q_stricmp (Cmd_Argv(1), "cs_items"))
3364 offset = CS_ITEMS;
3365 else if (!Q_stricmp (Cmd_Argv(1), "cs_playerskins"))
3366 offset = CS_PLAYERSKINS;
3367 else
3368 offset = atoi(Cmd_Argv(1));
3369
3370 if (offset >= CS_GENERAL + MAX_GENERAL)
3371 {
3372 Com_Printf ("Bad offset.\n", LOG_GENERAL);
3373 return;
3374 }
3375
3376 if (count == 0)
3377 count = 256;
3378
3379 if (offset + count > CS_GENERAL + MAX_GENERAL)
3380 {
3381 count = CS_GENERAL + MAX_GENERAL - offset;
3382 }
3383
3384 for (i = offset; i < offset + count; i++)
3385 {
3386 Com_Printf ("%3d/%3d: %s\n", LOG_GENERAL, i, i - offset, cl.configstrings[i]);
3387 }
3388
3389 return;
3390 }
3391 for (count = 0, i = CS_MODELS; i < CS_MODELS + MAX_MODELS; i++)
3392 {
3393 if (cl.configstrings[i][0])
3394 count++;
3395 }
3396 Com_Printf ("CS_MODELS : %d\n", LOG_GENERAL, count);
3397
3398 for (count = 0, i = CS_SOUNDS; i < CS_SOUNDS + MAX_SOUNDS; i++)
3399 {
3400 if (cl.configstrings[i][0])
3401 count++;
3402 }
3403 Com_Printf ("CS_SOUNDS : %d\n", LOG_GENERAL, count);
3404
3405 for (count = 0, i = CS_LIGHTS; i < CS_LIGHTS + MAX_LIGHTSTYLES; i++)
3406 {
3407 if (cl.configstrings[i][0])
3408 count++;
3409 }
3410 Com_Printf ("CS_LIGHTS : %d\n", LOG_GENERAL, count);
3411
3412 for (count = 0, i = CS_IMAGES; i < CS_IMAGES + MAX_IMAGES; i++)
3413 {
3414 if (cl.configstrings[i][0])
3415 count++;
3416 }
3417 Com_Printf ("CS_IMAGES : %d\n", LOG_GENERAL, count);
3418
3419 for (count = 0, i = CS_ITEMS; i < CS_ITEMS + MAX_ITEMS; i++)
3420 {
3421 if (cl.configstrings[i][0])
3422 count++;
3423 }
3424 Com_Printf ("CS_ITEMS : %d\n", LOG_GENERAL, count);
3425
3426 for (count = 0, i = CS_PLAYERSKINS; i < CS_PLAYERSKINS + MAX_CLIENTS; i++)
3427 {
3428 if (cl.configstrings[i][0])
3429 count++;
3430 }
3431 Com_Printf ("CS_PLAYERSKINS: %d\n", LOG_GENERAL, count);
3432
3433 for (count = 0, i = CS_GENERAL; i < CS_GENERAL + MAX_GENERAL; i++)
3434 {
3435 if (cl.configstrings[i][0])
3436 count++;
3437 }
3438 Com_Printf ("CS_GENERAL : %d\n", LOG_GENERAL, count);
3439 }
3440
_cl_http_max_connections_changed(cvar_t * c,char * old,char * new)3441 void _cl_http_max_connections_changed (cvar_t *c, char *old, char *new)
3442 {
3443 if (c->intvalue > 8)
3444 Cvar_Set (c->name, "8");
3445 else if (c->intvalue < 1)
3446 Cvar_Set (c->name, "1");
3447
3448 //not really needed any more, hopefully no one still uses apache...
3449 //if (c->intvalue > 2)
3450 // Com_Printf ("WARNING: Changing the maximum connections higher than 2 violates the HTTP specification recommendations. Doing so may result in you being blocked from the remote system and offers no performance benefits unless you are on a very high latency link (ie, satellite)\n", LOG_GENERAL);
3451 }
3452
_gun_changed(cvar_t * c,char * old,char * new)3453 void _gun_changed (cvar_t *c, char *old, char *new)
3454 {
3455 if (cls.state >= ca_connected && cls.serverProtocol == PROTOCOL_R1Q2)
3456 {
3457 MSG_BeginWriting (clc_setting);
3458 MSG_WriteShort (CLSET_NOGUN);
3459 MSG_WriteShort (c->intvalue ? 0 : 1);
3460 MSG_EndWriting (&cls.netchan.message);
3461 }
3462 }
3463
_player_updates_changed(cvar_t * c,char * old,char * new)3464 void _player_updates_changed (cvar_t *c, char *old, char *new)
3465 {
3466 if (c->intvalue > 10)
3467 Cvar_Set (c->name, "10");
3468 else if (c->intvalue < 0)
3469 Cvar_Set (c->name, "0");
3470
3471 if (cls.state >= ca_connected && cls.serverProtocol == PROTOCOL_R1Q2)
3472 {
3473 MSG_BeginWriting (clc_setting);
3474 MSG_WriteShort (CLSET_PLAYERUPDATE_REQUESTS);
3475 MSG_WriteShort (cl_player_updates->intvalue);
3476 MSG_EndWriting (&cls.netchan.message);
3477 }
3478
3479 if (c->intvalue == 0)
3480 cl.player_update_time = 0;
3481 }
3482
_updaterate_changed(cvar_t * c,char * old,char * new)3483 void _updaterate_changed (cvar_t *c, char *old, char *new)
3484 {
3485 if (c->intvalue < 0)
3486 Cvar_Set (c->name, "0");
3487
3488 if (cls.state >= ca_connected && cls.serverProtocol == PROTOCOL_R1Q2)
3489 {
3490 MSG_BeginWriting (clc_setting);
3491 MSG_WriteShort (CLSET_FPS);
3492 MSG_WriteShort (cl_updaterate->intvalue);
3493 MSG_EndWriting (&cls.netchan.message);
3494 }
3495 }
3496
3497 /*
3498 =================
3499 CL_InitLocal
3500 =================
3501 */
CL_InitLocal(void)3502 void CL_InitLocal (void)
3503 {
3504 const char *glVersion;
3505
3506 cls.proxyState = ps_none;
3507 cls.state = ca_disconnected;
3508 cls.realtime = Sys_Milliseconds ();
3509
3510 CL_InitInput ();
3511
3512 adr0 = Cvar_Get( "adr0", "", CVAR_ARCHIVE );
3513 adr1 = Cvar_Get( "adr1", "", CVAR_ARCHIVE );
3514 adr2 = Cvar_Get( "adr2", "", CVAR_ARCHIVE );
3515 adr3 = Cvar_Get( "adr3", "", CVAR_ARCHIVE );
3516 adr4 = Cvar_Get( "adr4", "", CVAR_ARCHIVE );
3517 adr5 = Cvar_Get( "adr5", "", CVAR_ARCHIVE );
3518 adr6 = Cvar_Get( "adr6", "", CVAR_ARCHIVE );
3519 adr7 = Cvar_Get( "adr7", "", CVAR_ARCHIVE );
3520 adr8 = Cvar_Get( "adr8", "", CVAR_ARCHIVE );
3521
3522 //
3523 // register our variables
3524 //
3525 #ifdef CL_STEREO_SUPPORT
3526 cl_stereo_separation = Cvar_Get( "cl_stereo_separation", "0.4", CVAR_ARCHIVE );
3527 cl_stereo = Cvar_Get( "cl_stereo", "0", 0 );
3528 #endif
3529
3530 cl_add_blend = Cvar_Get ("cl_blend", "1", 0);
3531 cl_add_lights = Cvar_Get ("cl_lights", "1", 0);
3532 cl_add_particles = Cvar_Get ("cl_particles", "1", 0);
3533 cl_add_entities = Cvar_Get ("cl_entities", "1", 0);
3534 cl_gun = Cvar_Get ("cl_gun", "1", CVAR_ARCHIVE);
3535 cl_gun->changed = _gun_changed;
3536
3537 cl_footsteps = Cvar_Get ("cl_footsteps", "1", 0);
3538 cl_noskins = Cvar_Get ("cl_noskins", "0", 0);
3539 // cl_autoskins = Cvar_Get ("cl_autoskins", "0", 0);
3540 cl_predict = Cvar_Get ("cl_predict", "1", 0);
3541 cl_backlerp = Cvar_Get ("cl_backlerp", "1", 0);
3542 // cl_minfps = Cvar_Get ("cl_minfps", "5", 0);
3543
3544 r_maxfps = Cvar_Get ("r_maxfps", "1000", 0);
3545 r_maxfps->changed = _maxfps_changed;
3546
3547 cl_maxfps = Cvar_Get ("cl_maxfps", "60", CVAR_ARCHIVE);
3548 cl_maxfps->changed = _maxfps_changed;
3549
3550 cl_async = Cvar_Get ("cl_async", "1", 0);
3551 cl_async->changed = _async_changed;
3552
3553 cl_upspeed = Cvar_Get ("cl_upspeed", "200", 0);
3554 cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", 0);
3555 cl_sidespeed = Cvar_Get ("cl_sidespeed", "200", 0);
3556 cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", 0);
3557 cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", 0);
3558 cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
3559
3560 cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE);
3561
3562 freelook = Cvar_Get( "freelook", "1", CVAR_ARCHIVE );
3563 lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE);
3564 lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE);
3565 sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE);
3566
3567 m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
3568 m_yaw = Cvar_Get ("m_yaw", "0.022", 0);
3569 m_forward = Cvar_Get ("m_forward", "1", 0);
3570 m_side = Cvar_Get ("m_side", "1", 0);
3571
3572 cl_shownet = Cvar_Get ("cl_shownet", "0", 0);
3573 cl_showmiss = Cvar_Get ("cl_showmiss", "0", 0);
3574 cl_showclamp = Cvar_Get ("showclamp", "0", 0);
3575 cl_timeout = Cvar_Get ("cl_timeout", "120", 0);
3576 cl_paused = Cvar_Get ("paused", "0", 0);
3577 cl_timedemo = Cvar_Get ("timedemo", "0", 0);
3578
3579 cl_filterchat = Cvar_Get ("cl_filterchat", "0", 0);
3580
3581 rcon_client_password = Cvar_Get ("rcon_password", "", 0);
3582 rcon_address = Cvar_Get ("rcon_address", "", 0);
3583
3584 cl_lightlevel = Cvar_Get ("r_lightlevel", "0", CVAR_NOSET);
3585
3586 //
3587 // userinfo
3588 //
3589 info_password = Cvar_Get ("password", "", CVAR_USERINFO);
3590 info_spectator = Cvar_Get ("spectator", "0", CVAR_USERINFO);
3591 name = Cvar_Get ("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE);
3592 skin = Cvar_Get ("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE);
3593 rate = Cvar_Get ("rate", "15000", CVAR_USERINFO | CVAR_ARCHIVE); // FIXME
3594 msg = Cvar_Get ("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE);
3595 hand = Cvar_Get ("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE);
3596 fov = Cvar_Get ("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE);
3597 gender = Cvar_Get ("gender", "male", CVAR_USERINFO | CVAR_ARCHIVE);
3598 gender_auto = Cvar_Get ("gender_auto", "1", CVAR_ARCHIVE);
3599 gender->modified = false; // clear this so we know when user sets it manually
3600
3601 cl_vwep = Cvar_Get ("cl_vwep", "1", CVAR_ARCHIVE);
3602
3603 //r1: only use experimental protocol in debug (not)
3604 #ifdef _DEBUG
3605 cl_protocol = Cvar_Get ("cl_protocol", "0", 0);
3606 #else
3607 cl_protocol = Cvar_Get ("cl_protocol", "0", 0);
3608 #endif
3609
3610 cl_protocol->changed = _protocol_changed;
3611
3612 //cl_defertimer = Cvar_Get ("cl_defertimer", "1", 0);
3613 cl_smoothsteps = Cvar_Get ("cl_smoothsteps", "3", 0);
3614 cl_instantpacket = Cvar_Get ("cl_instantpacket", "1", 0);
3615 cl_nolerp = Cvar_Get ("cl_nolerp", "0", 0);
3616 cl_instantack = Cvar_Get ("cl_instantack", "0", 0);
3617 cl_autorecord = Cvar_Get ("cl_autorecord", "0", 0);
3618
3619 cl_railtrail = Cvar_Get ("cl_railtrail", "0", 0);
3620 cl_railtrail->changed = _railtrail_changed;
3621
3622 //misc for testing
3623 cl_test = Cvar_Get ("cl_test", "0", 0);
3624 cl_test2 = Cvar_Get ("cl_test2", "0", 0);
3625 cl_test3 = Cvar_Get ("cl_test3", "0", 0);
3626
3627 cl_original_dlights = Cvar_Get ("cl_original_dlights", "1", 0);
3628
3629 cl_default_location = Cvar_Get ("cl_default_location", "", 0);
3630
3631 #ifdef _DEBUG
3632 cl_player_updates = Cvar_Get ("cl_player_updates", "0", 0);
3633 #else
3634 cl_player_updates = Cvar_Get ("cl_player_updates", "0", CVAR_NOSET);
3635 #endif
3636
3637 cl_player_updates->changed = _player_updates_changed;
3638
3639 cl_updaterate = Cvar_Get ("cl_updaterate", "0", 0);
3640 cl_updaterate->changed = _updaterate_changed;
3641
3642 #ifdef NO_SERVER
3643 allow_download = Cvar_Get ("allow_download", "1", CVAR_ARCHIVE);
3644 allow_download_players = Cvar_Get ("allow_download_players", "1", CVAR_ARCHIVE);
3645 allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_ARCHIVE);
3646 allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_ARCHIVE);
3647 allow_download_maps = Cvar_Get ("allow_download_maps", "1", CVAR_ARCHIVE);
3648 #endif
3649
3650 #ifdef USE_CURL
3651 cl_http_proxy = Cvar_Get ("cl_http_proxy", "", 0);
3652 cl_http_filelists = Cvar_Get ("cl_http_filelists", "1", 0);
3653 cl_http_downloads = Cvar_Get ("cl_http_downloads", "1", 0);
3654 cl_http_max_connections = Cvar_Get ("cl_http_max_connections", "4", 0);
3655 cl_http_max_connections->changed = _cl_http_max_connections_changed;
3656 #endif
3657
3658 cl_proxy = Cvar_Get ("cl_proxy", "", 0);
3659
3660 //haxx
3661 glVersion = Cvar_VariableString ("cl_version");
3662 (Cvar_ForceSet ("cl_version", va(PRODUCTNAME " " VERSION "; %s", glVersion[0] ? glVersion : "unknown renderer" )))->changed = version_update;
3663
3664 name->changed = _name_changed;
3665
3666 #ifdef _DEBUG
3667 dbg_framesleep = Cvar_Get ("dbg_framesleep", "0", 0);
3668 #endif
3669
3670 //cl_snaps = Cvar_Get ("cl_snaps", "1", 0);
3671 //cl_snaps->modified = false;
3672 //cl_snaps->changed = CL_SnapsMessage;
3673
3674 CL_FixCvarCheats ();
3675
3676 //
3677 // register our commands
3678 //
3679
3680 //r1: r1q2 stuff goes here so it never overrides autocomplete order for base q2 cmds
3681
3682 Cmd_AddCommand ("followip", CL_FollowIP_f);
3683 Cmd_AddCommand ("indexstats", CL_IndexStats_f);
3684
3685 //r1: allow passive connects
3686 Cmd_AddCommand ("passive", CL_Passive_f);
3687
3688 //r1: toggle cvar
3689 Cmd_AddCommand ("toggle", CL_Toggle_f);
3690
3691 //r1: server status (connectionless)
3692 Cmd_AddCommand ("serverstatus", CL_ServerStatus_f);
3693
3694 Cmd_AddCommand ("ignore", CL_Ignore_f);
3695 Cmd_AddCommand ("unignore", CL_Unignore_f);
3696
3697 //r1: loc support
3698 Cmd_AddCommand ("addloc", CL_AddLoc_f);
3699 Cmd_AddCommand ("saveloc", CL_SaveLoc_f);
3700
3701 #ifdef _DEBUG
3702 Cmd_AddCommand ("packet", CL_Packet_f);
3703 Cmd_AddCommand ("spam", CL_Spam_f);
3704 #endif
3705
3706 #ifdef CLIENT_DLL
3707 Cmd_AddCommand ("cl_restart", CL_ClDLL_Restart_f);
3708 #endif
3709
3710 Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
3711 Cmd_AddCommand ("pause", CL_Pause_f);
3712 Cmd_AddCommand ("pingservers", CL_PingServers_f);
3713 Cmd_AddCommand ("skins", CL_Skins_f);
3714
3715 Cmd_AddCommand ("userinfo", CL_Userinfo_f);
3716 Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
3717
3718 Cmd_AddCommand ("changing", CL_Changing_f);
3719 Cmd_AddCommand ("disconnect", CL_Disconnect_f);
3720 Cmd_AddCommand ("record", CL_Record_f);
3721 Cmd_AddCommand ("stop", CL_Stop_f);
3722
3723 Cmd_AddCommand ("quit", CL_Quit_f);
3724
3725 Cmd_AddCommand ("connect", CL_Connect_f);
3726 Cmd_AddCommand ("reconnect", CL_Reconnect_f);
3727
3728 Cmd_AddCommand ("rcon", CL_Rcon_f);
3729
3730 // Cmd_AddCommand ("packet", CL_Packet_f); // this is dangerous to leave in
3731
3732 //r1: serves no purpose other than to allow buffer overflows!
3733 // Cmd_AddCommand ("setenv", CL_Setenv_f );
3734
3735 Cmd_AddCommand ("precache", CL_Precache_f);
3736
3737 Cmd_AddCommand ("download", CL_Download_f);
3738
3739 //
3740 // forward to server commands
3741 //
3742 // the only thing this does is allow command completion
3743 // to work -- all unknown commands are automatically
3744 // forwarded to the server
3745 Cmd_AddCommand ("wave", NULL);
3746 Cmd_AddCommand ("inven", NULL);
3747 Cmd_AddCommand ("kill", NULL);
3748 Cmd_AddCommand ("use", NULL);
3749 Cmd_AddCommand ("drop", NULL);
3750
3751 //r1: loc support
3752 Cmd_AddCommand ("say", NULL);
3753 Cmd_AddCommand ("say_team", NULL);
3754
3755 Cmd_AddCommand ("info", NULL);
3756
3757 //???
3758 //Cmd_AddCommand ("prog", NULL);
3759
3760 Cmd_AddCommand ("give", NULL);
3761 Cmd_AddCommand ("god", NULL);
3762 Cmd_AddCommand ("notarget", NULL);
3763 Cmd_AddCommand ("noclip", NULL);
3764 Cmd_AddCommand ("invuse", NULL);
3765 Cmd_AddCommand ("invprev", NULL);
3766 Cmd_AddCommand ("invnext", NULL);
3767 Cmd_AddCommand ("invdrop", NULL);
3768 Cmd_AddCommand ("weapnext", NULL);
3769 Cmd_AddCommand ("weapprev", NULL);
3770 }
3771
3772
3773
3774 /*
3775 ===============
3776 CL_WriteConfiguration
3777
3778 Writes key bindings and archived cvars to config.cfg
3779 ===============
3780 */
CL_WriteConfiguration(void)3781 void CL_WriteConfiguration (void)
3782 {
3783 FILE *f;
3784 char path[MAX_QPATH];
3785
3786 if (cls.state == ca_uninitialized)
3787 return;
3788
3789 Com_sprintf (path, sizeof(path),"%s/config.cfg",FS_Gamedir());
3790 f = fopen (path, "w");
3791 if (!f)
3792 {
3793 Com_Printf ("Couldn't write config.cfg.\n", LOG_CLIENT);
3794 return;
3795 }
3796
3797 fprintf (f, "// generated by quake, do not modify\n");
3798 Key_WriteBindings (f);
3799 fclose (f);
3800
3801 Cvar_WriteVariables (path);
3802 }
3803
3804
3805 /*
3806 ==================
3807 CL_FixCvarCheats
3808
3809 ==================
3810 */
3811
3812 typedef struct
3813 {
3814 char *name;
3815 float value;
3816 float setval;
3817 cvar_t *var;
3818 } cheatvar_t;
3819
3820 cheatvar_t cheatvars[] = {
3821 {"timescale", 1, 1, NULL},
3822 {"timedemo", 0, 0, NULL},
3823 {"r_drawworld", 1, 1, NULL},
3824 {"cl_testlights", 0, 0, NULL},
3825 {"cl_testblend", 0, 0, NULL},
3826 {"r_fullbright", 0, 0, NULL},
3827 {"r_drawflat", 0, 0, NULL},
3828 {"paused", 0, 0, NULL},
3829 {"fixedtime", 0, 0, NULL},
3830 {"sw_draworder", 0, 0, NULL},
3831 {"gl_lightmap", 0, 0, NULL},
3832 {"gl_saturatelighting", 0, 0, NULL},
3833 {"gl_lockpvs", 0, 0, NULL},
3834 {"sw_lockpvs", 0, 0, NULL},
3835 {NULL, 0, 0, NULL}
3836 };
3837
3838 int numcheatvars;
3839
_cheatcvar_changed(cvar_t * cvar,char * oldValue,char * newValue)3840 void _cheatcvar_changed (cvar_t *cvar, char *oldValue, char *newValue)
3841 {
3842 int i;
3843 cheatvar_t *var;
3844
3845 #ifdef _DEBUG
3846 return;
3847 #endif
3848
3849 if (cls.state == ca_disconnected || cl.attractloop || Com_ServerState() == ss_demo || cl.maxclients == 1)
3850 return; // single player can cheat
3851
3852 for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
3853 {
3854 if (var->var == cvar && var->value != cvar->value)
3855 {
3856 Com_Printf ("%s is cheat protected.\n", LOG_GENERAL, var->name);
3857 Cvar_SetValue (var->name, var->setval);
3858 return;
3859 }
3860 }
3861 }
3862
CL_FixCvarCheats(void)3863 void CL_FixCvarCheats (void)
3864 {
3865 int i;
3866 cheatvar_t *var;
3867
3868 // find all the cvars if we haven't done it yet
3869 if (!numcheatvars)
3870 {
3871 while (cheatvars[numcheatvars].name)
3872 {
3873 cheatvars[numcheatvars].var = Cvar_Get (cheatvars[numcheatvars].name,
3874 va("%g", cheatvars[numcheatvars].value), 0);
3875 cheatvars[numcheatvars].var->changed = _cheatcvar_changed;
3876 numcheatvars++;
3877 }
3878 }
3879
3880 if (cls.state == ca_disconnected || cl.attractloop || Com_ServerState() == ss_demo || cl.maxclients == 1)
3881 return; // single player can cheat
3882
3883 // make sure they are all set to the proper values
3884 for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
3885 {
3886 if (var->var->value != var->value)
3887 {
3888 Cvar_SetValue (var->name, var->setval);
3889 }
3890 }
3891 }
3892
3893 //============================================================================
3894
3895
3896
3897 /*
3898 ==================
3899 CL_Frame
3900
3901 ==================
3902 */
3903
3904 #ifdef _WIN32
3905 extern qboolean ActiveApp;
3906 #endif
3907
3908 extern cvar_t *cl_lents;
3909 void LE_RunLocalEnts (void);
3910
3911 //CL_RefreshInputs
3912 //jec - updates all input events
3913
3914 void IN_Commands (void);
3915
3916 void CL_RefreshCmd (void);
CL_RefreshInputs(void)3917 void CL_RefreshInputs (void)
3918 {
3919 // process new key events
3920 Sys_SendKeyEvents ();
3921
3922 #if (defined JOYSTICK) || (defined LINUX)
3923 // process mice & joystick events
3924 IN_Commands ();
3925 #endif
3926
3927 // process console commands
3928 Cbuf_Execute ();
3929
3930 // process packets from server
3931 CL_ReadPackets();
3932
3933 //jec - update usercmd state
3934 if (cls.state > ca_connecting)
3935 CL_RefreshCmd();
3936 }
3937
CL_LoadDeferredModels(void)3938 void CL_LoadDeferredModels (void)
3939 {
3940 if (deferred_model_index == MAX_MODELS || !cl.refresh_prepped)
3941 return;
3942
3943 for (;;)
3944 {
3945 deferred_model_index ++;
3946
3947 if (deferred_model_index == MAX_MODELS)
3948 {
3949 re.EndRegistration ();
3950 Com_DPrintf ("CL_LoadDeferredModels: All done.\n");
3951 return;
3952 }
3953
3954 if (!cl.configstrings[CS_MODELS+deferred_model_index][0])
3955 continue;
3956
3957 if (cl.configstrings[CS_MODELS+deferred_model_index][0] != '#')
3958 {
3959 //Com_DPrintf ("CL_LoadDeferredModels: Now loading '%s'...\n", cl.configstrings[CS_MODELS+deferred_model_index]);
3960 cl.model_draw[deferred_model_index] = re.RegisterModel (cl.configstrings[CS_MODELS+deferred_model_index]);
3961 if (cl.configstrings[CS_MODELS+deferred_model_index][0] == '*')
3962 cl.model_clip[deferred_model_index] = CM_InlineModel (cl.configstrings[CS_MODELS+deferred_model_index]);
3963 else
3964 cl.model_clip[deferred_model_index] = NULL;
3965 }
3966
3967 break;
3968 }
3969 }
3970
CL_SendCommand_Synchronous(void)3971 void CL_SendCommand_Synchronous (void)
3972 {
3973 // get new key events
3974 Sys_SendKeyEvents ();
3975
3976 // allow mice or other external controllers to add commands
3977 #ifdef JOYSTICK
3978 IN_Commands ();
3979 #endif
3980
3981 // process console commands
3982 Cbuf_Execute ();
3983
3984 // fix any cheating cvars
3985 //CL_FixCvarCheats ();
3986
3987 // send intentions now
3988 CL_SendCmd_Synchronous ();
3989
3990 // resend a connection request if necessary
3991 CL_CheckForResend ();
3992 }
3993
CL_Synchronous_Frame(int msec)3994 void CL_Synchronous_Frame (int msec)
3995 {
3996 static int extratime;
3997
3998 if (dedicated->value)
3999 return;
4000
4001 extratime += msec;
4002
4003 if (!cl_timedemo->value)
4004 {
4005 if (cls.state == ca_connected)
4006 {
4007 if (extratime < 100 && !send_packet_now)
4008 return; // don't flood packets out while connecting
4009 }
4010 else
4011 {
4012 if (extratime < 1000/cl_maxfps->value)
4013 return;
4014 }
4015 }
4016
4017 // let the mouse activate or deactivate
4018 IN_Frame ();
4019
4020 // decide the simulation time
4021 cls.frametime = extratime/1000.0f;
4022 cl.time += extratime;
4023 cls.realtime = curtime;
4024
4025 extratime = 0;
4026 #if 0
4027 if (cls.frametime > (1.0f / cl_minfps->value))
4028 cls.frametime = (1.0f / cl_minfps->value);
4029 #else
4030 if (cls.frametime > (1.0f / 5))
4031 cls.frametime = (1.0f / 5);
4032 #endif
4033
4034 // if in the debugger last frame, don't timeout
4035 if (msec > 5000)
4036 cls.netchan.last_received = Sys_Milliseconds ();
4037
4038 send_packet_now = false;
4039
4040 #ifdef USE_CURL
4041 CL_RunHTTPDownloads ();
4042 #endif
4043
4044 // fetch results from server
4045 CL_ReadPackets ();
4046
4047 // send a new command message to the server
4048 CL_SendCommand_Synchronous ();
4049
4050 // predict all unacknowledged movements
4051 CL_PredictMovement ();
4052
4053 // allow rendering DLL change
4054 if (vid_ref->modified)
4055 {
4056 VID_ReloadRefresh ();
4057 }
4058
4059 if (!cl.refresh_prepped && cls.state == ca_active)
4060 CL_PrepRefresh ();
4061
4062 CL_LoadDeferredModels();
4063
4064 // update the screen
4065 SCR_UpdateScreen ();
4066
4067 // update audio
4068 S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up);
4069
4070 #ifdef CD_AUDIO
4071 CDAudio_Update();
4072 #endif
4073
4074 if (cls.spamTime && cls.spamTime < cls.realtime)
4075 {
4076 Cbuf_AddText ("say \"" PRODUCTNAME " " VERSION " " __TIMESTAMP__ " " CPUSTRING " " BUILDSTRING " [http://r1ch.net/r1q2]\"\n");
4077 cls.lastSpamTime = cls.realtime;
4078 cls.spamTime = 0;
4079 }
4080
4081 if (cl_lents->intvalue)
4082 LE_RunLocalEnts ();
4083
4084 // advance local effects for next frame
4085 CL_RunDLights ();
4086 CL_RunLightStyles ();
4087
4088 #ifdef CINEMATICS
4089 SCR_RunCinematic ();
4090 #endif
4091
4092 SCR_RunConsole ();
4093 }
4094
4095
4096 //CL_SendCommand
4097 //jec - prepare and send out the current usercmd state.
CL_SendCommand(void)4098 void CL_SendCommand (void)
4099 {
4100 // fix any cheating cvars
4101 //CL_FixCvarCheats ();
4102
4103 // send client packet to server
4104 CL_SendCmd ();
4105
4106 // resend a connection request if necessary
4107 CL_CheckForResend ();
4108 }
4109
CL_Frame(int msec)4110 void CL_Frame (int msec)
4111 {
4112 //jec - this function's been rearranged a lot,
4113 // to decouple various client activities.
4114 static int packet_delta,
4115 render_delta,
4116 misc_delta = 1000;
4117
4118 //static int inputCount = 0;
4119
4120 qboolean packet_frame=true,
4121 render_frame=true,
4122 misc_frame=true;
4123
4124 #ifndef NO_SERVER
4125 if (dedicated->intvalue)
4126 return;
4127 #endif
4128
4129 #ifdef _DEBUG
4130 //if (!ActiveApp)
4131 //NET_Client_Sleep (50);
4132
4133 if (dbg_framesleep->intvalue)
4134 Sys_Sleep (dbg_framesleep->intvalue);
4135 #else
4136 #ifdef _WIN32
4137 if (!ActiveApp && !Com_ServerState())
4138 NET_Client_Sleep (100);
4139 #endif
4140 #endif
4141
4142 if (cl_async->intvalue != 1)
4143 {
4144 CL_Synchronous_Frame (msec);
4145 return;
4146 }
4147
4148 //jec - set internal counters
4149 packet_delta += msec;
4150 render_delta += msec;
4151 misc_delta += msec;
4152
4153 //jec - set the frame counters
4154 cl.time += msec;
4155 cls.frametime = packet_delta/1000.0f;
4156 cls.realtime = curtime;
4157
4158 //if (cls.frametime > 0.05)
4159 // Com_Printf ("Hitch warning: %f (%d ms)\n", LOG_CLIENT, cls.frametime, msec);
4160
4161 //if in the debugger last frame, don't timeout
4162 if (msec > 5000)
4163 cls.netchan.last_received = Sys_Milliseconds ();
4164
4165 // don't extrapolate too far ahead
4166 if (cls.frametime > .5)
4167 cls.frametime = .5;
4168
4169 //jec - determine what all should be done...
4170 if (!cl_timedemo->intvalue)
4171 {
4172 // packet transmission rate is too high
4173 if (packet_delta < 1000/cl_maxfps->intvalue)
4174 packet_frame = false;
4175
4176 // don't need to do this stuff much.
4177 if( misc_delta < 250)
4178 misc_frame = false;
4179
4180 // framerate is too high
4181 if (render_delta < 1000/r_maxfps->intvalue)
4182 render_frame = false;
4183 }
4184
4185 // don't flood packets out while connecting
4186 if (cls.state == ca_connected)
4187 {
4188 if (packet_delta < 100)
4189 packet_frame = false;
4190
4191 #ifdef USE_CURL
4192 //we run full speed when connecting
4193 CL_RunHTTPDownloads ();
4194 #endif
4195 }
4196
4197 //jec - update the inputs (keybd, mouse, server, etc)
4198 CL_RefreshInputs ();
4199
4200 if ((send_packet_now && cl_instantpacket->intvalue) || userinfo_modified)
4201 {
4202 if (cls.state < ca_connected)
4203 {
4204 userinfo_modified = false;
4205 }
4206 else
4207 {
4208 packet_frame = true;
4209 }
4210 }
4211
4212 send_packet_now = false;
4213
4214 //jec- send commands to the server
4215 //if (++inputCount >= cl_snaps->value && packet_frame)
4216 if (packet_frame)
4217 {
4218 packet_delta = 0;
4219 CL_SendCommand ();
4220 CL_LoadDeferredModels();
4221
4222 #ifdef USE_CURL
4223 //we run less often in game
4224 CL_RunHTTPDownloads ();
4225 #endif
4226 }
4227
4228 //jec- Render the display
4229 if(render_frame)
4230 {
4231 render_delta = 0;
4232
4233 if(misc_frame)
4234 {
4235 //static qboolean snapsInitialized = false;
4236
4237 misc_delta = 0;
4238
4239 if (cls.spamTime && cls.spamTime < cls.realtime)
4240 {
4241 char buff[256];
4242 Com_sprintf (buff, sizeof(buff), "say \"R1Q2 %s %s %s %s [http://r1ch.net/r1q2]\"\n", VERSION,
4243 __TIMESTAMP__, CPUSTRING, BUILDSTRING
4244 );
4245 Cbuf_AddText (buff);
4246 cls.lastSpamTime = cls.realtime;
4247 cls.spamTime = 0;
4248 }
4249
4250 //set the mouse on/off
4251 IN_Frame();
4252
4253 if (vid_ref->modified)
4254 {
4255 VID_ReloadRefresh ();
4256 }
4257 }
4258
4259 if (!cl.refresh_prepped && cls.state == ca_active)
4260 CL_PrepRefresh ();
4261
4262 // predict all unacknowledged movements
4263 CL_PredictMovement ();
4264
4265 //r1: run local ent physics/thinking/etc
4266 if (cl_lents->intvalue)
4267 LE_RunLocalEnts ();
4268
4269 // update the screen
4270 SCR_UpdateScreen ();
4271
4272 // update audio
4273 S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up);
4274
4275 #ifdef CD_AUDIO
4276 if(misc_frame)
4277 CDAudio_Update();
4278 #endif
4279
4280 // advance local effects for next frame
4281 CL_RunDLights ();
4282 CL_RunLightStyles ();
4283 SCR_RunConsole ();
4284 }
4285 //cls.framecount++;
4286 //}
4287 }
4288
4289 //============================================================================
4290
4291 /*
4292 ====================
4293 CL_Init
4294 ====================
4295 */
4296 void LE_Init (void);
CL_Init(void)4297 void CL_Init (void)
4298 {
4299 int i;
4300
4301 #ifndef NO_SERVER
4302 if (dedicated->intvalue)
4303 return; // nothing running on the client
4304 #endif
4305
4306 //r1: init string table
4307 for (i = svc_max_enttypes; i < 256; i++)
4308 svc_strings[i] = svc_strings[0];
4309
4310 // all archived variables will now be loaded
4311
4312 // Cbuf_AddText ("exec autoexec.cfg\n");
4313 FS_ExecConfig ("autoexec.cfg");
4314 Cbuf_Execute ();
4315
4316 Con_Init ();
4317 #if defined __linux__ || defined __sgi || defined __FreeBSD__
4318 S_Init (true);
4319
4320 VID_Init ();
4321 //r1: after initing video new console size is probably in use... lets use it for remaining
4322 //messages
4323 Con_CheckResize();
4324 #else
4325 VID_Init ();
4326
4327 //r1: after initing video new console size is probably in use... lets use it for remaining
4328 //messages
4329 Con_CheckResize();
4330
4331 S_Init (true); // sound must be initialized after window is created
4332 #endif
4333
4334 V_Init ();
4335
4336 #ifdef CLIENT_DLL
4337 CL_ClDLL_Restart_f ();
4338 #endif
4339
4340 SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer));
4341
4342 M_Init ();
4343
4344 SCR_Init ();
4345 //cls.disable_screen = 0; // don't draw yet
4346
4347 #ifdef CD_AUDIO
4348 CDAudio_Init ();
4349 #endif
4350 CL_InitLocal ();
4351 IN_Init ();
4352
4353 LE_Init ();
4354
4355 CL_Loc_Init ();
4356
4357 #ifdef USE_CURL
4358 CL_InitHTTPDownloads ();
4359 #endif
4360
4361 FS_ExecConfig ("postinit.cfg");
4362 Cbuf_Execute ();
4363 }
4364
4365
4366 /*
4367 ===============
4368 CL_Shutdown
4369
4370 FIXME: this is a callback from Sys_Quit and Com_Error. It would be better
4371 to run quit through here before the final handoff to the sys code.
4372 ===============
4373 */
CL_Shutdown(void)4374 void CL_Shutdown(void)
4375 {
4376 static qboolean isdown = false;
4377
4378 if (isdown)
4379 {
4380 //printf ("recursive shutdown\n");
4381 return;
4382 }
4383 isdown = true;
4384
4385 #ifdef USE_CURL
4386 CL_HTTP_Cleanup (true);
4387 #endif
4388
4389 CL_FreeLocs ();
4390 CL_WriteConfiguration ();
4391
4392 #ifdef CD_AUDIO
4393 CDAudio_Shutdown ();
4394 #endif
4395 S_Shutdown();
4396 IN_Shutdown ();
4397 VID_Shutdown();
4398 }
4399