
   /* AMX Mod script.
   amx_emptyserver.sma            Version: 2.0
   **********************************************************************
   ****
                           AMX-Plugin: Empty Server II
               Empty Server Management & List Maps & MapCycle Generator
                   This file is provided as-is (no waranties).
   Description:
   ------------
       This plugin has 3 functionalities:
       1)  Automatically  changes  map after a specifed time if server is
   empty.
           There are 2 modes of mapchange:
           1.1) Change to amx_nextmap value. (Default)
           1.2)  Change  to  a  random  map  from installed ".bsp" and/or
   ".res" map files.
       2) List all the maps available on the server, not just the maps in
   mapcycle.
           You can use a mask to filter entries.
       3) Generate a mapcycle from all the maps installed on the server.
           Generated  mapcycle  file  will  have  automaticaly randomized
   entries.
           You can use a mask to filter entries.
           You  can  use  a  alternative  filename instead of the default
   mapcycle file.
   Requirements:
   -------------
       OLO's NextMap / NextMap II
       The plugin has been tested with AMX v0.9.3
   Comments:
   ---------
       This code is inspired by/parts taken from (modified) and replaces:
       - JustinHoMi's "empty_server.sma" v0.6
       - OLO's "admin_list_maps.sma" v0.9.3
       - OLO's "mapcycle_generator.sma" v0.1
       When you uncomment the "#define DEBUG_MODE" below,
       the plugin will complie in debug mode (doh).
       While in debug mode actual mapchangeing will not occour.
       You will get some heavy output in server console while building
       the map list.
   Server Config (Cvars):
   ----------------------
       sv_emptyserver_delay  <sec>            Set  the wait time, 0 means
   no auto mapchange.
                                                   (Default        300sec
   (5min))
       sv_emptyserver_realmaps   <flag>    0  =  Use  amx_nextmap  value.
   (Default)
                                                   1 = Use realmap files.
       sv_badmapsfile                     <filename>            (Default:
   "addons/amx/badmaps.ini")
   Console Commands:
   ----------------
       amx_emptyserver                   -   Shows   plugin   help,  with
   additional commands.
       amx_emptyserver  usage  [cmd]   - Show plugin commands or usage of
   command 'cmd'.
       amx_listmaps / listmaps       - Lists available maps on server.
                                                 (For      usage      see
   'amx_emptyserver usage amx_listmaps')
       amx_genmapcycle / genmapcycle - Generates mapcycle.
   Known BUG's:
   ------------
   -    None known at moment.
   Author:    Tripple Moon <tripple_moon_3m@yahoo.com>
   Alias:    Tri Moon (3M)
   Upgrade:    http://amxmod.net/amx-plugins.php?cat=7#80572
   **********************************************************************
   ******/
   /*********************************************************************
   ******
   Plugin Changelog:
   -----------------
   1.0     2003-04-25    3M    Initial release.
   1.1     2003-04-27    3M    Added help and settings console command.
                               Added some status display in console while
   running.
                               Changed delay time to integer number.
                               Modified  auto  mapchange  to  respond  to
   delay 0.
   1.2     2003-04-27    3M    Added a Watchdog.
                               Changed  message headers to reflect plugin
   name.
   1.3     2003-04-29    3M    Changed "amx_emptyserver_realmaps" default
   to "0"
                               Some cosmetic changes.
   1.4     2003-05-02    3M    Added extra check on Default maps.
                               Added sorting maplist.
                               Moved   tempMapname   to   MapName[0]  for
   sorting maplist.
                               Small  bugfix  in Watchdog kicking in with
   delay=0
                               Debugmode  output  to  server  console and
   logs only.
   1.5    2003-05-08    3M    Added info subcommand.
                               Added usage subcommand.
                               Added set subcommand(s).
                               Added BadMaps functionality.
                               Added mask usage on listmaps.
                               Privelidges for commands changed.
                               Created     synonym     "listmaps"     for
   "amx_listmaps".
                               Bugfix:  lastmap in list was not chosen in
   random process.
                               Cvars    renamed    from   "amx_xxxx"   to
   "sv_xxxx".
                               Moved        "amx_listmaps_init"        to
   "amx_emptyserver initmaps"
                               Changed  "sv_emptyserver_delay" from "120"
   to "300"
   2.0    2003-05-16    3M    Changed  flags  for  "sv_badmapsfile"  from
   FCVAR_SERVER to FCVAR_PROTECTED.
                               Created  2 default maps. One for CS1.5 and
   one for CS1.6 which is selectable by a define.
                               Bugfix: mask was limited to 7 chars.
                               Created seperate setmask function.
                               Added MapCycle generation functionality.
   **********************************************************************
   ******/
   #include <amxmod>
   #include <amxmisc>
   //#define DEBUG_MODE
   #define MAX_MAPS 512
   #define MAX_MAPNAME_LENGHT 32
   #define HELP_LIST_AMOUNT 10
   /*
       Use only one, comment-out the unused array !
   */
   // DefaultMaps for CS v1.5
   // DefaultMaps, Total incl. the last "EOT" = 21
   static DefaultMaps[21][MAX_MAPNAME_LENGHT+1] = {
   "de_survivor",
   "de_storm",
   "de_torn",
   "de_aztec",
   "as_oilrig",
   "de_dust2",
   "de_vertigo",
   "cs_siege",
   "de_cbble",
   "de_dust",
   "cs_747",
   "de_vegas",
   "de_prodigy",
   "cs_assault",
   "cs_office",
   "cs_italy",
   "de_inferno",
   "cs_backalley",
   "cs_militia",
   "de_train",
   "EOT"};    // Dont remove the last entry!
   /*
   // DefaultMaps for CS v1.6 STEAM
   // DefaultMaps, Total incl. the last "EOT" = 17
   static DefaultMaps[17][MAX_MAPNAME_LENGHT+1] = {
   "de_airstrip",
   "cs_havana",
   "de_chateau",
   "de_aztec",
   "as_oilrig",
   "cs_siege",
   "de_cbble",
   "de_dust",
   "cs_747",
   "de_prodigy",
   "cs_assault",
   "cs_office",
   "cs_italy",
   "cs_backalley",
   "cs_militia",
   "de_train",
   "EOT"};    // Dont remove the last entry!
   */
   /*    *****    Nothing to edit below here.    *****    */
   #pragma tabsize 3
   //  May'2003:  Plugin auto compiler on website does NOT support macros
   in register_plugin grrrr!!!!
   static    const
               PLUGIN_NAME[]        = "Empty Server II",
               PLUGIN_VERSION[]    = "2.0",
               PLUGIN_AUTHOR[]    = "3M";
   static    Float:WatchdogTime=60.0,
               bool:TaskActive=false,
               bool:UseRealMaps=false,
               Float:EmptyTimeStart=0.0,
               MapName[MAX_MAPS+1][MAX_MAPNAME_LENGHT+1],
               Maps=0,
               text[256],
               LogFile[256],
               BadMapsFile[256];
   enum    MASKTYPE{ UNMASKED, START, END, ANYWHERE };
   static    MASKTYPE:MaskMode,
               mask[MAX_MAPNAME_LENGHT+1];
   // Display Plugin Help.
   static PluginHelp(id,hcmd[]){
       format(    text, 255,
                   " AMX-Plugin: %s v%s by: %s",
                   PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR
       );
       console_print( id, text );
       console_print( id, "============== Plugin Help ==============" );
       console_print( id, "Console commands you can use are:" );
       console_print( id, "%s                   - This text.", hcmd );
       console_print( id, "%s usage [cmd]       - Show available usage on
   plugin commands or usage of command 'cmd'.", hcmd );
       console_print(  id,  "%s  info              - Show plugin info and
   status.", hcmd );
       console_print(  id,  "%s  initmaps          -  (re)initializes the
   maplist.", hcmd );
       console_print(  id,  "%s  [set]  [var  [val]]  - Set plugin var to
   val.", hcmd );
       console_print(  id,  "amx_listmaps  /  listmaps           -  Lists
   available maps on server." );
       console_print(  id, "amx_genmapcycle / genmapcycle     - Generates
   mapcycle.^n" );
   }
   // Display Plugin Info.
   static PluginInfo(id){
       format(    text, 255,
                   " AMX-Plugin: %s v%s by: %s",
                   PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR
       );
       console_print( id, text );
       console_print( id, "============== Plugin Info ==============" );
       console_print( id, "This plugin has 3 functionalities:" );
       console_print(  id, "1) Automatically changes map after a specifed
   time if server is empty." );
       console_print( id, "   There are 2 modes of mapchange:" );
       console_print(  id, "   1.1) Change to a random map from installed
   ^".bsp^" and ^".res^" map files." );
       console_print( id, "   1.2) Change to amx_nextmap value." );
       console_print(  id, "2) List all the maps installed on the server,
   not just the maps in rotation." );
       console_print( id, "   You can use a mask to filter entries." );
       console_print(  id,  "3)  Generate  a  mapcycle  from all the maps
   installed on the server." );
       console_print(   id,   "   Generated   mapcycle   file  will  have
   automaticaly randomized entries." );
       console_print( id, "   You can use a mask to filter entries." );
       console_print(  id, "   You can use a alternative filename instead
   of the default mapcycle file." );
       console_print( id, "^nServer CVars you can use are:" );
       format(    text, 255,
                   "sv_emptyserver_realmaps <1|0>  (Currently: %s)",
                   (get_cvar_num(  "sv_emptyserver_realmaps"  )==0  ?  "0
   [Off]" : "1 [On]")
       );
       console_print( id, text );
       format(    text, 255,
                   "sv_emptyserver_delay  <sec> (Currently: %d) Use ^"0^"
   to disable.",
                   get_cvar_num("sv_emptyserver_delay")
       );
       console_print( id, text );
       // Calculate hrs,mins,sec
       static    GT[4], ET[4];
       GT[0] = floatround( get_gametime() );
       ET[0] = floatround( EmptyTime() );
       GT[3] = GT[0]/3600;                        // hrs
       GT[2] = (GT[0]-GT[3]*3600)/60;        // min
       GT[1] = GT[0]-GT[3]*3600-GT[2]*60;    // sec
       ET[3] = ET[0]/3600;                        // hrs
       ET[2] = (ET[0]-ET[3]*3600)/60;        // min
       ET[1] = ET[0]-ET[3]*3600-ET[2]*60;    // sec
       format(    text, 255,
                   "Gametime: %d:%d'%d^". Empty %d:%d'%d^".",
                   GT[3], GT[2], GT[1],
                   ET[3], ET[2], ET[1]
       );
       console_print( id, text );
       if( get_cvar_num("sv_emptyserver_delay")>0 ){
           format(    text, 255,
                       "%.0f sec remaining for auto mapchange...",
                       get_cvar_float("sv_emptyserver_delay")           -
   EmptyTime()
           );
           console_print( id, text );
       }
   }
   // Display Plugin Command Usage.
   static PluginCmdUsage(id,cmd[]){
       format(    text, 255,
                   " AMX-Plugin: %s v%s by: %s",
                   PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR
       );
       console_print( id, text );
       console_print( id, "========== Plugin Command Usage =========" );
       if( equali(cmd,"badmaps") ){
               console_print(  id,  "This is not actualy a command, but a
   functionality.^n" );
               console_print( id, "If you create a ^"badmaps.ini^" in amx
   folder, it will prevent from changing" );
               console_print(  id,  "to those maps listed. (1 mapname per
   line)" );
               console_print(   id,  "I  created  this  functionality  to
   prevent the plugin from changing to maps" );
               console_print(  id, "that cause the server to crash, i had
   a few of them lol." );
               console_print( id, "When it finds a map in that file while
   atempting to changemaps," );
               console_print(  id,  "it  will  select  a  random map from
   realmaps on server." );
               console_print(  id, "(which is also been checked against)"
   );
       }else
       if( equali(cmd,"amx_listmaps") || equali(cmd,"listmaps") ){
               console_print(  id,  "%s [mask] [start [#entries]] - Lists
   available maps on server.^n", cmd );
               console_print( id, "You can use a mask to filter entries."
   );
               console_print(  id,  "Below  are  some  examples,^na = Not
   using a mask.^nb = Using a mask.^n" );
               console_print(  id,  "a)  %s           -  List all maps %d
   entries per page.", cmd, HELP_LIST_AMOUNT );
               console_print(  id, "   %s 10        - Same, starting from
   entry #10.", cmd );
               console_print( id, "   %s 10 20     - Same, 20 entries per
   page.^n", cmd );
               console_print(  id,  "b)  %s  *         - List all maps %d
   entries per page.", cmd, HELP_LIST_AMOUNT );
               console_print(  id,  "   %s de*       - List maps starting
   with ^"de^" %d entries per page.", cmd, HELP_LIST_AMOUNT );
               console_print( id, "   %s *747*     - List maps containing
   ^"747^" %d entries per page.", cmd, HELP_LIST_AMOUNT );
               console_print(    id,   "   %s   *dust*    -   Same   with
   ^"dust^".", cmd );
               console_print( id, "   %s dust      - Same.", cmd );
               console_print(  id, "   %s dust 7    - Same, starting from
   entry #7.", cmd );
               console_print(  id, "   %s dust 7 5  - Same, 5 entries per
   page.", cmd );
               console_print(  id,  "   %s  *ce       -  List maps ending
   with ^"ce^" %d entries per page.^n", cmd, HELP_LIST_AMOUNT );
               console_print(  id,  "Note:  To use a mask starting with a
   number you need to prepend a '*'." );
       }else
       if( equali(cmd,"delay") ){
               console_print(  id,  "[set]  %s  [sec] - Sets delay before
   auto mapchange.", cmd );
               console_print( id, "Use ^"0^" to disable.^n" );
               console_print(         id,         "Currently:        %d",
   get_cvar_num("sv_emptyserver_delay") );
       }else
       if( equali(cmd,"realmaps") ){
               console_print(   id,  "[set]  %s  [flag]  -  Changes  auto
   mapchange behaviour.", cmd );
               console_print( id, "[flag] = 'on' or 'off'^n" );
               console_print(   id,   "'off'  =  Use  amx_nextmap  value.
   (Default)" );
               console_print( id, "'on' = Use realmap files." );
               console_print(         id,         "Currently:        %s",
   (get_cvar_num("sv_emptyserver_realmaps")==0 ? "Off" : "On") );
       }else
       if( equali(cmd,"amx_genmapcycle") || equali(cmd,"genmapcycle") ){
               get_cvar_string( "mapcyclefile", text, 255 );
               console_print(  id,  "%s  <mask>  [filename]  -  Generates
   mapcycle.", cmd );
               console_print(  id,  "Generated  mapcycle  file  will have
   automaticaly randomized entries." );
               console_print( id, "You can use a mask to filter entries."
   );
               console_print(  id,  "You can use a filename to write to a
   alternate filename.^n" );
               console_print( id, "%s                         - Generates
   ^'%s^' with all maps.", cmd, text );
               console_print(  id,  "%s *                       - Same.",
   cmd );
               console_print(  id,  "%s  *  all_maps.txt          - Same,
   write to ^'all_maps.txt^'.", cmd );
               console_print( id, "%s *747*                   - Generates
   ^'%s^' with maps containing ^"747^".", cmd, text );
               console_print( id, "%s *dust*                  - Same with
   ^"dust^".", cmd );
               console_print(  id,  "%s dust                    - Same.",
   cmd );
               console_print(  id,  "%s  dust  dust_maps.txt      - Same,
   write to ^'dust_maps.txt^'.", cmd );
               console_print( id, "%s de*                     - Generates
   ^'%s^' with maps starting with ^"de^".", cmd, text );
               console_print(  id,  "%s  de*  de_maps.txt         - Same,
   write to ^'de_maps.txt^'.", cmd );
               console_print( id, "%s *custom                 - Generates
   ^'%s^' with maps ending with ^"custom^".", cmd, text );
               console_print(  id,  "%s  *custom  custom_maps.txt - Same,
   write to ^'custom_maps.txt^'.", cmd );
       }else
       {
               console_print(  id, "Additional Usage info is available on
   the following:" );
               console_print( id, "- badmaps" );
               console_print( id, "- delay" );
               console_print( id, "- realmaps" );
               console_print( id, "- amx_listmaps / listmaps" );
               console_print( id, "- amx_genmapcycle / genmapcycle" );
       }
   }
   // Set Plugin variables.
   static PluginSetVar(id,var[],val[]){
       // Check access privelidges.
       if ( !(get_user_flags(id) &
   (ADMIN_MAP|ADMIN_CVAR|ADMIN_CFG|ADMIN_RCON) ) ){
           console_print(id,"You have no access to that command")
           return;
       }
       if( equal(val,"") ){
               console_print(   id,  "[%s]  That  command  needs  another
   argument.", PLUGIN_NAME);
               return;
       }
       if( equali(var,"delay") ){
               if( strtonum(val)>=0 ){
                   if( strtonum(val)==0 ){
                       format(text,    255,    "[%s]    Disabling    auto
   mapchanges...", PLUGIN_NAME);
                   }else{
                       format(text,  255,  "[%s]  Changing auto mapchange
   delay to: %d seconds.", PLUGIN_NAME, strtonum(val));
                   }
                   console_print( id, text );
                   log_to_file( LogFile, text );
                   set_cvar_num("sv_emptyserver_delay", strtonum(val));
                   Upd_LastDisConnect();
               }else
                   console_print(  id, "[%s] You can't use negative delay
   for auto mapchanges.", PLUGIN_NAME);
       }else
       if( equali(var,"realmaps") ){
               if( equali(val,"on") ){
                   format(text,  255,  "[%s] Using realmap files for auto
   mapchanges.", PLUGIN_NAME);
                   console_print( id, text );
                   log_to_file( LogFile, text );
                   set_cvar_num("sv_emptyserver_realmaps",1);
               }else if( equali(val,"off") ){
                   format(text,  255,  "[%s]  Using amx_nextmap value for
   auto mapchanges.", PLUGIN_NAME);
                   console_print( id, text );
                   log_to_file( LogFile, text );
                   set_cvar_num("sv_emptyserver_realmaps",0);
               }else
                   console_print( id, "[%s] Only 'on' or 'off' allowed.",
   PLUGIN_NAME);
       }else
       {
               console_print(  id,  "[%s] Unknown plugin variable: '%s'",
   PLUGIN_NAME, var);
       }
   }
   // Initialize the Maps array.
   static InitMaps(id){
       new    nLen,
               fIndex,
               bool:found,
               loop;
       // Check access privelidges.
       if ( !(get_user_flags(id) &
   (ADMIN_MAP|ADMIN_CVAR|ADMIN_CFG|ADMIN_RCON) ) ){
           console_print(id,"You have no access to that command")
           return;
       }
       // Insert the DefaultMaps.
       fIndex=0;
       Maps=0;
       while( !equali(DefaultMaps[fIndex], "EOT") && Maps <= MAX_MAPS ){
           if( is_map_valid( DefaultMaps[fIndex] ) ){
               copy(         MapName[++Maps],         MAX_MAPNAME_LENGHT,
   DefaultMaps[fIndex] );
   #if defined(DEBUG_MODE)
               format(text,  255,  "[%s] Inserted #%d: %s into maplist.",
   PLUGIN_NAME, Maps, MapName[Maps] );
               server_print( text );
               log_to_file( LogFile, text );
           }else{
               format(text, 255, "[%s] Skipped invalid defaultmap #%d: %s
   !", PLUGIN_NAME, fIndex, DefaultMaps[fIndex] );
               server_print( text );
               log_to_file( LogFile, text );
   #endif
           }
           fIndex++;
       }
       // Insert the maps in directory.
       fIndex=0;
       while(    Maps < MAX_MAPS
                   &&    read_dir(    "maps",    fIndex++,    MapName[0],
   MAX_MAPNAME_LENGHT, nLen)
       ){
           // Check for map/resource files only.
           if(    nLen > 4
                   && (
                           containi( MapName[0], ".bsp" ) >0
                           || containi( MapName[0], ".res" ) >0
                   )
           ){
               // Check to see if already in array.
               loop=0;
               do{
                   loop++;
                   found = equali( MapName[loop], MapName[0], nLen - 4) ?
   true : false;
               }while( !found && loop<Maps )
               // Insert into Map Array if not found.
               if( !found ){
                   copy( MapName[Maps+1], nLen - 4, MapName[0] );
                   if( is_map_valid( MapName[Maps+1] ) ){
                       Maps++;
   #if defined(DEBUG_MODE)
                       format(text,  255, "[%s] Inserted #%d: %s(%s) into
   maplist.", PLUGIN_NAME, Maps, MapName[Maps], MapName[0] );
                       server_print( text );
                       log_to_file( LogFile, text );
   #endif
                   }
   #if defined(DEBUG_MODE)
                   else{
                       format(text,   255,   "[%s]  Skipped  invalid  map
   %s(%s).", PLUGIN_NAME, MapName[Maps+1], MapName[0] );
                       server_print( text );
                       log_to_file( LogFile, text );
                   }
               }else{
                   format(text,   255,   "[%s]   Skipped   existing   map
   #%d(%s).", PLUGIN_NAME, loop, MapName[0] );
                   server_print( text );
                   log_to_file( LogFile, text );
   #endif
               }
           }
       }
       format(text,  255,  "[%s]  Initialized  maplist  with  %d  maps.",
   PLUGIN_NAME, Maps );
       console_print( id, text );
   #if defined(DEBUG_MODE)
       log_to_file( LogFile, text );
       format(text,   255,   "[%s]   Sorting   maplist  with  %d  maps.",
   PLUGIN_NAME, Maps );
       server_print( text );
       log_to_file( LogFile, text );
   #endif
       SortMapList();
       format(text,  255,  "[%s]  Done  sorting  maplist  with %d maps.",
   PLUGIN_NAME, Maps );
       console_print( id, text );
   #if defined(DEBUG_MODE)
       log_to_file( LogFile, text );
   #endif
   }
   //    Sort  our  double  dimensioned string array using a "BubbleSort"
   algorithem.
   //    We use element [0] as swap area.
   static SortMapList(){
       new element, pos, swap;
       element=1;
       while(element<Maps){
           pos=0;
           swap = false;
           while( !swap && pos < MAX_MAPNAME_LENGHT){
               if( MapName[element][pos] > MapName[element+1][pos] ){
                   swap = true;
                   break;
               }
               if( MapName[element][pos] == MapName[element+1][pos] )
                   pos++;
               else
                   break;
           }
           if(swap){
   #if defined(DEBUG_MODE)
               format(    text, 255, "[%s] Swaping (%d)%s with (%d)%s.",
                           PLUGIN_NAME,
                           element, MapName[element],
                           element+1, MapName[element+1]
               );
               server_print( text );
               log_to_file( LogFile, text );
   #endif
               copy( MapName[0], MAX_MAPNAME_LENGHT, MapName[element] );
               copy(         MapName[element],        MAX_MAPNAME_LENGHT,
   MapName[element+1] );
               copy(  MapName[element+1],  MAX_MAPNAME_LENGHT, MapName[0]
   );
               if(element>1)
                   element--; // goback one element.
           }else
               element++; // Goto next element.
       }
   }
   static Upd_LastDisConnect(){
       new Float:Delay = get_cvar_float( "sv_emptyserver_delay" );
       EmptyTimeStart = get_gametime();
       if( Delay != 0 ){
           format(    text, 255,
                       "[%s]  Started  automapchange due to zero clients.
   Delay %d seconds.",
                       PLUGIN_NAME,
                       get_cvar_num( "sv_emptyserver_delay" )
           );
           server_print( text );
   #if defined(DEBUG_MODE)
           log_to_file( LogFile, text );
   #endif
           set_task( Delay, "change_map_3m", 1 );
           TaskActive = true;
       }
   }
   // Calculate Server Idle/Empty time.
   static Float:EmptyTime(){
       new Float:ret_value;
       if( get_cvar_num("sv_emptyserver_delay")>0 && TaskActive)
           ret_value = get_gametime() - EmptyTimeStart;
       else
           ret_value = Float:0;
       return ret_value;
   }
   // Return a portion of s, starting from pos[n1] and ending on pos[n2],
   in d with a max of len characters.
   static substr(d[], const len, const s[], const n1, const n2){
       static    loop;
       // Give runtime errors when these are not true.
       assert n1>=0
       assert n2>=n1
       for(loop=0; s[loop+n1] && loop+n1 <= n2 && loop<=len; loop++)
           d[loop] = s[loop+n1];
       d[loop]=0;    // Terminate string.
   }
   // Set mask and maskmode
   static setmask(s[]){
       static    tmpbuf[MAX_MAPNAME_LENGHT+1],
                   start,
                   end;
       MaskMode  =  MASKTYPE:ANYWHERE;        // Use maskmode "*mask*" as
   default.
       copy(mask, MAX_MAPNAME_LENGHT, s);
       if( contain(mask,".") ){    // Cut from '.' and further.
           copyc(tmpbuf, MAX_MAPNAME_LENGHT, mask, '.');
           copy(mask, MAX_MAPNAME_LENGHT, tmpbuf);
       }
       if( mask[0]==0 || equal(mask,"*") )        // empty string left or
   '*' means no mask.
           MaskMode = MASKTYPE:UNMASKED;
       else if( contain(mask,"*")!=-1 ){
           // Find last '*' at start of mask.
           start = 0;
           while( mask[start]=='*' )
               start++;
           // Find first '*' at end of mask.
           end = start;
           while( mask[end] && mask[end]!='*' )
               end++;
           if(start==0){                                    // Has no '*'
   at start.
               MaskMode = MASKTYPE:START;
           }else   if(start  &&  mask[end]=='*'){        //  Has  '*'  at
   start+end
               MaskMode = MASKTYPE:ANYWHERE;
           }else{                                            //  Has  '*'
   at start only.
               MaskMode = MASKTYPE:END;
           }
           // Get clean mask.
           substr(tmpbuf, MAX_MAPNAME_LENGHT, mask, start, end-1);
           copy(mask, MAX_MAPNAME_LENGHT, tmpbuf);
       }
   }
   // Main function
   public cmdEmptyserver(id,level,cid){
       new    hcmd[32],
               hinfo[128],
               hflag,
               args[3][256],
               argc,
               loop;
       // Check access privelidges.
       if ( !(get_user_flags(id) & level) ){
           console_print(id,"You have no access to that command")
           return PLUGIN_HANDLED;
       }
       // Get arguments.
       argc = read_argc()-1; // Command it self counts as 1...
       loop = 0;
       while( loop<=argc && loop<3){
           read_argv(loop+1,args[loop],31);
           loop++
       }
       get_concmd(cid,hcmd,31,hflag,hinfo,127,level);
       // Parse arguments.
       if( argc == 0 )    // No arguments, display usage.
                       PluginHelp(id,hcmd);
       else if( equali( args[0], "usage") )
                       PluginCmdUsage(id,args[1]);
       else if( equali( args[0], "info") )
                       PluginInfo(id);
       else if( equali( args[0], "initmaps") )
                       InitMaps(id);
       else if( equali( args[0], "set") )
                       PluginSetVar(id,args[1],args[2]);
       else if( equali( args[0], "delay") )
                       PluginSetVar(id,"delay",args[1]);
       else if( equali( args[0], "realmaps") )
                       PluginSetVar(id,"realmaps",args[1]);
       return PLUGIN_HANDLED;
   }
   // List Maps
   public cmdListMaps(id,level,cid){
       static    start,
                   tolist,
                   count,
                   loop,
                   bool:showit,
                   curarg, hcmd[32], hinfo[128], hflag;
       get_concmd(cid,hcmd,31,hflag,hinfo,127,level);
       if ( !(get_user_flags(id) & level) ){
           console_print(id,"You have no access to that command")
           return PLUGIN_HANDLED;
       }
       // Get mask.
       curarg=1;
       MaskMode = MASKTYPE:UNMASKED;
       if( read_argc() > curarg ){
           read_argv( curarg, text, 255);
           if( !('0'<=text[0]<='9') ){    // A mask does not start with a
   digit.
               setmask(text);
               curarg++;
           }
       }
       // Get start.
       if( read_argc() > curarg ){
           read_argv( curarg, text, 7);
           start = strtonum(text);
           if( start < 1 )        start = 1;
           if( start > Maps )    start = Maps;
       }else
           start = 1;
       // Get number of entries to list.
       curarg++;
       if( read_argc() > curarg ){
           read_argv( curarg, text, 7);
           tolist = strtonum(text);
       }else
           tolist = HELP_LIST_AMOUNT;
       console_print( id, "----- AMX Maps: -----" );
       for( loop=1,count=0; loop<=Maps; loop++){
           showit=true; // Show unless mask prevents to show.
           switch(MaskMode){
           case (MASKTYPE:START):        if(
   containi(MapName[loop],mask)!=0 )
                                                   showit=false;
           case (MASKTYPE:END):            if(
   containi(MapName[loop],mask)+strlen(mask)!=strlen(MapName[loop]) )
                                                   showit=false;
           case (MASKTYPE:ANYWHERE):    if(
   containi(MapName[loop],mask)==-1 )
                                                   showit=false;
           }
           // Allowed to show
           if( showit ){
               count++;
               // Only show from our starting number tolist entrie.
               if( count>=start && count-start<=tolist-1 )
                   console_print(    id,  "%4d: %s", count, MapName[loop]
   );
           }
       }
       if( count ){
           console_print( id, "----- Entries %i - %i of %i -----", start,
   start+tolist-1>count ? count : start+tolist-1, count );
           new output[256];
           format(    output, 255, "----- Use ^'%s ", hcmd);
           if( MaskMode ){
               switch(MaskMode){
               case  (MASKTYPE:START):        format(  text, 255, "%s* ",
   mask);
               case  (MASKTYPE:END):        format(  text,  255,  "*%s ",
   mask);
               case  (MASKTYPE:ANYWHERE):    format(  text, 255, "*%s* ",
   mask);
               }
               add(    output, 255, text);
           }
           if( start+tolist-1 < count ){
               format( text, 255, "%i^' for more -----", start+tolist);
               add(    output, 255, text);
           }else
               add(    output, 255, "1^' for begin -----");
           console_print( id, output );
       }else
           console_print( id, "----- No Entries found, try again..." );
       return PLUGIN_HANDLED;
   }
   // Generate MapCycle file.
   public cmdGenMapCycle(id,level,cid){
       static    count,
                   loop,
                   bool:showit,
                   MapCycleFile[256],
                   MapNameCycle[MAX_MAPS+1][MAX_MAPNAME_LENGHT+1],
                   curarg, hcmd[32], hinfo[128], hflag;
       get_concmd(cid,hcmd,31,hflag,hinfo,127,level);
       if ( !(get_user_flags(id) & level) ){
           console_print(id,"You have no access to that command")
           return PLUGIN_HANDLED;
       }
       // Get mask.
       curarg=1;
       MaskMode = MASKTYPE:UNMASKED;
       if( read_argc() > curarg ){
           read_argv( curarg, text, 255);
           setmask(text);
       }
       // Get filename
       curarg++;
       if( read_argc() > curarg )
           read_argv( curarg, MapCycleFile, 255);
       else
           get_cvar_string( "mapcyclefile", MapCycleFile, 255 );
       if( file_exists( MapCycleFile ) )
           delete_file( MapCycleFile );
       // Write file
       get_user_name( id, text, 31);
       format(  text,  255,  "//  Mapcycle file ^'%s^' generated by %s.",
   MapCycleFile, text);
       if( write_file( MapCycleFile, text ) == 0 ){
           console_print( id, "Couldn't write to file ^"%s^" !", text );
           return PLUGIN_HANDLED;
       }
       format(   text,255,"//  with  Plugin:  ^'%s^'  v%s",  PLUGIN_NAME,
   PLUGIN_VERSION );
       write_file( MapCycleFile, text );
       switch(MaskMode){
       case  (MASKTYPE:UNMASKED):    format(  text,255,"// using no mask"
   );
       case   (MASKTYPE:START):        format(  text,255,"//  using  mask
   ^'%s*^'", mask );
       case  (MASKTYPE:END):            format(  text,255,"//  using mask
   ^'*%s^'", mask );
       case   (MASKTYPE:ANYWHERE):    format(   text,255,"//  using  mask
   ^'*%s*^'", mask );
       }
       write_file( MapCycleFile, text );
       // Fill MapNameCycle with original MapName entries.
       for( loop=1; loop<=Maps; loop++){
           copy(MapNameCycle[loop], MAX_MAPNAME_LENGHT, MapName[loop]);
       }
       console_print(id, "[%s] Randomizing mapcycle...", PLUGIN_NAME );
       // Randomize the entries in MapNameCycle
       loop=1;
       do{
           new entry1,entry2;
           entry1 = random_num(1,Maps);
           do{    entry2 = random_num(1,Maps);
           }while(entry1==entry2);
           copy(MapNameCycle[0],                      MAX_MAPNAME_LENGHT,
   MapNameCycle[entry2]);
           copy(MapNameCycle[entry2],                 MAX_MAPNAME_LENGHT,
   MapNameCycle[entry1]);
           copy(MapNameCycle[entry1],                 MAX_MAPNAME_LENGHT,
   MapNameCycle[0]);
           loop++;
       }while(loop<=Maps*1000);// To get a good shuffle...
       // Fill file
       for( loop=1,count=0; loop<=Maps; loop++){
           showit=true; // Show unless mask prevents to show.
           switch(MaskMode){
           case (MASKTYPE:START):        if(
   containi(MapNameCycle[loop],mask)!=0 )
                                                   showit=false;
           case (MASKTYPE:END):            if(
   containi(MapNameCycle[loop],mask)+strlen(mask)!=strlen(MapNameCycle[lo
   op]) )
                                                   showit=false;
           case (MASKTYPE:ANYWHERE):    if(
   containi(MapNameCycle[loop],mask)==-1 )
                                                   showit=false;
           }
           // Allowed to show
           if( showit ){
               count++;
               write_file( MapCycleFile, MapNameCycle[loop] );
           }
       }
       console_print(id,  "[%s]  Generated  mapcycle  file ^'%s^' with %d
   entries.", PLUGIN_NAME, MapCycleFile, count );
       return PLUGIN_HANDLED;
   }
   public client_connect(id){
   #if defined(DEBUG_MODE)
       format(text, 255, "[%s] Client connection. %d Players total now.",
   PLUGIN_NAME, check_players());
       server_print( text );
   #endif
       if(TaskActive){
           remove_task(1);
           TaskActive = false;
           if( check_players()==1 ){
               format(text,  255,  "[%s]  Stopped  automapchange  due  to
   client connection.", PLUGIN_NAME );
               server_print( text );
           }
       }
       return PLUGIN_CONTINUE;
   }
   public client_disconnect(id){
   #if defined(DEBUG_MODE)
       format(text,  255,  "[%s]  Client  Disconnection. %d Players total
   now.", PLUGIN_NAME, check_players());
       server_print( text );
   #endif
       if(TaskActive){
           remove_task(1);
           TaskActive = false;
       }
       if( check_players()==0 )
           Upd_LastDisConnect();
       return PLUGIN_CONTINUE;
   }
   // Plugin Watchdog.
   // Needs to be public to be used as a task.
   // Special name to prevent name clashes in other amx plugins!
   public watchdog_3m(){
       if( !TaskActive && check_players()==0 &&
   get_cvar_num("sv_emptyserver_delay") ){
           format(text, 255, "[%s] Watchdog kicked in.", PLUGIN_NAME);
           server_print( text );
   #if defined(DEBUG_MODE)
           log_to_file( LogFile, text );
   #endif
           Upd_LastDisConnect();
       }
       return PLUGIN_CONTINUE;
   }
   public check_players(){
       new    curplayers,
               player_list[32],
               non_players;
       // Get players incl. connecting.
       curplayers = get_playersnum(1);
       // Get number of NON-Real player. fe. bots.
       get_players( player_list, non_players, "d" );
   #if defined(DEBUG_MODE)
       format(    text, 255,
                   "[%s] Total clients %d (%d non players).",
                   PLUGIN_NAME,
                   curplayers,
                   non_players
       );
       server_print( text );
       log_to_file( LogFile, text );
   #endif
       return (curplayers - non_players);
   }
   // Needs to be public to be used as a task.
   // Special name to prevent name clashes in other amx plugins!
   public change_map_3m(){
       new rand_num;
       if( check_players() == 0
           && get_cvar_num( "sv_emptyserver_delay" ) >0
           && EmptyTime() >= get_cvar_float( "sv_emptyserver_delay" )
       ){
           UseRealMaps  = (get_cvar_num( "sv_emptyserver_realmaps" )==0 ?
   false : true);
           if( UseRealMaps ){
               // Get current mapname
               get_mapname(MapName[0],MAX_MAPNAME_LENGHT);

               // Make sure you dont select the same map as current one.
               do{
                   rand_num = random_num( 1, Maps );
               }while(
                   equali( MapName[0], MapName[rand_num] )
               )
               copy( MapName[0], MAX_MAPNAME_LENGHT, MapName[rand_num] );
           }else{
               // Get nextmap name
               get_cvar_string(         "amx_nextmap",        MapName[0],
   MAX_MAPNAME_LENGHT );
           }
   // Check BadMaps, for maps that make the server crash...
           if( is_badmap(MapName[0]) ){
               // Notify operator.
               format(text,  255,  "[%s] nextmap %s was in BadMaps list",
   PLUGIN_NAME, MapName[0] );
               server_print( text );
               log_to_file( LogFile, text );
               do{
                   rand_num = random_num( 1, Maps );
               }while( is_badmap( MapName[rand_num] ) )
               copy( MapName[0], MAX_MAPNAME_LENGHT, MapName[rand_num] );
           }
           // Notify operator.
           format(text,  255,  "[%s]  Server Empty time reached, changing
   map to: %s", PLUGIN_NAME, MapName[0] );
           server_print( text );
           log_to_file( LogFile, text );
           // Set nextmap name.
           set_cvar_string( "amx_nextmap", MapName[0] );
   #if !defined(DEBUG_MODE)
           // Make the mapchange.
           server_cmd( "changelevel %s", MapName[0] );
   #endif
       }
   }
   // Check if mapname in arg is in BadMapsFile.
   // Function returns 0(false) if not found.
   public bool:is_badmap(MapToCheck[]){
       new    bool:found,
               line_nr,
               line[256],
               line_lenght,
               tempMapname[MAX_MAPNAME_LENGHT+1];
       get_cvar_string( "sv_badmapsfile", BadMapsFile, 255);
       if( !file_exists(BadMapsFile) )    return false;
       line_nr=0;
       do{
           line_nr   =   read_file(   BadMapsFile,  line_nr,  line,  255,
   line_lenght)
           if( line_nr>0 ){
               if( line[0] == ';' || line[0] == '#' )
                   continue;    // Skip comment lines.
               copyc( tempMapname, MAX_MAPNAME_LENGHT, line, ' ' );
               found = equali( tempMapname, MapToCheck ) ? true : false;
           }
       }while( !found && line_nr>0)
       return found;
   }
   public plugin_init(){
   //  May'2003:  Plugin auto compiler on website does NOT support macros
   in register_plugin grrrr!!!!
       register_plugin("Empty Server II","2.0","3M");
   //    register_plugin( PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_AUTHOR );
       register_concmd(    "amx_emptyserver","cmdEmptyserver",_,"-  Shows
   plugin help, with additional commands.");
       register_concmd(    "amx_listmaps","cmdListMaps",_,"[mask]  [start
   [#entries]]   - Lists available maps on server.");
       register_concmd(    "listmaps","cmdListMaps",_,"[mask]      [start
   [#entries]]   - Lists available maps on server.");
       register_concmd(    "amx_genmapcycle","cmdGenMapCycle",ADMIN_CFG|A
   DMIN_RCON,"<mask> [filename] - Generates mapcycle.");
       register_srvcmd(    "genmapcycle","cmdGenMapCycle",_,"<mask>
   [filename] - Generates mapcycle.");
       register_cvar(    "sv_emptyserver_delay",                   "300",
   FCVAR_SERVER|FCVAR_SPONLY );
       register_cvar(    "sv_emptyserver_realmaps",                  "0",
   FCVAR_SERVER|FCVAR_SPONLY );
       register_cvar(    "sv_badmapsfile",      "addons/amx/badmaps.ini",
   FCVAR_PROTECTED|FCVAR_SPONLY );
       server_cmd("amx_emptyserver initmaps");
       Upd_LastDisConnect();
       // Work-around for server dropped clients.
       set_task( WatchdogTime, "watchdog_3m", 2,"",0, "b" );
       // Use only one !
       LogFile="addons/amx/admin.log";
   //    get_time("addons/amx/logs/admin%m%d.log",LogFile,255)
       get_cvar_string( "sv_badmapsfile", BadMapsFile, 255);
       return PLUGIN_CONTINUE;
   }
