// $Id: clean.pas,v 1.9 2001/05/11 14:25:02 druid Exp $
unit clean;
interface
uses
    Windows,
    Classes;
{ This is the misc. thread function, also known as the 'simple task thread'.
  This thread takes care of autosaves and autocleans,
  and serves as a watchdog for both the timer thread and the individual
  user threads. }
type GCleanThread = class(TThread)
     private
       t_log : string;
     protected
       procedure AutoSave;
       procedure SyncWrite;
       procedure SyncWritelog(s:string);
       procedure Execute; override;
     public
       constructor Create;
     end;
implementation
uses
    SysUtils,
    chars,
    conns,
    constants,
    dtypes,
    area,
    util,
    timers,
    Winsock2,
    mudthread,
    mudsystem;
constructor GCleanThread.Create;
begin
  inherited Create(false);
  SyncWritelog('Started cleanup thread.');
  SetThreadPriority(Handle, THREAD_PRIORITY_IDLE);
  freeonterminate := true;
end;
procedure GCleanThread.SyncWrite;
begin
  write_console(t_log);
end;
procedure GCleanThread.SyncWritelog(s:string);
begin
  t_log := s;
  Synchronize(SyncWrite);
end;
procedure GCleanThread.AutoSave;
var
   ch : GCharacter;
   node : GListNode;
begin
  SyncWritelog('Autosaving characters...');
  node := char_list.head;
  while (node <> nil) do
    begin
    ch := node.element;
    if (not ch.IS_NPC) then
      GPlayer(ch).save(ch.name^);
    node := node.next;
    end;
end;
// kill a non-responsive thread after 30 seconds
const
     THREAD_TIMEOUT = 0.5 / 1440.0;
procedure GCleanThread.Execute;
var
   a : integer;
   node, node_next : GListNode;
   conn : GConnection;
begin
  a := 0;
  repeat
    sleep(10000);
    inc(a);
    if (a = 15) then
      begin
      AutoSave;
      a := 0;
      end;
{$IFNDEF NOCRASHDETECTION}
    node := connection_list.head;
    while (node <> nil) do
      begin
      node_next := node.next;
      conn := node.element;
      if (GGameThread(conn.thread).last_update + THREAD_TIMEOUT < Now()) then
        begin
        bugreport('update_main', 'timers.pas', 'Thread of ' + conn.ch.name^ + ' probably died',
                  'The server has detected a malfunctioning user thread and will terminate it.');
        conn.ch.emptyBuffer;
        conn.send('Your previous command possibly triggered an illegal action on this server.'#13#10);
        conn.send('The administration has been notified, and you have been disconnected'#13#10);
        conn.send('to prevent any data loss.'#13#10);
        conn.send('Your character is linkless, and it would be wise to reconnect as soon'#13#10);
        conn.send('as possible.'#13#10);
        closesocket(conn.socket);
        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);
        conn.Free;
        TerminateThread(conn.thread.handle, 1);
        node := node_next;
        continue;
        end;
        
      node := node_next;
      end;
{$ENDIF}
    if (GTimerThread(timer_thread).last_update + THREAD_TIMEOUT < Now()) then
      begin
      bugreport('update_main', 'timers.pas', 'Timer thread probably died',
                'The server has detected that the timer is malfunctioning and will try to restart it.');
      TerminateThread(timer_thread.handle, 1);
      timer_thread := GTimerThread.Create;
      end;
    cleanChars;
    cleanObjects;
  until (Terminated);
  SyncWritelog('Simple task thread terminated.');
end;
end.