<?

// 22.11.2006

class recorder extends users {
      public $proc;
      public $procid;
      public $num;
      public $type = "recorder";
      public $recordmode = "no";
      public $live = "no";
      public $stdin;
      public $stdout;
      public $stderr;
      public $port;
      public $game = -1;
      public $demolinked = false;
      public $broadcasters = array();
      public $autodisc = 1;
      public $pausedat = 0;
      public $advert = 0;
      public $readynum = 4;
   function __construct($ip, $gpw, $ettvpw, $slots = 0) {
      global $procs, $pipes, $pipetoproc;

      $this->source = $ip;
      $this->readynum = READYNUM;

      $ip = escapeshellarg($ip);
      $gpw = escapeshellarg($gpw);
      $ettvpw = escapeshellarg($ettvpw);
      $slots = intval($slots);

      $recnum = 1;
      while(findrecorder($recnum)) {
         $recnum++;
      }

      $descriptorspec = array(0 => array("pipe", "r"), 2 => array("pipe", "w"));

      if($slots != 0 && defined('HUBNAME')) {
         $hostname = constant('HUBNAME');
      }
      else {
         $hostname = constant('RECNAME');
      }
      if($slots != 0 && defined('HUBCLIENT')) {
         $clientname = constant('HUBCLIENT');
      }
      else {
         $clientname = constant('CLIENTNAME');
      }

      $hunk = round(48 + ($slots * 0.8));
      if($slots < 2) {
         $slots = 2;
      }

      $cmd = "./ettv.x86 +set net_port 4989".$recnum." +set ettv_mtu 32000 +set net_ip ".ETTVIP." +set sv_hostname \"".$hostname.$recnum."\" +set com_hunkMegs $hunk +set sv_maxclients $slots +set ettv_sv_maxslaves $slots +set fs_game etpro +set fs_homepath rec".$recnum." +exec recorder.cfg +set ettv_clientname \"".$clientname.$recnum."\" +tv connect $ip $ettvpw $gpw";

      $this->proc = proc_open($cmd, $descriptorspec, $mypipes, RECDIR);

      if($this->procid < 1) {
         $this->procid = intval($this->proc);  // WARNING: procid is not neccesarly the real procid anymore, its more a way to create a unique id !!
      }

      $pipes['stdin'][$this->procid] = $mypipes[0];
      $pipes['stdout'][$this->procid] = $mypipes[2];

      $pipetoproc[intval($mypipes[2])] = $this->procid; // This allows to find a proc belonging to a task faster

      $this->num = $recnum;
      debug("Recorder $this->num created\n");

      $this->logfile = fopen("recorder$recnum.log", "a");

   }

   function restart() {
      debug("[$this->type$this->num] Restart of server requested, but not implemented ;-)\n");
   }

   function oneready() {
      parent::oneready();
      $this->checkreadynum();

   }

   function checkreadynum() {
      if($this->recordmode == "ready") {
         if($this->readyplayers >= $this->readynum && !$this->recording) {
            debug($this->readynum." Players ready, we should start recording\n");
            $this->record();
         }
      }
   }

   function roundstart() {
      parent::roundstart();
      if($this->recordmode == "ready") {
         if(!$this->recording) {
            debug("Round was started by ref - start recording right away\n");
            $this->record();
         }
      }
   }

   function gameinit() {
      parent::gameinit();
      if($this->recordmode == "connect") {
         if(!$this->recording) {
            $this->record();
         }
      }
   }

   function gameanounce() {
      global $procs;
      if($this->numplayers >= NEXTLIVESTARTPLAYERS && !$this->demolinked) {
         foreach($procs AS &$proc) {
            if($proc->type == "replayer" && $this->broadcasters[$proc->num]) {
               $proc->send("cp ".CLIVE."Next Live: ".C1."$this->gamename");
            }
         }
         unset($proc);
         addtimer(time() + NEXTLIVEINTERVAL, "if(\$procs[$this->procid]) \$procs[$this->procid]->gameanounce();");
         $this->anouncer = true;
      }
      else {
         $this->anouncer = false;
      }
   }

   function warmupstart() {
      parent::warmupstart();
   }

   function werecord($demo) {
      parent::werecord($demo);
      global $procs, $demolist;
      if($this->game < 0) {
         $this->game = count($demolist);
         $demolist[$this->game] = array("dir" => $this->recorddir, "name" => $this->gamename, "demos" => array());
         $out = fopen(DEMODIR."/$this->recorddir/name.txt","w");
         fputs($out, "$this->gamename\n");
         fclose($out);
      }
      $demolist[$this->game]['demos'][] = $demo;
      $this->map = count($demolist[$this->game]['demos']) - 1;
      $demolist[$this->game]['recordtime'][$this->map] = time(); // recordtime is a temporary value to prevent autoreplaying demos earlier than ettv_delay
      foreach($procs AS &$proc) {
         if($proc->type == "replayer" && $proc->liverec == $this->num) {
            $proc->playdelayed($this->game, $this->map);
         }
      }
      unset($proc);
   }

   function recordmode($mode) {
      if($mode == "connect") {
         if($this->connected && !$this->recording) {
            $this->record();
         }
         $this->recordmode = $mode;
         return true;
      }
      elseif($mode == "ready") {
         $this->recordmode = $mode;
         $this->checkreadynum();
         return true;
      }
      else {
         return false;
      }
   }

   function isliveon($rep) {
      global $procs;
      $this->broadcasters[$rep] = true;
      if($this->demolinked) {
         $rep_proc = findreplayer($rep);
         if($rep_proc) {
            $procs[$rep_proc]->isliveon($this->num);
         }
      }
   }

   function endofdemo() {
      if($this->recording) {
         global $procs;
         foreach($procs AS &$proc) {
            if($proc->type == "replayer" && $proc->liverec == $this->num) {
               $proc->endoflive();
            }
         }
         unset($proc);
      }
   }

   function record() {
      global $procs;
      if(!$this->demolinked) {
         $timestamp = date("Y.m.d-H.i")."_$this->num";
         @mkdir(DEMODIR."/$timestamp");
         @rmdir(RECDIR."/rec$this->num/etpro/demos");
         @unlink(RECDIR."/rec$this->num/etpro/demos");
         symlink(DEMODIR."/$timestamp", RECDIR."/rec$this->num/etpro/demos");

         $this->recorddir = $timestamp;
         $this->demolinked = true;

         foreach($this->broadcasters AS $rep => $tmp) {
            $rep_proc = findreplayer($rep);
            if($rep_proc) {
               $procs[$rep_proc]->isliveon($this->num);
            }
         }
      }

      $this->send("set ettv_autorecord 1\n");
      $this->send("record\n");
      $this->recording = true;
      $this->pausedat = 0;
   }

   function setgamename($name) {
      $this->gamename = $name;
      if($this->game > -1) {
         global $demolist;
         $demolist[$this->game]['name'] = $this->gamename;
         $out = fopen(DEMODIR."/$this->recorddir/name.txt","w");
         fputs($out, "$this->gamename\n");
         fclose($out);
      }
   }

   function pauserecord() {
      $this->endofdemo();
      $this->send("set ettv_autorecord 0\n");
      $this->send("stoprecord\n");
      $this->recording = false;
      $this->pausedat = time();
   }

   function playercount() {
      parent::playercount();
      if($this->demolinked) {
         if($this->recording) {
            if($this->playerstartnum != -1 && $this->numplayers <= ($this->playerstartnum * PAUSEPERC)) {
               $this->pauserecord();
               debug("[$this->type$this->num] Paused recording - not enough players\n");
               addtimer(time() + AUTODISCTIME, "if(\$procs[$this->procid]) \$procs[$this->procid]->pausetimeout();");
               debug("[$this->type$this->num] Recorder will stop in ".AUTODISCTIME."sec");
            }
         }
         else {
            if($this->playerstartnum != -1 && $this->numplayers >= ($this->playerstartnum * RESUMEPERC)) {
               $this->record();
               debug("[$this->type$this->num] Resumed record with $this->numplayers players\n");
            }
         }
      }
      if(!$this->recording && !$this->anouncer && $this->numplayers >= NEXTLIVESTARTPLAYERS && $this->recordmode != "no") {
         $this->gameanounce();
      }
   }

   function pausetimeout() {
      if($this->pausedat != 0 && $this->pausedat <= time() - AUTODISCTIME) {
         if($this->autodisc) {
            debug("[$this->type$this->num] No action for ".AUTODISCTIME."sec - shutting down\n");
            $this->shutdown();
         }
         else {
            debug("[$this->type$this->num] No action for ".AUTODISCTIME."sec - Recording stopped\n");
            $this->stoprecord();
         }
      }
   }

   function stoprecord() {
      @unlink(RECDIR."/rec$this->num/etpro/demos");

      $this->send("set ettv_autorecord 0\n");
      $this->send("stoprecord\n");

      $this->endofdemo();
      $this->game = -1;
      $this->recording = false;
      $this->demolinked = false;
   }

   function hopserver($ip, $gpw, $ettvpw) {
      $this->source = $ip;
      $this->send("tv connect $ip $ettvpw $gpw");
   }

   function readynum($num) {
      $this->readynum = $num;
      $this->checkreadynum();
   }

   function shutdown() {
      global $procs, $pipes;

      if($this->recoding) {
         $this->stoprecord();
      }
      foreach($procs AS &$proc) {
         if($proc->type == "replayer" && $proc->liverec == $this->num) {
            $proc->liverec = -1;
         }
      }
      unset($proc);

      $this->send("quit");
      addtimer(time() + 5, "unset(\$procs[$this->procid]);");
      debug("Sent quit to Recorder $this->num");

      $this->defunct = true;
   }

   function closeproc() {
      global $pipes;
      debug("Recorder $this->num closing the process");

      @fclose($pipes['stdin'][$this->procid]);
      unset($pipes['stdin'][$this->procid]);

      @fclose($pipes['stdout'][$this->procid]);
      unset($pipes['stdout'][$this->procid]);

      proc_terminate($this->proc);
      @proc_close($this->proc);
   }

   function __destruct() {
      $this->closeproc();
      debug("Recorder $this->num object removed");
   }
}