1 /*
2 ===========================================================================
3
4 Return to Castle Wolfenstein single player GPL Source Code
5 Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Return to Castle Wolfenstein single player GPL Source Code (RTCW SP Source Code).
8
9 RTCW SP Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 RTCW SP Source Code 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 RTCW SP Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code. If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29
30
31
32 // this file holds commands that can be executed by the server console, but not remote clients
33
34 #include "g_local.h"
35
36
37 /*
38 ==============================================================================
39
40 PACKET FILTERING
41
42
43 You can add or remove addresses from the filter list with:
44
45 addip <ip>
46 removeip <ip>
47
48 The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
49
50 Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host.
51
52 listip
53 Prints the current list of filters.
54
55 g_filterban <0 or 1>
56
57 If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting.
58
59 If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network.
60
61
62 ==============================================================================
63 */
64
65
66
67
68
69 typedef struct ipFilter_s
70 {
71 unsigned mask;
72 unsigned compare;
73 } ipFilter_t;
74
75 #define MAX_IPFILTERS 1024
76
77 static ipFilter_t ipFilters[MAX_IPFILTERS];
78 static int numIPFilters;
79
80 /*
81 =================
82 StringToFilter
83 =================
84 */
StringToFilter(char * s,ipFilter_t * f)85 static qboolean StringToFilter( char *s, ipFilter_t *f ) {
86 char num[128];
87 int i, j;
88 byte b[4];
89 byte m[4];
90
91 for ( i = 0 ; i < 4 ; i++ )
92 {
93 b[i] = 0;
94 m[i] = 0;
95 }
96
97 for ( i = 0 ; i < 4 ; i++ )
98 {
99 if ( *s < '0' || *s > '9' ) {
100 G_Printf( "Bad filter address: %s\n", s );
101 return qfalse;
102 }
103
104 j = 0;
105 while ( *s >= '0' && *s <= '9' )
106 {
107 num[j++] = *s++;
108 }
109 num[j] = 0;
110 b[i] = atoi( num );
111 if ( b[i] != 0 ) {
112 m[i] = 255;
113 }
114
115 if ( !*s ) {
116 break;
117 }
118 s++;
119 }
120
121 f->mask = *(unsigned *)m;
122 f->compare = *(unsigned *)b;
123
124 return qtrue;
125 }
126
127 /*
128 =================
129 UpdateIPBans
130 =================
131 */
UpdateIPBans(void)132 static void UpdateIPBans( void ) {
133 byte b[4] = {0};
134 byte m[4] = {0};
135 int i,j;
136 char iplist_final[MAX_CVAR_VALUE_STRING] = {0};
137 char ip[64] = {0};
138
139 *iplist_final = 0;
140 for ( i = 0 ; i < numIPFilters ; i++ )
141 {
142 if ( ipFilters[i].compare == 0xffffffff ) {
143 continue;
144 }
145
146 *(unsigned *)b = ipFilters[i].compare;
147 *(unsigned *)m = ipFilters[i].mask;
148 *ip = 0;
149 for ( j = 0 ; j < 4 ; j++ )
150 {
151 if ( m[j] != 255 ) {
152 Q_strcat( ip, sizeof( ip ), "*" );
153 } else {
154 Q_strcat( ip, sizeof( ip ), va( "%i", b[j] ) );
155 }
156 Q_strcat( ip, sizeof( ip ), ( j < 3 ) ? "." : " " );
157 }
158 if ( strlen( iplist_final ) + strlen( ip ) < MAX_CVAR_VALUE_STRING ) {
159 Q_strcat( iplist_final, sizeof( iplist_final ), ip );
160 } else
161 {
162 Com_Printf( "g_banIPs overflowed at MAX_CVAR_VALUE_STRING\n" );
163 break;
164 }
165 }
166
167 trap_Cvar_Set( "g_banIPs", iplist_final );
168 }
169
170 /*
171 =================
172 G_FilterPacket
173 =================
174 */
G_FilterPacket(char * from)175 qboolean G_FilterPacket( char *from ) {
176 int i;
177 unsigned in;
178 byte m[4] = {0};
179 char *p;
180
181 i = 0;
182 p = from;
183 while ( *p && i < 4 ) {
184 m[i] = 0;
185 while ( *p >= '0' && *p <= '9' ) {
186 m[i] = m[i] * 10 + ( *p - '0' );
187 p++;
188 }
189 if ( !*p || *p == ':' ) {
190 break;
191 }
192 i++, p++;
193 }
194
195 in = *(unsigned *)m;
196
197 for ( i = 0 ; i < numIPFilters ; i++ )
198 if ( ( in & ipFilters[i].mask ) == ipFilters[i].compare ) {
199 return g_filterBan.integer != 0;
200 }
201
202 return g_filterBan.integer == 0;
203 }
204
205 /*
206 =================
207 AddIP
208 =================
209 */
AddIP(char * str)210 static void AddIP( char *str ) {
211 int i;
212
213 for ( i = 0 ; i < numIPFilters ; i++ )
214 if ( ipFilters[i].compare == 0xffffffff ) {
215 break;
216 } // free spot
217 if ( i == numIPFilters ) {
218 if ( numIPFilters == MAX_IPFILTERS ) {
219 G_Printf( "IP filter list is full\n" );
220 return;
221 }
222 numIPFilters++;
223 }
224
225 if ( !StringToFilter( str, &ipFilters[i] ) ) {
226 ipFilters[i].compare = 0xffffffffu;
227 }
228
229 UpdateIPBans();
230 }
231
232 /*
233 =================
234 G_ProcessIPBans
235 =================
236 */
G_ProcessIPBans(void)237 void G_ProcessIPBans( void ) {
238 char *s, *t;
239 char str[MAX_TOKEN_CHARS];
240
241 Q_strncpyz( str, g_banIPs.string, sizeof( str ) );
242
243 for ( t = s = g_banIPs.string; *t; /* */ ) {
244 s = strchr( s, ' ' );
245 if ( !s ) {
246 break;
247 }
248 while ( *s == ' ' )
249 *s++ = 0;
250 if ( *t ) {
251 AddIP( t );
252 }
253 t = s;
254 }
255 }
256
257
258 /*
259 =================
260 Svcmd_AddIP_f
261 =================
262 */
Svcmd_AddIP_f(void)263 void Svcmd_AddIP_f( void ) {
264 char str[MAX_TOKEN_CHARS];
265
266 if ( trap_Argc() < 2 ) {
267 G_Printf( "Usage: addip <ip-mask>\n" );
268 return;
269 }
270
271 trap_Argv( 1, str, sizeof( str ) );
272
273 AddIP( str );
274
275 }
276
277 /*
278 =================
279 Svcmd_RemoveIP_f
280 =================
281 */
Svcmd_RemoveIP_f(void)282 void Svcmd_RemoveIP_f( void ) {
283 ipFilter_t f;
284 int i;
285 char str[MAX_TOKEN_CHARS];
286
287 if ( trap_Argc() < 2 ) {
288 G_Printf( "Usage: removeip <ip-mask>\n" );
289 return;
290 }
291
292 trap_Argv( 1, str, sizeof( str ) );
293
294 if ( !StringToFilter( str, &f ) ) {
295 return;
296 }
297
298 for ( i = 0 ; i < numIPFilters ; i++ ) {
299 if ( ipFilters[i].mask == f.mask &&
300 ipFilters[i].compare == f.compare ) {
301 ipFilters[i].compare = 0xffffffffu;
302 G_Printf( "Removed.\n" );
303
304 UpdateIPBans();
305 return;
306 }
307 }
308
309 G_Printf( "Didn't find %s.\n", str );
310 }
311
312 /*
313 ===================
314 Svcmd_EntityList_f
315 ===================
316 */
Svcmd_EntityList_f(void)317 void Svcmd_EntityList_f( void ) {
318 int e;
319 gentity_t *check;
320
321 check = g_entities;
322 for ( e = 0; e < level.num_entities ; e++, check++ ) {
323 if ( !check->inuse ) {
324 continue;
325 }
326 G_Printf( "%3i:", e );
327 switch ( check->s.eType ) {
328 case ET_GENERAL:
329 G_Printf( "ET_GENERAL " );
330 break;
331 case ET_PLAYER:
332 G_Printf( "ET_PLAYER " );
333 break;
334 case ET_ITEM:
335 G_Printf( "ET_ITEM " );
336 break;
337 case ET_MISSILE:
338 G_Printf( "ET_MISSILE " );
339 break;
340 case ET_MOVER:
341 G_Printf( "ET_MOVER " );
342 break;
343 case ET_BEAM:
344 G_Printf( "ET_BEAM " );
345 break;
346 case ET_PORTAL:
347 G_Printf( "ET_PORTAL " );
348 break;
349 case ET_SPEAKER:
350 G_Printf( "ET_SPEAKER " );
351 break;
352 case ET_PUSH_TRIGGER:
353 G_Printf( "ET_PUSH_TRIGGER " );
354 break;
355 case ET_TELEPORT_TRIGGER:
356 G_Printf( "ET_TELEPORT_TRIGGER " );
357 break;
358 case ET_INVISIBLE:
359 G_Printf( "ET_INVISIBLE " );
360 break;
361 case ET_GRAPPLE:
362 G_Printf( "ET_GRAPPLE " );
363 break;
364 case ET_EXPLOSIVE:
365 G_Printf( "ET_EXPLOSIVE " );
366 break;
367 case ET_TESLA_EF:
368 G_Printf( "ET_TESLA_EF " );
369 break;
370 case ET_SPOTLIGHT_EF:
371 G_Printf( "ET_SPOTLIGHT_EF " );
372 break;
373 case ET_EFFECT3:
374 G_Printf( "ET_EFFECT3 " );
375 break;
376 case ET_ALARMBOX:
377 G_Printf( "ET_ALARMBOX " );
378 break;
379 default:
380 G_Printf( "%3i ", check->s.eType );
381 break;
382 }
383
384 if ( check->classname ) {
385 G_Printf( "%s", check->classname );
386 }
387 G_Printf( "\n" );
388 }
389 }
390
ClientForString(const char * s)391 gclient_t *ClientForString( const char *s ) {
392 gclient_t *cl;
393 int i;
394 int idnum;
395
396 // numeric values are just slot numbers
397 if ( s[0] >= '0' && s[0] <= '9' ) {
398 idnum = atoi( s );
399 if ( idnum < 0 || idnum >= level.maxclients ) {
400 Com_Printf( "Bad client slot: %i\n", idnum );
401 return NULL;
402 }
403
404 cl = &level.clients[idnum];
405 if ( cl->pers.connected == CON_DISCONNECTED ) {
406 G_Printf( "Client %i is not connected\n", idnum );
407 return NULL;
408 }
409 return cl;
410 }
411
412 // check for a name match
413 for ( i = 0 ; i < level.maxclients ; i++ ) {
414 cl = &level.clients[i];
415 if ( cl->pers.connected == CON_DISCONNECTED ) {
416 continue;
417 }
418 if ( !Q_stricmp( cl->pers.netname, s ) ) {
419 return cl;
420 }
421 }
422
423 G_Printf( "User %s is not on the server\n", s );
424
425 return NULL;
426 }
427
428 /*
429 ===================
430 Svcmd_ForceTeam_f
431
432 forceteam <player> <team>
433 ===================
434 */
Svcmd_ForceTeam_f(void)435 void Svcmd_ForceTeam_f( void ) {
436 gclient_t *cl;
437 char str[MAX_TOKEN_CHARS];
438
439 if ( trap_Argc() < 3 ) {
440 G_Printf("Usage: forceteam <player> <team>\n");
441 return;
442 }
443
444 // find the player
445 trap_Argv( 1, str, sizeof( str ) );
446 cl = ClientForString( str );
447 if ( !cl ) {
448 return;
449 }
450
451 // set the team
452 trap_Argv( 2, str, sizeof( str ) );
453 SetTeam( &g_entities[cl - level.clients], str );
454 }
455
456 char *ConcatArgs( int start );
457
458 /*
459 =================
460 ConsoleCommand
461
462 =================
463 */
ConsoleCommand(void)464 qboolean ConsoleCommand( void ) {
465 char cmd[MAX_TOKEN_CHARS];
466
467 trap_Argv( 0, cmd, sizeof( cmd ) );
468
469 // Ridah, savegame
470 if ( Q_stricmp( cmd, "savegame" ) == 0 ) {
471
472 // don't allow a manual savegame command while we are waiting for the game to start/exit
473 if ( g_reloading.integer ) {
474 return qtrue;
475 }
476 if ( saveGamePending ) {
477 return qtrue;
478 }
479
480 trap_Argv( 1, cmd, sizeof( cmd ) );
481 if ( strlen( cmd ) > 0 ) {
482 // strip the extension if provided
483 if ( strrchr( cmd, '.' ) ) {
484 cmd[strrchr( cmd,'.' ) - cmd] = '\0';
485 }
486 if ( !Q_stricmp( cmd, "current" ) ) { // beginning of map
487 Com_Printf( "sorry, '%s' is a reserved savegame name. please use another name.\n", cmd );
488 return qtrue;
489 }
490
491 if ( G_SaveGame( cmd ) ) {
492 trap_SendServerCommand( -1, "cp gamesaved" ); // deletedgame
493 } else {
494 G_Printf( "Unable to save game.\n" );
495 }
496
497 } else { // need a name
498 G_Printf( "syntax: savegame <name>\n" );
499 }
500
501 return qtrue;
502 }
503 // done.
504
505 if ( Q_stricmp( cmd, "entitylist" ) == 0 ) {
506 Svcmd_EntityList_f();
507 return qtrue;
508 }
509
510 if ( Q_stricmp( cmd, "forceteam" ) == 0 ) {
511 Svcmd_ForceTeam_f();
512 return qtrue;
513 }
514
515 if ( Q_stricmp( cmd, "game_memory" ) == 0 ) {
516 Svcmd_GameMem_f();
517 return qtrue;
518 }
519
520 if ( Q_stricmp( cmd, "addbot" ) == 0 ) {
521 Svcmd_AddBot_f();
522 return qtrue;
523 }
524
525 // TTimo: took out games/g_arenas.c
526 /*
527 if (Q_stricmp (cmd, "abort_podium") == 0) {
528 Svcmd_AbortPodium_f();
529 return qtrue;
530 }
531 */
532
533 if ( Q_stricmp( cmd, "addip" ) == 0 ) {
534 Svcmd_AddIP_f();
535 return qtrue;
536 }
537
538 if ( Q_stricmp( cmd, "removeip" ) == 0 ) {
539 Svcmd_RemoveIP_f();
540 return qtrue;
541 }
542
543 if ( Q_stricmp( cmd, "listip" ) == 0 ) {
544 trap_SendConsoleCommand( EXEC_INSERT, "g_banIPs\n" );
545 return qtrue;
546 }
547
548 if ( g_dedicated.integer ) {
549 if ( Q_stricmp( cmd, "say" ) == 0 ) {
550 trap_SendServerCommand( -1, va( "print \"server: %s\n\"", ConcatArgs( 1 ) ) );
551 return qtrue;
552 }
553 // everything else will also be printed as a say command
554 trap_SendServerCommand( -1, va( "print \"server: %s\n\"", ConcatArgs( 0 ) ) );
555 return qtrue;
556 }
557
558 return qfalse;
559 }
560
561