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