root/src/game/g_svcmds.c @ 111:64713adfb27b

Revision 111:64713adfb27b, 15.2 kB (checked in by mdoison@…, 2 years ago)

* Add lua basic support

Line 
1/*
2===========================================================================
3Copyright (C) 1999-2005 Id Software, Inc.
4Copyright (C) 2000-2006 Tim Angus
5
6This file is part of Tremulous.
7
8Tremulous is free software; you can redistribute it
9and/or modify it under the terms of the GNU General Public License as
10published by the Free Software Foundation; either version 2 of the License,
11or (at your option) any later version.
12
13Tremulous is distributed in the hope that it will be
14useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with Tremulous; if not, write to the Free Software
20Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21===========================================================================
22*/
23
24// this file holds commands that can be executed by the server console, but not remote clients
25
26#include "g_local.h"
27
28
29/*
30==============================================================================
31
32PACKET FILTERING
33
34
35You can add or remove addresses from the filter list with:
36
37addip <ip>
38removeip <ip>
39
40The ip address is specified in dot format, and you can use '*' to match any value
41so you can specify an entire class C network with "addip 192.246.40.*"
42
43Removeip will only remove an address specified exactly the same way.  You cannot addip a subnet, then removeip a single host.
44
45listip
46Prints the current list of filters.
47
48g_filterban <0 or 1>
49
50If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game.  This is the default setting.
51
52If 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.
53
54TTimo NOTE: for persistence, bans are stored in g_banIPs cvar MAX_CVAR_VALUE_STRING
55The size of the cvar string buffer is limiting the banning to around 20 masks
56this could be improved by putting some g_banIPs2 g_banIps3 etc. maybe
57still, you should rely on PB for banning instead
58
59==============================================================================
60*/
61
62// extern vmCvar_t  g_banIPs;
63// extern vmCvar_t  g_filterBan;
64
65
66typedef struct ipFilter_s
67{
68  unsigned  mask;
69  unsigned  compare;
70} ipFilter_t;
71
72#define MAX_IPFILTERS 1024
73
74static ipFilter_t ipFilters[ MAX_IPFILTERS ];
75static int        numIPFilters;
76
77/*
78=================
79StringToFilter
80=================
81*/
82static qboolean StringToFilter( char *s, ipFilter_t *f )
83{
84  char  num[ 128 ];
85  int   i, j;
86  byte  b[ 4 ];
87  byte  m[ 4 ];
88
89  for( i = 0; i < 4; i++ )
90  {
91    b[ i ] = 0;
92    m[ i ] = 0;
93  }
94
95  for( i = 0; i < 4; i++ )
96  {
97    if( *s < '0' || *s > '9' )
98    {
99      if( *s == '*' ) // 'match any'
100      {
101        //b[ i ] and m[ i ] to 0
102        s++;
103        if ( !*s )
104          break;
105
106        s++;
107        continue;
108      }
109
110      G_Printf( "Bad filter address: %s\n", s );
111      return qfalse;
112    }
113
114    j = 0;
115    while( *s >= '0' && *s <= '9' )
116      num[ j++ ] = *s++;
117
118    num[ j ] = 0;
119    b[ i ] = atoi( num );
120
121    m[ i ] = 255;
122
123    if( !*s )
124      break;
125
126    s++;
127  }
128
129  f->mask = *(unsigned *)m;
130  f->compare = *(unsigned *)b;
131
132  return qtrue;
133}
134
135/*
136=================
137UpdateIPBans
138=================
139*/
140static void UpdateIPBans( void )
141{
142  byte  b[ 4 ];
143  byte  m[ 4 ];
144  int    i, j;
145  char  iplist_final[ MAX_CVAR_VALUE_STRING ];
146  char  ip[ 64 ];
147
148  *iplist_final = 0;
149
150  for( i = 0 ; i < numIPFilters ; i++ )
151  {
152    if( ipFilters[ i ].compare == 0xffffffff )
153      continue;
154
155    *(unsigned *)b = ipFilters[ i ].compare;
156    *(unsigned *)m = ipFilters[ i ].mask;
157    *ip = 0;
158
159    for( j = 0 ; j < 4 ; j++ )
160    {
161      if( m[ j ] != 255 )
162        Q_strcat( ip, sizeof( ip ), "*" );
163      else
164        Q_strcat( ip, sizeof( ip ), va( "%i", b[ j ] ) );
165
166      Q_strcat( ip, sizeof( ip ), ( j < 3 ) ? "." : " " );
167    }
168
169    if( strlen( iplist_final ) + strlen( ip ) < MAX_CVAR_VALUE_STRING )
170      Q_strcat( iplist_final, sizeof( iplist_final ), ip );
171    else
172    {
173      Com_Printf( "g_banIPs overflowed at MAX_CVAR_VALUE_STRING\n" );
174      break;
175    }
176  }
177
178  trap_Cvar_Set( "g_banIPs", iplist_final );
179}
180
181/*
182=================
183G_FilterPacket
184=================
185*/
186qboolean G_FilterPacket( char *from )
187{
188  int       i;
189  unsigned  in;
190  byte      m[ 4 ];
191  char      *p;
192
193  i = 0;
194  p = from;
195  while( *p && i < 4 )
196  {
197    m[ i ] = 0;
198    while( *p >= '0' && *p <= '9' )
199    {
200      m[ i ] = m[ i ] * 10 + ( *p - '0' );
201      p++;
202    }
203
204    if( !*p || *p == ':' )
205      break;
206
207    i++, p++;
208  }
209
210  in = *(unsigned *)m;
211
212  for( i = 0; i < numIPFilters; i++ )
213    if( ( in & ipFilters[ i ].mask ) == ipFilters[ i ].compare )
214      return g_filterBan.integer != 0;
215
216  return g_filterBan.integer == 0;
217}
218
219/*
220=================
221AddIP
222=================
223*/
224static void AddIP( char *str )
225{
226  int   i;
227
228  for( i = 0 ; i < numIPFilters ; i++ )
229    if( ipFilters[ i ].compare == 0xffffffff )
230      break;    // free spot
231
232  if( i == numIPFilters )
233  {
234    if( numIPFilters == MAX_IPFILTERS )
235    {
236      G_Printf( "IP filter list is full\n" );
237      return;
238    }
239
240    numIPFilters++;
241  }
242
243  if( !StringToFilter( str, &ipFilters[ i ] ) )
244    ipFilters[ i ].compare = 0xffffffffu;
245
246  UpdateIPBans( );
247}
248
249/*
250=================
251G_ProcessIPBans
252=================
253*/
254void G_ProcessIPBans( void )
255{
256  char *s, *t;
257  char str[ MAX_CVAR_VALUE_STRING ];
258
259  Q_strncpyz( str, g_banIPs.string, sizeof( str ) );
260
261  for( t = s = g_banIPs.string; *t; /* */ )
262  {
263    s = strchr( s, ' ' );
264
265    if( !s )
266      break;
267
268    while( *s == ' ' )
269      *s++ = 0;
270
271    if( *t )
272      AddIP( t );
273
274    t = s;
275  }
276}
277
278
279/*
280=================
281Svcmd_AddIP_f
282=================
283*/
284void Svcmd_AddIP_f( void )
285{
286  char str[ MAX_TOKEN_CHARS ];
287
288  if( trap_Argc( ) < 2 )
289  {
290    G_Printf( "Usage:  addip <ip-mask>\n" );
291    return;
292  }
293
294  trap_Argv( 1, str, sizeof( str ) );
295
296  AddIP( str );
297}
298
299/*
300=================
301Svcmd_RemoveIP_f
302=================
303*/
304void Svcmd_RemoveIP_f( void )
305{
306  ipFilter_t  f;
307  int         i;
308  char        str[ MAX_TOKEN_CHARS ];
309
310  if( trap_Argc( ) < 2 )
311  {
312    G_Printf( "Usage:  sv removeip <ip-mask>\n" );
313    return;
314  }
315
316  trap_Argv( 1, str, sizeof( str ) );
317
318  if( !StringToFilter( str, &f ) )
319    return;
320
321  for( i = 0; i < numIPFilters; i++ )
322  {
323    if( ipFilters[ i ].mask == f.mask &&
324        ipFilters[ i ].compare == f.compare)
325    {
326      ipFilters[ i ].compare = 0xffffffffu;
327      G_Printf ( "Removed.\n" );
328
329      UpdateIPBans( );
330      return;
331    }
332  }
333
334  G_Printf ( "Didn't find %s.\n", str );
335}
336
337/*
338===================
339Svcmd_EntityList_f
340===================
341*/
342void  Svcmd_EntityList_f( void )
343{
344  int       e;
345  gentity_t *check;
346
347  check = g_entities + 1;
348
349  for( e = 1; e < level.num_entities; e++, check++ )
350  {
351    if( !check->inuse )
352      continue;
353
354    G_Printf( "%3i:", e );
355
356    switch( check->s.eType )
357    {
358      case ET_GENERAL:
359        G_Printf( "ET_GENERAL          " );
360        break;
361      case ET_PLAYER:
362        G_Printf( "ET_PLAYER           " );
363        break;
364      case ET_ITEM:
365        G_Printf( "ET_ITEM             " );
366        break;
367      case ET_BUILDABLE:
368        G_Printf( "ET_BUILDABLE        " );
369        break;
370      case ET_MISSILE:
371        G_Printf( "ET_MISSILE          " );
372        break;
373      case ET_MOVER:
374        G_Printf( "ET_MOVER            " );
375        break;
376      case ET_BEAM:
377        G_Printf( "ET_BEAM             " );
378        break;
379      case ET_PORTAL:
380        G_Printf( "ET_PORTAL           " );
381        break;
382      case ET_SPEAKER:
383        G_Printf( "ET_SPEAKER          " );
384        break;
385      case ET_PUSH_TRIGGER:
386        G_Printf( "ET_PUSH_TRIGGER     " );
387        break;
388      case ET_TELEPORT_TRIGGER:
389        G_Printf( "ET_TELEPORT_TRIGGER " );
390        break;
391      case ET_INVISIBLE:
392        G_Printf( "ET_INVISIBLE        " );
393        break;
394      case ET_GRAPPLE:
395        G_Printf( "ET_GRAPPLE          " );
396        break;
397      default:
398        G_Printf( "%3i                 ", check->s.eType );
399        break;
400    }
401
402    if( check->classname )
403      G_Printf( "%s", check->classname );
404
405    G_Printf( "\n" );
406  }
407}
408
409gclient_t *ClientForString( const char *s )
410{
411  gclient_t *cl;
412  int       i;
413  int       idnum;
414
415  // numeric values are just slot numbers
416  if( s[ 0 ] >= '0' && s[ 0 ] <= '9' )
417  {
418    idnum = atoi( s );
419
420    if( idnum < 0 || idnum >= level.maxclients )
421    {
422      Com_Printf( "Bad client slot: %i\n", idnum );
423      return NULL;
424    }
425
426    cl = &level.clients[ idnum ];
427
428    if( cl->pers.connected == CON_DISCONNECTED )
429    {
430      G_Printf( "Client %i is not connected\n", idnum );
431      return NULL;
432    }
433
434    return cl;
435  }
436
437  // check for a name match
438  for( i = 0; i < level.maxclients; i++ )
439  {
440    cl = &level.clients[ i ];
441    if( cl->pers.connected == CON_DISCONNECTED )
442      continue;
443
444    if( !Q_stricmp( cl->pers.netname, s ) )
445      return cl;
446  }
447
448  G_Printf( "User %s is not on the server\n", s );
449
450  return NULL;
451}
452
453/*
454===================
455Svcmd_ForceTeam_f
456
457forceteam <player> <team>
458===================
459*/
460void  Svcmd_ForceTeam_f( void )
461{
462  gclient_t *cl;
463  char      str[ MAX_TOKEN_CHARS ];
464
465  // find the player
466  trap_Argv( 1, str, sizeof( str ) );
467  cl = ClientForString( str );
468
469  if( !cl )
470    return;
471
472  // set the team
473  trap_Argv( 2, str, sizeof( str ) );
474  /*SetTeam( &g_entities[cl - level.clients], str );*/
475  //FIXME: tremulise this
476}
477
478/*
479===================
480Svcmd_LayoutSave_f
481
482layoutsave <name>
483===================
484*/
485void  Svcmd_LayoutSave_f( void )
486{
487  char str[ MAX_QPATH ];
488  char str2[ MAX_QPATH - 4 ];
489  char *s;
490  int i = 0;
491
492  if( trap_Argc( ) != 2 )
493  {
494    G_Printf( "usage: layoutsave LAYOUTNAME\n" );
495    return;
496  }
497  trap_Argv( 1, str, sizeof( str ) );
498
499  // sanitize name
500  s = &str[ 0 ];
501  while( *s && i < sizeof( str2 ) - 1 )
502  {
503    if( ( *s >= '0' && *s <= '9' ) ||
504      ( *s >= 'a' && *s <= 'z' ) ||
505      ( *s >= 'A' && *s <= 'Z' ) || *s == '-' || *s == '_' )
506    {
507      str2[ i++ ] = *s;
508      str2[ i ] = '\0';
509    }
510    s++;
511  }
512
513  if( !str2[ 0 ] )
514  {
515    G_Printf("layoutsave: invalid name \"%s\"\n", str );
516    return;
517  }
518
519  G_LayoutSave( str2 );
520}
521
522char  *ConcatArgs( int start );
523
524/*
525===================
526Svcmd_LayoutLoad_f
527
528layoutload [<name> [<name2> [<name3 [...]]]]
529
530This is just a silly alias for doing:
531 set g_layouts "name name2 name3"
532 map_restart
533===================
534*/
535void  Svcmd_LayoutLoad_f( void )
536{
537  char layouts[ MAX_CVAR_VALUE_STRING ];
538  char *s;
539
540  s = ConcatArgs( 1 );
541  Q_strncpyz( layouts, s, sizeof( layouts ) );
542  trap_Cvar_Set( "g_layouts", layouts ); 
543  trap_SendConsoleCommand( EXEC_APPEND, "restartmap\n" );
544  level.restarted = qtrue;
545}
546
547static void Svcmd_AdmitDefeat_f( void )
548{
549  int  team;
550  char teamNum[ 2 ];
551
552  if( trap_Argc( ) != 2 )
553  {
554    G_Printf("admitdefeat: must provide a team\n");
555    return;
556  }
557  trap_Argv( 1, teamNum, sizeof( teamNum ) );
558  team = atoi( teamNum );
559  if( team == PTE_ALIENS || teamNum[ 0 ] == 'a' )
560  {
561    level.surrenderTeam = PTE_ALIENS;
562    G_BaseSelfDestruct( PTE_ALIENS );
563    G_TeamCommand( PTE_ALIENS, "cp \"Hivemind Link Broken\" 1");
564    trap_SendServerCommand( -1, "print \"Alien team has admitted defeat\n\"" );
565  }
566  else if( team == PTE_HUMANS || teamNum[ 0 ] == 'h' )
567  {
568    level.surrenderTeam = PTE_HUMANS;
569    G_BaseSelfDestruct( PTE_HUMANS );
570    G_TeamCommand( PTE_HUMANS, "cp \"Life Support Terminated\" 1");
571    trap_SendServerCommand( -1, "print \"Human team has admitted defeat\n\"" );
572  }
573  else
574  {
575    G_Printf("admitdefeat: invalid team\n");
576  } 
577}
578
579/* 
580=================
581Svcmd_LuaRestart_f
582=================
583*/ 
584static void Svcmd_LuaRestart_f(void)
585{
586  G_ShutdownLua();
587  G_InitLua();
588}
589
590/* 
591=================
592Svcmd_Script_f
593=================
594*/ 
595static void Svcmd_Script_f(void)
596{
597  char filename[128];
598  trap_Argv( 1, filename, 128 );
599  G_LoadLuaScript(NULL, filename);
600}
601
602/*
603=================
604ConsoleCommand
605
606=================
607*/
608qboolean  ConsoleCommand( void )
609{
610  char cmd[ MAX_TOKEN_CHARS ];
611
612  trap_Argv( 0, cmd, sizeof( cmd ) );
613
614  if( Q_stricmp( cmd, "entitylist" ) == 0 )
615  {
616    Svcmd_EntityList_f( );
617    return qtrue;
618  }
619
620  if( Q_stricmp( cmd, "forceteam" ) == 0 )
621  {
622    Svcmd_ForceTeam_f( );
623    return qtrue;
624  }
625
626  if( Q_stricmp( cmd, "game_memory" ) == 0 )
627  {
628    Svcmd_GameMem_f( );
629    return qtrue;
630  }
631
632  if( Q_stricmp( cmd, "addip" ) == 0 )
633  {
634    Svcmd_AddIP_f( );
635    return qtrue;
636  }
637
638  if( Q_stricmp( cmd, "removeip" ) == 0 )
639  {
640    Svcmd_RemoveIP_f( );
641    return qtrue;
642  }
643
644  if( Q_stricmp( cmd, "listip" ) == 0 )
645  {
646    trap_SendConsoleCommand( EXEC_NOW, "g_banIPs\n" );
647    return qtrue;
648  }
649
650  if( Q_stricmp( cmd, "mapRotation" ) == 0 )
651  {
652    char *rotationName = ConcatArgs( 1 );
653
654    if( !G_StartMapRotation( rotationName, qfalse ) )
655      G_Printf( "Can't find map rotation %s\n", rotationName );
656
657    return qtrue;
658  }
659
660  if( Q_stricmp( cmd, "stopMapRotation" ) == 0 )
661  {
662    G_StopMapRotation( );
663
664    return qtrue;
665  }
666
667  if( Q_stricmp( cmd, "advanceMapRotation" ) == 0 )
668  {
669    G_AdvanceMapRotation( );
670
671    return qtrue;
672  }
673
674  if( Q_stricmp( cmd, "alienWin" ) == 0 )
675  {
676    int       i;
677    gentity_t *e;
678
679    for( i = 1, e = g_entities + i; i < level.num_entities; i++, e++ )
680    {
681      if( e->s.modelindex == BA_H_SPAWN )
682        G_Damage( e, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE );
683    }
684
685    return qtrue;
686  }
687
688  if( Q_stricmp( cmd, "humanWin" ) == 0 )
689  {
690    int       i;
691    gentity_t *e;
692
693    for( i = 1, e = g_entities + i; i < level.num_entities; i++, e++ )
694    {
695      if( e->s.modelindex == BA_A_SPAWN )
696        G_Damage( e, NULL, NULL, NULL, NULL, 10000, 0, MOD_SUICIDE );
697    }
698
699    return qtrue;
700  }
701
702  if( !Q_stricmp( cmd, "layoutsave" ) )
703  {
704    Svcmd_LayoutSave_f( );
705    return qtrue;
706  }
707 
708  if( !Q_stricmp( cmd, "layoutload" ) )
709  {
710    Svcmd_LayoutLoad_f( );
711    return qtrue;
712  }
713 
714  if( !Q_stricmp( cmd, "admitdefeat" ) )
715  {
716    Svcmd_AdmitDefeat_f( );
717    return qtrue;
718  }
719
720  if( !Q_stricmp( cmd, "evacuation" ) )
721  {
722    trap_SendServerCommand( -1, "print \"Evacuation ordered\n\"" );
723    level.lastWin = PTE_NONE;
724    trap_SetConfigstring( CS_WINNER, "Evacuation" );
725    LogExit( "Evacuation." );
726    return qtrue;
727  }
728 
729  // see if this is a a admin command
730  if( G_admin_cmd_check( NULL, qfalse ) )
731    return qtrue;
732
733  if( !Q_stricmp(cmd, "restartLuaGameVM") )
734  {
735    Svcmd_LuaRestart_f();
736    return qtrue;
737  }
738  if( !Q_stricmp(cmd, "script") )
739  {
740    Svcmd_Script_f();
741    return qtrue;
742  }
743
744  if( g_dedicated.integer )
745  {
746    if( Q_stricmp( cmd, "say" ) == 0 )
747    {
748      trap_SendServerCommand( -1, va( "print \"server: %s\n\"", ConcatArgs( 1 ) ) );
749      return qtrue;
750    }
751    else if( !Q_stricmp( cmd, "chat" ) )
752    {
753      trap_SendServerCommand( -1, va( "chat \"%s\" -1 0", ConcatArgs( 1 ) ) );
754      G_Printf( "chat: %s\n", ConcatArgs( 1 ) );
755      return qtrue;
756    }
757    else if( !Q_stricmp( cmd, "cp" ) )
758    {
759      trap_SendServerCommand( -1, va( "cp \"%s\"", ConcatArgs( 1 ) ) );
760      G_Printf( "cp: %s\n", ConcatArgs( 1 ) );
761      return qtrue;
762    }
763    else if( !Q_stricmp( cmd, "m" ) )
764    {
765      G_PrivateMessage( NULL );
766      return qtrue;
767    }
768    else if( !Q_stricmp( cmd, "a" ) || !Q_stricmp( cmd, "say_admins" ))
769    {
770      G_Say( NULL, NULL, SAY_ADMINS, ConcatArgs( 1 )  );
771      return qtrue;
772    }
773    G_Printf( "unknown command: %s\n", cmd );
774    return qtrue;
775  }
776
777  return qfalse;
778}
Note: See TracBrowser for help on using the browser.