1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2013 - 2015, OpenJK contributors
7 
8 This file is part of the OpenJK source code.
9 
10 OpenJK is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 ===========================================================================
22 */
23 
24 #include "../server/exe_headers.h"
25 
26 #include "server.h"
27 #include "../game/weapons.h"
28 #include "../game/g_items.h"
29 #include "../game/statindex.h"
30 
31 #include "../win32/AutoVersion.h"
32 
33 
34 /*
35 ===============================================================================
36 
37 OPERATOR CONSOLE ONLY COMMANDS
38 
39 These commands can only be entered from stdin or by a remote operator datagram
40 ===============================================================================
41 */
42 
43 qboolean qbLoadTransition = qfalse;
44 
45 //=========================================================
46 // don't call this directly, it should only be called from SV_Map_f() or SV_MapTransition_f()
47 //
SV_Map_(ForceReload_e eForceReload)48 static bool SV_Map_( ForceReload_e eForceReload )
49 {
50 	char		*map = NULL;
51 	char		expanded[MAX_QPATH] = {0};
52 
53 	map = Cmd_Argv(1);
54 	if ( !*map ) {
55 		Com_Printf ("no map specified\n");
56 		return false;
57 	}
58 
59 	// make sure the level exists before trying to change, so that
60 	// a typo at the server console won't end the game
61 	if (strchr (map, '\\') ) {
62 		Com_Printf ("Can't have mapnames with a \\\n");
63 		return false;
64 	}
65 
66 	Com_sprintf (expanded, sizeof(expanded), "maps/%s.bsp", map);
67 
68 	if ( FS_ReadFile (expanded, NULL) == -1 ) {
69 		Com_Printf ("Can't find map %s\n", expanded);
70 		extern	cvar_t	*com_buildScript;
71 		if (com_buildScript && com_buildScript->integer)
72 		{//yes, it's happened, someone deleted a map during my build...
73 			Com_Error( ERR_FATAL, "Can't find map %s\n", expanded );
74 		}
75 		return false;
76 	}
77 
78 	if (map[0]!='_')
79 	{
80 		SG_WipeSavegame("auto");
81 	}
82 
83 	SV_SpawnServer( map, eForceReload, qtrue );	// start up the map
84 	return true;
85 }
86 
87 
88 
89 // Save out some player data for later restore if this is a spawn point with KEEP_PREV (spawnflags&1) set...
90 //
91 // (now also called by auto-save code to setup the cvars correctly
SV_Player_EndOfLevelSave(void)92 void SV_Player_EndOfLevelSave(void)
93 {
94 	int	i;
95 
96 	// I could just call GetClientState() but that's in sv_bot.cpp, and I'm not sure if that's going to be deleted for
97 	//	the single player build, so here's the guts again...
98 	//
99 	client_t* cl = &svs.clients[0];	// 0 because only ever us as a player
100 
101 	if (cl
102 		&&
103 		cl->gentity && cl->gentity->client	// crash fix for voy4->brig transition when you kill Foster.
104 											//	Shouldn't happen, but does sometimes...
105 		)
106 	{
107 		Cvar_Set( sCVARNAME_PLAYERSAVE, "");	// default to blank
108 
109 //		clientSnapshot_t*	pFrame = &cl->frames[cl->netchan.outgoingSequence & PACKET_MASK];
110 		playerState_t*		pState = cl->gentity->client;
111 		const char	*s2;
112 		const char *s;
113 #ifdef JK2_MODE
114 		s = va("%i %i %i %i %i %i %i %f %f %f %i %i %i %i %i %i",
115 						pState->stats[STAT_HEALTH],
116 						pState->stats[STAT_ARMOR],
117 						pState->stats[STAT_WEAPONS],
118 						pState->stats[STAT_ITEMS],
119 						pState->weapon,
120 						pState->weaponstate,
121 						pState->batteryCharge,
122 						pState->viewangles[0],
123 						pState->viewangles[1],
124 						pState->viewangles[2],
125 						pState->forcePowersKnown,
126 						pState->forcePower,
127 						pState->saberActive,
128 						pState->saberAnimLevel,
129 						pState->saberLockEnemy,
130 						pState->saberLockTime
131 						);
132 #else
133 				//				|general info				  |-force powers |-saber 1		|-saber 2										  |-general saber
134 				s = va("%i %i %i %i %i %i %i %f %f %f %i %i %i %i %i %s %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %s %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
135 						pState->stats[STAT_HEALTH],
136 						pState->stats[STAT_ARMOR],
137 						pState->stats[STAT_WEAPONS],
138 						pState->stats[STAT_ITEMS],
139 						pState->weapon,
140 						pState->weaponstate,
141 						pState->batteryCharge,
142 						pState->viewangles[0],
143 						pState->viewangles[1],
144 						pState->viewangles[2],
145 						//force power data
146 						pState->forcePowersKnown,
147 						pState->forcePower,
148 						pState->forcePowerMax,
149 						pState->forcePowerRegenRate,
150 						pState->forcePowerRegenAmount,
151 						//saber 1 data
152 						pState->saber[0].name,
153 						pState->saber[0].blade[0].active,
154 						pState->saber[0].blade[1].active,
155 						pState->saber[0].blade[2].active,
156 						pState->saber[0].blade[3].active,
157 						pState->saber[0].blade[4].active,
158 						pState->saber[0].blade[5].active,
159 						pState->saber[0].blade[6].active,
160 						pState->saber[0].blade[7].active,
161 						pState->saber[0].blade[0].color,
162 						pState->saber[0].blade[1].color,
163 						pState->saber[0].blade[2].color,
164 						pState->saber[0].blade[3].color,
165 						pState->saber[0].blade[4].color,
166 						pState->saber[0].blade[5].color,
167 						pState->saber[0].blade[6].color,
168 						pState->saber[0].blade[7].color,
169 						//saber 2 data
170 						pState->saber[1].name,
171 						pState->saber[1].blade[0].active,
172 						pState->saber[1].blade[1].active,
173 						pState->saber[1].blade[2].active,
174 						pState->saber[1].blade[3].active,
175 						pState->saber[1].blade[4].active,
176 						pState->saber[1].blade[5].active,
177 						pState->saber[1].blade[6].active,
178 						pState->saber[1].blade[7].active,
179 						pState->saber[1].blade[0].color,
180 						pState->saber[1].blade[1].color,
181 						pState->saber[1].blade[2].color,
182 						pState->saber[1].blade[3].color,
183 						pState->saber[1].blade[4].color,
184 						pState->saber[1].blade[5].color,
185 						pState->saber[1].blade[6].color,
186 						pState->saber[1].blade[7].color,
187 						//general saber data
188 						pState->saberStylesKnown,
189 						pState->saberAnimLevel,
190 						pState->saberLockEnemy,
191 						pState->saberLockTime
192 						);
193 #endif
194 		Cvar_Set( sCVARNAME_PLAYERSAVE, s );
195 
196 		//ammo
197 		s2 = "";
198 		for (i=0;i< AMMO_MAX; i++)
199 		{
200 			s2 = va("%s %i",s2, pState->ammo[i]);
201 		}
202 		Cvar_Set( "playerammo", s2 );
203 
204 		//inventory
205 		s2 = "";
206 		for (i=0;i< INV_MAX; i++)
207 		{
208 			s2 = va("%s %i",s2, pState->inventory[i]);
209 		}
210 		Cvar_Set( "playerinv", s2 );
211 
212 		// the new JK2 stuff - force powers, etc...
213 		//
214 		s2 = "";
215 		for (i=0;i< NUM_FORCE_POWERS; i++)
216 		{
217 			s2 = va("%s %i",s2, pState->forcePowerLevel[i]);
218 		}
219 		Cvar_Set( "playerfplvl", s2 );
220 	}
221 }
222 
223 
224 // Restart the server on a different map
225 //
SV_MapTransition_f(void)226 static void SV_MapTransition_f(void)
227 {
228 	const char	*spawntarget;
229 
230 #ifdef JK2_MODE
231 	SCR_PrecacheScreenshot();
232 #endif
233 	SV_Player_EndOfLevelSave();
234 
235 	spawntarget = Cmd_Argv(2);
236 	if ( *spawntarget != '\0' )
237 	{
238 		Cvar_Set( "spawntarget", spawntarget );
239 	}
240 	else
241 	{
242 		Cvar_Set( "spawntarget", "" );
243 	}
244 
245 	SV_Map_( eForceReload_NOTHING );
246 }
247 
248 /*
249 ==================
250 SV_Map_f
251 
252 Restart the server on a different map, but clears a cvar so that typing "map blah" doesn't try and preserve
253 player weapons/ammo/etc from the previous level that you haven't really exited (ie ignores KEEP_PREV on spawn points)
254 ==================
255 */
256 #ifdef JK2_MODE
257 extern void SCR_UnprecacheScreenshot();
258 #endif
SV_Map_f(void)259 static void SV_Map_f( void )
260 {
261 	Cvar_Set( sCVARNAME_PLAYERSAVE, "");
262 	Cvar_Set( "spawntarget", "" );
263 	Cvar_Set("tier_storyinfo", "0");
264 	Cvar_Set("tiers_complete", "");
265 #ifdef JK2_MODE
266 	SCR_UnprecacheScreenshot();
267 #endif
268 
269 	ForceReload_e eForceReload = eForceReload_NOTHING;	// default for normal load
270 
271 	char *cmd = Cmd_Argv( 0 );
272 	if ( !Q_stricmp( cmd, "devmapbsp") )
273 		eForceReload = eForceReload_BSP;
274 	else if ( !Q_stricmp( cmd, "devmapmdl") )
275 		eForceReload = eForceReload_MODELS;
276 	else if ( !Q_stricmp( cmd, "devmapall") )
277 		eForceReload = eForceReload_ALL;
278 
279 	qboolean cheat = (qboolean)(!Q_stricmpn( cmd, "devmap", 6 ) );
280 
281 	// retain old cheat state
282 	if ( !cheat && Cvar_VariableIntegerValue( "helpUsObi" ) )
283 		cheat = qtrue;
284 
285 	if (SV_Map_( eForceReload ))
286 	{
287 		// set the cheat value
288 		// if the level was started with "map <levelname>", then
289 		// cheats will not be allowed.  If started with "devmap <levelname>"
290 		// then cheats will be allowed
291 		Cvar_Set( "helpUsObi", cheat ? "1" : "0" );
292 	}
293 #ifdef JK2_MODE
294 	Cvar_Set( "cg_missionstatusscreen", "0" );
295 #endif
296 }
297 
298 /*
299 ==================
300 SV_LoadTransition_f
301 ==================
302 */
SV_LoadTransition_f(void)303 void SV_LoadTransition_f(void)
304 {
305 	const char	*map;
306 	const char	*spawntarget;
307 
308 	map = Cmd_Argv(1);
309 	if ( !*map ) {
310 		return;
311 	}
312 
313 	qbLoadTransition = qtrue;
314 
315 #ifdef JK2_MODE
316 	SCR_PrecacheScreenshot();
317 #endif
318 	SV_Player_EndOfLevelSave();
319 
320 	//Save the full current state of the current map so we can return to it later
321 	SG_WriteSavegame( va("hub/%s", sv_mapname->string), qfalse );
322 
323 	//set the spawntarget if there is one
324 	spawntarget = Cmd_Argv(2);
325 	if ( *spawntarget != '\0' )
326 	{
327 		Cvar_Set( "spawntarget", spawntarget );
328 	}
329 	else
330 	{
331 		Cvar_Set( "spawntarget", "" );
332 	}
333 
334 	if ( !SV_TryLoadTransition( map ) )
335 	{//couldn't load a savegame
336 		SV_Map_( eForceReload_NOTHING );
337 	}
338 	qbLoadTransition = qfalse;
339 }
340 //===============================================================
341 
ivtos(const vec3_t v)342 char	*ivtos( const vec3_t v ) {
343 	static	int		index;
344 	static	char	str[8][32];
345 	char	*s;
346 
347 	// use an array so that multiple vtos won't collide
348 	s = str[index];
349 	index = (index + 1)&7;
350 
351 	Com_sprintf (s, 32, "( %i %i %i )", (int)v[0], (int)v[1], (int)v[2]);
352 
353 	return s;
354 }
355 
356 /*
357 ================
358 SV_Status_f
359 ================
360 */
SV_Status_f(void)361 static void SV_Status_f( void ) {
362 	client_t	*cl;
363 
364 	// make sure server is running
365 	if ( !com_sv_running->integer ) {
366 		Com_Printf( "Server is not running.\n" );
367 		return;
368 	}
369 
370 	cl = &svs.clients[0];
371 
372 	if ( !cl ) {
373 		Com_Printf("Server is not running.\n");
374 		return;
375 	}
376 
377 #if defined(_WIN32)
378 #define STATUS_OS "Windows"
379 #elif defined(__linux__)
380 #define STATUS_OS "Linux"
381 #elif defined(MACOS_X)
382 #define STATUS_OS "OSX"
383 #else
384 #define STATUS_OS "Unknown"
385 #endif
386 
387 	Com_Printf( "name    : %s^7\n", cl->name );
388 	Com_Printf( "score   : %i\n", cl->gentity->client->persistant[PERS_SCORE] );
389 	Com_Printf( "version : %s %s %i\n", STATUS_OS, VERSION_STRING_DOTTED, PROTOCOL_VERSION );
390 #ifdef JK2_MODE
391 	Com_Printf( "game    : Jedi Outcast %s\n", FS_GetCurrentGameDir() );
392 #else
393 	Com_Printf( "game    : Jedi Academy %s\n", FS_GetCurrentGameDir() );
394 #endif
395 	Com_Printf( "map     : %s at %s\n", sv_mapname->string, ivtos( cl->gentity->client->origin ) );
396 }
397 
398 /*
399 ===========
400 SV_Serverinfo_f
401 
402 Examine the serverinfo string
403 ===========
404 */
SV_Serverinfo_f(void)405 static void SV_Serverinfo_f( void ) {
406 	Com_Printf ("Server info settings:\n");
407 	Info_Print ( Cvar_InfoString( CVAR_SERVERINFO ) );
408 }
409 
410 
411 /*
412 ===========
413 SV_Systeminfo_f
414 
415 Examine or change the serverinfo string
416 ===========
417 */
SV_Systeminfo_f(void)418 static void SV_Systeminfo_f( void ) {
419 	Com_Printf ("System info settings:\n");
420 	Info_Print ( Cvar_InfoString( CVAR_SYSTEMINFO ) );
421 }
422 
423 
424 /*
425 ===========
426 SV_DumpUser_f
427 
428 Examine all a users info strings FIXME: move to game
429 ===========
430 */
SV_DumpUser_f(void)431 static void SV_DumpUser_f( void ) {
432 	client_t	*cl;
433 
434 	// make sure server is running
435 	if ( !com_sv_running->integer ) {
436 		Com_Printf( "Server is not running.\n" );
437 		return;
438 	}
439 
440 	if ( Cmd_Argc() != 1 ) {
441 		Com_Printf ("Usage: info\n");
442 		return;
443 	}
444 
445 	cl = &svs.clients[0];
446 	if ( !cl->state ) {
447 		Com_Printf("Client is not active\n");
448 		return;
449 	}
450 
451 	Com_Printf( "userinfo\n" );
452 	Com_Printf( "--------\n" );
453 	Info_Print( cl->userinfo );
454 }
455 
456 //===========================================================
457 
458 /*
459 ==================
460 SV_CompleteMapName
461 ==================
462 */
SV_CompleteMapName(char * args,int argNum)463 static void SV_CompleteMapName( char *args, int argNum ) {
464 	if ( argNum == 2 )
465 		Field_CompleteFilename( "maps", "bsp", qtrue, qfalse );
466 }
467 
468 /*
469 ==================
470 SV_CompleteMapName
471 ==================
472 */
SV_CompleteSaveName(char * args,int argNum)473 static void SV_CompleteSaveName( char *args, int argNum ) {
474 	if ( argNum == 2 )
475 		Field_CompleteFilename( "saves", "sav", qtrue, qtrue );
476 }
477 
478 /*
479 ==================
480 SV_AddOperatorCommands
481 ==================
482 */
SV_AddOperatorCommands(void)483 void SV_AddOperatorCommands( void ) {
484 	static qboolean	initialized;
485 
486 	if ( initialized ) {
487 		return;
488 	}
489 	initialized = qtrue;
490 
491 	Cmd_AddCommand ("status", SV_Status_f);
492 	Cmd_AddCommand ("serverinfo", SV_Serverinfo_f);
493 	Cmd_AddCommand ("systeminfo", SV_Systeminfo_f);
494 	Cmd_AddCommand ("dumpuser", SV_DumpUser_f);
495 	Cmd_AddCommand ("sectorlist", SV_SectorList_f);
496 	Cmd_AddCommand ("map", SV_Map_f);
497 	Cmd_SetCommandCompletionFunc( "map", SV_CompleteMapName );
498 	Cmd_AddCommand ("devmap", SV_Map_f);
499 	Cmd_SetCommandCompletionFunc( "devmap", SV_CompleteMapName );
500 	Cmd_AddCommand ("devmapbsp", SV_Map_f);
501 	Cmd_SetCommandCompletionFunc( "devmapbsp", SV_CompleteMapName );
502 	Cmd_AddCommand ("devmapmdl", SV_Map_f);
503 	Cmd_SetCommandCompletionFunc( "devmapmdl", SV_CompleteMapName );
504 	Cmd_AddCommand ("devmapsnd", SV_Map_f);
505 	Cmd_SetCommandCompletionFunc( "devmapsnd", SV_CompleteMapName );
506 	Cmd_AddCommand ("devmapall", SV_Map_f);
507 	Cmd_SetCommandCompletionFunc( "devmapall", SV_CompleteMapName );
508 	Cmd_AddCommand ("maptransition", SV_MapTransition_f);
509 	Cmd_AddCommand ("load", SV_LoadGame_f);
510 	Cmd_SetCommandCompletionFunc( "load", SV_CompleteSaveName );
511 	Cmd_AddCommand ("loadtransition", SV_LoadTransition_f);
512 	Cmd_AddCommand ("save", SV_SaveGame_f);
513 	Cmd_AddCommand ("wipe", SV_WipeGame_f);
514 
515 //#ifdef _DEBUG
516 //	extern void UI_Dump_f(void);
517 //	Cmd_AddCommand ("ui_dump", UI_Dump_f);
518 //#endif
519 }
520 
521 /*
522 ==================
523 SV_RemoveOperatorCommands
524 ==================
525 */
SV_RemoveOperatorCommands(void)526 void SV_RemoveOperatorCommands( void ) {
527 #if 0
528 	// removing these won't let the server start again
529 	Cmd_RemoveCommand ("status");
530 	Cmd_RemoveCommand ("serverinfo");
531 	Cmd_RemoveCommand ("systeminfo");
532 	Cmd_RemoveCommand ("dumpuser");
533 	Cmd_RemoveCommand ("serverrecord");
534 	Cmd_RemoveCommand ("serverstop");
535 	Cmd_RemoveCommand ("sectorlist");
536 #endif
537 }
538 
539