/
CVS/
boards/CVS/
clans/
gmc/CVS/
help/CVS/
include/CVS/
players/
progs/CVS/
races/CVS/
system/CVS/
text/
text/CVS/
todo/
todo/CVS/
units/CVS/
// $Id: mudthread.pas,v 1.53 2001/06/06 18:48:41 xenon Exp $

unit mudthread;

interface

uses
    Classes,
    Windows,
    Winsock2,
    SysUtils,
    ansiio,
    constants,
    conns,
    chars,
    race,
    clan,
    area,
    dtypes,
    skills,
    strip,
    util,
    bulletinboard,
    mudhelp,
    mudsystem,
    math,
    gvm;

type
    GGameThread = class(TThread)
    private
      socket : TSocket;
      client_addr : TSockAddr_Storage;
      copyover : boolean;
      copyover_name : string;

    protected
      procedure Execute; override;

    public
      last_update : TDateTime;
      
      constructor Create(s : TSocket; a : TSockAddr_Storage; copy : boolean; name : string);
    end;

    COMMAND_FUNC = procedure(ch : GCharacter; param : string);

    GCommandFunc = class
      name : string;
      func : COMMAND_FUNC;
    end;

    GCommand = class
      name : string;
      func_name : string;
      ptr : COMMAND_FUNC;
      level : integer;             { minimum level }
      position : integer;          { minimum position }
      addArg0 : boolean;           { send arg[0] (the command itself) to func? }
    end;

var
   commands : GHashTable;
   func_list : GDLinkedList;

procedure load_commands;
procedure interpret(ch : GCharacter; line : string);

procedure registerCommand(name : string; func : COMMAND_FUNC);
procedure registerCommands;

implementation

uses
    magic,
    md5,
    update,
    timers,
    debug,
    mudspell,
    fight,
    NameGen,
    Channels;

constructor GGameThread.Create(s : TSocket; a : TSockAddr_Storage; copy : boolean; name : string);
begin
  inherited Create(False);

  socket := s;
  client_addr := a;
  copyover := copy;
  copyover_name := name;
  last_update := Now();
end;

{$I include\command.inc}

procedure do_dummy(ch : GCharacter; param : string);
begin
  ch.sendBuffer('This is a DUMMY command. Please contact the ADMINISTRATION.'#13#10);
end;

function findCommand(s : string) : COMMAND_FUNC;
var
   node : GListNode;
   f : GCommandFunc;
begin
  Result := nil;

  node := func_list.head;

  while (node <> nil) do
    begin
    f := node.element;

    if (f.name = s) then
      begin
      Result := f.func;
      exit;
      end;

    node := node.next;
    end;

  write_console('Could not find command "' + s + '"');
end;

procedure load_commands;
var f:textfile;
    s,g:string;
    cmd : GCommand;
    alias : GCommand;
begin
  assignfile(f, 'system\commands.dat');
  {$I-}
  reset(f);
  {$I+}
  if IOResult<>0 then
    begin
    bugreport('load_commands', 'mudthread.pas', 'could not open system\commands.dat',
              'The system file commands.dat could not be opened.');
    exit;
    end;

  commands := GHashTable.Create(128);
  commands.setHashFunc(firstHash);

  repeat
    repeat
      readln(f,s);
    until (uppercase(s) = '#COMMAND') or eof(f);

    if (eof(f)) then
      break;

    alias := nil;
    cmd := GCommand.Create;
    cmd.position := POS_MEDITATE;

    with cmd do
      repeat
      readln(f,s);
      g:=uppercase(left(s,':'));

      if g='NAME' then
        name := uppercase(right(s,' '))
      else
      if g='ALIAS' then
        begin
        // create an alias
        alias := GCommand.Create;
        alias.name := uppercase(right(s,' '));
        end
      else
      if g='LEVEL' then
        level:=strtoint(right(s,' '))
      else
      if g='POSITION' then
        position:=strtoint(right(s,' '))
      else
      if g='FUNCTION' then
        begin
        func_name := right(s,' ');
        ptr := findCommand(func_name);
        end
      else
      if g='ADDARG0' then
        begin
          addarg0 := (trim(uppercase(right(s,' '))) = 'TRUE');
        end;
      until (uppercase(s)='#END') or eof(f);

    if (assigned(cmd.ptr)) then
      begin
      commands.put(cmd.name, cmd);

      if (alias <> nil) then
        begin
        // update settings
        alias.level := cmd.level;
        alias.ptr := cmd.ptr;
        alias.func_name := cmd.func_name;
        alias.position := cmd.position;
        alias.addarg0 := cmd.addarg0;

        commands.put(alias.name, alias);
        end;
      end
    else
      begin
      cmd.Free;

      if (alias <> nil) then
        alias.Free;
      end;
  until eof(f);

  closefile(f);
end;

procedure clean_cmdline(var line : string);
var
   d : integer;
begin
  d := pos('$', line);

  while (d > 0) do
    begin
    delete(line, d, 1);

    d := pos('$', line);
    end;
end;

procedure interpret(ch : GCharacter; line : string);
var
    a : longint;
    gc : GCommand;
    cmd : GCommand;
    node : GListNode;
    cmdline, param : string;
    hash, time : cardinal;
    al : GAlias;
begin
  with ch do
    begin
    { Check if keyboard is locked - Nemesis }
    if (conn <> nil) and (not IS_NPC) and (IS_KEYLOCKED) then
      begin
      if (length(line) = 0) then
        begin
        sendBuffer('Enter your password to unlock keyboard.'#13#10);
        exit;
        end;

      if (not MD5Match(GPlayer(ch).md5_password, MD5String(line))) then
        begin
        sendBuffer('Wrong password!'#13#10);
        exit;
        end
      else
        begin
        GConnection(conn).afk := false;
        GConnection(conn).keylock := false;

        act(AT_REPORT,'You are now back at your keyboard.',false,ch,nil,nil,to_char);
        act(AT_REPORT,'$n has returned to $s keyboard.',false,ch,nil,nil,to_room);
        exit;
        end;
      end;

    { AFK revised with keylock - Nemesis }
    if (conn <> nil) and (IS_AFK) and (not IS_NPC) and (not IS_KEYLOCKED) then
      begin
      GConnection(conn).afk := false;

      act(AT_REPORT,'You are now back at your keyboard.',false,ch,nil,nil,to_char);
      act(AT_REPORT,'$n has returned to $s keyboard.',false,ch,nil,nil,to_room);
      end;

    if (ch.position = POS_CASTING) then
      begin
      act(AT_REPORT, 'You stop casting.', false, ch, nil, nil, TO_CHAR);
      unregisterTimer(ch, TIMER_CAST);
      ch.position := POS_STANDING;
      end;

    if (length(line) = 0) then
      begin
      ch.sendBuffer(' ');
      exit;
      end;

    if (snooped_by <> nil) then
        GConnection(snooped_by.conn).send(line + #13#10);

    clean_cmdline(line);

    param := one_argument(line, cmdline);
    cmdline := uppercase(cmdline);

    // check for aliases first
    if (not ch.IS_NPC) then
      begin
      node := GPlayer(ch).aliases.head;

      while (node <> nil) do
        begin
        al := node.element;

        if (uppercase(al.alias) = cmdline) then
          begin
          line := al.expand + ' ' + param;
          param := one_argument(line, cmdline);
          cmdline := uppercase(cmdline);

          break;
          end;

        node := node.next;
        end;
      end;

    cmd := nil;

    hash := commands.getHash(cmdline);
    node := commands.bucketList[hash].head;

    while (node <> nil) do
      begin
      gc := GCommand(GHashValue(node.element).value);
      if (cmdline = gc.name) or
         ((pos(cmdline, gc.name) = 1) and (length(cmdline) <= length(gc.name)) and (length(cmdline) > 1)) or
         ((copy(cmdline, 1, length(gc.name)) = gc.name) and (length(cmdline) = 1))
         then
        begin
        cmd := gc;
        break;
        end;

      node := node.next;
      end;

    if (cmd <> nil) then
      begin
      a := ch.getTrust;

      if (a >= cmd.level) then
        begin
        if (cmd.position > position) then
          case position of
            POS_SLEEPING: ch.sendBuffer('You are off to dreamland.'#13#10);
            POS_MEDITATE: ch.sendBuffer('You must break out of your trance first.'#13#10);
             POS_RESTING: ch.sendBuffer('You feel too relaxed to do this.'#13#10);
             POS_SITTING: ch.sendBuffer('You must get on your feet first.'#13#10);
            POS_FIGHTING: ch.sendBuffer('You are fighting!'#13#10);
          end
        else
          begin
          if (system_info.log_all) or (ch.logging) then
            write_log(ch.name^ + ': ' + line);

          if (cmd.level >= LEVEL_IMMORTAL) then
            write_console('[LOG] ' + ch.name^ + ': ' + cmd.name + ' (' + inttostr(cmd.level) + ')');

          try
            time := GetTickCount;

            if cmd.addarg0 then
              cmd.ptr(ch, cmdline + ' ' + param)
            else
              cmd.ptr(ch, param);

            ch.last_cmd := @cmd.ptr;

            { Disabled this, this is too computer specific, e.g. on a 486
              some commands could lag while on a Pentium they would not.
              Uncomment if CPU leaks are to be traced. - Grimlord }

            time := GetTickCount - time;

            if (time > 1500) and (not ch.CHAR_DIED) then
              bugreport('interpret','mudthread.pas', cmd.func_name + ', ch ' + ch.name^ + ' lagged', 'The command took over 1.5 sec to complete.');
          except
            on E : EExternal do
            begin
              outputError(E);
            end;

              //bugreport('interpret', 'mudthread.pas', cmd.func_name + ', ch ' + ch.name^ + ' bugged',
              //          'The specified command caused an error and has been terminated.');
          end;
          end;
        end
      else
        cmd := nil;
      end;

    if (cmd = nil) and (not checkSocial(ch, cmdline, param)) then
      begin
      a := random(9);
      if a<1 then
        cmdline := 'Sorry, that command doesn''t exist in my vocabulaire!'
      else
      if a<2 then
        cmdline := 'I don''t understand you.'
      else
      if a<3 then
        cmdline := 'What are you saying?'
      else
      if a<4 then
        cmdline := 'Learn some english!'
      else
      if a<5 then
        cmdline := 'Hey, I don''t know that command. Try again.'
      else
      if a<6 then
        cmdline := 'What??'
      else
      if a<7 then
        cmdline := 'Huh?'
      else
      if a<8 then
        cmdline := 'Yeah, right!'
      else
      if a<9 then
        cmdline := 'What you say??';

      act(AT_DGREEN, cmdline, false, ch, nil, nil, TO_CHAR);
      end;
  end;
end;

procedure nanny(conn : GConnection; argument : string);
var ch, vict : GPlayer;
    node : GListNode;
    race : GRace;
    digest : MD5Digest;
    h,top,x,temp:integer;
    buf, pwd : string;
begin
  ch := conn.ch;

  case conn.state of
        CON_NAME: begin
                  pwd := one_argument(argument, argument);
                  
                  if (length(argument) = 0) then
                    begin
                    conn.send('Please enter your name: ');
                    exit;
                    end;

                  if (uppercase(argument) = 'CREATE') then
                    begin
                    conn.send(#13#10'By what name do you wish to be known? ');
                    conn.state := CON_NEW_NAME;
                    exit;
                    end;

                  vict := findPlayerWorld(nil, argument);

                  if (vict <> nil) and (not vict.IS_NPC) and (vict.conn <> nil) and (cap(vict.name^) = cap(argument)) then
                    begin
                    if (not MD5Match(MD5String(pwd), GPlayer(vict).md5_password)) then
                      begin
                      conn.send(#13#10'You are already logged in under that name! Type your name and password on one line to break in.'#13#10);
                      closesocket(conn.socket);
                      conn.thread.Terminate;
                      end
                    else
                      begin
                      GConnection(vict.conn).thread.Terminate;

                      while (not IS_SET(vict.flags, PLR_LINKLESS)) do;

                      vict.conn := conn;
                      conn.ch := vict;
                      ch := vict;
                      REMOVE_BIT(conn.ch.flags,PLR_LINKLESS);
                      conn.state := CON_PLAYING;
                      conn.ch.sendPrompt;
                      end;

                    exit;
                    end;

                  if (not ch.load(argument)) then
                    begin
                    conn.send(#13#10'Are you sure about that name?'#13#10'Name: ');
                    exit;
                    end;

                  conn.state:=CON_PASSWORD;
                  conn.send('Password: ');
                  end;
    CON_PASSWORD: begin
                  if (length(argument) = 0) then
                    begin
                    conn.send('Password: ');
                    exit;
                    end;

                  if (not MD5Match(MD5String(argument), ch.md5_password)) then
                    begin
                    write_console('(' + inttostr(conn.socket) + ') Failed password');
                    conn.send('Wrong password.'#13#10);
                    closesocket(conn.socket);
                    exit;
                    end;

                  vict := findPlayerWorld(nil, ch.name^);

                  if (vict <> nil) and (vict.conn = nil) then
                    begin
                    ch.Free;

                    conn.ch := vict;
                    ch := vict;
                    vict.conn := conn;

                    ch.ld_timer := 0;

                    conn.send('You have reconnected.'#13#10);
                    act(AT_REPORT, '$n has reconnected.', false, ch, nil, nil, TO_ROOM);
                    REMOVE_BIT(ch.flags, PLR_LINKLESS);
                    write_console('(' + inttostr(conn.socket) + ') ' + ch.name^ + ' has reconnected');

                    ch.sendPrompt;
                    conn.state := CON_PLAYING;
                    exit;
                    end;

                  if (ch.IS_IMMORT) then
                    conn.send(ch.ansiColor(2) + #13#10 + findHelp('IMOTD').text)
                  else
                    conn.send(ch.ansiColor(2) + #13#10 + findHelp('MOTD').text);

                  conn.send('Press Enter.'#13#10);
                  conn.state := CON_MOTD;
                  end;
        CON_MOTD: begin
                  conn.send(ch.ansiColor(6) + #13#10#13#10'Welcome, ' + ch.name^ + ', to this MUD. May your stay be pleasant.'#13#10);

                  with system_info do
                    begin
                    user_cur := connection_list.getSize;
                    if (user_cur > user_high) then
                      user_high := user_cur;
                    end;

                  ch.toRoom(ch.room);

                  act(AT_WHITE, '$n enters through a magic portal.', true, ch, nil, nil, TO_ROOM);
                  write_console('(' + inttostr(conn.socket) + ') '+ ch.name^ +' has logged in');

                  ch.node_world := char_list.insertLast(ch);
                  ch.logon_now := Now;

                  if (ch.level = LEVEL_RULER) then
                    do_uptime(ch, '')
                  else
                    ch.sendPrompt;

                  conn.state := CON_PLAYING;
                  conn.fcommand := true;
                  end;
    CON_NEW_NAME: begin
                  if (length(argument) = 0) then
                    begin
                    conn.send('By what name do you wish to be known? ');
                    exit;
                    end;

                  if (FileExists('players\' + argument + '.usr')) then
                    begin
                    conn.send('That name is already used.'#13#10);
                    conn.send('By what name do you wish to be known? ');
                    exit;
                    end;

                  (* vict := findCharWorld(nil, argument);
                  if ((banned_names.indexof(uppercase(argument)) <> -1) or ((vict <> nil) and (uppercase(vict.name) = uppercase(argument)))) then
                    begin
                    send_to_conn(conn,'That name cannot be used.'#13#10);
                     send_to_conn(conn,'By what name do you wish to be known? ');
                    exit;
                    end; *)

                  if (length(argument) < 3) or (length(argument) > 15) then
                    begin
                    conn.send('Your name must be between 3 and 15 characters long.'#13#10);
                    conn.send('By what name do you wish to be known? ');
                    exit;
                    end;

                  ch.name := hash_string(cap(argument));
                  conn.state := CON_NEW_PASSWORD;
                  conn.send(#13#10'Allright, '+ch.name^+', choose a password: ');
                  end;
CON_NEW_PASSWORD: begin
                  if (length(argument)=0) then
                    begin
                    conn.send('Choose a password: ');
                    exit;
                    end;

                  ch.md5_password := MD5String(argument);
                  conn.state := CON_CHECK_PASSWORD;
                  conn.send(#13#10'Please retype your password: ');
                  end;
CON_CHECK_PASSWORD: begin
                    if (length(argument) = 0) then
                      begin
                      conn.send('Please retype your password: ');
                      exit;
                      end;

                    if (not MD5Match(MD5String(argument), ch.md5_password)) then
                      begin
                      conn.send(#13#10'Password did not match!'#13#10'Choose a password: ');
                      conn.state := CON_NEW_PASSWORD;
                      exit;
                      end
                    else
                      begin
                      conn.state := CON_NEW_SEX;
                      conn.send(#13#10'What sex do you wish to be (M/F/N): ');
                      exit;
                      end;
                    end;
     CON_NEW_SEX: begin
                  if (length(argument) = 0) then
                    begin
                    conn.send('Choose a sex (M/F/N): ');
                    exit;
                    end;

                  case upcase(argument[1]) of
                    'M':ch.sex:=0;
                    'F':ch.sex:=1;
                    'N':ch.sex:=2;
                  else
                    begin
                    conn.send('That is not a valid sex.'#13#10);
                    conn.send('Choose a sex (M/F/N): ');
                    exit;
                    end;
                  end;

                  conn.state:=CON_NEW_RACE;
                  conn.send(#13#10'Available races: '#13#10#13#10);

                  h:=1;
                  node := race_list.head;
                  while (node <> nil) do
                    begin
                    race := node.element;

                    buf := '  ['+inttostr(h)+']  '+pad_string(race.name,15);

                    if (race.def_alignment < 0) then
                      buf := buf + ANSIColor(12,0) + '<- EVIL'+ANSIColor(7,0);

                    buf := buf + #13#10;

                    conn.send(buf);

                    inc(h);
                    node := node.next;
                    end;

                  conn.send(#13#10'Choose a race: ');
                  end;
    CON_NEW_RACE: begin
                  if (length(argument)=0) then
                    begin
                    conn.send(#13#10'Choose a race: ');
                    exit;
                    end;

                  try
                    x:=strtoint(argument);
                  except
                    x:=-1;
                  end;

                  race := nil;
                  node := race_list.head;
                  h := 1;

                  while (node <> nil) do
                    begin
                    if (h = x) then
                      begin
                      race := node.element;
                      break;
                      end;

                    inc(h);
                    node := node.next;
                    end;

                  if (race = nil) then
                    begin
                    conn.send('Not a valid race.'#13#10);

                    h:=1;
                    node := race_list.head;
                    while (node <> nil) do
                      begin
                      race := node.element;

                      buf := '  ['+inttostr(h)+']  '+pad_string(race.name,15);

                      if (race.def_alignment < 0) then
                        buf := buf + ANSIColor(12,0) + '<- EVIL'+ANSIColor(7,0);

                      buf := buf + #13#10;

                      conn.send(buf);

                      inc(h);
                      node := node.next;
                      end;

                    conn.send(#13#10'Choose a race: ');
                    exit;
                    end;

                  ch.race:=race;
                  conn.send(race.description);
                  conn.send('250 stat points will be randomly distributed over your five attributes.'#13#10);
                  conn.send('It is impossible to get a lower or a higher total of stat points.'#13#10);

                  with ch do
                    begin
                    top:=250;

                    str:=URange(25,random(UMax(top,75))+ch.race.str_bonus,75);
                    dec(top,str);

                    con:=URange(25,random(UMax(top,75))+ch.race.con_bonus,75);
                    dec(top,con);

                    dex:=URange(25,random(UMax(top,75))+ch.race.dex_bonus,75);
                    dec(top,dex);

                    int:=URange(25,random(UMax(top,75))+ch.race.int_bonus,75);
                    dec(top,int);

                    wis:=URange(25,random(UMax(top,75))+ch.race.wis_bonus,75);
                    dec(top,wis);

                    while (top>0) do
                      begin
                      x:=random(5);

                      case x of
                        0:begin
                          temp:=UMax(75-str,top);
                          if (temp>0) then
                            begin
                            dec(top,temp);
                            str := str + temp;
                            end;
                          end;
                        1:begin
                          temp:=UMax(75-con,top);
                          if (temp>0) then
                            begin
                            dec(top,temp);
                            con := con + temp;
                            end;
                          end;
                        2:begin
                          temp:=UMax(75-dex,top);
                          if (temp>0) then
                            begin
                            dec(top,temp);
                            dex := dex + temp;
                            end;
                          end;
                        3:begin
                          temp:=UMax(75-int,top);
                          if (temp>0) then
                            begin
                            dec(top,temp);
                            int := int + temp;
                            end;
                          end;
                        4:begin
                          temp:=UMax(75-wis,top);
                          if (temp>0) then
                            begin
                            dec(top,temp);
                            wis := wis + temp;
                            end;
                          end;
                      end;
                      end;

                    top:=str+con+dex+int+wis;
                    end;

                  conn.send(#13#10'Your character statistics are: '#13#10#13#10);

                  buf := 'Strength:     '+ANSIColor(10,0)+inttostr(ch.str)+ANSIColor(7,0)+#13#10 +
                         'Constitution: '+ANSIColor(10,0)+inttostr(ch.con)+ANSIColor(7,0)+#13#10 +
                         'Dexterity:    '+ANSIColor(10,0)+inttostr(ch.dex)+ANSIColor(7,0)+#13#10 +
                         'Intelligence: '+ANSIColor(10,0)+inttostr(ch.int)+ANSIColor(7,0)+#13#10 +
                         'Wisdom:       '+ANSIColor(10,0)+inttostr(ch.wis)+ANSIColor(7,0)+#13#10;
                  conn.send(buf);

                  conn.send(#13#10'Do you wish to (C)ontinue, (R)eroll or (S)tart over? ');
                  conn.state:=CON_NEW_STATS;
                  end;
   CON_NEW_STATS: begin
                  if (length(argument) =0) then
                    begin
                    conn.send(#13#10'Do you wish to (C)ontinue, (R)eroll or (S)tart over? ');
                    exit;
                    end;

                  case (upcase(argument[1])) of
                    'C':begin
                        digest := ch.md5_password;

                        ch.load(ch.name^);
                        ch.md5_password := digest;
                        ch.save(ch.name^);

                        conn.send(#13#10'Thank you. You have completed your entry.'#13#10);

                        conn.send(ch.ansiColor(2) + #13#10);

                        if (ch.IS_IMMORT) then
                          conn.send(ch.ansiColor(2) + #13#10 + findHelp('IMOTD').text)
                        else
                          conn.send(ch.ansiColor(2) + #13#10 + findHelp('MOTD').text);

                        conn.send('Press Enter.'#13#10);
                        conn.state:=CON_MOTD;
                        end;
                    'R':begin
                        with ch do
                          begin
                          top:=250;

                          str:=URange(25,random(UMax(top,75))+ch.race.str_bonus,75);
                          dec(top,str);

                          con:=URange(25,random(UMax(top,75))+ch.race.con_bonus,75);
                          dec(top,con);

                          dex:=URange(25,random(UMax(top,75))+ch.race.dex_bonus,75);
                          dec(top,dex);

                          int:=URange(25,random(UMax(top,75))+ch.race.int_bonus,75);
                          dec(top,int);

                          wis:=URange(25,random(UMax(top,75))+ch.race.wis_bonus,75);
                          dec(top,wis);

                          while (top>0) do
                            begin
                            x:=random(5);

                            case x of
                              0:begin
                                temp:=UMax(75-str,top);
                                if (temp>0) then
                                  begin
                                  dec(top,temp);
                                  str := str + temp;
                                  end;
                                end;
                              1:begin
                                temp:=UMax(75-con,top);
                                if (temp>0) then
                                  begin
                                  dec(top,temp);
                                  con := con + temp;
                                  end;
                                end;
                              2:begin
                                temp:=UMax(75-dex,top);
                                if (temp>0) then
                                  begin
                                  dec(top,temp);
                                  dex := dex + temp;
                                  end;
                                end;
                              3:begin
                                temp:=UMax(75-int,top);
                                if (temp>0) then
                                  begin
                                  dec(top,temp);
                                  int := int + temp;
                                  end;
                                end;
                              4:begin
                                temp:=UMax(75-wis,top);
                                if (temp>0) then
                                  begin
                                  dec(top,temp);
                                  wis := wis + temp;
                                  end;
                                end;
                            end;
                            end;

                          top:=str+con+dex+int+wis;
                          end;

                        conn.send(#13#10'Your character statistics are: '#13#10#13#10);

                        buf := 'Strength:     '+ANSIColor(10,0)+inttostr(ch.str)+ANSIColor(7,0)+#13#10 +
                               'Constitution: '+ANSIColor(10,0)+inttostr(ch.con)+ANSIColor(7,0)+#13#10 +
                               'Dexterity:    '+ANSIColor(10,0)+inttostr(ch.dex)+ANSIColor(7,0)+#13#10 +
                               'Intelligence: '+ANSIColor(10,0)+inttostr(ch.int)+ANSIColor(7,0)+#13#10 +
                               'Wisdom:       '+ANSIColor(10,0)+inttostr(ch.wis)+ANSIColor(7,0)+#13#10;
                        conn.send(buf);

                        conn.send(#13#10'Do you wish to (C)ontinue, (R)eroll or (S)tart over? ');
                        end;
                    'S':begin
                        conn.send(#13#10'Very well, restarting.'#13#10);
                        conn.send('By what name do you wish to be known?');
                        conn.state:=CON_NEW_NAME;
                        end;
                  else
                    conn.send('Do you wish to (C)ontinue, (R)eroll or (S)art over? ');
                    exit;
                 end;
                 end;
    else
      bugreport('nanny', 'mudthread.pas', 'illegal state ' + inttostr(conn.state),
                'The specified connection state is unknown. Someone may be hacking.');
  end;
end;

procedure GGameThread.Execute;
var conn : GConnection;
    cmdline : string;
    temp_buf : string;
    ch : GPlayer;
    i : integer;

label nameinput,stopthread;
begin
  freeonterminate := true;

  conn := GConnection.Create(socket, client_addr, Self);

  write_console('(' + inttostr(socket) + ') New connection (' + conn.host_string + ')');

  if (isMaskBanned(conn.host_string)) then
    begin
    write_console('('+inttostr(socket)+') Closed banned IP (' + conn.host_string + ')');

    conn.send(system_info.mud_name+#13#10#13#10);
    conn.send('Your site has been banned from this server.'#13#10);
    conn.send('For more information, please mail the administration, '+system_info.admin_email+'.'#13#10);
    closesocket(socket);
    conn.Free;
    exit;
    end;

  ch := GPlayer.Create;
  conn.ch := ch;
  ch.conn := conn;

  if (not copyover) then
    begin
    conn.state := CON_NAME;

    conn.send(AnsiColor(2,0) + findHelp('M_DESCRIPTION_').text);

    temp_buf := AnsiColor(6,0) + #13#10;

    temp_buf := temp_buf + version_info + ', ' + version_number + '.'#13#10;
    temp_buf := temp_buf + version_copyright + '.';
    temp_buf := temp_buf + #13#10'This is free software, with ABSOLUTELY NO WARRANTY; view LICENSE.TXT.';
    temp_buf := temp_buf + AnsiColor(7,0) + #13#10;

    conn.send(temp_buf);

    conn.send(#13#10#13#10'Enter your name or CREATE to create a new character.'#13#10'Please enter your name: ');
    end
  else
    begin
    conn.state := CON_MOTD;

    conn.ch.name := hash_string(copyover_name);
    conn.ch.load(copyover_name);
    conn.send(#13#10#13#10'Gradually, the clouds form real images again, recreating the world...'#13#10);
    conn.send('Copyover complete!'#13#10);

    nanny(conn, '');
    end;

  repeat
    if (conn.fcommand) then
      begin
      if (conn.pagepoint <> 0) then
        conn.outputPager
      else
        conn.ch.emptyBuffer;
      end;

    conn.fcommand:=false;
    sleep(100);

    last_update := Now();

    conn.checkReceive;

    if (not Terminated) then
      conn.read;

    if (not Terminated) and (conn.ch.wait > 0) then
      continue;

    if (not Terminated) then
      conn.readBuffer;

    if (length(conn.comm_buf) > 0) then
      begin
      cmdline := trim(conn.comm_buf);

      i := pos(#13, cmdline);
      if (i <> 0) then
        delete(cmdline, i, 1);

      i := pos(#10, cmdline);
      if (i <> 0) then
        delete(cmdline, i, 1);

      conn.comm_buf := '';
      conn.fcommand := true;

      if (conn.pagepoint <> 0) then
        conn.setPagerInput(cmdline)
      else
        case conn.state of
          CON_PLAYING: begin
                       if (not conn.ch.IS_NPC) and (IS_SET(conn.ch.flags,PLR_FROZEN)) and (cmdline <> 'quit') then
                         begin
                         conn.ch.sendBuffer('You have been frozen by the gods and cannot do anything.'#13#10);
                         conn.ch.sendBuffer('To be unfrozen, send an e-mail to the administration, '+system_info.admin_email+'.'#13#10);
                         continue;
                         end;

                       conn.ch.in_command:=true;
                       interpret(conn.ch, cmdline);

                       if (not conn.ch.CHAR_DIED) then
                         conn.ch.in_command := false;
                       end;
          CON_EDIT_HANDLE: conn.ch.editBuffer(cmdline);
          CON_EDITING: conn.ch.editBuffer(cmdline);
          else
            nanny(conn, cmdline);
        end;
      end;
  until Terminated;

  if (not conn.ch.CHAR_DIED) and ((conn.state=CON_PLAYING) or (conn.state=CON_EDITING)) then
    begin
    write_console('(' + inttostr(conn.socket) + ') '+conn.ch.name^+' has lost the link');

    interpret(conn.ch, 'return');

    conn.ch.conn := nil;

    act(AT_REPORT,'$n has lost $s link.',false,conn.ch,nil,nil,TO_ROOM);
    SET_BIT(conn.ch.flags,PLR_LINKLESS);
    end
  else
  if (conn.state = CON_LOGGED_OUT) then
    dec(system_info.user_cur)
  else
    begin
    write_console('('+inttostr(conn.socket)+') Connection reset by peer');
    conn.ch.Free;
    end;

  closesocket(socket);

  conn.Free;
end;

// command stuff
procedure registerCommand(name : string; func : COMMAND_FUNC);
var
   g : GCommandFunc;
   node : GListNode;
begin
  node := func_list.head;

  while (node <> nil) do
    begin
    g := node.element;

    if (g.name = name) or (pointer(@g.func) = pointer(@func)) then
      begin
      bugreport('registerCommand', 'mudthread.pas', 'Command ' + name + ' registered twice.', 'Command ' + name + ' registered twice.');
      exit;
      end;

    node := node.next;
    end;

  g := GCommandFunc.Create;

  g.name := name;
  g.func := func;

  func_list.insertLast(g);
end;

procedure registerCommands;
begin
  registerCommand('do_quit', do_quit);
  registerCommand('do_save', do_save);
  registerCommand('do_afk', do_afk);
  registerCommand('do_help', do_help);
  registerCommand('do_remort', do_remort);
  registerCommand('do_delete', do_delete);
  registerCommand('do_wimpy', do_wimpy);
  registerCommand('do_time', do_time);
  registerCommand('do_weather', do_weather);
  registerCommand('do_look', do_look);
  registerCommand('do_inventory', do_inventory);
  registerCommand('do_equipment', do_equipment);
  registerCommand('do_score', do_score);
  registerCommand('do_stats', do_stats);
  registerCommand('do_who', do_who);
  registerCommand('do_title', do_title);
  registerCommand('do_group', do_group);
  registerCommand('do_follow', do_follow);
  registerCommand('do_armor', do_armor);
  registerCommand('do_config', do_config);
  registerCommand('do_visible', do_visible);
  registerCommand('do_trophy', do_trophy);
  registerCommand('do_ditch', do_ditch);
  registerCommand('do_world', do_world);
  registerCommand('do_where', do_where);
  registerCommand('do_kill', do_kill);
  registerCommand('do_north', do_north);
  registerCommand('do_south', do_south);
  registerCommand('do_east', do_east);
  registerCommand('do_west', do_west);
  registerCommand('do_up', do_up);
  registerCommand('do_down', do_down);
  registerCommand('do_sleep', do_sleep);
  registerCommand('do_wake', do_wake);
  registerCommand('do_meditate', do_meditate);
  registerCommand('do_rest', do_rest);
  registerCommand('do_sit', do_sit);
  registerCommand('do_stand', do_stand);
  registerCommand('do_flee', do_flee);
  registerCommand('do_flurry', do_flurry);
  registerCommand('do_assist', do_assist);
  registerCommand('do_disengage', do_disengage);
  registerCommand('do_cast', do_cast);
  registerCommand('do_bash', do_bash);
  registerCommand('do_kick', do_kick);
  registerCommand('do_fly', do_fly);
  registerCommand('do_sneak', do_sneak);
  registerCommand('do_spells', do_spells);
  registerCommand('do_skills', do_skills);
  registerCommand('do_learn', do_learn);
  registerCommand('do_practice', do_practice);
  registerCommand('do_enter', do_enter);
  registerCommand('do_search', do_search);
  registerCommand('do_backstab', do_backstab);
  registerCommand('do_circle', do_circle);
  registerCommand('do_tell', do_tell);
  registerCommand('do_reply', do_reply);
  registerCommand('do_suggest', do_suggest);
  registerCommand('do_pray', do_pray);
  registerCommand('do_emote', do_emote);
  registerCommand('do_shutdown', do_shutdown);
  registerCommand('do_echo', do_echo);
  registerCommand('do_wizinvis', do_wizinvis);
  registerCommand('do_sla', do_sla);
  registerCommand('do_slay', do_slay);
  registerCommand('do_affects', do_affects);
  registerCommand('do_socials', do_socials);
  registerCommand('do_advance', do_advance);
  registerCommand('do_get', do_get);
  registerCommand('do_wear', do_wear);
  registerCommand('do_remove', do_remove);
  registerCommand('do_drop', do_drop);
  registerCommand('do_swap', do_swap);
  registerCommand('do_drink', do_drink);
  registerCommand('do_eat', do_eat);
  registerCommand('do_scalp', do_scalp);
  registerCommand('do_give', do_give);
  registerCommand('do_throw', do_throw);
  registerCommand('do_alias', do_alias);
  registerCommand('do_clanadd', do_clanadd);
  registerCommand('do_clanremove', do_clanremove);
  registerCommand('do_clan', do_clan);
  registerCommand('do_brag', do_brag);
  registerCommand('do_force', do_force);
  registerCommand('do_restore', do_restore);
  registerCommand('do_goto', do_goto);
  registerCommand('do_transfer', do_transfer);
  registerCommand('do_peace', do_peace);
  registerCommand('do_areas', do_areas);
  registerCommand('do_connections', do_connections);
  registerCommand('do_uptime', do_uptime);
  registerCommand('do_grace', do_grace);
  registerCommand('do_open', do_open);
  registerCommand('do_close', do_close);
  registerCommand('do_consider', do_consider);
  registerCommand('do_scan',  do_scan);
  registerCommand('do_sacrifice', do_sacrifice);
  registerCommand('do_bgset', do_bgset);
  registerCommand('do_battle', do_battle);
  registerCommand('do_auction', do_auction);
  registerCommand('do_bid', do_bid);
  registerCommand('do_balance', do_balance);
  registerCommand('do_withdraw', do_withdraw);
  registerCommand('do_deposit', do_deposit);
  registerCommand('do_list', do_list);
  registerCommand('do_buy', do_buy);
  registerCommand('do_sell', do_sell);
  registerCommand('do_rescue', do_rescue);
  registerCommand('do_disconnect', do_disconnect);
  registerCommand('do_wizhelp', do_wizhelp);
  registerCommand('do_rstat', do_rstat);
  registerCommand('do_pstat', do_pstat);
  registerCommand('do_ostat', do_ostat);
  registerCommand('do_report', do_report);
  registerCommand('do_destroy', do_destroy);
  registerCommand('do_loadup', do_loadup);
  registerCommand('do_freeze', do_freeze);
  registerCommand('do_silence', do_silence);
  registerCommand('do_log', do_log);
  registerCommand('do_snoop', do_snoop);
  registerCommand('do_switch', do_switch);
  registerCommand('do_return', do_return);
  registerCommand('do_sconfig', do_sconfig);
  registerCommand('do_track', do_track);
  registerCommand('do_bamfin', do_bamfin);
  registerCommand('do_bamfout', do_bamfout);
  registerCommand('do_mload', do_mload);
  registerCommand('do_oload', do_oload);
  registerCommand('do_mfind', do_mfind);
  registerCommand('do_ofind', do_ofind);
  registerCommand('do_put', do_put);
  registerCommand('do_sset', do_sset);
  registerCommand('do_taunt', do_taunt);
  registerCommand('do_nourish', do_nourish);
  registerCommand('do_mana', do_mana);
  registerCommand('do_fill', do_fill);
  registerCommand('do_unlock', do_unlock);
  registerCommand('do_lock', do_lock);
  registerCommand('do_pset', do_pset);
  registerCommand('do_revive', do_revive);
  registerCommand('do_setpager', do_setpager);
  registerCommand('do_autoloot', do_autoloot);
  registerCommand('do_autosac', do_autosac);
  registerCommand('do_password', do_password);
  registerCommand('do_ban', do_ban);
  registerCommand('do_allow', do_allow);
  registerCommand('do_last', do_last);
  registerCommand('do_unlearn', do_unlearn);
  registerCommand('do_hashstats', do_hashstats);
  registerCommand('do_keylock', do_keylock);
  registerCommand('do_take', do_take);
  registerCommand('do_holywalk', do_holywalk);
  registerCommand('do_prename', do_prename);
  registerCommand('do_peek', do_peek);
  registerCommand('do_ocreate', do_ocreate);
  registerCommand('do_oedit', do_oedit);
  registerCommand('do_olist', do_olist);
  registerCommand('do_redit', do_redit);
  registerCommand('do_rlink', do_rlink);
  registerCommand('do_rmake', do_rmake);
  registerCommand('do_rclone', do_rclone);
  registerCommand('do_aassign', do_aassign);
  registerCommand('do_ranges', do_ranges);
  registerCommand('do_acreate', do_acreate);
  registerCommand('do_aset', do_aset);
  registerCommand('do_astat', do_astat);
  registerCommand('do_raceinfo', do_raceinfo);
  registerCommand('do_checkarea', do_checkarea);
  registerCommand('do_savearea', do_savearea);
  registerCommand('do_loadarea', do_loadarea);
  registerCommand('do_reset', do_reset);
  registerCommand('do_map', do_map);
  registerCommand('do_holylight', do_holylight);
  registerCommand('do_prompt', do_prompt);
  registerCommand('do_at', do_at);
  registerCommand('do_namegen', do_namegen);
  registerCommand('do_note', do_note);
  registerCommand('do_board', do_board);
  registerCommand('do_apropos', do_apropos);
  registerCommand('do_say', do_say);
  registerCommand('do_channel', do_channel);
  registerCommand('do_vnums', do_vnums);
  registerCommand('do_aranges', do_aranges);
  registerCommand('do_rlist', do_rlist);
  registerCommand('do_rdelete', do_rdelete);
  registerCommand('do_coordgen', do_coordgen);
  registerCommand('do_findpath', do_findpath);
  registerCommand('do_reload', do_reload);
end;

begin
  func_list := GDLinkedList.Create;
end.