1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: d_netcmd.c 1566 2020-12-19 06:22:58Z wesleyjohnson $
5 //
6 // Copyright (C) 1998-2016 by DooM Legacy Team.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 //
19 // $Log: d_netcmd.c,v $
20 // Revision 1.36  2005/12/20 14:58:25  darkwolf95
21 // Monster behavior CVAR - Affects how monsters react when they shoot each other
22 //
23 // Revision 1.35  2003/10/15 18:53:39  darkwolf95
24 // "kill" command added to the console
25 //
26 // Revision 1.34  2003/07/13 13:16:15  hurdler
27 //
28 // Revision 1.33  2002/09/28 06:53:11  tonyd
29 // fixed CR problem, fixed game options crash
30 //
31 // Revision 1.32  2002/09/12 20:10:50  hurdler
32 // Added some cvars
33 //
34 // Revision 1.31  2001/12/15 18:41:35  hurdler
35 // small commit, mainly splitscreen fix
36 //
37 // Revision 1.30  2001/11/02 21:39:45  judgecutor
38 // Added Frag's weapon falling
39 //
40 // Revision 1.29  2001/08/20 20:40:39  metzgermeister
41 // Revision 1.28  2001/08/12 15:21:04  bpereira
42 // see my log
43 //
44 // Revision 1.27  2001/08/08 20:34:43  hurdler
45 // Big TANDL update
46 //
47 // Revision 1.26  2001/05/21 14:57:04  crashrl
48 // Readded directory crawling file search function
49 //
50 // Revision 1.25  2001/05/16 17:12:52  crashrl
51 // Added md5-sum support, removed recursiv wad search
52 //
53 // Revision 1.24  2001/04/01 17:35:06  bpereira
54 //
55 // Revision 1.23  2001/03/19 21:18:48  metzgermeister
56 //   * missing textures in HW mode are replaced by default texture
57 //   * fixed crash bug with P_SpawnMissile(.) returning NULL
58 //   * deep water trick and other nasty thing work now in HW mode (tested with tnt/map02 eternal/map02)
59 //   * added cvar gr_correcttricks
60 //
61 // Revision 1.22  2001/02/24 13:35:19  bpereira
62 // Revision 1.21  2001/02/10 12:27:13  bpereira
63 //
64 // Revision 1.20  2001/01/25 22:15:41  bpereira
65 // added heretic support
66 //
67 // Revision 1.19  2000/11/26 20:36:14  hurdler
68 // Adding autorun2
69 //
70 // Revision 1.18  2000/11/11 13:59:45  bpereira
71 // Revision 1.17  2000/11/02 19:49:35  bpereira
72 // Revision 1.16  2000/10/08 13:30:00  bpereira
73 // Revision 1.15  2000/09/10 10:39:06  metzgermeister
74 // Revision 1.14  2000/08/31 14:30:55  bpereira
75 // Revision 1.13  2000/08/16 14:10:01  hurdler
76 // add master server code
77 //
78 // Revision 1.12  2000/08/10 14:51:25  ydario
79 // OS/2 port
80 //
81 // Revision 1.11  2000/05/13 19:52:10  metzgermeister
82 // cd vol jiggle
83 //
84 // Revision 1.10  2000/04/23 16:19:52  bpereira
85 // Revision 1.9  2000/04/16 18:38:07  bpereira
86 //
87 // Revision 1.8  2000/04/07 23:11:17  metzgermeister
88 // added mouse move
89 //
90 // Revision 1.7  2000/04/04 00:32:45  stroggonmeth
91 // Initial Boom compatability plus few misc changes all around.
92 //
93 // Revision 1.6  2000/03/29 19:39:48  bpereira
94 //
95 // Revision 1.5  2000/03/06 15:58:47  hurdler
96 // Add Bell Kin's changes
97 //
98 // Revision 1.4  2000/03/05 17:10:56  bpereira
99 // Revision 1.3  2000/02/27 00:42:10  hurdler
100 // Revision 1.2  2000/02/26 00:28:42  hurdler
101 // Mostly bug fix (see borislog.txt 23-2-2000, 24-2-2000)
102 //
103 //
104 // DESCRIPTION:
105 //      host/client network commands
106 //      commands are executed through the command buffer
107 //      like console commands
108 //      other miscellaneous commands (at the end)
109 //
110 //-----------------------------------------------------------------------------
111 
112 #include "doomincl.h"
113 
114 #include "console.h"
115 #include "command.h"
116 
117 #include "d_netcmd.h"
118 #include "i_system.h"
119   // I_ functions
120 #include "dstrings.h"
121 #include "d_main.h"
122   // D_ functions
123 #include "g_game.h"
124   // G_ functions
125 #include "byteptr.h"
126   // WRITEBYTE, READBYTE
127 
128 // cv_ vars and settings from many sources, needed by Command_ functions
129 #include "hu_stuff.h"
130 #include "g_input.h"
131 #include "r_local.h"
132 #include "r_things.h"
133 #include "p_inter.h"
134 #include "p_local.h"
135 #include "p_setup.h"
136 #include "s_sound.h"
137 #include "m_misc.h"
138 #include "am_map.h"
139 #include "d_netfil.h"
140 #include "p_spec.h"
141 #include "m_cheat.h"
142 #include "d_clisrv.h"
143 #include "mserv.h"
144 #include "v_video.h"
145 
146 // ------
147 // protos
148 // ------
149 void Command_Color_f(void);
150 void Command_Name_f(void);
151 
152 void Command_BindJoyaxis_f();
153 void Command_UnbindJoyaxis_f();
154 
155 void Command_WeaponPref(void);
156 
157 void Got_NetXCmd_NameColor(xcmd_t * xc);
158 void Got_NetXCmd_WeaponPref(xcmd_t * xc);
159 void Got_NetXCmd_Mapcmd(xcmd_t * xc);
160 void Got_NetXCmd_ExitLevelcmd(xcmd_t * xc);
161 void Got_NetXCmd_LoadGame_cmd(xcmd_t * xc);
162 void Got_NetXCmd_SaveGame_cmd(xcmd_t * xc);
163 void Got_NetXCmd_Pause(xcmd_t * xc);
164 void Got_NetXCmd_UseArtifact(xcmd_t * xc);
165 
166 void Command_Playdemo_f(void);
167 void Command_Timedemo_f(void);
168 void Command_Stopdemo_f(void);
169 void Command_Map_f(void);
170 void Command_Restart_f(void);
171 
172 void Command_Addfile(void);
173 void Command_Pause(void);
174 
175 void Command_Frags_f(void);
176 void Command_TeamFrags_f(void);
177 void Command_Version_f(void);
178 void Command_Quit_f(void);
179 
180 void Command_ExitLevel_f(void);
181 void Command_Load_f(void);
182 void Command_Save_f(void);
183 void Command_ExitGame_f(void);
184 
185 void Command_Kill(void);
186 
187 
188 // =========================================================================
189 //                           CLIENT VARIABLES
190 // =========================================================================
191 
192 static void Send_WeaponPref_pind(byte pind);
193 static void Send_NameColor_pind(byte pind);
194 
195 // [WDJ] Or could just send both when any change is made?
196 // See Send_PlayerConfig
197 static
Send_NameColor1(void)198 void Send_NameColor1(void)
199 {
200     Send_NameColor_pind(0);
201 }
202 static
Send_NameColor2(void)203 void Send_NameColor2(void)
204 {
205     Send_NameColor_pind(1);
206 }
207 
208 static
Send_WeaponPref1(void)209 void Send_WeaponPref1(void)
210 {
211     Send_WeaponPref_pind(0);
212 }
213 static
Send_WeaponPref2(void)214 void Send_WeaponPref2(void)
215 {
216     Send_WeaponPref_pind(1);
217 }
218 
219 // Has CV_CFG1 where does not have support for insert into drawmode config file.
220 // these are just meant to be saved to the config
221 consvar_t cv_playername[2] = {
222   { "name", NULL, CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, NULL, Send_NameColor1 },
223   { "name2", "big b", CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, NULL, Send_NameColor2 }
224 };
225 
226 consvar_t cv_playercolor[2] = {
227   { "color", "0", CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, Color_cons_t, Send_NameColor1 },
228   { "color2", "1", CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, Color_cons_t, Send_NameColor2 }
229 };
230 
231 // player's skin, saved for commodity, when using a favorite skins wad..
232 consvar_t cv_skin[2] = {
233   { "skin", DEFAULTSKIN, CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, NULL /*skin_cons_t */ , Send_NameColor1 },
234   { "skin2", DEFAULTSKIN, CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, NULL /*skin_cons_t */ , Send_NameColor2 }
235 };
236 
237 consvar_t cv_autoaim[2] = {
238   { "autoaim",  "1", CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, CV_OnOff, Send_WeaponPref1 },
239   { "autoaim2", "1", CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, CV_OnOff, Send_WeaponPref2 }
240 };
241 
242 consvar_t cv_weaponpref[2] = {
243   { "weaponpref", "014576328", CV_SAVE | CV_STRING | CV_CALL | CV_NOINIT | CV_CFG1, NULL, Send_WeaponPref1 },
244   { "weaponpref2", "014576328", CV_SAVE | CV_STRING | CV_CALL | CV_NOINIT | CV_CFG1, NULL, Send_WeaponPref2 },
245 };
246 
247 consvar_t cv_originalweaponswitch[2] = {
248   { "originalweaponswitch", "0", CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, CV_OnOff, Send_WeaponPref1 },
249   { "originalweaponswitch2", "0", CV_SAVE | CV_CALL | CV_NOINIT | CV_CFG1, CV_OnOff, Send_WeaponPref2 }
250 };
251 
252 
253 consvar_t cv_netstat = { "netstat", "0", 0, CV_OnOff };
254 
255 // =========================================================================
256 //                           CLIENT STARTUP
257 // =========================================================================
258 
259 // Register client and server commands.
D_Register_ClientCommands(void)260 void D_Register_ClientCommands(void)
261 {
262     int i;
263 
264     for (i = 0; i < NUMSKINCOLORS; i++)
265         Color_cons_t[i].strvalue = Color_Names[i];
266 
267     //
268     // register commands
269     //
270     Register_NetXCmd(XD_NAMEANDCOLOR, Got_NetXCmd_NameColor);
271     Register_NetXCmd(XD_WEAPONPREF, Got_NetXCmd_WeaponPref);
272     Register_NetXCmd(XD_MAP, Got_NetXCmd_Mapcmd);
273     Register_NetXCmd(XD_EXITLEVEL, Got_NetXCmd_ExitLevelcmd);
274     Register_NetXCmd(XD_PAUSE, Got_NetXCmd_Pause);
275     Register_NetXCmd(XD_USEARTIFACT, Got_NetXCmd_UseArtifact);
276 
277     COM_AddCommand("playdemo", Command_Playdemo_f, CC_command);
278     COM_AddCommand("timedemo", Command_Timedemo_f, CC_command);
279     COM_AddCommand("stopdemo", Command_Stopdemo_f, CC_command);
280     COM_AddCommand("map", Command_Map_f, CC_command);
281     COM_AddCommand("restartlevel", Command_Restart_f, CC_command);
282     COM_AddCommand("exitgame", Command_ExitGame_f, CC_command);
283     COM_AddCommand("exitlevel", Command_ExitLevel_f, CC_command);
284 
285     COM_AddCommand("addfile", Command_Addfile, CC_command);
286     COM_AddCommand("pause", Command_Pause, CC_command);
287 
288     COM_AddCommand("turbo", Command_Turbo_f, CC_command);   // turbo speed
289     COM_AddCommand("quit", Command_Quit_f, CC_command);
290     COM_AddCommand("screenshot", M_ScreenShot, CC_command);
291     COM_AddCommand("kill", Command_Kill, CC_command);
292 
293     COM_AddCommand("chatmacro", Command_Chatmacro_f, CC_chat);   // hu_stuff.c
294     COM_AddCommand("setcontrol", Command_Setcontrol_f, CC_control);
295     COM_AddCommand("setcontrol2", Command_Setcontrol2_f, CC_control);
296     COM_AddCommand("bindjoyaxis", Command_BindJoyaxis_f, CC_control);
297 
298     COM_AddCommand("version", Command_Version_f, CC_info);
299     COM_AddCommand("frags", Command_Frags_f, CC_info);
300     COM_AddCommand("teamfrags", Command_TeamFrags_f, CC_info);
301 
302     COM_AddCommand("saveconfig", Command_SaveConfig_f, CC_config);
303     COM_AddCommand("loadconfig", Command_LoadConfig_f, CC_config);
304     COM_AddCommand("changeconfig", Command_ChangeConfig_f, CC_config);
305 
306 
307     //Added by Hurdler for master server connection
308     MS_Register_Commands();
309 
310     // Any cv_ with CV_SAVE needs to be registered, even if it is not used.
311     // Otherwise there will be error messages when config is loaded.
312 
313     // register these so it is saved to config
314     cv_playername[0].defaultvalue = I_GetUserName();
315     if (cv_playername[0].defaultvalue == NULL)
316         cv_playername[0].defaultvalue = "gi john";
317 
318     // Main player
319     CV_RegisterVar(&cv_playername[0]);
320     CV_RegisterVar(&cv_playercolor[0]);
321     CV_RegisterVar(&cv_skin[0]);  // r_things.c (skin NAME)
322     CV_RegisterVar(&cv_autoaim[0]);
323     CV_RegisterVar(&cv_weaponpref[0]);
324     CV_RegisterVar(&cv_originalweaponswitch[0]);
325 
326     // Splitscreen player
327     CV_RegisterVar(&cv_playername[1]);
328     CV_RegisterVar(&cv_playercolor[1]);
329     CV_RegisterVar(&cv_skin[1]);
330     CV_RegisterVar(&cv_autoaim[1]);
331     CV_RegisterVar(&cv_weaponpref[1]);
332     CV_RegisterVar(&cv_originalweaponswitch[1]);
333 
334     //misc
335     CV_RegisterVar(&cv_netstat);
336 
337     //
338     //  The above commands are enough for dedicated server
339     //
340     if (dedicated)
341         return;
342 
343     COM_AddCommand("load", Command_Load_f, CC_savegame);
344     Register_NetXCmd(XD_LOADGAME, Got_NetXCmd_LoadGame_cmd);
345     COM_AddCommand("save", Command_Save_f, CC_savegame);
346     Register_NetXCmd(XD_SAVEGAME, Got_NetXCmd_SaveGame_cmd);
347 
348     // add cheat commands, I'm bored of deh patches renaming the idclev ! :-)
349     COM_AddCommand("noclip", Command_CheatNoClip_f, CC_cheat);
350     COM_AddCommand("god", Command_CheatGod_f, CC_cheat);
351     COM_AddCommand("gimme", Command_CheatGimme_f, CC_cheat);
352 
353 /* ideas of commands names from Quake
354     "status"
355     "notarget"
356     "fly"
357     "changelevel"
358     "reconnect"
359     "tell"
360     "kill"
361     "spawn"
362     "begin"
363     "prespawn"
364     "ping"
365 
366     "startdemos"
367     "demos"
368     "stopdemo"
369 */
370 
371 }
372 
373 //--- string
374 // [WDJ] The compiler will likely inline these.
375 // The macro versions were unreadable, and thus unmaintainable.
376 
377 // Only use this on internal strings that are known to have 0 term.
378 // Will always terminate the string.
379 // Return next buffer location.
write_string(byte * dst,const char * src)380 byte *  write_string(byte *dst, const char* src)
381 {
382   // copy src str0 to buffer dst, until reach 0 term.
383   do {
384     WRITECHAR(dst, *src);
385   }
386   while ( *(src++) );
387   return dst;
388 }
389 
390 // Will always terminate the string.
391 // Return next buffer location
write_stringn(byte * dst,const char * src,int num)392 byte *  write_stringn( byte *dst, const char* src, int num )
393 {
394   // copy src str0 to buffer dst, until reach 0 term or num of char reached.
395   for(;;) {
396     WRITECHAR(dst, *src);
397     if ( *(src++) == 0 )  break;
398     num--;
399     if(num == 0) {  // do not exceed num char
400       dst[-1] = 0;  // overwrite last char with 0
401       break;
402     }
403   }
404   return dst;
405 }
406 
407 
408 // =========================================================================
409 //                            CLIENT STUFF
410 // =========================================================================
411 
412 // [WDJ] Currently, these are being sent without cv_splitscreen knowledge,
413 // so when not splitscreen, they may be mysterious settings to other nodes.
414 
415 
416 #if 0
417 // By Server
418 //   pn : player pid
419 void Send_NameColor_player( byte pn, byte pind )
420 {
421     player_t * plp = & players[pn];
422     const char * skinname = ( skins[plp->skin] )? skins[plp->skin]->name : NULL;
423     Send_NameColor_pn( pn, player_names[pn], plp->skincolor, skinname, pind );
424 }
425 #endif
426 
427 
428 // By Client.
429 //  name, color, or skin has changed
430 //  pind : player index, [0]=main player, [1]=splitscreen player
431 static
Send_NameColor_pind(byte pind)432 void  Send_NameColor_pind( byte pind )
433 {
434     byte pn = localplayer[pind];
435     if( pn < MAXPLAYERS )
436         Send_NameColor_pn( pn, cv_playername[pind].string, cv_playercolor[pind].EV, cv_skin[pind].string, pind );
437 }
438 
439 // Server, Client
440 //   playername : player name
441 //   skinname : skin name, NULL if not skins
442 //   textcmd_pind : textcmd channel index, [0]=main player, [1]=splitscreen player, [2]=server (bots)
Send_NameColor_pn(byte pn,const char * playername,byte color,const char * skinname,byte textcmd_pind)443 void  Send_NameColor_pn( byte pn, const char * playername, byte color, const char * skinname, byte textcmd_pind )
444 {
445     byte buf[MAXPLAYERNAME + 1 + SKINNAMESIZE + 1];
446     byte *p;
447 
448     p = buf;
449     // Format:  color byte, player_name str0, skin_name str0.
450     WRITEBYTE(p, color);
451     p = write_stringn(p, playername, MAXPLAYERNAME);
452 
453     // Send the skin by name.
454     // Check if player has the skin loaded
455     // (it may be the name of a skin that was available in the previous game).
456     if( (! skinname) || (! R_SkinAvailable( skinname )) )
457         skinname = DEFAULTSKIN;
458     p = write_stringn(p, skinname, SKINNAMESIZE);
459 
460     // Automatic routing, for Clients, and Bots.
461     Send_NetXCmd_auto(XD_NAMEANDCOLOR, buf, (p - buf), textcmd_pind, pn);
462 }
463 
Got_NetXCmd_NameColor(xcmd_t * xc)464 void Got_NetXCmd_NameColor(xcmd_t * xc)
465 {
466     int  pn = xc->playernum;
467     char * lcp = (char*)xc->curpos; // local cp
468     char * pname;
469     player_t * p;
470     byte  sk;
471 
472     if( pn >= MAXPLAYERS )
473     {
474         GenPrintf( EMSG_error, "NameColor: invalid player num %i\n", pn );
475         return;
476     }
477 
478     pname = player_names[pn];
479     p = &players[pn];
480 
481     // Format:  color byte, player_name str0, skin_name str0.
482     // color
483     sk = READBYTE(lcp); // unsigned read
484     P_SetPlayer_color( p, sk );
485 
486     // Players 0..(MAXPLAYERS-1) are init as Player 1 ..
487     // name
488     if( EV_legacy >= 128 )
489     {
490         // compacted string space in message
491         if (strcasecmp(pname, lcp))
492             CONS_Printf("%s renamed to %s\n", pname, lcp);
493         // [WDJ] String overflow safe
494         {
495             int pn_len = strlen( lcp ) + 1;
496             int read_len = min( pn_len, MAXPLAYERNAME-1 );  // length safe
497             memcpy(pname, lcp, read_len);
498             pname[MAXPLAYERNAME-1] = '\0';
499             lcp += pn_len;  // whole
500         }
501     }
502     else
503     {
504         // constant string space in message
505         memcpy(pname, lcp, MAXPLAYERNAME);
506         lcp += MAXPLAYERNAME;
507     }
508 
509     // Protection against malicious packet.
510     if( (byte*)lcp >= xc->endpos )  goto done;
511 
512     // skin
513     if( EV_legacy < 120 || EV_legacy >= 125 )
514     {
515         if( EV_legacy >= 128 )
516         {
517             // compacted string space in message
518             SetPlayerSkin(pn, lcp);
519             SKIPSTRING(lcp);
520         }
521         else
522         {
523             // constant string space in message
524             SetPlayerSkin(pn, lcp);
525             lcp += (SKINNAMESIZE + 1);
526         }
527     }
528 done:
529     xc->curpos = (byte*)lcp;  // OUT once
530 }
531 
532 //  pind : player index, [0]=main player, [1]=splitscreen player
533 static
Send_WeaponPref_pind(byte pind)534 void Send_WeaponPref_pind( byte pind )
535 {
536     char buf[NUMWEAPONS + 4];  // need NUMWEAPONS+2
537 
538     // Format: original_weapon_switch  byte,
539     //         weapon_pref  char[NUMWEAPONS],
540     //         autoaim  byte.
541     buf[0] = cv_originalweaponswitch[pind].value;
542 
543     int wplen = strlen(cv_weaponpref[pind].string);
544     memcpy(buf + 1, cv_weaponpref[pind].string, wplen);
545     if( wplen != NUMWEAPONS)
546     {
547         CONS_Printf("weaponpref invalid length: %d, should be %d, player pind=%d\n", wplen, NUMWEAPONS, pind);
548         // pad with 0
549 	for( ; wplen < NUMWEAPONS; wplen++ )  buf[wplen+1] = '0';
550     }
551     buf[1 + NUMWEAPONS] = cv_autoaim[pind].value;
552 
553     Send_NetXCmd_pind(XD_WEAPONPREF, buf, NUMWEAPONS + 2, pind);
554 }
555 
Got_NetXCmd_WeaponPref(xcmd_t * xc)556 void Got_NetXCmd_WeaponPref(xcmd_t * xc)
557 {
558     player_t * p = &players[xc->playernum];
559     // Format: original_weapon_switch  byte,
560     //         weapon_pref  char[NUMWEAPONS],
561     //         autoaim  byte.
562     p->originalweaponswitch = *(xc->curpos++);
563     memcpy(p->favoritweapon, xc->curpos, NUMWEAPONS);
564     xc->curpos += NUMWEAPONS;
565     p->autoaim_toggle = *(xc->curpos++);
566 }
567 
D_Send_PlayerConfig(void)568 void D_Send_PlayerConfig(void)
569 {
570     Send_NameColor_pind(0);
571     Send_WeaponPref_pind(0);
572     if (cv_splitscreen.value)
573     {
574         Send_NameColor_pind(1);
575         Send_WeaponPref_pind(1);
576     }
577 }
578 
579 // ========================================================================
580 
581 //  play a demo, add .lmp for external demos
582 //  eg: playdemo demo1 plays the internal game demo
583 //
584 
Command_Playdemo_f(void)585 void Command_Playdemo_f(void)
586 {
587     char name[MAX_WADPATH];  // MAX_WADPATH for length checking
588     COM_args_t  carg;
589 
590     COM_Args( &carg );
591 
592     if (carg.num != 2)
593     {
594         CONS_Printf("playdemo <demoname> : playback a demo\n");
595         return;
596     }
597 
598     // disconnect from server here ?
599     if (demoplayback)
600         G_StopDemo();
601     // Ignore seq playdemo command issued during menu, if since disabled
602     if( demo_ctrl == (DEMO_seq_playdemo | DEMO_seq_disabled))
603     {
604         demo_ctrl = DEMO_seq_disabled;
605         return;
606     }
607     demo_ctrl &= ~ DEMO_seq_playdemo;
608     if (netgame)
609     {
610         CONS_Printf("\nYou can't play a demo while in net game\n");
611         return;
612     }
613 
614     // copy demo lump name, or demo file name (.lmp will be added later)
615     strncpy(name, carg.arg[1], MAX_WADPATH-1);
616     name[MAX_WADPATH-1] = '\0';
617     // dont add .lmp so internal game demos can be played
618 
619     CONS_Printf("Playing back demo '%s'.\n", name);
620 
621     G_DoPlayDemo(name);
622 }
623 
Command_Timedemo_f(void)624 void Command_Timedemo_f(void)
625 {
626     char name[MAX_WADPATH];  // MAX_WADPATH for length checking
627     COM_args_t  carg;
628 
629     COM_Args( &carg );
630 
631     if (carg.num != 2)
632     {
633         CONS_Printf("timedemo <demoname> : time a demo\n");
634         return;
635     }
636 
637     // disconnect from server here ?
638     if (demoplayback)
639         G_StopDemo();
640     if (netgame)
641     {
642         CONS_Printf("\nYou can't play a demo while in net game\n");
643         return;
644     }
645 
646     // copy demo lump name, or demo file name (.lmp will be added later)
647     strncpy(name, carg.arg[1], MAX_WADPATH-1);
648     name[MAX_WADPATH-1] = '\0';
649     // dont add .lmp so internal game demos can be played
650 
651     CONS_Printf("Timing demo '%s'.\n", name);
652 
653     G_TimeDemo(name);
654 }
655 
656 //  stop current demo
657 //
Command_Stopdemo_f(void)658 void Command_Stopdemo_f(void)
659 {
660     G_CheckDemoStatus();
661     CONS_Printf("Stopped demo.\n");
662 }
663 
664 //  Warp to map code.
665 //  Called either from map <mapname> console command, or idclev cheat.
666 //
Command_Map_f(void)667 void Command_Map_f(void)
668 {
669     // Build complex net command in buf.
670     char buf[MAX_WADPATH + 3];
671 #define MAPNAME (&buf[2])
672     int i;
673     COM_args_t  carg;
674 
675     COM_Args( &carg );
676 
677     if (carg.num < 2 || carg.num > 7)
678     {
679         CONS_Printf("map <mapname[.wad]> [-skill <1..5>] [-monsters <0/1>] [-noresetplayers]: warp to map\n");
680         return;
681     }
682 
683     if (!server)
684     {
685         CONS_Printf("Only the server can change the map\n");
686         return;
687     }
688 
689     // By Server.
690     strncpy(MAPNAME, carg.arg[1], MAX_WADPATH-1);
691     MAPNAME[MAX_WADPATH-1] = '\0';
692 
693     if (FIL_CheckExtension(MAPNAME))
694     {
695         // here check if file exist !!!
696         // Owner security permissions.
697         if (!findfile(MAPNAME, NULL, false, NULL))
698         {
699             CONS_Printf("\2File %s' not found\n", MAPNAME);
700             return;
701         }
702     }
703     else
704     {
705         // internal wad lump
706         if( ! VALID_LUMP( W_CheckNumForName(MAPNAME) ) )
707         {
708             CONS_Printf("\2Internal game map '%s' not found\n" "(use .wad extension for external maps)\n", MAPNAME);
709             return;
710         }
711     }
712 
713     // Format: skill byte, (no_reset_players, no_monsters) byte,
714     //         map_name str0.
715 
716     // Options of the map command.
717     if ((i = COM_CheckParm("-skill")) != 0)
718         buf[0] = atoi(COM_Argv(i + 1)) - 1;
719     else
720         buf[0] = gameskill;
721 
722     // Signal using single bits.
723     //  bit 0: no monsters
724     //  bit 1: no reset players
725     buf[1] = 0;
726     if ((i = COM_CheckParm("-monsters")) != 0)
727     {
728         if( atoi(COM_Argv(i + 1)) == 0 )
729             buf[1] = 0x01;
730     }
731     else if( nomonsters )
732         buf[1] = 0x01;
733 
734     if (COM_CheckParm("-noresetplayers"))
735         buf[1] |= 0x02;
736 
737     // Spawn the server if needed.
738     // When that detects a new player, then Reset players.
739     if (SV_SpawnServer())
740     {
741         // Added a new player.
742         buf[1] &= ~0x02;
743     }
744 
745 #ifdef WAIT_GAME_START_INTERMISSION
746     if(server && netgame && num_wait_game_start)
747     {
748 #if 1
749         // [WDJ] 1.48 Warn user that Map command does not handle waiting players.
750         GenPrintf(EMSG_warn,"Waiting players: use exitlevel\n");
751 #else
752 #if 0
753 // [WDJ] The Map command will override any attempt to stay in Intermission, so THIS DOES NOT WORK.
754         // [WDJ] Adding players seems to only work using Intermission.
755         if( gamestate != GS_INTERMISSION )
756         {
757             G_ExitLevel();
758             if( gamestate == GS_LEVEL )
759                 G_DoCompleted ();
760             G_Start_Intermission();  // setup intermission
761         }
762 #endif
763         // Activate waiting clients
764 	// TODO: make this work.
765         // Server won't stay in Intermission, client gets stuck in Intermission.
766         SV_Add_game_start_waiting_players( 1 );
767 #endif
768     }
769 #endif
770 
771     SV_Send_NetXCmd(XD_MAP, buf, 2 + strlen(MAPNAME) + 1); // as server
772 }
773 
Got_NetXCmd_Mapcmd(xcmd_t * xc)774 void Got_NetXCmd_Mapcmd(xcmd_t * xc)
775 {
776     char mapname[MAX_WADPATH];
777     byte opt, skill;
778     int  resetplayer = 1;
779 
780     // Format: skill byte, (no_reset_players, no_monsters) byte,
781     //         map_name str0.
782     skill = READBYTE(xc->curpos);
783     if( EV_legacy >= 128 )
784     {
785         // [WDJ] Do not use boolean nomonsters as an int.
786         opt = READBYTE(xc->curpos);
787         if( EV_legacy >= 129 )
788         {
789             nomonsters = ( (opt & 0x01) != 0 );
790             resetplayer = ( (opt & 0x02) == 0 );
791         }
792         else
793         {
794             nomonsters = (opt > 0);
795         }
796     }
797     strncpy(mapname, (char*)xc->curpos, MAX_WADPATH-1);
798     mapname[MAX_WADPATH-1] = '\0';
799     xc->curpos += strlen(mapname) + 1;
800 
801     CONS_Printf("Warping to map...\n");
802     if (demoplayback && !timingdemo)
803         precache = false;
804     G_InitNew(skill, mapname, resetplayer);
805     if (demoplayback && !timingdemo)
806         precache = true;
807     CON_ToggleOff();
808     if (timingdemo)
809         G_DoneLevelLoad();
810 }
811 
Command_Restart_f(void)812 void Command_Restart_f(void)
813 {
814     if (netgame)
815     {
816         CONS_Printf("Restartlevel don't work in network\n");
817         return;
818     }
819 
820     if (gamestate == GS_LEVEL)
821         G_DoLoadLevel(true);
822     else
823         CONS_Printf("You should be in a level to restart it !\n");
824 }
825 
826 // Command, or KEY_PAUSE
Command_Pause(void)827 void Command_Pause(void)
828 {
829     char buf;
830     // Format: (pause) byte.
831     if (COM_Argc() > 1)
832         buf = atoi(COM_Argv(1)) != 0;
833     else
834         buf = !paused;
835 
836     Send_NetXCmd(XD_PAUSE, &buf, 1);  // as mainplayer
837 }
838 
Got_NetXCmd_Pause(xcmd_t * xc)839 void Got_NetXCmd_Pause(xcmd_t * xc)
840 {
841     // Format: (pause) byte.
842     if( EV_legacy < 131 )
843         paused ^= 1;
844     else
845         paused = READBYTE(xc->curpos);
846 
847     if (!demoplayback)
848     {
849         if (netgame)
850         {
851             char * bystr = player_names[xc->playernum];
852             if (paused)
853                 GenPrintf(EMSG_hud, "Game paused by %s\n", bystr);
854             else
855                 GenPrintf(EMSG_hud, "Game unpaused by %s\n", bystr);
856         }
857 
858         if (paused)
859         {
860             if (!menuactive || netgame)
861                 S_PauseSound();
862         }
863         else
864             S_ResumeSound();
865 
866         // Pause updates mouse, grab.
867         I_StartupMouse( !(paused || menuactive) );
868     }
869 }
870 
871 //  Add a pwad at run-time
872 //  Search for sounds, maps, musics, etc..
873 //
Command_Addfile(void)874 void Command_Addfile(void)
875 {
876     if (COM_Argc() != 2)
877     {
878         CONS_Printf("addfile <wadfile.wad> : load wad file\n");
879         return;
880     }
881 
882     P_AddWadFile(COM_Argv(1), NULL);
883 }
884 
885 // =========================================================================
886 //                            MISC. COMMANDS
887 // =========================================================================
888 
Command_Frags_f(void)889 void Command_Frags_f(void)
890 {
891     int i, j;
892 
893     if( ! deathmatch )
894     {
895         CONS_Printf("Frags : show the frag table\n");
896         CONS_Printf("Only for deathmatch games\n");
897         return;
898     }
899 
900     for (i = 0; i < MAXPLAYERS; i++)
901     {
902         if (playeringame[i])
903         {
904             CONS_Printf("%-16s", player_names[i]);
905             for (j = 0; j < MAXPLAYERS; j++)
906                 if (playeringame[j])
907                     CONS_Printf(" %3d", players[i].frags[j]);
908             CONS_Printf("\n");
909         }
910     }
911 }
912 
Command_TeamFrags_f(void)913 void Command_TeamFrags_f(void)
914 {
915     int i, j;
916     fragsort_t unused[MAXPLAYERS];
917     int frags[MAXPLAYERS];
918     int fragtbl[MAXPLAYERS][MAXPLAYERS];
919 
920     if( ! (deathmatch && cv_teamplay.EV) )
921     {
922         CONS_Printf("teamfrags : show the frag table for teams\n");
923         CONS_Printf("Only for deathmatch teamplay games\n");
924         return;
925     }
926 
927     HU_Create_TeamFragTbl(unused, frags, fragtbl);
928 
929     for (i = 0; i < 11; i++)
930     {
931         if (teamingame(i))
932         {
933             CONS_Printf("%-8s", get_team_name(i));
934             for (j = 0; j < 11; j++)
935                 if (teamingame(j))
936                     CONS_Printf(" %3d", fragtbl[i][j]);
937             CONS_Printf("\n");
938         }
939     }
940 }
941 
942 //  Returns program version.
943 //
Command_Version_f(void)944 void Command_Version_f(void)
945 {
946   CONS_Printf("%s (" __DATE__ " " __TIME__ ")\n", VERSION_BANNER);
947 }
948 
949 //  Quit the game immediately
950 //
Command_Quit_f(void)951 void Command_Quit_f(void)
952 {
953     I_Quit();  // No return
954 }
955 
956 
Command_ExitLevel_f(void)957 void Command_ExitLevel_f(void)
958 {
959     if (!server)
960     {
961         CONS_Printf("Only the server can exit the level\n");
962         return;
963     }
964 
965     // By Server.
966     if (gamestate != GS_LEVEL || demoplayback)
967         CONS_Printf("You should be in a level to exit it !\n");
968 
969     SV_Send_NetXCmd(XD_EXITLEVEL, NULL, 0);  // as server
970 }
971 
Got_NetXCmd_ExitLevelcmd(xcmd_t * xc)972 void Got_NetXCmd_ExitLevelcmd(xcmd_t * xc)
973 {
974     G_ExitLevel();
975 }
976 
Command_Load_f(void)977 void Command_Load_f(void)
978 {
979     byte slot;
980 
981     if (COM_Argc() != 2)
982     {
983         CONS_Printf("load <slot>: load a saved game\n");
984         return;
985     }
986 
987     if (!server)
988     {
989         CONS_Printf("Only server can do a load game\n");
990         return;
991     }
992 
993     // By Server.
994     D_DisableDemo();
995 
996     // spawn a server if needed
997     SV_SpawnServer();
998 
999     // Format: save_slot byte.
1000     slot = atoi(COM_Argv(1));
1001     SV_Send_NetXCmd(XD_LOADGAME, &slot, 1); // as server
1002 }
1003 
Got_NetXCmd_LoadGame_cmd(xcmd_t * xc)1004 void Got_NetXCmd_LoadGame_cmd(xcmd_t * xc)
1005 {
1006     // Format: save_slot byte.
1007     byte slot = *(xc->curpos++);
1008     G_DoLoadGame(slot);
1009 }
1010 
Command_Save_f(void)1011 void Command_Save_f(void)
1012 {
1013     char p[SAVESTRINGSIZE + 1];
1014 
1015     if (COM_Argc() != 3)
1016     {
1017         CONS_Printf("save <slot> <description>: save game\n");
1018         return;
1019     }
1020 
1021     if (!server)
1022     {
1023         CONS_Printf("Only server can do a save game\n");
1024         return;
1025     }
1026 
1027     // Format: save_slot byte, save_description str0.
1028     // By Server.
1029     p[0] = atoi(COM_Argv(1));  // slot num 0..99
1030     // save description string at [1]
1031     strncpy(&p[1], COM_Argv(2), SAVESTRINGSIZE-1);
1032     p[SAVESTRINGSIZE] = '\0';
1033 
1034     SV_Send_NetXCmd(XD_SAVEGAME, &p, strlen(&p[1]) + 2);  // as server
1035 }
1036 
Got_NetXCmd_SaveGame_cmd(xcmd_t * xc)1037 void Got_NetXCmd_SaveGame_cmd(xcmd_t * xc)
1038 {
1039     byte slot;
1040     char description[SAVESTRINGSIZE];
1041 
1042     // Format: save_slot byte, save_description str0.
1043     slot = *(xc->curpos++);
1044     // Transmitted as SAVESTRINGSIZE, but protect against net error or attack.
1045     strncpy(description, (char*)xc->curpos, SAVESTRINGSIZE-1);
1046     description[SAVESTRINGSIZE-1] = '\0';
1047     xc->curpos += strlen(description) + 1;
1048 
1049     // Write the save game file
1050     G_DoSaveGame(slot, description);
1051 }
1052 
Command_ExitGame_f(void)1053 void Command_ExitGame_f(void)
1054 {
1055     D_Quit_NetGame();
1056     CL_Reset();
1057     D_StartTitle();
1058 }
1059 
Got_NetXCmd_UseArtifact(xcmd_t * xc)1060 void Got_NetXCmd_UseArtifact(xcmd_t * xc)
1061 {
1062     // Format: artifact  byte.
1063     byte art = READBYTE(xc->curpos);
1064     P_PlayerUseArtifact(&players[xc->playernum], art);
1065 }
1066 
Command_Kill(void)1067 void Command_Kill(void)
1068 {
1069     P_KillMobj(players[consoleplayer].mo, NULL, players[consoleplayer].mo);
1070 }
1071