/
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: timers.pas,v 1.12 2001/05/11 14:24:24 druid Exp $

unit timers;

interface

uses
    Windows,
    SysUtils,
    Classes,
    skills,
    dtypes,
    chars;


type
    TIMER_FUNC = procedure;
    SPEC_FUNC = procedure(ch, victim : GCharacter; sn : GSkill);

    GTimer = class
      name : string;
      timer_func : TIMER_FUNC;
      counter, timeout : integer;
      looping : boolean;

      constructor Create(name_ : string; func_ : TIMER_FUNC; timeout_ : integer; looping_ : boolean);
    end;

    GSpecTimer = class (GTimer)
      spec_func : SPEC_FUNC;

      ch, victim : GCharacter;

      timer_type : integer;

      sn : GSkill;

      constructor Create(timer_type_ : integer; func_ : SPEC_FUNC; timeout_ : integer; ch_, victim_ : GCharacter; sn_ : GSkill);
    end;

    GTimerThread = class (TThread)
      last_update : TDateTime;

      procedure Execute; override;
      constructor Create;
    end;

var
   timer_list : GDLinkedList;

procedure registerTimer(name_ : string; func_ : TIMER_FUNC; timeout_ : integer; looping_ : boolean); overload;
procedure registerTimer(timer_type_ : integer; func_ : SPEC_FUNC; timeout_ : integer; ch_, victim_ : GCharacter; sn_ : GSkill); overload;

procedure unregisterTimer(name_ : string); overload;
procedure unregisterTimer(ch : GCharacter; timer_type : integer); overload;

function hasTimer(ch : GCharacter; timer_type : integer) : GTimer;

implementation

uses
    Winsock2,
    constants,
    mudsystem,
    util,
    mudthread,
    update,
    debug,
    area,
    conns,
    Channels;


// GTimer
constructor GTimer.Create(name_ : string; func_ : TIMER_FUNC; timeout_ : integer; looping_ : boolean);
begin
  inherited Create;

  name := name_;
  timer_func := func_;
  timeout := timeout_;
  counter := timeout_;
  looping := looping_;
end;

// GSpecTimer
constructor GSpecTimer.Create(timer_type_ : integer; func_ : SPEC_FUNC; timeout_ : integer; ch_, victim_ : GCharacter; sn_ : GSkill);
begin
  inherited Create(timer_names[timer_type_], nil, timeout_, false);

  spec_func := func_;
  timer_type := timer_type_;
  ch := ch_;
  victim := victim_;
  sn := sn_;
end;


// GTimerThread
constructor GTimerThread.Create;
begin
  inherited Create(false);
end;

procedure GTimerThread.Execute;
var
   node, node_next : GListNode;
   timer : GTimer;
   spec : GSpecTimer;
begin
  while (not Terminated) do
    begin
    last_update := Now();

    node := timer_list.head;

    while (node <> nil) do
      begin
      timer := node.element;
      node_next := node.next;

      try
      dec(timer.counter);

      if (timer.counter = 0) then
        begin
        if (timer is GSpecTimer) then
          begin
          spec := GSpecTimer(timer);

          if (assigned(spec.spec_func)) then
            spec.spec_func(spec.ch, spec.victim, spec.sn);
          end
        else
          if (assigned(timer.timer_func)) then
            timer.timer_func;

        if (not timer.looping) then
          begin
          timer_list.remove(node);
          timer.Free;
          end
        else
          timer.counter := timer.timeout;
        end;

      except
        on E : EExternal do
          begin
          bugreport('GTimerThread.Execute', 'timers.pas', 'Timer "' + timer.name + '" failed to execute correctly', 'Timer "' + timer.name + '" failed to execute correctly');
          outputError(E);
          end
        else
          bugreport('GTimerThread.Execute', 'timers.pas', 'Timer "' + timer.name + '" failed to execute correctly', 'Timer "' + timer.name + '" failed to execute correctly');

{        if (timer is GSpecTimer) then
          begin
          timer_list.remove(node);
          timer.Free;
          end
        else
          timer.counter := timer.timeout; }
      end;

      node := node_next;
      end;

    Sleep(250);
    end;
end;

procedure registerTimer(name_ : string; func_ : TIMER_FUNC; timeout_ : integer; looping_ : boolean);
var
   timer : GTimer;
begin
  timer := GTimer.Create(name_, func_, timeout_, looping_);

  timer_list.insertLast(timer);
end;

procedure registerTimer(timer_type_ : integer; func_ : SPEC_FUNC; timeout_ : integer; ch_, victim_ : GCharacter; sn_ : GSkill); overload;
var
   timer : GSpecTimer;
begin
  timer := GSpecTimer.Create(timer_type_, func_, timeout_, ch_, victim_, sn_);

  timer_list.insertLast(timer);
end;

procedure unregisterTimer(name_ : string);
var
   timer : GTimer;
   node : GListNode;
begin
  node := timer_list.head;

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

    if (timer.name = name_) then
      begin
      timer_list.remove(node);
      timer.Free;
      break;
      end;

    node := node.next;
    end;
end;

procedure unregisterTimer(ch : GCharacter; timer_type : integer);
var
   timer : GTimer;
   spec : GSpecTimer;
   node : GListNode;
begin
  node := timer_list.head;

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

    if (timer is GSpecTimer) then
      begin
      spec := GSpecTimer(timer);

      if (spec.ch = ch) and (spec.timer_type = timer_type) then
        begin
        timer_list.remove(node);
        timer.Free;
        break;
        end;
      end;

    node := node.next;
    end;
end;

function hasTimer(ch : GCharacter; timer_type : integer) : GTimer;
var
   timer : GTimer;
   spec : GSpecTimer;
   node : GListNode;
begin
  Result := nil;

  node := timer_list.head;

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

    if (timer is GSpecTimer) then
      begin
      spec := GSpecTimer(timer);

      if (spec.ch = ch) and (spec.timer_type = timer_type) then
        begin
        Result := timer;
        break;
        end;
      end;

    node := node.next;
    end;
end;



// main timers
procedure update_auction;
begin
  if (auction_good.pulse > 0) then
    dec(auction_good.pulse)
  else
  if (auction_good.item <> nil) then
    begin
    auction_good.update;
    auction_good.pulse := CPULSE_AUCTION;
    end;

  if (auction_evil.pulse > 0) then
    dec(auction_evil.pulse)
  else
  if (auction_evil.item <> nil) then
    begin
    auction_evil.update;
    auction_evil.pulse := CPULSE_AUCTION;
    end;
end;

procedure update_main;
var
   node, node_next : GListNode;
   conn : GConnection;
begin
  node := connection_list.head;

  while (node <> nil) do
    begin
    node_next := node.next;
    conn := node.element;

    inc(conn.idle);

    if (((conn.state = CON_NAME) and (conn.idle > IDLE_NAME)) or
     ((conn.state <> CON_PLAYING) and (conn.idle > IDLE_NOT_PLAYING)) or
      (conn.idle > IDLE_PLAYING)) and ((conn.ch <> nil) and (not conn.ch.IS_IMMORT)) then
       begin
       conn.send(#13#10'You have been idle too long. Disconnecting.'#13#10);
       conn.thread.terminate;

       node := node_next;
       continue;
       end;

    if (conn.state=CON_PLAYING) and (not conn.ch.in_command) then
      GPlayer(conn.ch).emptyBuffer;

    if (conn.state=CON_PLAYING) and (conn.ch.wait>0) then
      dec(conn.ch.wait);

    node := node_next;
    end;
end;

procedure update_gamehour;
var
   node, node_next : GListNode;
   area : GArea;
   ch : GCharacter;
begin
  status := GetHeapStatus;

  update_affects;
  update_tracks;

  { update age of areas and reset if hit timer }
  node := area_list.head;

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

    area.update;

    node := node.next;
    end;

  node := char_list.head;

  while (node <> nil) do
    begin
    ch := node.element;
    node_next := node.next;

    if (not ch.IS_NPC) and (IS_SET(GPlayer(ch).flags, PLR_LINKLESS)) then
      begin
      inc(GPlayer(ch).ld_timer);

      if (GPlayer(ch).ld_timer > IDLE_LINKDEAD) then
        begin
        node := node_next;
        GPlayer(ch).quit;
        continue;
        end;
      end;

    node := node_next;
    end;
end;

procedure update_sec;
begin
  calculateonline;

  if (boot_info.timer >= 0) then
    begin
    dec(boot_info.timer);

    case boot_info.timer of
      60,30,10,5 :  begin
                    case boot_info.boot_type of
                      BOOTTYPE_SHUTDOWN:begin
                                        write_log(inttostr(boot_info.timer)+' seconds till shutdown');
                                        to_channel(nil, '$B$1 ---- Server $3shutdown$1 in $7' + inttostr(boot_info.timer) + '$1 seconds! ----',CHANNEL_ALL,AT_REPORT);
                                        end;
                        BOOTTYPE_REBOOT:begin
                                        write_log(inttostr(boot_info.timer)+' seconds till reboot');
                                        to_channel(nil, '$B$1 ---- Server $3reboot$1 in $7' + inttostr(boot_info.timer) + '$1 seconds! ----',CHANNEL_ALL,AT_REPORT);
                                        end;
                      BOOTTYPE_COPYOVER:begin
                                        write_log(inttostr(boot_info.timer)+' seconds till reboot');
                                        to_channel(nil, '$B$1 ---- Server $3copyover$1 in $7' + inttostr(boot_info.timer) + '$1 seconds! ----',CHANNEL_ALL,AT_REPORT);
                                        end;
                    end;
                    end;

      0 :           begin
                    case boot_info.boot_type of
                      BOOTTYPE_SHUTDOWN:begin
                                        write_log('Timer reached zero, starting shutdown now');
                                        to_channel(nil, '$B$1 ---- Server will $3shutdown $7NOW!$1 ----',CHANNEL_ALL,AT_REPORT);
                                        end;
                        BOOTTYPE_REBOOT:begin
                                        write_log('Timer reached zero, starting reboot now');
                                        to_channel(nil, '$B$1 ---- Server will $3reboot $7NOW!$1 ----',CHANNEL_ALL,AT_REPORT);
                                        end;
                      BOOTTYPE_COPYOVER:begin
                                        write_log('Timer reached zero, starting copyover now');
                                        to_channel(nil, '$B$1 ---- Server will $3copyover $7NOW!$1 ----',CHANNEL_ALL,AT_REPORT);
                                        end;
                    end;

                    boot_type := boot_info.boot_type;
                    grace_exit := true;

                    halt;
                    end;
    end;
    end;

  if (bg_info.count > 0) then
    begin
    dec(bg_info.count);

    case bg_info.count of
      60,30,10,5,2 : battlegroundMessage;
      0 : startBattleground;
    end;
    end;

  regenerate_chars;
end;


begin
  timer_list := GDLinkedList.Create;

  registerTimer('main', update_main, 1, true);
  registerTimer('auction', update_auction, 1, true);
  registerTimer('gamehour', update_gamehour, CPULSE_GAMEHOUR, true);
  registerTimer('second', update_sec, CPULSE_PER_SEC, true);
end.