/*********************************************************
 *      Bugblatter Name Management Extension - V3.3      *
 *********************************************************
 *                                                       *
 * This plug-in is linux & win32 friendly.               *
 *                                                       *
 * Version history                                       *
 *                                                       *
 * Version 3.3:                                          *
 *                                                       *
 *  - Removed warnings compiling on Admin Mod 2.50.56    *
 *                                                       *
 * Version 3.2:                                          *
 *  - Updated for language plugin version 3.3            *
 *                                                       *
 * Version 3.1:                                          *
 *  -  Added NSPlayer to default banned names            *
 *                                                       *
 * Version 2.6:                                          *
 *                                                       *
 *  -  Added colour and timing settings for all          *
 *     messageex class on 2.51.xx alpha builds.          *
 *  -  Copes with plugin_info firing before              *
 *     plugin_connect                                    *
 *                                                       *
 * Version 2.5:                                          *
 *  -  Removes unrecognised characters. Kicks players    *
 *     with an empty name.                               *
 *                                                       *
 * Version 2.4:                                          *
 *  -  Updated for adminmod 2.50.50                      *
 *  -  Uses bbdir vault setting for file locations, then *
 *     modfolder/addons/adminmod/config then mod folder. *
 *  -  Checks for names ini files on startup             *
 *  -  Does not return PLUGIN_FAILRE during init         *
 *  -  Works around bug in calling admin_command bb,,,   *
 *     from in a .cfg file saved in DOS format           *
 *  -  Move prominent error messages in log              *
 *                                                       *
 * Version 2.3:                                          *
 *  -  Checks values of server.cfg vars during init      *
 *                                                       *
 * Version 2.2:                                          *
 *  - Can now allow a limited number of name changes     *
 *                                                       *
 * Version 2.1:                                          *
 *  - Multi lingual support added                        *
 *  - Updated for adminmod 2.50.37                       *
 *                                                       *
 *                                                       *
 * Version 2.0:                                          *
 *  - Plugin now works alongside cheating-death in       *
 *    optional mode.                                     *
 *  - Some explanation messages were being truncated     *
 *    because string buffers were too small              *
 *                                                       *
 *                                                       *
 * Version 1.9:                                          *
 *  - Plugin is now bot-compatible.                      *
 *  - Report can be turned on and off now                *
 *                                                       *
 *                                                       *
 * Version 1.8:                                          *
 *                                                       *
 *  - Plugin name changed from "Anti-Player".            *
 *  - Minimum name length is now configurable.           *
 *  - Minimum letters length option added.               *
 *  - De-leet function added to attempt to make name     *
 *    legal                                              *
 *  - Plugin can now ban all player name changes         *
 *  - Fixed bug where last lines of badplayernames.ini/  *
 *    newplayernames.ini were ignored.                   *
 *  - Instructions to user on how to change name now     *
 *    specify using setinfo name instead of name.        *
 *  - bbname_report and bbname_reset commands added.     *
 *    Support functions moved from sma file to include   *
 *    files.                                             *
 *  - bbname_lanmask command and lag tasks feature added *
 *  - checks immunity before changing names to prevent   *
 *    an admins name being changed.                      *
 *                                                       *
 *                                                       *
 * Version 1.7:                                          *
 *                                                       *
 *   - No changes                                        *
 *                                                       *
 * Version 1.6:                                          *
 *                                                       *
 *   - Now picks names in a random order                 *
 *                                                       *
 * Version 1.5 - Initial Release                         *
 *                                                       *
 *********************************************************/
 
#include <core>
#include <console>
#include <string>
#include <admin>
#include <adminlib>
#include <plugin>
 
#include "settings"
#include "leet"
#include "clientio"
#include "ip"
#include "language"
#include "stringx"
#include "connections"
 
new g_Version[]="3.3";
 
#define DEFAULT_RENAME 1
#define MIN_RENAME 0
#define MAX_RENAME 1
 
#define DEFAULT_CHANGES -1
#define MIN_CHANGES -1
#define MAX_CHANGES 1000
 
#define DEFAULT_MINLENGTH 3
#define MIN_MINLENGTH 0
#define MAX_MINLENGTH 16
 
#define DEFAULT_MINLETTERS 1
#define MIN_MINLETTERS 0
#define MAX_MINLETTERS 16
 
#define DEFAULT_LANTAGS 1
#define MIN_LANTAGS 0
#define MAX_LANTAGS 1
 
#define DEFAULT_AUTOCHASECAM 1
#define MIN_AUTOCHASECAM 0
#define MAX_AUTOCHASECAM 2
 
#define DEFAULT_REPORT 1
#define MIN_REPORT 0
#define MAX_REPORT 1
 
 
#define DEFAULT_LANTAGMASK -1
 
#define ERROR_RED 255
#define ERROR_GREEN 10
#define ERROR_BLUE 10
 
 
new g_MSG_NOCHANGES[]="You are not allowed to change your name mid-game on this server.|Vous n'avez pas le droit de changer votre nom au milieu du jeu sur ce serveur.|Namenswechsel ist waehrend des Spiels auf diesem Server verboten";
new g_MSG_CHANGELIMIT[]="You are only allowed to change your name %1i times mid-game on this server.|Du darfst deinen Nick auf diesem Server nur %1i mal mitten im Spiel wechseln.";
new g_MSG_REPORT_TITLE[]="Bugblatter Name Management configuration:|Configuration du management de nom Bugblatter:|Bugblatter Name-Management Konfiguration:";
new g_MSG_RPT_RENAME_ON[]=" - Players with ^"bad^" names will be renamed.(bbname_rename)| - Les joueurs avec les noms ^"bad^" seront renommes.(bbname_rename)| - Spieler mit ^"boesen^" Namen werden umbenannt.(bbname_rename)";
new g_MSG_RPT_RENAME_OFF[]=" - Players with ^"bad^" names will NOT be renamed.(bbname_rename)|- Les joueurs avec les noms ^"bad^" ne seront pas renommes.(bbname_rename)| - Spieler mit ^"boesen^" Namen werden NICHT umbenannt.(bbname_rename)";
new g_MSG_RPT_CHANGE_ON[]=" - Players are allowed to change their name while connected.(bbname_changes)| - Les joueurs peuvent changer leur nom quand ils se connectent.(bbname_changes)| - Namenswechsel waehrend der Verbindung ist erlaubt.(bbname_changes)";
new g_MSG_RPT_CHANGE_OFF[]=" - Players are NOT allowed to change their name while connected.(bbname_changes)| - Les joueurs ne peuvent pas changer leur nom quand ils se connectent.(bbname_changes)| - Namenswechsel waehrend der Verbindung ist NICHT erlaubt.(bbname_changes)";
new g_MSG_RPT_CHANGE_NUM[]=" - Players are allowed to change their name %1i times while connected.(bbname_changes)| - Spieler duerfen nach dem Connect ihren Nick %1i mal wechseln. (bbname_changes)";
new g_MSG_RPT_IPSUBNET[]=" - IP address for local subnet is %1s (bbname_lantagmask)| - L'adresse IP du subnet local est %1s (bbname_lantagmask)| - IP Adresse des lokalen Subnets ist %1s (bbname_lantagmask)";
new g_MSG_RPT_LANTAG_ON[]=" - Players names are changed to include Lan Tags.(bbname_langtags)| - Les noms des joueurs sont modifies pour inclure Lan Tags.(bbname_lantags) | - Lan-Tags werden in die Namen eingefuegt.(bbname_lantags)";
new g_MSG_RPT_LANTAG_OFF[]=" - Players names are NOT changed to include Lan Tags.(bbname_lantags)| - Les noms des joueurs ne sont pas modifies pour inclure Lan Tags.(bbname_lantags)| - Lan-Tags werden NICHT in die Namen eingefuegt.(bbname_lantags)";
new g_MSG_RPT_CHASE[]=" - Automatic control of forced chase camera is set to %1i.(bbname_autochasecam)| - Le controle automatique du suivi force de la camera est regle a %1i.(bbname_autochasecam)| - Automatische Anpassung von forced-chase-camera ist auf %1i gesetzt.(bbname_autochasecam)";
new g_MSG_RPT_MINLENGTH[]=" - Minimum player name length: %1i characters.(bbname_minlength)| - Longueur minimum du nom du joueur : %1i caracteres.(bbname_minlength)| - Minimale Namenslaenge: %1i Zeichen.(bbname_minlength)";
new g_MSG_RPT_MINCONSEC[]=" - Player names must have %1i consecutive letters.(bbname_minletters)| - Les noms des joueurs doivent contenir %1i lettres consecutives.(bbname_minletters)| - Namen muessen %1i aufeinander folgende Buchstaben beinhalten.(bbname_minletters)";
new g_MSG_RPT_WARN[]="WARNING: bbname_minlettters is larger than bbname_minlength - no names are valid!|ATTENTION: bbname_minletters est plus grand que bb_name_minlength - aucun nom n'est valide!|ACHTUNG: bbname_minlettters ist groesser als bbname_minlength - alle Namen sind ungueltig!";
new g_MSG_LIST_TITLE[]="ID: User Name  -  Original Name  -  IP  -  Lan|ID: Nom d'utilisateur  -  Nom Original  -  IP  -  Lan|ID: User Name  -  Original Name  -  IP  -  Lan";
 
new g_MSG_LANWARN[]="WARNING: Players marked ^"Ln:^" may be able to see each others screens.|ATTENTION: Les joueurs qui ont ^"Ln:^" peuvent voir chacun des autres ecrans.|ACHTUNG: Mit ^"Ln:^" markierte Spieler koennten gegenseitig die Bildschirme sehen.";
 
new g_MSG_FREELOOK_OFF[]="Free-look when dead has been disabled as some players are on a LAN|La vue libre a ete desactivee comme certaines personnes sont en LAN|Free-Look der Toten wurde deaktiviert, da manche Spieler im selben LAN sind.";
new g_MSG_FREELOOK_ON[]="Free-look when dead is now enabled|La vue libre est maintenant activee|Free-Look der Toten ist jetzt aktiviert.";
new g_MSG_NAMELENGTH[]="Your name must be at least %1i characters and has been changed to: %2s.|Votre nom doit contenir au moins %1i caracteres et a ete changee en: %2s|Dein Name muss mindestens %1i Zeichen enthalten und wurde geaendert auf: %2s.";
new g_MSG_NAMELETTERS[]="Your name must have %1i consecutive letters, and has been changed to: %2s.|Votre nom doit avoir %1i lettres consecutives, et a ete changee en: %2s|Dein Name muss %1i aufeinander folgende Buchstaben enthalten, und wurde geaendert auf: %2s";
new g_MSG_NAMELAN_ON[]="Your name has been changed because the server believes you might be able to see another player's screen.|Votre nom a ete change car le serveur pense que vous pouvez voir les ecrans des autres joueurs.|Dein Name wurde geaendert, da der Server glaubt, Du kannst evtl. den Bildschirm eines anderen Spieler sehen.";
new g_MSG_NAMELAN_OFF[]="Your name has been restored because the other player on your LAN has left.|Votre nom a ete retabli car l'autre joueur de votre LAN est parti.|Dein Name wurde wieder hergestellt, da der andere Spieler in deinem LAN gegangen ist";
new g_MSG_NAMEBANNED[]="The name %1s is not  allowed on this server. You name has been changed to: %2s|Le nom %1s n'est pas  autorise sur ce serveur. Votre nom a ete change en: %2s|Der Name %1s ist nicht erlaubt auf diesem Server. Dein Name wurde geaendert auf: %2s";
new g_MSG_HOWTOCHANGE[]="Type the following in this console to change your name:|Tapez ceci dans votre console pour changer votre nom:|Gib folgendes in diese Konsole ein um Deinen Namen zu aendern:";
 
 
 
 
/* Configuration variables */
new g_Rename=DEFAULT_RENAME;                            /* Should players be renamed? 1 = yes */
new g_Changes=DEFAULT_CHANGES;                          /* Should plauers be allowed to rename themselves? 1 = yes */
new g_MinLength=DEFAULT_MINLENGTH;                      /* Minimum allowable name length */
new g_MinLetters=DEFAULT_MINLETTERS;                    /* Minimum allowable sequence of consecutive letters */
new g_Report=DEFAULT_REPORT;                            /* Automatically show report after command? */
new g_LanTagMask=DEFAULT_LANTAGMASK;                    /* Network mask multiple players must match to be on same lan */
new g_LanTags = DEFAULT_LANTAGS;                        /* Enables / Disables updating names with lan tags */
new g_AutoChaseCam = DEFAULT_AUTOCHASECAM;              /* Enabler automatic control of forcechasecam */
 
/* Status variables */
new g_OriginalNames[MAX_PLAYERS+1][MAX_NAME_LENGTH];    /* Names players joined with - sometimes updated though */
new g_PermitNames[MAX_PLAYERS+1][MAX_NAME_LENGTH];      /* Names players can change to - prevents feedback loops */
new g_IP[MAX_PLAYERS+1];                                /* IP address of players */
new g_Lan[MAX_PLAYERS+1];                               /* Lan tag number of players */
new g_LogdDetected=0;                                   /* True if logd found on server */
new g_WaitForTeam[MAX_PLAYERS+1];                       /* Indicates a player name should not change until they join a team */
new g_IgnoreNext[MAX_PLAYERS+1];                        /* Indicates a player name should not change until they join a team */
new g_LanUsersPresent=0;                                /* Flag to indicate if lan users have been detected */
new g_RecheckTimer=0;                                   /* Timer for checking again if playerinfo fails */
new g_Registered=0;
new g_CheatingDeath=0;                                  /* Cheating death detected */
new g_Changed[MAX_PLAYERS+1];                           /* Number of name changes each player has made */
new g_Allowed[MAX_DATA_LENGTH]="ABCDEFGHIJKLMNOPQRSTUVWXZabcdefghijklmnopqrstuvwxyz1234567890-=:! $%&*()_+{}[]@'#/?,.<>\|^^"; /* Name filter */
 
forward RegisterPlugin();
forward BBLogdWorldAction(HLCommand,HLData,HLUserName,UserIndex);
forward BBLogdTeamSelect(HLCommand,HLData,HLUserName,UserIndex);
forward BBDeleet(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameRename(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameChanges(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameMinLength(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameMinLetters(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameLanTagMask(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameLanTags(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameAutoChaseCam(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameReset(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameReport(HLCommand,HLData,HLUserName,UserIndex);
forward BBNameUsers(HLCommand,HLData,HLUserName,UserIndex);
forward ShowConfig(Force,UserIndex);
forward ShowUsers(UserIndex);
forward FreeLan();
forward LanTagAnnounce();
forward CheckNames();
forward CheckNamesAgain();
forward SetChaseCam(val);
forward CheckPlayerName(OldName[],NewName[],CheckNewNames,UserIndex,DeleetName,&OnLan);
forward ChoosePlayerName(Name[],UserIndex,Reason,OnLan);
forward ConsiderName(OldName[],NewName[],UserIndex,Reason,OnLan);
forward SetPlayerName(Name[],NewName[],UserIndex,Reason,OnLan);
 
/*********************************************************/
/* Standard event handlers for adminmod                  */
/*********************************************************/
 
public plugin_init() {
  plugin_registerinfo("Bugblatter's Name Management plugin","Regulates player naming on your server",g_Version);
 
  /* Check server is configured properly */
  new fOK = checkFileAccessRead();
  fOK = checkAllowClientExec() && fOK;
  fOK = checkVaultOK() && fOK;
  if (fOK == 0) {
    return PLUGIN_CONTINUE;
  }
  /* No need to abort if this one fails */
  checkLanguage();
 
  new NamesFile[MAX_DATA_LENGTH];
  if (getfilelocation(NamesFile,"badplayernames.ini")==0) {
    configError("Unable to find file badplayernames.ini");
    return PLUGIN_CONTINUE;
  }
 
  if (getfilelocation(NamesFile,"newplayernames.ini")==0) {
    configError("Unable to find file newplayernames.ini");
    return PLUGIN_CONTINUE;
  }
 
  readvaultnum("bbname_rename",g_Rename,DEFAULT_RENAME,MIN_RENAME,MAX_RENAME);
  readvaultnum("bbname_changes",g_Changes,DEFAULT_CHANGES,MIN_CHANGES,MAX_CHANGES);
  readvaultnum("bbname_minlength",g_MinLength,DEFAULT_MINLENGTH,MIN_MINLENGTH,MAX_MINLENGTH);
  readvaultnum("bbname_minlettters", g_MinLetters,DEFAULT_MINLETTERS,MIN_MINLETTERS,MAX_MINLETTERS);
  readvaultnum("bbname_lantags", g_LanTags,DEFAULT_LANTAGS,MIN_LANTAGS,MAX_LANTAGS);
  readvaultnum("bbname_autochasecam", g_AutoChaseCam,DEFAULT_AUTOCHASECAM,MIN_AUTOCHASECAM,MAX_AUTOCHASECAM);
  readvaultnum("bbname_report",g_Report,DEFAULT_REPORT,MIN_REPORT,MAX_REPORT);
 
  new Buffer[16]="";
  get_vaultdata("bbname_lantagmask",Buffer,16);
  strtoip(Buffer,g_LanTagMask);
 
  /* Register with logd if its installed. If its not this will
     cause an error in your server logs, but will have no other
     detremental effects
 
   * NOTE: No access level numerically coded because its missing
           from include/admin.inc and I don't want to cause a
           name clash with future releases of include files
  */
 
  plugin_registercmd("bbname_logdwa","BBLogdWorldAction",131072,"");
  exec("logd_reg 62 admin_command bbname_logdwa",0);
  plugin_registercmd("bbname_logdts","BBLogdTeamSelect",131072,"");
  exec("logd_reg 54 admin_command bbname_logdts",0);
 
  new cd[MAX_TEXT_LENGTH];
  if (getstrvar("cdrequired",cd,MAX_TEXT_LENGTH)) {
    g_CheatingDeath = 1;
  }
 
  new i;
  for(i=1;i<=MAX_PLAYERS;i++) {
    g_IP[i]=0;
    g_WaitForTeam[i] = 0;
    g_IgnoreNext[i]= 0;
  }
 
  set_timer("LanTagAnnounce",300,99999);
 
  language_init();
  RegisterPlugin();
 
  return PLUGIN_CONTINUE;
}
 
 
RegisterPlugin() {
  /* Register commands with adminmod */
 
  plugin_registercmd("bbname_rename","BBNameRename",ACCESS_CONFIG,
                     "bbname_rename < ^"on^" | ^"off^" >: Enables automatic renaming of players with invalid names.");
  plugin_registercmd("bbname_changes","BBNameChanges",ACCESS_CONFIG,
           "bbname_changes < ^"on^" | ^"off^" | n >: Enables players to change their name specified number of times.");
  plugin_registercmd("bbname_minlength","BBNameMinLength",ACCESS_CONFIG,
           "bbname_minlength <count>: Sets the minimum permissable name length.");
  plugin_registercmd("bbname_minletters","BBNameMinLetters",ACCESS_CONFIG,
           "bbname_minletters <count>: Sets the minimum permissable length of consecutive letters.");
  plugin_registercmd("bbname_report","BBNameReport",ACCESS_CONFIG,
                     "bbname_report [ ^"on^" | ^"off^" ]: shows the name management config, and enables/disable report after every command");
  plugin_registercmd("bbname_reset","BBNameReset",ACCESS_CONFIG,
           "bbname_reset: Restores all name management settings to defaults.");
  plugin_registercmd("bbname_lantagmask","BBNameLanTagMask",ACCESS_CONFIG,
           "bbname_lantagmask <subnetmask>: Sets the subnet mask multiple players must match to be considered on the same lan.");
  plugin_registercmd("bbname_users","BBNameUsers",ACCESS_CONFIG,
           "bbname_users: Lists users connected to the server with original names, IP addresses and Lan number.");
  plugin_registercmd("bbname_lantags","BBNameLanTags",ACCESS_CONFIG,
         "bbname_lantags <^"on^" | ^"off^">: Enables / disable the use of lan tags.");
  plugin_registercmd("bbname_autochasecam","BBNameAutoChaseCam",ACCESS_CONFIG,
         "bbname_autochasecam < ^"0^" | ^"1^" | ^"2^" >: Enables automatic control of force chase cam.");
  g_Registered=1;
}
 
 
public plugin_connect(HLUserName, HLIP, UserIndex) {
  if (g_Registered ==0) {
    return PLUGIN_FAILURE;
  }
  PlayerConnected(UserIndex);
 
  new Data[MAX_DATA_LENGTH];
  new i;
  safe_convert(HLIP,Data,MAX_DATA_LENGTH);
 
  new UserName[MAX_NAME_LENGTH];
  safe_convert(HLUserName, UserName, MAX_NAME_LENGTH);
 
  strfilter(UserName,g_Allowed);
  if (strlen(UserName)==0) {
    log("Attempting to kick");
    log("Attempting to kick");
    log("Attempting to kick");
    log("Attempting to kick");
    log("Attempting to kick");
    log("Attempting to kick");
    log("Attempting to kick");
    log("Attempting to kick");
 
    new strIndex[10];
    numtostr(UserIndex,strIndex);
    kick(strIndex);
  }
 
  /*new msg[MAX_TEXT_LENGTH];
  snprintf(msg,MAX_TEXT_LENGTH,"%s connected as user %i",UserName,UserIndex);
  log(msg);*/
 
  /*If Logd has been detected, don't change this players
   *name until they have selected a team - this ensures
   *messags are displayed to the player correctly */
  g_WaitForTeam[UserIndex]=g_LogdDetected;
  g_IgnoreNext[UserIndex]=0;
  g_Changed[UserIndex]=0;
 
  if (strtoip(Data,g_IP[UserIndex])) {
    if (g_LanTagMask != 0) {
      for (i=1;i<=MAX_PLAYERS;i++) {
        if ((i != UserIndex) && (g_IP[i] !=0)) {
          if ((g_IP[i] & g_LanTagMask) == (g_IP[UserIndex] & g_LanTagMask)) {
            if (g_Lan[i] == 0) {
              g_Lan[i] = FreeLan();
            }
            g_Lan[UserIndex]=g_Lan[i];
            return PLUGIN_CONTINUE;
          }
        }
      }
    }
  }
 
  return PLUGIN_CONTINUE;
}
 
public plugin_disconnect(HLUserName, UserIndex) {
  if (g_Registered ==0) {
    return PLUGIN_FAILURE;
  }
  PlayerDisconnected(UserIndex);
 
  new UserName[MAX_NAME_LENGTH];
  safe_convert(HLUserName, UserName, MAX_NAME_LENGTH);
  /*new msg[MAX_TEXT_LENGTH];
  snprintf(msg,MAX_TEXT_LENGTH,"%s disconnected as user %i",UserName,UserIndex);
  log(msg);*/
 
 
  g_IP[UserIndex]=0;
  g_Lan[UserIndex]=0;
  g_Changed[UserIndex]=0;
  g_IgnoreNext[UserIndex]= 0;
  set_timer("CheckNames",5,1);
 
  return PLUGIN_CONTINUE;
}
 
public plugin_info(HLOldName, HLNewName, UserIndex) {
  if (g_Registered ==0) {
    return PLUGIN_FAILURE;
  }
 
  if (IsPlayerConnected(UserIndex)==0) {
    return PLUGIN_CONTINUE;
  }
 
  /* Prevent a feedback loop */
  if (g_IgnoreNext[UserIndex] == 1) {
    g_IgnoreNext[UserIndex] = 0;
    return PLUGIN_CONTINUE;
  }
 
  /* Ignore initial plugin_info call that occurs before plugin_connect,
   * or if the player is a bot */
  if (g_IP[UserIndex] == 0) {
    return PLUGIN_CONTINUE;
  }
 
  new NewName[MAX_NAME_LENGTH];
  new OldName[MAX_NAME_LENGTH];
  new Timer=3;
 
  safe_convert(HLNewName, NewName, MAX_NAME_LENGTH);
  safe_convert(HLOldName, OldName, MAX_NAME_LENGTH);
 
 
 
 
  new Command[MAX_DATA_LENGTH];
  new OnLan=0;
 
  if (streq(OldName,NewName)) {
    /* Name hasn't changed - ignore */
    return PLUGIN_CONTINUE;
  }
 
  if (streq(g_PermitNames[UserIndex],NewName)) {
    return PLUGIN_CONTINUE;
  }
 
 
  if (OldName[0] == NULL_CHAR) {
    /* User is setting name during connection */
    g_OriginalNames[UserIndex] = NewName;
    if (g_LogdDetected) {
      /*If Logd has been detected, don't change this players
       *name until they have selected a team - this ensures
       *messags are displayed to the player correctly */
      g_WaitForTeam[UserIndex]=1;
    }
    else {
      Timer=30; /* Delay name checking on player joining so they are more likely to read the message */
    }
  }
  else {
    /* Name change */
    if ((g_Changes>-1) && (g_Changed[UserIndex]>=g_Changes))  {
      /* Stop C-D causing a loop */
      if ((strncmp(OldName,NewName,30)==0)) {
        return PLUGIN_CONTINUE;
      }
 
      if(streq(g_OriginalNames[UserIndex],NewName)==0) {
        if (CheckPlayerName(OldName,OldName,1,UserIndex,0,OnLan)) {
          /* If current name is one of our banned names, or a name
           * we have given the player, allow them to change it */
          g_OriginalNames[UserIndex] = NewName;
        }
        else {
          if (g_Changes == -1) {
            language_say(UserIndex,g_MSG_NOCHANGES,print_type:print_console);
          }
          else {
            language_sayf(UserIndex,g_MSG_CHANGELIMIT,print_type:print_console,0,0,0,0,g_Changes);
          }
          snprintf(Command, MAX_TEXT_LENGTH, "setinfo name ^"%s^"", OldName);
          strcpy(g_PermitNames[UserIndex],OldName,MAX_NAME_LENGTH);
          execclient(OldName, Command);
          return PLUGIN_CONTINUE;
        }
      }
    }
  }
 
  /* If new name is invalid, but old name was valid,
   * change back to old name, rather than to one from newplayernames.ini */
  if (OldName[0] != NULL_CHAR) {
    new Reason=CheckPlayerName(OldName,NewName,0,UserIndex,0,OnLan);
    new Dummy=0;
    if (Reason>=5) {
      /* Only thing wrong with the newname is the lantag is missing */
      SetPlayerName(OldName,NewName,UserIndex,Reason,OnLan);
    }
    else if (Reason>0) {
      if (CheckPlayerName(OldName,OldName,0,UserIndex,0,Dummy)==0) {
        SetPlayerName(OldName,OldName,UserIndex,Reason,OnLan);
      }
    }
    else {
      g_Changed[UserIndex]=g_Changed[UserIndex]+1;
    }
  }
 
  if (g_RecheckTimer==0) {
    set_timer("CheckNames",Timer,1);
  }
 
  return PLUGIN_CONTINUE;
}
 
 
/*********************************************************/
/* LogD command handlers                                 */
/*********************************************************/
public BBLogdWorldAction(HLCommand,HLData,HLUserName,UserIndex) {
  g_LogdDetected =1;
  return PLUGIN_HANDLED;
}
 
public BBLogdTeamSelect(HLCommand,HLData,HLUserName,UserIndex) {
  new Data[MAX_DATA_LENGTH];
  safe_convert(HLData, Data, MAX_DATA_LENGTH);
  new i = strtonum(Data);
 
  if ((i>0) && (i<=MAX_PLAYERS)) {
    if (g_WaitForTeam[i]==1) {
      /*new msg[MAX_TEXT_LENGTH];
      snprintf(msg,MAX_TEXT_LENGTH,"Wait period has expired for user %i",i);
      log(msg);*/
      g_WaitForTeam[i] = 0;
      if (g_RecheckTimer == 0) {
        set_timer("CheckNames",10,1);
      }
    }
  }
 
  return PLUGIN_HANDLED;
}
 
 
/*********************************************************/
/* Command handlers for admin commands                   */
/*********************************************************/
 
public BBDeleet(HLCommand,HLData,HLUserName,UserIndex) {
  new Data[MAX_DATA_LENGTH];
  safe_convert(HLData, Data, MAX_DATA_LENGTH);
  deleet(Data);
  selfmessage(Data);
}
 
public BBNameRename(HLCommand,HLData,HLUserName,UserIndex) {
  if (readHLonoff(HLData,g_Rename,DEFAULT_RENAME,MIN_RENAME,MAX_RENAME)) {
  writevaultnum("bbname_rename",g_Rename);
  }
  CheckNames();
  return ShowConfig(0,UserIndex);
}
 
public BBNameChanges(HLCommand,HLData,HLUserName,UserIndex) {
  if (readHLonoff(HLData,g_Changes,DEFAULT_CHANGES,MIN_CHANGES,MAX_CHANGES,-1)) {
  writevaultnum("bbname_changes",g_Changes);
  }
  CheckNames();
  return ShowConfig(0,UserIndex);
}
 
public BBNameMinLength(HLCommand,HLData,HLUserName,UserIndex) {
  if (readHLnum(HLData,g_MinLength,DEFAULT_MINLENGTH,MIN_MINLENGTH,MAX_MINLENGTH)) {
  writevaultnum("bbname_minlength",g_MinLength);
  }
  CheckNames();
  return ShowConfig(0,UserIndex);
}
 
public BBNameMinLetters(HLCommand,HLData,HLUserName,UserIndex) {
  if (readHLnum(HLData,g_MinLetters,DEFAULT_MINLETTERS,MIN_MINLETTERS,MAX_MINLETTERS)) {
  writevaultnum("bbname_minlettters",g_MinLetters);
  }
  CheckNames();
  return ShowConfig(0,UserIndex);
}
 
public BBNameLanTagMask(HLCommand,HLData,HLUserName,UserIndex) {
 
  new Data[MAX_DATA_LENGTH];
  new i;
  new j;
 
  safe_convert(HLData,Data,MAX_DATA_LENGTH);
 
  if (strtoip(Data,g_LanTagMask)) {
    set_vaultdata("bbname_lantagmask",Data);
 
    /* Recheck all connected players for LANness with new mask */
    for (j=1;j<=MAX_PLAYERS;j++) {
      g_Lan[j] = 0;
    }
 
    for (j=1;j<=MAX_PLAYERS;j++) {
      if (g_IP[j] != 0) {
  for (i=j+1;i<=MAX_PLAYERS;i++) {
    if (g_IP[i] != 0) {
      if ((g_IP[i] & g_LanTagMask) == (g_IP[j] & g_LanTagMask)) {
        if (g_Lan[i] == 0) {
    g_Lan[i] = FreeLan();
        }
        g_Lan[j]=g_Lan[i];
      }
    }
  }
      }
    }
  }
  CheckNames();
  ShowConfig(0,UserIndex);
 
  return PLUGIN_HANDLED;
}
 
public BBNameLanTags(HLCommand,HLData,HLUserName,UserIndex) {
  if (readHLonoff(HLData,g_LanTags,DEFAULT_LANTAGS,MIN_LANTAGS,MAX_LANTAGS)) {
  writevaultnum("bbname_lantags",g_LanTags);
  }
  CheckNames();
  return ShowConfig(0,UserIndex);
}
 
public BBNameAutoChaseCam(HLCommand,HLData,HLUserName,UserIndex) {
  if (readHLnum(HLData,g_AutoChaseCam,DEFAULT_AUTOCHASECAM,MIN_AUTOCHASECAM,MAX_AUTOCHASECAM)) {
  writevaultnum("bbname_autochasecam",g_AutoChaseCam);
  }
  CheckNames();
  return ShowConfig(0,UserIndex);
}
 
public BBNameReset(HLCommand,HLData,HLUserName,UserIndex) {
  g_Rename=DEFAULT_RENAME;
  g_Changes=DEFAULT_CHANGES;
  g_MinLength=DEFAULT_MINLENGTH;
  g_MinLetters=DEFAULT_MINLETTERS;
  g_LanTagMask=DEFAULT_LANTAGMASK;
  g_LanTags=DEFAULT_LANTAGS;
  g_AutoChaseCam=DEFAULT_AUTOCHASECAM;
 
  writevaultnum("bbname_rename",g_Rename);
  writevaultnum("bbname_changes",g_Changes);
  writevaultnum("bbname_minlength",g_MinLength);
  writevaultnum("bbname_minlettters",g_MinLetters);
  writevaultnum("bbname_lantags",g_LanTags);
  writevaultnum("bbname_autochasecam",g_AutoChaseCam);
  set_vaultdata("bbname_lantagmask","0.0.0.0");
  CheckNames();
  return ShowConfig(0,UserIndex);
}
 
 
public BBNameReport(HLCommand,HLData,HLUserName,UserIndex) {
  if (readHLonoff(HLData,g_Report,DEFAULT_REPORT,MIN_REPORT,MAX_REPORT)) {
    writevaultnum("bbname_report",g_Report);
    ShowConfig(0,UserIndex);
  }
  else {
    ShowConfig(1,UserIndex);
  }
  return PLUGIN_HANDLED;
}
 
public BBNameUsers(HLCommand,HLData,HLUserName,UserIndex) {
  return ShowUsers(UserIndex);
}
 
ShowConfig(Force,UserIndex) {
  if ((Force==0) && (g_Report==0)) {
    return PLUGIN_HANDLED;
  }
 
  language_say(UserIndex,g_MSG_REPORT_TITLE,print_type:print_console);
  if (g_Rename) {
    language_say(UserIndex,g_MSG_RPT_RENAME_ON,print_type:print_console);
  }
  else {
    language_say(UserIndex,g_MSG_RPT_RENAME_OFF,print_type:print_console);
  }
 
  if (g_Changes==-1) {
    language_say(UserIndex,g_MSG_RPT_CHANGE_ON,print_type:print_console);
  }
  else if (g_Changes==0) {
    language_say(UserIndex,g_MSG_RPT_CHANGE_OFF,print_type:print_console);
  }
  else {
    language_sayf(UserIndex,g_MSG_RPT_CHANGE_NUM,print_type:print_console,0,0,0,0,g_Changes);
  }
 
 
  new data[MAX_TEXT_LENGTH];
  iptostr(g_LanTagMask,data);
  language_sayf(UserIndex,g_MSG_RPT_IPSUBNET,print_type:print_console,0,0,0,0,data);
 
  if (g_LanTags) {
    language_say(UserIndex,g_MSG_RPT_LANTAG_ON,print_type:print_console);
  }
  else {
    language_say(UserIndex,g_MSG_RPT_LANTAG_OFF,print_type:print_console);
  }
 
  language_sayf(UserIndex,g_MSG_RPT_CHASE,print_type:print_console,0,0,0,0,g_AutoChaseCam);
 
  language_sayf(UserIndex,g_MSG_RPT_MINLENGTH,print_type:print_console,0,0,0,0,g_MinLength);
  language_sayf(UserIndex,g_MSG_RPT_MINCONSEC,print_type:print_console,0,0,0,0,g_MinLetters);
 
  if (g_MinLetters > g_MinLength) {
    language_say(UserIndex,g_MSG_RPT_WARN,print_type:print_console);
  }
 
  return PLUGIN_HANDLED;
}
 
ShowUsers(UserIndex) {
 
  language_say(UserIndex,g_MSG_LIST_TITLE,print_type:print_console);
  new Name[MAX_NAME_LENGTH];
  new strIP[16];
  new msg[150];
  new i;
  new ui;
  new ServerID;
  new c=maxplayercount();
  for (i=1;i<=c;i++) {
    if (playerinfo(i,Name,MAX_NAME_LENGTH,ServerID)) {
      if (get_userindex(Name,ui)) {
  iptostr(g_IP[ui],strIP);
  snprintf(msg,150,"<%i> %s  -  %s  -  %s   -  %i",ServerID,Name,g_OriginalNames[ui],strIP,g_Lan[ui]);
  selfmessage(msg);
      }
    }
  }
  return PLUGIN_HANDLED;
}
 
 
/* Returns the lowest positive integer number that doesn't appear in g_Lan */
FreeLan() {
  new i;
  new r=1;
 
  for(i=1;i<MAX_PLAYERS;i++) {
    if (g_Lan[i]==r) {
      i=0; /* Slightly hacky, but hey */
      r++;
    }
  }
  return r;
}
 
 
public LanTagAnnounce() {
  if (g_LanTags && g_LanUsersPresent) {
    language_sayall(g_MSG_LANWARN,print_type:print_chat);
  }
}
 
/*********************************************************/
/* Functions to perform name analysis                    */
/*********************************************************/
 
public CheckNames()
{
  new c = maxplayercount();
  new Name[MAX_NAME_LENGTH];
  new dummy;
  new i;
  new OnLan;
  new tested=0;
  new AuthID[MAX_AUTHID_LENGTH];
  new wonid=0;
  new dead=0;
  new team=0;
 
  if (g_RecheckTimer) {
    return 0;
  }
 
  new LanUsersPresent = 0;
 
  for(i=1;i<=c;i++) {
    if (playerinfo(i,Name,MAX_NAME_LENGTH,dummy,wonid,team,dead,AuthID)) {
      if ((g_WaitForTeam[i] == 0) && (strlen(AuthID) > 0)) {
  new Reason=CheckPlayerName(Name,Name,0,i,1,OnLan);
 
  if (OnLan) {
    LanUsersPresent = 1;
  }
  if (Reason>=5) {
    SetPlayerName(Name,Name,i,Reason,OnLan);
  }
  else if (Reason > 0) {
      ChoosePlayerName(Name,i,Reason,OnLan);
  }
  tested++;
      }
      else {
  tested++;
      }
    }
  }
 
  g_LanUsersPresent = LanUsersPresent;
 
  if (tested<playercount() || g_CheatingDeath) {
    if (tested<playercount()) {
      new msg[MAX_TEXT_LENGTH];
      snprintf(msg,MAX_TEXT_LENGTH,"Bugblatter name manager: Failed to check some players names - will try again in <30 seconds",i);
      log(msg);
    }
 
    if (g_RecheckTimer == 0) {
      g_RecheckTimer = set_timer("CheckNamesAgain",30,1);
    }
  }
 
 
  if (g_AutoChaseCam) {
    new chase = getvar("mp_forcechasecam");
    if (g_LanUsersPresent) {
      if (chase!=g_AutoChaseCam) {
  SetChaseCam(g_AutoChaseCam);
      }
    } else {
      if (chase!=0) {
  SetChaseCam(0);
      }
    }
  }
  return 0;
}
 
public CheckNamesAgain() {
  g_RecheckTimer = 0;
  CheckNames();
}
 
SetChaseCam(val) {
  new cmd[MAX_TEXT_LENGTH];
  snprintf(cmd,MAX_TEXT_LENGTH,"mp_forcechasecam %i",val);
  exec(cmd);
  if (val) {
    language_sayall(g_MSG_FREELOOK_OFF,print_type:print_pretty);
  }
  else {
    language_sayall(g_MSG_FREELOOK_ON,print_type:print_pretty);
  }
}
 
/* Returns non-zero if the Name doesn't match the test
 *
 * If CheckNewNames = 1, then it returns 1 for players
 * that have one of the new names.  This is used to check
 * if manual renaming is permitted
 */
 
CheckPlayerName(OldName[],NewName[],CheckNewNames,UserIndex,DeleetName,&OnLan)
{
  new i;
  new HasLanTag=0;
  OnLan=0;
 
  /* Admin's with immunity always have valid names */
  if (check_immunity(OldName)) {
    return 0;
  }
 
  strtrim(NewName," ");
 
  new pos=0;
 
  if (g_CheatingDeath) {
    if (strstr(NewName,"[No C-D]")==0) {
      pos=8;
    }
    if (strstr(NewName,"[Old C-D]")==0) {
      pos=9;
    }
  }
 
  if (NewName[pos]=='L') {
    if ((NewName[pos+1]='1') && (NewName[pos+2]>=48) && (NewName[pos+2] <= 57) && (NewName[pos+3] == ':')) {
      HasLanTag=1;
    }
    else if ((NewName[pos+1]>=48) && (NewName[pos+1] <= 57) && (NewName[pos+2] == ':')) {
      HasLanTag=1;
    }
  }
 
  /* Test if user should have a lan tag and doesn't? */
  if (g_Lan[UserIndex] > 0) {
    new lancount=0;
    for (i=0;i<=MAX_PLAYERS;i++) {
      if (g_Lan[i]==g_Lan[UserIndex]) {
        lancount++;
      }
    }
    if (lancount>1) {
      OnLan = 1;
    }
    else {
      g_Lan[UserIndex]=0;
    }
  }
 
  /* Filter out bad characters */
  if (strfilter(NewName,g_Allowed)==0) {
    return 1;
  }
 
 
  /* Is name too short? */
  if (strlen(NewName) < g_MinLength) {
    return 1;
  }
 
  /* Too few consecutive letters? */
  if (g_MinLetters > 0) {
    if (strseqlen(NewName,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") < g_MinLetters) {
      if (DeleetName) {
        new LeetName[MAX_NAME_LENGTH];
        strcpy(LeetName,NewName,MAX_NAME_LENGTH);
 
        deleet(LeetName);
        if (strseqlen(LeetName,"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") < g_MinLetters) {
          return 2;
        }
        else {
          SetPlayerName(OldName,LeetName,UserIndex,2,OnLan);
          return 0;
        }
      }
      else {
        return 2;
      }
    }
  }
 
  /* On the banned names list? */
  if (g_Rename) {
    new NamesFile[MAX_DATA_LENGTH];
    new ReadCount;
    new TestName[MAX_NAME_LENGTH];
 
    if (getfilelocation(NamesFile,"badplayernames.ini")) {
      ReadCount = filesize(NamesFile)+1;
 
 
      for(i=0;i<ReadCount;i++) {
        readfile(NamesFile,TestName,i,MAX_NAME_LENGTH);
        if (strcasecmp(NewName,TestName) == 0) {
        return 3;
        }
      }
    }
 
    if (CheckNewNames) {
      if (getfilelocation(NamesFile,"newplayernames.ini")) {
        ReadCount = filesize(NamesFile)+1;
 
        for(i=0;i<ReadCount;i++) {
          readfile(NamesFile,TestName,i,MAX_NAME_LENGTH);
          if (strcasecmp(NewName,TestName) == 0) {
            return 4;
          }
        }
      }
    }
  }
 
  /* Is the only thing wrong with the name a missing lan tag? */
  if ((OnLan==1) && (HasLanTag==0) && (g_LanTags==1))  {
    return 5;
  }
 
  /* Is the only thing wrong with the name an extra lan tag? */
  if ((HasLanTag==1) && (OnLan==0)) {
    return 6;
  }
  return 0;
 
}
 
ChoosePlayerName(Name[],UserIndex,Reason,OnLan) {
  new NamesFile[MAX_DATA_LENGTH];
 
  if (getfilelocation(NamesFile,"newplayernames.ini")==0) {
    return 0;
  }
 
  new ReadCount = filesize(NamesFile);
  new i;
  new NewName[MAX_NAME_LENGTH];
 
  /* First pick a random name from the new ones */
  new count=filesize(NamesFile,fsize_unit:lines)+1;
  new pick=random(count);
  readfile(NamesFile,NewName,pick,MAX_NAME_LENGTH);
  if (ConsiderName(Name,NewName,UserIndex,Reason,OnLan)) {
    return 1;
  }
 
  /* If random pick is in use, then pick first free one */
  for(i=0;i<ReadCount;i++) {
    readfile(NamesFile,NewName,i,MAX_NAME_LENGTH);
    if (ConsiderName(Name,NewName,UserIndex,Reason,OnLan)) {
      return 1;
    }
  }
 
  return 0;
}
 
ConsiderName(OldName[],NewName[],UserIndex,Reason,OnLan) {
  new dummy;
 
  if ((NewName[0] != '/') && (NewName[0] != NULL_CHAR)) {
    if (get_userindex(NewName,dummy) ==0) {
      /* Name is not already used */
      SetPlayerName(OldName,NewName,UserIndex,Reason,OnLan);
      return 1;
    }
  }
  return 0;
}
 
SetPlayerName(Name[],NewName[],UserIndex,Reason,OnLan)  {
 
  /* Name is being changed to add a lan tag */
  new LanName[MAX_NAME_LENGTH];
 
  snprintf(LanName,MAX_NAME_LENGTH,"L%i:",g_Lan[UserIndex]);
  if ((strstr(NewName,LanName) ==0) || (OnLan == 0) || (g_LanTags==0)) {
    LanName[0]=NULL_CHAR;
  }
 
  new LanNameLen =0;
  new fLoop=1;
  while (fLoop) {
    fLoop=0;
    if (NewName[LanNameLen]=='L') {
      if ((NewName[LanNameLen+1]=='1') && (NewName[LanNameLen+2]>=48) && (NewName[LanNameLen+2] <= 57) && (NewName[LanNameLen+3] == ':')) {
  LanNameLen=LanNameLen+4;
  fLoop=1;
      }
      else if ((NewName[LanNameLen+1]>=48) && (NewName[LanNameLen+1] <= 57) && (NewName[LanNameLen+2] == ':')) {
  LanNameLen=LanNameLen+3;
  fLoop=1;
      }
    }
 
 
    if (streqindex(NewName,"[No C-D]",LanNameLen)) {
      LanNameLen=LanNameLen+8;
      fLoop=1;
    }
 
    if (streqindex(NewName,"[Old C-D]",LanNameLen)) {
      LanNameLen=LanNameLen+9;
      fLoop=1;
    }
 
  }
 
  new i;
  for(i=LanNameLen;i<MAX_NAME_LENGTH;i++) {
    NewName[i-LanNameLen]=NewName[i];
  }
 
  strcat(LanName,NewName,MAX_NAME_LENGTH);
 
  if (Reason == 1) {
    language_sayf(UserIndex,g_MSG_NAMELENGTH,print_type:print_pretty,15,ERROR_RED,ERROR_GREEN,ERROR_BLUE,g_MinLength,LanName);
  } else if (Reason == 2) {
    language_sayf(UserIndex,g_MSG_NAMELETTERS,print_type:print_pretty,15,ERROR_RED,ERROR_GREEN,ERROR_BLUE,g_MinLetters,LanName);
  } else if (Reason == 5) {
    language_say(UserIndex,g_MSG_NAMELAN_ON,print_type:print_pretty,15,ERROR_RED,ERROR_GREEN,ERROR_BLUE);
  } else if (Reason == 6) {
    language_say(UserIndex,g_MSG_NAMELAN_OFF,print_type:print_pretty,15,ERROR_RED,ERROR_GREEN,ERROR_BLUE);
  } else {
    language_sayf(UserIndex,g_MSG_NAMEBANNED,print_type:print_pretty,15,ERROR_RED,ERROR_GREEN,ERROR_BLUE,Name,LanName);
  }
  language_say(UserIndex,g_MSG_HOWTOCHANGE,print_type:print_chat);
  messageex(Name,"setinfo name <yournewname>",print_type:print_chat);
 
  if (g_CheatingDeath ==0) {
    g_IgnoreNext[UserIndex]=1; /* Prevent feedback loop */
  }
  new cmd[MAX_TEXT_LENGTH];
  snprintf(cmd,MAX_TEXT_LENGTH,"setinfo name ^"%s^"",LanName);
  safe_execclient(Name,cmd);
  strcpy(g_PermitNames[UserIndex],LanName,MAX_NAME_LENGTH);
  strcpy(g_OriginalNames[UserIndex],LanName,MAX_NAME_LENGTH);
  return 0;
}