1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20 // cl_main.c -- client main loop
21
22 #include "quakedef.h"
23 #include "winquake.h"
24 #ifdef _WIN32
25 #include "winsock.h"
26 #else
27 #include <netinet/in.h>
28 #endif
29
30
31 // we need to declare some mouse variables here, because the menu system
32 // references them even when on a unix system.
33
34 qboolean noclip_anglehack; // remnant from old quake
35
36
37 cvar_t rcon_password = CVAR3("rcon_password", "", false);
38
39 cvar_t rcon_address = CVAR2("rcon_address", "");
40
41 cvar_t cl_timeout = CVAR2("cl_timeout", "60");
42
43 cvar_t cl_shownet = CVAR2("cl_shownet","0"); // can be 0, 1, or 2
44
45 cvar_t cl_sbar = CVAR3("cl_sbar", "0", true);
46 cvar_t cl_hudswap = CVAR3("cl_hudswap", "0", true);
47 cvar_t cl_maxfps = CVAR3("cl_maxfps", "0", true);
48
49 cvar_t lookspring = CVAR3("lookspring","0", true);
50 cvar_t lookstrafe = CVAR3("lookstrafe","0", true);
51 cvar_t sensitivity = CVAR3("sensitivity","3", true);
52
53 cvar_t m_pitch = CVAR3("m_pitch","0.022", true);
54 cvar_t m_yaw = CVAR2("m_yaw","0.022");
55 cvar_t m_forward = CVAR2("m_forward","1");
56 cvar_t m_side = CVAR2("m_side","0.8");
57
58 cvar_t entlatency = CVAR2("entlatency", "20");
59 cvar_t cl_predict_players = CVAR2("cl_predict_players", "1");
60 cvar_t cl_predict_players2 = CVAR2("cl_predict_players2", "1");
61 cvar_t cl_solid_players = CVAR2("cl_solid_players", "1");
62
63 cvar_t localid = CVAR2("localid", "");
64
65 static qboolean allowremotecmd = true;
66
67 //
68 // info mirrors
69 //
70 cvar_t password = CVAR4("password", "", false, true);
71 cvar_t spectator = CVAR4("spectator", "", false, true);
72 cvar_t name = CVAR4("name","unnamed", true, true);
73 cvar_t team = CVAR4("team","", true, true);
74 cvar_t skin = CVAR4("skin","", true, true);
75 cvar_t topcolor = CVAR4("topcolor","0", true, true);
76 cvar_t bottomcolor = CVAR4("bottomcolor","0", true, true);
77 cvar_t rate = CVAR4("rate","2500", true, true);
78 cvar_t noaim = CVAR4("noaim","0", true, true);
79 cvar_t msg = CVAR4("msg","1", true, true);
80
81 extern cvar_t cl_hightrack;
82
83
84 client_static_t cls;
85 client_state_t cl;
86
87 entity_state_t cl_baselines[MAX_EDICTS];
88 efrag_t cl_efrags[MAX_EFRAGS];
89 entity_t cl_static_entities[MAX_STATIC_ENTITIES];
90 lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES];
91 dlight_t cl_dlights[MAX_DLIGHTS];
92
93 // refresh list
94 // this is double buffered so the last frame
95 // can be scanned for oldorigins of trailing objects
96 int cl_numvisedicts, cl_oldnumvisedicts;
97 entity_t *cl_visedicts, *cl_oldvisedicts;
98 entity_t cl_visedicts_list[2][MAX_VISEDICTS];
99
100 double connect_time = -1; // for connection retransmits
101
102 quakeparms_t host_parms;
103
104 qboolean host_initialized; // true if into command execution
105 qboolean nomaster;
106
107 double host_frametime;
108 double realtime; // without any filtering or bounding
109 double oldrealtime; // last frame run
110 int host_framecount;
111
112 int host_hunklevel;
113
114 byte *host_basepal;
115 byte *host_colormap;
116
117 netadr_t master_adr; // address of the master server
118
119 cvar_t host_speeds = CVAR2("host_speeds","0"); // set for running times
120 cvar_t show_fps = CVAR2("show_fps","0"); // set for running times
121 cvar_t developer = CVAR2("developer","0");
122
123 int fps_count;
124
125 jmp_buf host_abort;
126
127 void Master_Connect_f (void);
128
129 float server_version = 0; // version of server we connected to
130
131 char emodel_name[] =
132 { 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
133 char pmodel_name[] =
134 { 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
135 char prespawn_name[] =
136 { 'p'^0xff, 'r'^0xff, 'e'^0xff, 's'^0xff, 'p'^0xff, 'a'^0xff, 'w'^0xff, 'n'^0xff,
137 ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '0'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
138 char modellist_name[] =
139 { 'm'^0xff, 'o'^0xff, 'd'^0xff, 'e'^0xff, 'l'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
140 ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
141 char soundlist_name[] =
142 { 's'^0xff, 'o'^0xff, 'u'^0xff, 'n'^0xff, 'd'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
143 ' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
144
145 /*
146 ==================
147 CL_Quit_f
148 ==================
149 */
CL_Quit_f(void)150 void CL_Quit_f (void)
151 {
152 if (1 /* key_dest != key_console */ /* && cls.state != ca_dedicated */)
153 {
154 M_Menu_Quit_f ();
155 return;
156 }
157 CL_Disconnect ();
158 Sys_Quit ();
159 }
160
161 /*
162 =======================
163 CL_Version_f
164 ======================
165 */
CL_Version_f(void)166 void CL_Version_f (void)
167 {
168 Con_Printf ("Version %4.2f\n", VERSION);
169 Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
170 }
171
172
173 /*
174 =======================
175 CL_SendConnectPacket
176
177 called by CL_Connect_f and CL_CheckResend
178 ======================
179 */
CL_SendConnectPacket(void)180 void CL_SendConnectPacket (void)
181 {
182 netadr_t adr;
183 char data[2048];
184 double t1, t2;
185 // JACK: Fixed bug where DNS lookups would cause two connects real fast
186 // Now, adds lookup time to the connect time.
187 // Should I add it to realtime instead?!?!
188
189 if (cls.state != ca_disconnected)
190 return;
191
192 t1 = Sys_DoubleTime ();
193
194 if (!NET_StringToAdr (cls.servername, &adr))
195 {
196 Con_Printf ("Bad server address\n");
197 connect_time = -1;
198 return;
199 }
200
201 if (!NET_IsClientLegal(&adr))
202 {
203 Con_Printf ("Illegal server address\n");
204 connect_time = -1;
205 return;
206 }
207
208 if (adr.port == 0)
209 adr.port = BigShort (27500);
210 t2 = Sys_DoubleTime ();
211
212 connect_time = realtime+t2-t1; // for retransmit requests
213
214 cls.qport = Cvar_VariableValue("qport");
215
216 Info_SetValueForStarKey (cls.userinfo, "*ip", NET_AdrToString(adr), MAX_INFO_STRING);
217
218 // Con_Printf ("Connecting to %s...\n", cls.servername);
219 sprintf (data, "%c%c%c%cconnect %i %i %i \"%s\"\n",
220 255, 255, 255, 255, PROTOCOL_VERSION, cls.qport, cls.challenge, cls.userinfo);
221 NET_SendPacket (strlen(data), data, adr);
222 }
223
224 /*
225 =================
226 CL_CheckForResend
227
228 Resend a connect message if the last one has timed out
229
230 =================
231 */
CL_CheckForResend(void)232 void CL_CheckForResend (void)
233 {
234 netadr_t adr;
235 char data[2048];
236 double t1, t2;
237
238 if (connect_time == -1)
239 return;
240 if (cls.state != ca_disconnected)
241 return;
242 if (connect_time && realtime - connect_time < 5.0)
243 return;
244
245 t1 = Sys_DoubleTime ();
246 if (!NET_StringToAdr (cls.servername, &adr))
247 {
248 Con_Printf ("Bad server address\n");
249 connect_time = -1;
250 return;
251 }
252 if (!NET_IsClientLegal(&adr))
253 {
254 Con_Printf ("Illegal server address\n");
255 connect_time = -1;
256 return;
257 }
258
259 if (adr.port == 0)
260 adr.port = BigShort (27500);
261 t2 = Sys_DoubleTime ();
262
263 connect_time = realtime+t2-t1; // for retransmit requests
264
265 Con_Printf ("Connecting to %s...\n", cls.servername);
266 sprintf (data, "%c%c%c%cgetchallenge\n", 255, 255, 255, 255);
267 NET_SendPacket (strlen(data), data, adr);
268 }
269
CL_BeginServerConnect(void)270 void CL_BeginServerConnect(void)
271 {
272 connect_time = 0;
273 CL_CheckForResend();
274 }
275
276 /*
277 ================
278 CL_Connect_f
279
280 ================
281 */
CL_Connect_f(void)282 void CL_Connect_f (void)
283 {
284 char *server;
285
286 if (Cmd_Argc() != 2)
287 {
288 Con_Printf ("usage: connect <server>\n");
289 return;
290 }
291
292 server = Cmd_Argv (1);
293
294 CL_Disconnect ();
295
296 strncpy (cls.servername, server, sizeof(cls.servername)-1);
297 CL_BeginServerConnect();
298 }
299
300
301 /*
302 =====================
303 CL_Rcon_f
304
305 Send the rest of the command line over as
306 an unconnected command.
307 =====================
308 */
CL_Rcon_f(void)309 void CL_Rcon_f (void)
310 {
311 char message[1024];
312 int i;
313 netadr_t to;
314
315 if (!rcon_password.string)
316 {
317 Con_Printf ("You must set 'rcon_password' before\n"
318 "issuing an rcon command.\n");
319 return;
320 }
321
322 message[0] = 255;
323 message[1] = 255;
324 message[2] = 255;
325 message[3] = 255;
326 message[4] = 0;
327
328 strcat (message, "rcon ");
329
330 strcat (message, rcon_password.string);
331 strcat (message, " ");
332
333 for (i=1 ; i<Cmd_Argc() ; i++)
334 {
335 strcat (message, Cmd_Argv(i));
336 strcat (message, " ");
337 }
338
339 if (cls.state >= ca_connected)
340 to = cls.netchan.remote_address;
341 else
342 {
343 if (!strlen(rcon_address.string))
344 {
345 Con_Printf ("You must either be connected,\n"
346 "or set the 'rcon_address' cvar\n"
347 "to issue rcon commands\n");
348
349 return;
350 }
351 NET_StringToAdr (rcon_address.string, &to);
352 }
353
354 NET_SendPacket (strlen(message)+1, message
355 , to);
356 }
357
358
359 /*
360 =====================
361 CL_ClearState
362
363 =====================
364 */
CL_ClearState(void)365 void CL_ClearState (void)
366 {
367 int i;
368
369 S_StopAllSounds (true);
370
371 Con_DPrintf ("Clearing memory\n");
372 D_FlushCaches ();
373 Mod_ClearAll ();
374 if (host_hunklevel) // FIXME: check this...
375 Hunk_FreeToLowMark (host_hunklevel);
376
377 CL_ClearTEnts ();
378
379 // wipe the entire cl structure
380 memset (&cl, 0, sizeof(cl));
381
382 SZ_Clear (&cls.netchan.message);
383
384 // clear other arrays
385 memset (cl_efrags, 0, sizeof(cl_efrags));
386 memset (cl_dlights, 0, sizeof(cl_dlights));
387 memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
388
389 //
390 // allocate the efrags and chain together into a free list
391 //
392 cl.free_efrags = cl_efrags;
393 for (i=0 ; i<MAX_EFRAGS-1 ; i++)
394 cl.free_efrags[i].entnext = &cl.free_efrags[i+1];
395 cl.free_efrags[i].entnext = NULL;
396 }
397
398 /*
399 =====================
400 CL_Disconnect
401
402 Sends a disconnect message to the server
403 This is also called on Host_Error, so it shouldn't cause any errors
404 =====================
405 */
CL_Disconnect(void)406 void CL_Disconnect (void)
407 {
408 char final[10];
409
410 connect_time = -1;
411
412 #ifdef _WIN32
413 SetWindowText (mainwindow, "QuakeWorld: disconnected");
414 #endif
415
416 // stop sounds (especially looping!)
417 S_StopAllSounds (true);
418
419 // if running a local server, shut it down
420 if (cls.demoplayback)
421 CL_StopPlayback ();
422 else if (cls.state != ca_disconnected)
423 {
424 if (cls.demorecording)
425 CL_Stop_f ();
426
427 final[0] = clc_stringcmd;
428 strcpy (final+1, "drop");
429 Netchan_Transmit (&cls.netchan, 6, (byte*) final);
430 Netchan_Transmit (&cls.netchan, 6, (byte*) final);
431 Netchan_Transmit (&cls.netchan, 6, (byte*) final);
432
433 cls.state = ca_disconnected;
434
435 cls.demoplayback = cls.demorecording = cls.timedemo = false;
436 }
437 Cam_Reset();
438
439 if (cls.download) {
440 fclose(cls.download);
441 cls.download = NULL;
442 }
443
444 CL_StopUpload();
445
446 }
447
CL_Disconnect_f(void)448 void CL_Disconnect_f (void)
449 {
450 CL_Disconnect ();
451 }
452
453 /*
454 ====================
455 CL_User_f
456
457 user <name or userid>
458
459 Dump userdata / masterdata for a user
460 ====================
461 */
CL_User_f(void)462 void CL_User_f (void)
463 {
464 int uid;
465 int i;
466
467 if (Cmd_Argc() != 2)
468 {
469 Con_Printf ("Usage: user <username / userid>\n");
470 return;
471 }
472
473 uid = atoi(Cmd_Argv(1));
474
475 for (i=0 ; i<MAX_CLIENTS ; i++)
476 {
477 if (!cl.players[i].name[0])
478 continue;
479 if (cl.players[i].userid == uid
480 || !strcmp(cl.players[i].name, Cmd_Argv(1)) )
481 {
482 Info_Print (cl.players[i].userinfo);
483 return;
484 }
485 }
486 Con_Printf ("User not in server.\n");
487 }
488
489 /*
490 ====================
491 CL_Users_f
492
493 Dump userids for all current players
494 ====================
495 */
CL_Users_f(void)496 void CL_Users_f (void)
497 {
498 int i;
499 int c;
500
501 c = 0;
502 Con_Printf ("userid frags name\n");
503 Con_Printf ("------ ----- ----\n");
504 for (i=0 ; i<MAX_CLIENTS ; i++)
505 {
506 if (cl.players[i].name[0])
507 {
508 Con_Printf ("%6i %4i %s\n", cl.players[i].userid, cl.players[i].frags, cl.players[i].name);
509 c++;
510 }
511 }
512
513 Con_Printf ("%i total users\n", c);
514 }
515
CL_Color_f(void)516 void CL_Color_f (void)
517 {
518 // just for quake compatability...
519 int top, bottom;
520 char num[16];
521
522 if (Cmd_Argc() == 1)
523 {
524 Con_Printf ("\"color\" is \"%s %s\"\n",
525 Info_ValueForKey (cls.userinfo, "topcolor"),
526 Info_ValueForKey (cls.userinfo, "bottomcolor") );
527 Con_Printf ("color <0-13> [0-13]\n");
528 return;
529 }
530
531 if (Cmd_Argc() == 2)
532 top = bottom = atoi(Cmd_Argv(1));
533 else
534 {
535 top = atoi(Cmd_Argv(1));
536 bottom = atoi(Cmd_Argv(2));
537 }
538
539 top &= 15;
540 if (top > 13)
541 top = 13;
542 bottom &= 15;
543 if (bottom > 13)
544 bottom = 13;
545
546 sprintf (num, "%i", top);
547 Cvar_Set ("topcolor", num);
548 sprintf (num, "%i", bottom);
549 Cvar_Set ("bottomcolor", num);
550 }
551
552 /*
553 ==================
554 CL_FullServerinfo_f
555
556 Sent by server when serverinfo changes
557 ==================
558 */
CL_FullServerinfo_f(void)559 void CL_FullServerinfo_f (void)
560 {
561 char *p;
562 float v;
563
564 if (Cmd_Argc() != 2)
565 {
566 Con_Printf ("usage: fullserverinfo <complete info string>\n");
567 return;
568 }
569
570 strcpy (cl.serverinfo, Cmd_Argv(1));
571
572 if ((p = Info_ValueForKey(cl.serverinfo, "*vesion")) && *p) {
573 v = Q_atof(p);
574 if (v) {
575 if (!server_version)
576 Con_Printf("Version %1.2f Server\n", v);
577 server_version = v;
578 }
579 }
580 }
581
582 /*
583 ==================
584 CL_FullInfo_f
585
586 Allow clients to change userinfo
587 ==================
588 Casey was here :)
589 */
CL_FullInfo_f(void)590 void CL_FullInfo_f (void)
591 {
592 char key[512];
593 char value[512];
594 char *o;
595 char *s;
596
597 if (Cmd_Argc() != 2)
598 {
599 Con_Printf ("fullinfo <complete info string>\n");
600 return;
601 }
602
603 s = Cmd_Argv(1);
604 if (*s == '\\')
605 s++;
606 while (*s)
607 {
608 o = key;
609 while (*s && *s != '\\')
610 *o++ = *s++;
611 *o = 0;
612
613 if (!*s)
614 {
615 Con_Printf ("MISSING VALUE\n");
616 return;
617 }
618
619 o = value;
620 s++;
621 while (*s && *s != '\\')
622 *o++ = *s++;
623 *o = 0;
624
625 if (*s)
626 s++;
627
628 if (!strcasecmp(key, pmodel_name) || !strcasecmp(key, emodel_name))
629 continue;
630
631 Info_SetValueForKey (cls.userinfo, key, value, MAX_INFO_STRING);
632 }
633 }
634
635 /*
636 ==================
637 CL_SetInfo_f
638
639 Allow clients to change userinfo
640 ==================
641 */
CL_SetInfo_f(void)642 void CL_SetInfo_f (void)
643 {
644 if (Cmd_Argc() == 1)
645 {
646 Info_Print (cls.userinfo);
647 return;
648 }
649 if (Cmd_Argc() != 3)
650 {
651 Con_Printf ("usage: setinfo [ <key> <value> ]\n");
652 return;
653 }
654 if (!stricmp(Cmd_Argv(1), pmodel_name) || !strcmp(Cmd_Argv(1), emodel_name))
655 return;
656
657 Info_SetValueForKey (cls.userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING);
658 if (cls.state >= ca_connected)
659 Cmd_ForwardToServer ();
660 }
661
662 /*
663 ====================
664 CL_Packet_f
665
666 packet <destination> <contents>
667
668 Contents allows \n escape character
669 ====================
670 */
CL_Packet_f(void)671 void CL_Packet_f (void)
672 {
673 char send[2048];
674 int i, l;
675 char *in, *out;
676 netadr_t adr;
677
678 if (Cmd_Argc() != 3)
679 {
680 Con_Printf ("packet <destination> <contents>\n");
681 return;
682 }
683
684 if (!NET_StringToAdr (Cmd_Argv(1), &adr))
685 {
686 Con_Printf ("Bad address\n");
687 return;
688 }
689
690 in = Cmd_Argv(2);
691 out = send+4;
692 send[0] = send[1] = send[2] = send[3] = 0xff;
693
694 l = strlen (in);
695 for (i=0 ; i<l ; i++)
696 {
697 if (in[i] == '\\' && in[i+1] == 'n')
698 {
699 *out++ = '\n';
700 i++;
701 }
702 else
703 *out++ = in[i];
704 }
705 *out = 0;
706
707 NET_SendPacket (out-send, send, adr);
708 }
709
710
711 /*
712 =====================
713 CL_NextDemo
714
715 Called to play the next demo in the demo loop
716 =====================
717 */
CL_NextDemo(void)718 void CL_NextDemo (void)
719 {
720 char str[1024];
721
722 if (cls.demonum == -1)
723 return; // don't play demos
724
725 if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
726 {
727 cls.demonum = 0;
728 if (!cls.demos[cls.demonum][0])
729 {
730 // Con_Printf ("No demos listed with startdemos\n");
731 cls.demonum = -1;
732 return;
733 }
734 }
735
736 sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
737 Cbuf_InsertText (str);
738 cls.demonum++;
739 }
740
741
742 /*
743 =================
744 CL_Changing_f
745
746 Just sent as a hint to the client that they should
747 drop to full console
748 =================
749 */
CL_Changing_f(void)750 void CL_Changing_f (void)
751 {
752 if (cls.download) // don't change when downloading
753 return;
754
755 S_StopAllSounds (true);
756 cl.intermission = 0;
757 cls.state = ca_connected; // not active anymore, but not disconnected
758 Con_Printf ("\nChanging map...\n");
759 }
760
761
762 /*
763 =================
764 CL_Reconnect_f
765
766 The server is changing levels
767 =================
768 */
CL_Reconnect_f(void)769 void CL_Reconnect_f (void)
770 {
771 if (cls.download) // don't change when downloading
772 return;
773
774 S_StopAllSounds (true);
775
776 if (cls.state == ca_connected) {
777 Con_Printf ("reconnecting...\n");
778 MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
779 MSG_WriteString (&cls.netchan.message, "new");
780 return;
781 }
782
783 if (!*cls.servername) {
784 Con_Printf("No server to reconnect to...\n");
785 return;
786 }
787
788 CL_Disconnect();
789 CL_BeginServerConnect();
790 }
791
792 /*
793 =================
794 CL_ConnectionlessPacket
795
796 Responses to broadcasts, etc
797 =================
798 */
CL_ConnectionlessPacket(void)799 void CL_ConnectionlessPacket (void)
800 {
801 char *s;
802 int c;
803
804 MSG_BeginReading ();
805 MSG_ReadLong (); // skip the -1
806
807 c = MSG_ReadByte ();
808 if (!cls.demoplayback)
809 Con_Printf ("%s: ", NET_AdrToString (net_from));
810 // Con_DPrintf ("%s", net_message.data + 5);
811 if (c == S2C_CONNECTION)
812 {
813 Con_Printf ("connection\n");
814 if (cls.state >= ca_connected)
815 {
816 if (!cls.demoplayback)
817 Con_Printf ("Dup connect received. Ignored.\n");
818 return;
819 }
820 Netchan_Setup (&cls.netchan, net_from, cls.qport);
821 MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
822 MSG_WriteString (&cls.netchan.message, "new");
823 cls.state = ca_connected;
824 Con_Printf ("Connected.\n");
825 allowremotecmd = false; // localid required now for remote cmds
826 return;
827 }
828 // remote command from gui front end
829 if (c == A2C_CLIENT_COMMAND)
830 {
831 char cmdtext[2048];
832
833 Con_Printf ("client command\n");
834
835 if ((*(unsigned *)net_from.ip != *(unsigned *)net_local_adr.ip
836 && *(unsigned *)net_from.ip != htonl(INADDR_LOOPBACK)) )
837 {
838 Con_Printf ("Command packet from remote host. Ignored.\n");
839 return;
840 }
841 #ifdef _WIN32
842 ShowWindow (mainwindow, SW_RESTORE);
843 SetForegroundWindow (mainwindow);
844 #endif
845 s = MSG_ReadString ();
846
847 strncpy(cmdtext, s, sizeof(cmdtext) - 1);
848 cmdtext[sizeof(cmdtext) - 1] = 0;
849
850 s = MSG_ReadString ();
851
852 while (*s && isspace(*s))
853 s++;
854 while (*s && isspace(s[strlen(s) - 1]))
855 s[strlen(s) - 1] = 0;
856
857 if (!allowremotecmd && (!*localid.string || strcmp(localid.string, s))) {
858 if (!*localid.string) {
859 Con_Printf("===========================\n");
860 Con_Printf("Command packet received from local host, but no "
861 "localid has been set. You may need to upgrade your server "
862 "browser.\n");
863 Con_Printf("===========================\n");
864 return;
865 }
866 Con_Printf("===========================\n");
867 Con_Printf("Invalid localid on command packet received from local host. "
868 "\n|%s| != |%s|\n"
869 "You may need to reload your server browser and QuakeWorld.\n",
870 s, localid.string);
871 Con_Printf("===========================\n");
872 Cvar_Set("localid", "");
873 return;
874 }
875
876 Cbuf_AddText (cmdtext);
877 allowremotecmd = false;
878 return;
879 }
880 // print command from somewhere
881 if (c == A2C_PRINT)
882 {
883 Con_Printf ("print\n");
884
885 s = MSG_ReadString ();
886 Con_Print (s);
887 return;
888 }
889
890 // ping from somewhere
891 if (c == A2A_PING)
892 {
893 char data[6];
894
895 Con_Printf ("ping\n");
896
897 data[0] = 0xff;
898 data[1] = 0xff;
899 data[2] = 0xff;
900 data[3] = 0xff;
901 data[4] = A2A_ACK;
902 data[5] = 0;
903
904 NET_SendPacket (6, &data, net_from);
905 return;
906 }
907
908 if (c == S2C_CHALLENGE) {
909 Con_Printf ("challenge\n");
910
911 s = MSG_ReadString ();
912 cls.challenge = atoi(s);
913 CL_SendConnectPacket ();
914 return;
915 }
916
917 #if 0
918 if (c == svc_disconnect) {
919 Con_Printf ("disconnect\n");
920
921 Host_EndGame ("Server disconnected");
922 return;
923 }
924 #endif
925
926 Con_Printf ("unknown: %c\n", c);
927 }
928
929
930 /*
931 =================
932 CL_ReadPackets
933 =================
934 */
CL_ReadPackets(void)935 void CL_ReadPackets (void)
936 {
937 // while (NET_GetPacket ())
938 while (CL_GetMessage())
939 {
940 //
941 // remote command packet
942 //
943 if (*(int *)net_message.data == -1)
944 {
945 CL_ConnectionlessPacket ();
946 continue;
947 }
948
949 if (net_message.cursize < 8)
950 {
951 Con_Printf ("%s: Runt packet\n",NET_AdrToString(net_from));
952 continue;
953 }
954
955 //
956 // packet from server
957 //
958 if (!cls.demoplayback &&
959 !NET_CompareAdr (net_from, cls.netchan.remote_address))
960 {
961 Con_DPrintf ("%s:sequenced packet without connection\n"
962 ,NET_AdrToString(net_from));
963 continue;
964 }
965 if (!Netchan_Process(&cls.netchan))
966 continue; // wasn't accepted for some reason
967 CL_ParseServerMessage ();
968
969 // if (cls.demoplayback && cls.state >= ca_active && !CL_DemoBehind())
970 // return;
971 }
972
973 //
974 // check timeout
975 //
976 if (cls.state >= ca_connected
977 && realtime - cls.netchan.last_received > cl_timeout.value)
978 {
979 Con_Printf ("\nServer connection timed out.\n");
980 CL_Disconnect ();
981 return;
982 }
983
984 }
985
986 //=============================================================================
987
988 /*
989 =====================
990 CL_Download_f
991 =====================
992 */
CL_Download_f(void)993 void CL_Download_f (void)
994 {
995 char *p, *q;
996
997 if (cls.state == ca_disconnected)
998 {
999 Con_Printf ("Must be connected.\n");
1000 return;
1001 }
1002
1003 if (Cmd_Argc() != 2)
1004 {
1005 Con_Printf ("Usage: download <datafile>\n");
1006 return;
1007 }
1008
1009 sprintf (cls.downloadname, "%s/%s", com_gamedir, Cmd_Argv(1));
1010
1011 p = cls.downloadname;
1012 for (;;) {
1013 if ((q = strchr(p, '/')) != NULL) {
1014 *q = 0;
1015 Sys_mkdir(cls.downloadname);
1016 *q = '/';
1017 p = q + 1;
1018 } else
1019 break;
1020 }
1021
1022 strcpy(cls.downloadtempname, cls.downloadname);
1023 cls.download = fopen (cls.downloadname, "wb");
1024 cls.downloadtype = dl_single;
1025
1026 MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
1027 SZ_Print (&cls.netchan.message, va("download %s\n",Cmd_Argv(1)));
1028 }
1029
1030 #ifdef _WINDOWS
1031 #include <windows.h>
1032 /*
1033 =================
1034 CL_Minimize_f
1035 =================
1036 */
CL_Windows_f(void)1037 void CL_Windows_f (void) {
1038 // if (modestate == MS_WINDOWED)
1039 // ShowWindow(mainwindow, SW_MINIMIZE);
1040 // else
1041 SendMessage(mainwindow, WM_SYSKEYUP, VK_TAB, 1 | (0x0F << 16) | (1<<29));
1042 }
1043 #endif
1044
1045 /*
1046 =================
1047 CL_Init
1048 =================
1049 */
CL_Init(void)1050 void CL_Init (void)
1051 {
1052 extern cvar_t baseskin;
1053 extern cvar_t noskins;
1054 char st[80];
1055
1056 cls.state = ca_disconnected;
1057
1058 Info_SetValueForKey (cls.userinfo, "name", "unnamed", MAX_INFO_STRING);
1059 Info_SetValueForKey (cls.userinfo, "topcolor", "0", MAX_INFO_STRING);
1060 Info_SetValueForKey (cls.userinfo, "bottomcolor", "0", MAX_INFO_STRING);
1061 Info_SetValueForKey (cls.userinfo, "rate", "2500", MAX_INFO_STRING);
1062 Info_SetValueForKey (cls.userinfo, "msg", "1", MAX_INFO_STRING);
1063 sprintf (st, "%4.2f-%04d", VERSION, build_number());
1064 Info_SetValueForStarKey (cls.userinfo, "*ver", st, MAX_INFO_STRING);
1065
1066 CL_InitInput ();
1067 CL_InitTEnts ();
1068 CL_InitPrediction ();
1069 CL_InitCam ();
1070 Pmove_Init ();
1071
1072 //
1073 // register our commands
1074 //
1075 Cvar_RegisterVariable (&show_fps);
1076 Cvar_RegisterVariable (&host_speeds);
1077 Cvar_RegisterVariable (&developer);
1078
1079 Cvar_RegisterVariable (&cl_warncmd);
1080 Cvar_RegisterVariable (&cl_upspeed);
1081 Cvar_RegisterVariable (&cl_forwardspeed);
1082 Cvar_RegisterVariable (&cl_backspeed);
1083 Cvar_RegisterVariable (&cl_sidespeed);
1084 Cvar_RegisterVariable (&cl_movespeedkey);
1085 Cvar_RegisterVariable (&cl_yawspeed);
1086 Cvar_RegisterVariable (&cl_pitchspeed);
1087 Cvar_RegisterVariable (&cl_anglespeedkey);
1088 Cvar_RegisterVariable (&cl_shownet);
1089 Cvar_RegisterVariable (&cl_sbar);
1090 Cvar_RegisterVariable (&cl_hudswap);
1091 Cvar_RegisterVariable (&cl_maxfps);
1092 Cvar_RegisterVariable (&cl_timeout);
1093 Cvar_RegisterVariable (&lookspring);
1094 Cvar_RegisterVariable (&lookstrafe);
1095 Cvar_RegisterVariable (&sensitivity);
1096
1097 Cvar_RegisterVariable (&m_pitch);
1098 Cvar_RegisterVariable (&m_yaw);
1099 Cvar_RegisterVariable (&m_forward);
1100 Cvar_RegisterVariable (&m_side);
1101
1102 Cvar_RegisterVariable (&rcon_password);
1103 Cvar_RegisterVariable (&rcon_address);
1104
1105 Cvar_RegisterVariable (&entlatency);
1106 Cvar_RegisterVariable (&cl_predict_players2);
1107 Cvar_RegisterVariable (&cl_predict_players);
1108 Cvar_RegisterVariable (&cl_solid_players);
1109
1110 Cvar_RegisterVariable (&localid);
1111
1112 Cvar_RegisterVariable (&baseskin);
1113 Cvar_RegisterVariable (&noskins);
1114
1115 //
1116 // info mirrors
1117 //
1118 Cvar_RegisterVariable (&name);
1119 Cvar_RegisterVariable (&password);
1120 Cvar_RegisterVariable (&spectator);
1121 Cvar_RegisterVariable (&skin);
1122 Cvar_RegisterVariable (&team);
1123 Cvar_RegisterVariable (&topcolor);
1124 Cvar_RegisterVariable (&bottomcolor);
1125 Cvar_RegisterVariable (&rate);
1126 Cvar_RegisterVariable (&msg);
1127 Cvar_RegisterVariable (&noaim);
1128
1129
1130 Cmd_AddCommand ("version", CL_Version_f);
1131
1132 Cmd_AddCommand ("changing", CL_Changing_f);
1133 Cmd_AddCommand ("disconnect", CL_Disconnect_f);
1134 Cmd_AddCommand ("record", CL_Record_f);
1135 Cmd_AddCommand ("rerecord", CL_ReRecord_f);
1136 Cmd_AddCommand ("stop", CL_Stop_f);
1137 Cmd_AddCommand ("playdemo", CL_PlayDemo_f);
1138 Cmd_AddCommand ("timedemo", CL_TimeDemo_f);
1139
1140 Cmd_AddCommand ("skins", Skin_Skins_f);
1141 Cmd_AddCommand ("allskins", Skin_AllSkins_f);
1142
1143 Cmd_AddCommand ("quit", CL_Quit_f);
1144
1145 Cmd_AddCommand ("connect", CL_Connect_f);
1146 Cmd_AddCommand ("reconnect", CL_Reconnect_f);
1147
1148 Cmd_AddCommand ("rcon", CL_Rcon_f);
1149 Cmd_AddCommand ("packet", CL_Packet_f);
1150 Cmd_AddCommand ("user", CL_User_f);
1151 Cmd_AddCommand ("users", CL_Users_f);
1152
1153 Cmd_AddCommand ("setinfo", CL_SetInfo_f);
1154 Cmd_AddCommand ("fullinfo", CL_FullInfo_f);
1155 Cmd_AddCommand ("fullserverinfo", CL_FullServerinfo_f);
1156
1157 Cmd_AddCommand ("color", CL_Color_f);
1158 Cmd_AddCommand ("download", CL_Download_f);
1159
1160 Cmd_AddCommand ("nextul", CL_NextUpload);
1161 Cmd_AddCommand ("stopul", CL_StopUpload);
1162
1163 //
1164 // forward to server commands
1165 //
1166 Cmd_AddCommand ("kill", NULL);
1167 Cmd_AddCommand ("pause", NULL);
1168 Cmd_AddCommand ("say", NULL);
1169 Cmd_AddCommand ("say_team", NULL);
1170 Cmd_AddCommand ("serverinfo", NULL);
1171
1172 //
1173 // Windows commands
1174 //
1175 #ifdef _WINDOWS
1176 Cmd_AddCommand ("windows", CL_Windows_f);
1177 #endif
1178 }
1179
1180
1181 /*
1182 ================
1183 Host_EndGame
1184
1185 Call this to drop to a console without exiting the qwcl
1186 ================
1187 */
Host_EndGame(char * message,...)1188 void Host_EndGame (char *message, ...)
1189 {
1190 va_list argptr;
1191 char string[1024];
1192
1193 va_start (argptr,message);
1194 vsprintf (string,message,argptr);
1195 va_end (argptr);
1196 Con_Printf ("\n===========================\n");
1197 Con_Printf ("Host_EndGame: %s\n",string);
1198 Con_Printf ("===========================\n\n");
1199
1200 CL_Disconnect ();
1201
1202 longjmp (host_abort, 1);
1203 }
1204
1205 /*
1206 ================
1207 Host_Error
1208
1209 This shuts down the client and exits qwcl
1210 ================
1211 */
Host_Error(char * error,...)1212 void Host_Error (char *error, ...)
1213 {
1214 va_list argptr;
1215 char string[1024];
1216 static qboolean inerror = false;
1217
1218 if (inerror)
1219 Sys_Error ("Host_Error: recursively entered");
1220 inerror = true;
1221
1222 va_start (argptr,error);
1223 vsprintf (string,error,argptr);
1224 va_end (argptr);
1225 Con_Printf ("Host_Error: %s\n",string);
1226
1227 CL_Disconnect ();
1228 cls.demonum = -1;
1229
1230 inerror = false;
1231
1232 // FIXME
1233 Sys_Error ("Host_Error: %s\n",string);
1234 }
1235
1236
1237 /*
1238 ===============
1239 Host_WriteConfiguration
1240
1241 Writes key bindings and archived cvars to config.cfg
1242 ===============
1243 */
Host_WriteConfiguration(void)1244 void Host_WriteConfiguration (void)
1245 {
1246 FILE *f;
1247
1248 if (host_initialized)
1249 {
1250 f = fopen (va("%s/config.cfg",com_gamedir), "w");
1251 if (!f)
1252 {
1253 Con_Printf ("Couldn't write config.cfg.\n");
1254 return;
1255 }
1256
1257 Key_WriteBindings (f);
1258 Cvar_WriteVariables (f);
1259
1260 fclose (f);
1261 }
1262 }
1263
1264
1265 //============================================================================
1266
1267 #if 0
1268 /*
1269 ==================
1270 Host_SimulationTime
1271
1272 This determines if enough time has passed to run a simulation frame
1273 ==================
1274 */
1275 qboolean Host_SimulationTime(float time)
1276 {
1277 float fps;
1278
1279 if (oldrealtime > realtime)
1280 oldrealtime = 0;
1281
1282 if (cl_maxfps.value)
1283 fps = max(30.0, min(cl_maxfps.value, 72.0));
1284 else
1285 fps = max(30.0, min(rate.value/80.0, 72.0));
1286
1287 if (!cls.timedemo && (realtime + time) - oldrealtime < 1.0/fps)
1288 return false; // framerate is too high
1289 return true;
1290 }
1291 #endif
1292
1293
1294 /*
1295 ==================
1296 Host_Frame
1297
1298 Runs all active servers
1299 ==================
1300 */
1301 int nopacketcount;
Host_Frame(float time)1302 void Host_Frame (float time)
1303 {
1304 static double time1 = 0;
1305 static double time2 = 0;
1306 static double time3 = 0;
1307 int pass1, pass2, pass3;
1308 float fps;
1309 if (setjmp (host_abort) )
1310 return; // something bad happened, or the server disconnected
1311
1312 // decide the simulation time
1313 realtime += time;
1314 if (oldrealtime > realtime)
1315 oldrealtime = 0;
1316
1317 if (cl_maxfps.value)
1318 fps = max(30.0, min(cl_maxfps.value, 72.0));
1319 else
1320 fps = max(30.0, min(rate.value/80.0, 72.0));
1321
1322 if (!cls.timedemo && realtime - oldrealtime < 1.0/fps)
1323 return; // framerate is too high
1324
1325 host_frametime = realtime - oldrealtime;
1326 oldrealtime = realtime;
1327 if (host_frametime > 0.2)
1328 host_frametime = 0.2;
1329
1330 // get new key events
1331 Sys_SendKeyEvents ();
1332
1333 // allow mice or other external controllers to add commands
1334 IN_Commands ();
1335
1336 // process console commands
1337 Cbuf_Execute ();
1338
1339 // fetch results from server
1340 CL_ReadPackets ();
1341
1342 // send intentions now
1343 // resend a connection request if necessary
1344 if (cls.state == ca_disconnected) {
1345 CL_CheckForResend ();
1346 } else
1347 CL_SendCmd ();
1348
1349 // Set up prediction for other players
1350 CL_SetUpPlayerPrediction(false);
1351
1352 // do client side motion prediction
1353 CL_PredictMove ();
1354
1355 // Set up prediction for other players
1356 CL_SetUpPlayerPrediction(true);
1357
1358 // build a refresh entity list
1359 CL_EmitEntities ();
1360
1361 // update video
1362 if (host_speeds.value)
1363 time1 = Sys_DoubleTime ();
1364
1365 SCR_UpdateScreen ();
1366
1367 if (host_speeds.value)
1368 time2 = Sys_DoubleTime ();
1369
1370 // update audio
1371 if (cls.state == ca_active)
1372 {
1373 S_Update (r_origin, vpn, vright, vup);
1374 CL_DecayLights ();
1375 }
1376 else
1377 S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
1378
1379 CDAudio_Update();
1380
1381 if (host_speeds.value)
1382 {
1383 pass1 = (time1 - time3)*1000;
1384 time3 = Sys_DoubleTime ();
1385 pass2 = (time2 - time1)*1000;
1386 pass3 = (time3 - time2)*1000;
1387 Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
1388 pass1+pass2+pass3, pass1, pass2, pass3);
1389 }
1390
1391 host_framecount++;
1392 fps_count++;
1393 }
1394
simple_crypt(char * buf,int len)1395 static void simple_crypt(char *buf, int len)
1396 {
1397 while (len--)
1398 *buf++ ^= 0xff;
1399 }
1400
Host_FixupModelNames(void)1401 void Host_FixupModelNames(void)
1402 {
1403 simple_crypt(emodel_name, sizeof(emodel_name) - 1);
1404 simple_crypt(pmodel_name, sizeof(pmodel_name) - 1);
1405 simple_crypt(prespawn_name, sizeof(prespawn_name) - 1);
1406 simple_crypt(modellist_name, sizeof(modellist_name) - 1);
1407 simple_crypt(soundlist_name, sizeof(soundlist_name) - 1);
1408 }
1409
1410 //============================================================================
1411
1412 /*
1413 ====================
1414 Host_Init
1415 ====================
1416 */
Host_Init(quakeparms_t * parms)1417 void Host_Init (quakeparms_t *parms)
1418 {
1419 COM_InitArgv (parms->argc, parms->argv);
1420 COM_AddParm ("-game");
1421 COM_AddParm ("qw");
1422
1423 Sys_mkdir("qw");
1424
1425 if (COM_CheckParm ("-minmemory"))
1426 parms->memsize = MINIMUM_MEMORY;
1427
1428 host_parms = *parms;
1429
1430 if (parms->memsize < MINIMUM_MEMORY)
1431 Sys_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000);
1432
1433 Memory_Init (parms->membase, parms->memsize);
1434 Cbuf_Init ();
1435 Cmd_Init ();
1436 V_Init ();
1437
1438 COM_Init ();
1439
1440 Host_FixupModelNames();
1441
1442 NET_Init (PORT_CLIENT);
1443 Netchan_Init ();
1444
1445 W_LoadWadFile ("gfx.wad");
1446 Key_Init ();
1447 Con_Init ();
1448 M_Init ();
1449 Mod_Init ();
1450
1451 // Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
1452 Con_Printf ("%4.1f megs RAM used.\n",parms->memsize/ (1024*1024.0));
1453
1454 R_InitTextures ();
1455
1456 host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp");
1457 if (!host_basepal)
1458 Sys_Error ("Couldn't load gfx/palette.lmp");
1459 host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp");
1460 if (!host_colormap)
1461 Sys_Error ("Couldn't load gfx/colormap.lmp");
1462 #ifdef __linux__
1463 IN_Init ();
1464 CDAudio_Init ();
1465 VID_Init (host_basepal);
1466 Draw_Init ();
1467 SCR_Init ();
1468 R_Init ();
1469
1470 // S_Init (); // S_Init is now done as part of VID. Sigh.
1471
1472 cls.state = ca_disconnected;
1473 Sbar_Init ();
1474 CL_Init ();
1475 #else
1476 VID_Init (host_basepal);
1477 Draw_Init ();
1478 SCR_Init ();
1479 R_Init ();
1480 // S_Init (); // S_Init is now done as part of VID. Sigh.
1481 #ifdef GLQUAKE
1482 S_Init();
1483 #endif
1484
1485 cls.state = ca_disconnected;
1486 CDAudio_Init ();
1487 Sbar_Init ();
1488 CL_Init ();
1489 IN_Init ();
1490 #endif
1491
1492 Cbuf_InsertText ("exec quake.rc\n");
1493 Cbuf_AddText ("echo Type connect <internet address> or use GameSpy to connect to a game.\n");
1494 Cbuf_AddText ("cl_warncmd 1\n");
1495
1496 Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
1497 host_hunklevel = Hunk_LowMark ();
1498
1499 host_initialized = true;
1500
1501 Con_Printf ("\nClient Version %4.2f (Build %04d)\n\n", VERSION, build_number());
1502
1503 Con_Printf ("������� QuakeWorld Initialized �������\n");
1504 }
1505
1506
1507 /*
1508 ===============
1509 Host_Shutdown
1510
1511 FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better
1512 to run quit through here before the final handoff to the sys code.
1513 ===============
1514 */
Host_Shutdown(void)1515 void Host_Shutdown(void)
1516 {
1517 static qboolean isdown = false;
1518
1519 if (isdown)
1520 {
1521 printf ("recursive shutdown\n");
1522 return;
1523 }
1524 isdown = true;
1525
1526 Host_WriteConfiguration ();
1527
1528 CDAudio_Shutdown ();
1529 NET_Shutdown ();
1530 S_Shutdown();
1531 IN_Shutdown ();
1532 if (host_basepal)
1533 VID_Shutdown();
1534 }
1535
1536