Advertisement
  1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Issues with block logger.

Comments in 'Plugin Development' started by Dinokiller, Jan 19, 2014.

  1. Dinokiller
    Offline

    Dinokiller Notable Member

    Joined:
    Sep 22, 2013
    Posts:
    376
    Minecraft User:
    Dinokiller_
    I'm making a block logger which is able to rollback blocks. I have spent 3 days on this and a long time debugging and for some reason the rollback command is not working, It doesn't show a error message it just does nothing.

    PHP:
    <?php

    /*
    __PocketMine Plugin__
    name=DinoLogger
    description=
    version=0.1.0
    author=Dinokiller
    class=DinoLogger
    apiversion=11
    */

    class DinoLogger implements Plugin{
      private 
    $api$config$db;
      private 
    $inspect = array();
     
      public function 
    __construct(ServerAPI $api$server false){
        
    define("DINOLOGGER_DEBUG"true);
        
    $this->api $api;
      }
      public function 
    init(){
        
    $this->db = new SQLite3($this->api->plugin->configPath($this) . "log.db");
        
    console("[INFO] [DinoLogger] Loaded database sucessfully.");
        
    $this->db->exec("CREATE TABLE IF NOT EXISTS blocks (id INT, meta INT, x INT, y INT, z INT,level TEXT, time INT, player TEXT, tool TEXT, placed BIT);");
        
    $this->db->exec("CREATE TABLE IF NOT EXISTS tiles (class INT, data TEXT, x INT, y INT, z INT, level TEXT, time INT);");
        
    $this->api->console->register("dinologger""[action] [args]", array($this"handleCommand"));
        
    $this->api->console->alias("dl","dinologger");
        
    $this->config = new Config($this->api->plugin->configPath($this) ."config.yml"CONFIG_YAML,array(
          
    "max-rollback-radius" => 32,
          
    "min-rollback-radius" => 1,     
        ));
        
    $this->config $this->api->plugin->readYAML($this->api->plugin->configPath($this) ."config.yml");
        
    $this->api->addHandler("tile.update", array($this"eventHandler"),30);
        
    $this->api->addHandler("player.block.touch", array($this"eventHandler"),30);
        
    $this->api->addHandler("player.block.break", array($this"eventHandler"),30);
        
    $this->api->addHandler("player.block.place", array($this"eventHandler"),30);
        
    $this->api->addHandler("player.quit", array($this"eventHandler"),15);
        
    $this->api->addHandler("server.close", array($this"eventHandler"),15);
      }
     
      public function 
    rollback($x null$y null$z nullLevel $level$radius null$ms null){
        if(
    $x===null || $y===null || $z===null || $ms===null || $radius===null || $radius<|| $radius>256 || !$level instanceof Level){
          return 
    false;
        }
        
    $rbblock 0;
        
    $rbtile 0;
        
    $start time();
        
    $time time() - $ms;
        
    $blocks $this->db->prepare("SELECT * FROM blocks WHERE x >= :x1 AND x <= :x2 AND y <= :y1 AND y >= :y2 AND z <= :z1 AND z >= :z2 AND level = :level AND time <= :time");
        
    $blocks->bindValue(":x1"$x $radius);
        
    $blocks->bindValue(":y1"$y $radius);
        
    $blocks->bindValue(":z1"$z $radius);
        
    $blocks->bindValue(":x2"$x $radius);
        
    $blocks->bindValue(":y2"$y $radius);
        
    $blocks->bindValue(":z2"$z $radius);
        
    $blocks->bindValue(":time"$time);
        
    $blocks->bindValue(":level"$level->getName());
        
    $blocks $blocks->execute();
        
    $blocks $blocks->fetchArray();
        if(
    DINOLOGGER_DEBUG){
          
    var_dump($blocks);
        }
        if(
    countDim($blocks)===1){
          
    $blocks[0] = $blocks;
        }
        foreach(
    $blocks as &$i){
          if(
    $i["placed"]===0){
            
    $level->setBlock(new Vector3($i["x"], $i["y"], $i["z"]),$i["id"],$i["meta"]);
            
    $rbblock++;
            
    $n false;
            switch(
    $i["id"]){
              case 
    SIGN_POST:
              case 
    WALL_SIGN:
                
    $n TILE_SIGN;
              break;
              case 
    CHEST:
                
    $n TILE_CHEST;
              break;
              case 
    FURNACE:
              case 
    BURNING_FURNACE:
              case 
    LIT_FURNACE:
                
    $n TILE_FURNACE;
            }
            if(!
    $n===false){
              
    $tile $this->db->prepare("SELECT * FROM tiles WHERE class = :tile AND x = :x AND y = :y AND z = :z AND level = :level AND time = :time");
              
    $tile->bindValue(":x"$i["x"]);
              
    $tile->bindValue(":y"$i["y"]);
              
    $tile->bindValue(":z"$i["z"]);
              
    $tile->bindValue(":tile"$n);
              
    $tile->bindValue(":time"$time);
              
    $tile->bindValue(":level"$level->getName());
              
    $tile $tile->execute();
              
    $tile $tile->fetchArray();
              if(
    is_array($tile)){
                
    $tile["data"] = unserialize($tile["data"]);
                
    $this->api->tile->add($level$n$i["x"], $i["y"], $i["z"], $tile["data"]);
                
    $rbtile++;
              }       
            }
          }else{
            if(
    $i["time"]>$time){
              
    $level->setBlock(new Vector3($i["x"], $i["y"], $i["z"]), AIR);
              
    $rbblock++;
            }
          }
          unset(
    $i);
        }
        return array(
    time() - $start$rbtile$rbblock);
      }

      public function 
    eventHandler(&$data,$event){
        switch(
    $event){
          case 
    "player.quit":
            if(isset(
    $this->inspect[$data->username])){
              unset(
    $this->inspect[$data->username]);
            }
          break;
          case 
    "server.close":
            
    $this->api->plugin->writeYAML($this->api->plugin->configPath($this) ."config.yml"$this->config);
          break;
          case 
    "tile.update":
            
    $sql $this->db->prepare("INSERT OR REPLACE INTO tiles (class, data, x, y, z, level, time) VALUES (:tile, :data, :x, :y, :z, :level, :time);");
            
    $sql->bindValue(":tile"$data->class);
            
    $sql->bindValue(":data"serialize($data->data));
            
    $sql->bindValue(":x"$data->x);
            
    $sql->bindValue(":y"$data->y);
            
    $sql->bindValue(":z"$data->z);
            
    $sql->bindValue(":level"$data->level->getName());
            
    $sql->bindValue(":time"time());
            
    $sql->execute();
          break;
          case 
    "player.block.touch":
            
    $this->showLog($data["player"], $data["target"]->x$data["target"]->y$data["target"]->z);
          break;
          case 
    "player.block.place":
            if(
    $data["block"]->getID()===AIR){
              break;
            }
            
    $this->showLog($data["player"], $data["block"]->x$data["block"]->y$data["block"]->z);
            
    $sql $this->db->prepare("INSERT INTO blocks (id, meta, x, y, z, level, time, player, tool, placed) VALUES (:id, :meta, :x, :y, :z, :level, :time, :username, :tool, :placed);");
            
    $sql->bindValue(":id"$data["block"]->getID());
            
    $sql->bindValue(":meta"$data["block"]->getMetadata());
            
    $sql->bindValue(":x"$data["block"]->x);
            
    $sql->bindValue(":y"$data["block"]->y);
            
    $sql->bindValue(":z"$data["block"]->z);
            
    $sql->bindValue(":level"$data["block"]->level->getName());
            
    $sql->bindValue(":time"time());
            
    $sql->bindValue(":username"$data["player"]->username);
            
    $sql->bindValue(":tool""noTool");
            
    $sql->bindValue(":placed"1);
            
    $sql->execute();
            unset(
    $sql);
          break;
          case 
    "player.block.break":
            if(
    $data["target"]->getID()===AIR){
              break;
            }
            
    $this->showLog($data["player"], $data["target"]->x$data["target"]->y$data["target"]->z);
            
    $n false;
            
    $time time();
            switch(
    $data["target"]->getID()){
              case 
    SIGN_POST:
              case 
    WALL_SIGN:
                
    $n TILE_SIGN;
              break;
              case 
    CHEST:
                
    $n TILE_CHEST;
              break;
              case 
    FURNACE:
              case 
    BURNING_FURNACE:
              case 
    LIT_FURNACE:
                
    $n TILE_FURNACE;
            }
            if(!
    $n===false){
              
    $tile $this->api->tile->get(new Position($data["target"]->x$data["target"]->y$data["target"]->z$data["target"]->level));
              
    $sql $this->db->prepare("INSERT INTO tiles (class, data, x, y, z, level, time) VALUES (:tile, :data, :x, :y, :z, :level, :time);");
              
    $sql->bindValue(":tile"$n);
              
    $sql->bindValue(":data"serialize($tile->data));
              
    $sql->bindValue(":x"$tile->x);
              
    $sql->bindValue(":y"$tile->y);
              
    $sql->bindValue(":z"$tile->z);
              
    $sql->bindValue(":level"$tile->level->getName());
              
    $sql->bindValue(":time"$time);
              
    $sql->execute();
              unset(
    $sql);
            }
            
    $tool $data["item"];
            if(
    $tool->isTool()){
              
    $tool $tool->getName();
            }else{
              
    $tool "noTool";
            }
            
    $sql $this->db->prepare("INSERT INTO blocks (id, meta, x, y, z, level, time, player, tool, placed) VALUES (:id, :meta, :x, :y, :z, :level, :time, :username, :tool, 0);");
            
    $sql->bindValue(":id"$data["target"]->getID());
            
    $sql->bindValue(":meta"$data["target"]->getMetadata());
            
    $sql->bindValue(":x"$data["target"]->x);
            
    $sql->bindValue(":y"$data["target"]->y);
            
    $sql->bindValue(":z"$data["target"]->z);
            
    $sql->bindValue(":level"$data["target"]->level->getName());
            
    $sql->bindValue(":time"$time);
            
    $sql->bindValue(":username"$data["player"]->username);
            
    $sql->bindValue(":tool"$tool);
            
    $sql->execute();
            unset(
    $sql);
          break;
        }
      }
     
      public function 
    handleCommand($cmd,$params,$issuer){
        switch(
    $cmd){
          case 
    "dinologger":
            if(!
    $issuer instanceof Player) break;
            
    $subCommand $params[0];
            unset(
    $params[0]);
            switch(
    $subCommand){
              case 
    "i":
              case 
    "inspect":
                if(!isset(
    $this->inspect[$issuer->username]) || !$this->inspect[$issuer->username]===true){
                  
    $issuer->sendChat("Block inspector enabled do command again to turn off. Tap a block to see it's log.");
                  
    $this->inspect[$issuer->username] = true;
                }else{
                  
    $this->inspect[$issuer->username] = false;
                  
    $issuer->sendChat("Block inspector disabled.");
                }
              break;
              case 
    "r":
              case 
    "rollback":
                
    $params implode(" "$params);
                if(!
    strpos("h:")===false){
                  
    $n = (int)strpos("h:") + 2;
                  
    $i $n;
                  while(!
    $i===strlen($params)){
                    if(
    substr($params$i$i+1)===" "){
                      break;
                    }
                    
    $s .= substr($params$i$i+1);
                    
    $i++;
                  }
                  
    $s = (int)$s;
                  
    $hours $s;
                }
                if(!
    strpos("m:")===false){
                  
    $n = (int)strpos("m:") + 2;
                  
    $i $n;
                  while(!
    $i===strlen($params)){
                    if(!
    substr($params$i$i+1)===" "){
                      break;
                    }
                    
    $s .= substr($params$i$i+1);
                    
    $i++;
                  }
                  
    $s = (int)$s;
                  
    $mins $s;
                }
                if(!
    strpos("s:")===false){
                  
    $n = (int)strpos("s:") + 2;
                  
    $i $n;
                  while(!
    $i===strlen($params)){
                    if(!
    substr($params$i$i+1)===" "){
                      break;
                    }
                    
    $s .= substr($params$i$i+1);
                    
    $i++;
                  }
                  
    $s = (int)$s;
                  
    $secs $s;
                }
                if(!
    strpos("r:")===false){
                  
    $n = (int)strpos("s:") + 2;
                  
    $i $n;
                  while(!
    $i===strlen($params)){
                    if(!
    substr($params$i$i+1)===" "){
                      break;
                    }
                    
    $s .= substr($params$i$i+1);
                    
    $i++;
                  }
                  
    $s = (int)$s;
                  
    $radius $s;
                }
                if(
    $radius $this->config["max-rollback-radius"]){
                  
    $issuer->sendChat("That radius is too high and could cause lag! Radius is set to be between ".$this->config["max-rollback-radius"]."-".$this->config["mix-rollback-radius"]);
                }
                if(
    $radius $this->config["min-rollback-radius"]){
                  
    $issuer->sendChat("That radius is too low! Radius is set to be between ".$this->config["max-rollback-radius"]."-".$this->config["mix-rollback-radius"]);
                }
                
    $ms = ($secs 1000) + ($mins 60 1000) + ($hours 60 60 1000);
                
    $rollback $this->rollback($issuer->x,$issuer);
                
    $msg "Rolled back ";
                if(
    $rollback[1]>0){
                  
    $msg .= $rollback[1]." blocks";
                }
                if(
    $rollback[2]>&& $rollback[1]>0){
                  
    $msg .= " and ".$rollback[2]." tiles";
                }elseif(
    $rollback[2]>0){
                  
    $msg .= $rollback[2]." tiles";
                }
                
    $msg.= " in ".$rollback[0]." seconds.";
                
    $issuer->sendChat($msg);
              break;
            }
          break;
        }
      }
     
      private function 
    showLog($player$x$y$z){
        
    $log "";
        if(!isset(
    $this->inspect[$player->username]) || !$this->inspect[$player->username]===true || !$player instanceof Player){
          return 
    null;
        }
        
    $time time();
        
    $mintime $time 432000000;
        
    $sql $this->db->prepare("SELECT * FROM blocks WHERE x = :x AND y = :y AND z = :z AND level = :level AND time >= :time");
        
    $sql->bindValue(":x"$x);
        
    $sql->bindValue(":y"$y);
        
    $sql->bindValue(":z"$z);
        
    $sql->bindValue(":level"$player->level->getName());
        
    $sql->bindValue(":time"$mintime);
        
    $sql $sql->execute();
        
    $blocks $sql->fetchArray();
        unset(
    $sql);
        if(
    DINOLOGGER_DEBUG){
          
    var_dump($blocks);
        }
        if(
    $blocks === false){
          
    $player->sendChat("No data for this location.");
          return 
    null;
        }
        if(
    $this->countDim($blocks)===1){
          
    $word " placed ";
          
    $tool "noTool";
          if(
    $blocks["placed"]===0){
            
    $word " destroyed ";
            
    $tool $blocks["tool"];
          }
          
    $log .= $player->username .$word.$blocks["id"];
          if(!
    $tool==="noTool"){
            
    $log .= "using ".$tool;
          }
          
    $log .= " at ".date("d/m/Y H:i:s"$blocks["time"]);
        }else{
          
    $i 0;
          while(!
    $i>count($blocks)){
            
    $block $blocks[$i];
            
    $word " placed ";
            
    $tool "noTool";
            if(
    $block["placed"]===0){
              
    $word " destroyed ";
              
    $tool $block["tool"];
            }
            
    $log .= $player->username .$word.$block["id"];
            if(!
    $tool==="noTool"){
              
    $log .= "using ".$tool;
            }
            
    $log .= " at ".date("d/m/Y H:i:s"$block["time"])."\n";
            
    $i++;
          }
        }
        
    $player->sendChat($log);
      }
     
      private function 
    countDim($array){
        if(
    is_array(reset($array)))  {
          
    $return $this->countDim(reset($array)) + 1;
        }else{
          
    $return 1;
        }
        return 
    $return;
      }
     
      public function 
    __destruct(){
      }

    }
  2. Darunia18
    Offline

    Darunia18 Staff Member Sectional Moderator

    Joined:
    Aug 23, 2013
    Posts:
    755
    Plugins:
    2
    Minecraft User:
    Darunia18
    I was about to say that unless this used a database, it would be kinda useless, but I see you already did that! :D To be honest, all of your code makes barely any sense to me right now, but I hope you can find the issue because I'd love for something like this to be released :D
  3. Dinokiller
    Offline

    Dinokiller Notable Member

    Joined:
    Sep 22, 2013
    Posts:
    376
    Minecraft User:
    Dinokiller_
    Lol I know it's pretty complicated. Also who's dumb enough to use YAML files for loggers.
  4. Darunia18
    Offline

    Darunia18 Staff Member Sectional Moderator

    Joined:
    Aug 23, 2013
    Posts:
    755
    Plugins:
    2
    Minecraft User:
    Darunia18
    It's funny because two developers already did before, and one of them is in the plugin repository XD

    Too bad no one is allowed to make an auth plugin that uses a database. I just wish @shoghicp would update SimpleAuth to use a database instead of YAML files :/ but I know he's a busy guy!
  5. Falk
    Offline

    Falk Staff Member Global Moderator

    Joined:
    Sep 2, 2013
    Posts:
    1,707
    Plugins:
    22
    Minecraft User:
    Falkirknh
    SimpleAuth is fast as it is SQLite won't improve speed considerably.
  6. Dinokiller
    Offline

    Dinokiller Notable Member

    Joined:
    Sep 22, 2013
    Posts:
    376
    Minecraft User:
    Dinokiller_
    It is fast, It can get records very quickly. With YAML you have to search the whole file to get a record.
  7. Dinokiller
    Offline

    Dinokiller Notable Member

    Joined:
    Sep 22, 2013
    Posts:
    376
    Minecraft User:
    Dinokiller_
    Anyway can someone actually answer my question.
  8. Dinokiller
    Offline

    Dinokiller Notable Member

    Joined:
    Sep 22, 2013
    Posts:
    376
    Minecraft User:
    Dinokiller_
    My only issue is rollback everything else works alrightish. Just look in the rollback command and rollback method.
    Last edited: Jan 19, 2014
  9. Dinokiller
    Offline

    Dinokiller Notable Member

    Joined:
    Sep 22, 2013
    Posts:
    376
    Minecraft User:
    Dinokiller_
    I added a couple of comments to point out what things are.

    PHP:
    <?php

    /*
    __PocketMine Plugin__
    name=DinoLogger
    description=
    version=0.1.0
    author=Dinokiller
    class=DinoLogger
    apiversion=11
    */

    class DinoLogger implements Plugin{
      private 
    $api$config$db;
      private 
    $inspect = array();
     
      public function 
    __construct(ServerAPI $api$server false){
        
    define("DINOLOGGER_DEBUG"true);
        
    $this->api $api;
      }
      public function 
    init(){
        
    $this->db = new SQLite3($this->api->plugin->configPath($this) . "log.db");
        
    console("[INFO] [DinoLogger] Loaded database sucessfully.");
     
        
    $this->db->exec("CREATE TABLE IF NOT EXISTS blocks (id INT, meta INT, x INT, y INT, z INT,level TEXT, time INT, player TEXT, tool TEXT, placed BIT);"); // Create the table containing placed and destroyed blocks. id is Block ID, meta is Block Meta, (X, Y, Z , Level the block in, quiet obviously), time is the unix timestamp when the block was placed/broken, tool is the name of the tool that the block was modified with noTool means your fist was used or the block was placed and not broken and finally the placed is a bit which tells if the block was placed or broken.
       
        
    $this->db->exec("CREATE TABLE IF NOT EXISTS tiles (class INT, data TEXT, x INT, y INT, z INT, level TEXT, time INT);");
        
    // Creates a table containing modified tiles (tiles can store more data then blocks can and is used for things like signs, chests or furnaces)
     
      // Creates the standard plugin command.
        
    $this->api->console->register("dinologger""[action] [args]", array($this"handleCommand"));
        
    $this->api->console->alias("dl","dinologger");
       
        
    // Creates the loggers config file if it's not present.
        
    $this->config = new Config($this->api->plugin->configPath($this) ."config.yml"CONFIG_YAML,array(
          
    "max-rollback-radius" => 32,
          
    "min-rollback-radius" => 1,     
        ));
       
        
    // Reads the config into an array.
        
    $this->config $this->api->plugin->readYAML($this->api->plugin->configPath($this) ."config.yml");
       
        
    // Adds the event handlers used for the logger.
        
    $this->api->addHandler("tile.update", array($this"eventHandler"),30);
        
    $this->api->addHandler("player.block.touch", array($this"eventHandler"),30);
        
    $this->api->addHandler("player.block.break", array($this"eventHandler"),30);
        
    $this->api->addHandler("player.block.place", array($this"eventHandler"),30);
        
    $this->api->addHandler("player.quit", array($this"eventHandler"),15);
        
    $this->api->addHandler("server.close", array($this"eventHandler"),15);
      }
     
      
    // This function is where the rollback takes place. x, y, z and level is where the player doing the rolling back is and radius is how far from the coords should be rolled back in blocks. ms is the time in milliseconds to rollback to.
      
    public function rollback($x null$y null$z nullLevel $level$radius null$ms null){
        if(
    $x===null || $y===null || $z===null || $ms===null || $radius===null || $radius<|| $radius>256 || !$level instanceof Level){
          return 
    false;
        }
        
    $rbblock 0;
        
    $rbtile 0;
        
    $start time();
        
    $time time() - $ms;
       
        
    // Create a rectangle region of where to rollback using the coords and radius.
        
    $blocks $this->db->prepare("SELECT * FROM blocks WHERE x >= :x1 AND x <= :x2 AND y <= :y1 AND y >= :y2 AND z <= :z1 AND z >= :z2 AND level = :level AND time <= :time");
        
    $blocks->bindValue(":x1"$x $radius);
        
    $blocks->bindValue(":y1"$y $radius);
        
    $blocks->bindValue(":z1"$z $radius);
        
    $blocks->bindValue(":x2"$x $radius);
        
    $blocks->bindValue(":y2"$y $radius);
        
    $blocks->bindValue(":z2"$z $radius);
        
    $blocks->bindValue(":time"$time);
        
    $blocks->bindValue(":level"$level->getName());
       
       
        
    $blocks $blocks->execute();
        
    $blocks $blocks->fetchArray();
       
        if(
    DINOLOGGER_DEBUG){
          
    var_dump($blocks); // If plugin is in debug mode the $blocks variable will be shown on console.
        
    }
     
        if(
    countDim($blocks)===1){
          
    $blocks[0] = $blocks;
        }
        foreach(
    $blocks as &$i){
          if(
    $i["placed"]===0){
            
    $level->setBlock(new Vector3($i["x"], $i["y"], $i["z"]),$i["id"],$i["meta"]);
            
    $rbblock++; // This is how many blocks have been rolled back.
            
    $n false;
           
            switch(
    $i["id"]){ // If the block is supposed to have a tile then find the tile and roll it back.
              
    case SIGN_POST:
              case 
    WALL_SIGN:
                
    $n TILE_SIGN;
              break;
              case 
    CHEST:
                
    $n TILE_CHEST;
              break;
              case 
    FURNACE:
              case 
    BURNING_FURNACE:
              case 
    LIT_FURNACE:
                
    $n TILE_FURNACE;
            }
            if(!
    $n===false){
              
    $tile $this->db->prepare("SELECT * FROM tiles WHERE class = :tile AND x = :x AND y = :y AND z = :z AND level = :level AND time = :time");
              
    $tile->bindValue(":x"$i["x"]);
              
    $tile->bindValue(":y"$i["y"]);
              
    $tile->bindValue(":z"$i["z"]);
              
    $tile->bindValue(":tile"$n);
              
    $tile->bindValue(":time"$time);
              
    $tile->bindValue(":level"$level->getName());
              
    $tile $tile->execute();
              
    $tile $tile->fetchArray();
              if(
    is_array($tile)){
                
    $tile["data"] = unserialize($tile["data"]);
                
    $this->api->tile->add($level$n$i["x"], $i["y"], $i["z"], $tile["data"]);
                
    $rbtile++; // This is how many tiles have been rolled back.
              
    }       
            }
           
          }else{
            if(
    $i["time"]>$time){
              
    $level->setBlock(new Vector3($i["x"], $i["y"], $i["z"]), AIR); // Set blocks that were placed after the time that has been rolled back to, to air.
              
    $rbblock++; // This is how many blocks have been rolled back.
            
    }
          }
          unset(
    $i);
        }
        return array(
    time() - $start$rbtile$rbblock);
      }

      public function 
    eventHandler(&$data,$event){
        switch(
    $event){
          case 
    "player.quit":
            if(isset(
    $this->inspect[$data->username])){
              unset(
    $this->inspect[$data->username]); // $this->inspect is an array which tells who has the "block inspector" turned on.
            
    }
          break;
          case 
    "server.close":
            
    $this->api->plugin->writeYAML($this->api->plugin->configPath($this) ."config.yml"$this->config); // Save the config when server closes (Not needed but I have it in case I make the config changeable in next versions).
          
    break;
          case 
    "tile.update":
            
    // Add the modified tile to the database.
            
    $sql $this->db->prepare("INSERT OR REPLACE INTO tiles (class, data, x, y, z, level, time) VALUES (:tile, :data, :x, :y, :z, :level, :time);");
            
    $sql->bindValue(":tile"$data->class);
            
    $sql->bindValue(":data"serialize($data->data));
            
    $sql->bindValue(":x"$data->x);
            
    $sql->bindValue(":y"$data->y);
            
    $sql->bindValue(":z"$data->z);
            
    $sql->bindValue(":level"$data->level->getName());
            
    $sql->bindValue(":time"time());
            
    $sql->execute();
          break;
          case 
    "player.block.touch":
            
    $this->showLog($data["player"], $data["target"]->x$data["target"]->y$data["target"]->z); // If "block inspector" is enabled show the log.
          
    break;
          case 
    "player.block.place":
            if(
    $data["block"]->getID()===AIR){
              break;
            }
            
    $this->showLog($data["player"], $data["block"]->x$data["block"]->y$data["block"]->z); // If "block inspector" is enabled show the log.
            
    $sql $this->db->prepare("INSERT INTO blocks (id, meta, x, y, z, level, time, player, tool, placed) VALUES (:id, :meta, :x, :y, :z, :level, :time, :username, :tool, :placed);");
            
    $sql->bindValue(":id"$data["block"]->getID());
            
    $sql->bindValue(":meta"$data["block"]->getMetadata());
            
    $sql->bindValue(":x"$data["block"]->x);
            
    $sql->bindValue(":y"$data["block"]->y);
            
    $sql->bindValue(":z"$data["block"]->z);
            
    $sql->bindValue(":level"$data["block"]->level->getName());
            
    $sql->bindValue(":time"time());
            
    $sql->bindValue(":username"$data["player"]->username);
            
    $sql->bindValue(":tool""noTool");
            
    $sql->bindValue(":placed"1);
            
    $sql->execute();
            unset(
    $sql);
          break;
          case 
    "player.block.break":
            if(
    $data["target"]->getID()===AIR){
              break; 
    // Don't record air.
            
    }
            
    $this->showLog($data["player"], $data["target"]->x$data["target"]->y$data["target"]->z); // If "block inspector" is enabled show the log.
            
    $n false;
            
    $time time();
         
            switch(
    $data["target"]->getID()){
              case 
    SIGN_POST:
              case 
    WALL_SIGN:
                
    $n TILE_SIGN;
              break;
              case 
    CHEST:
                
    $n TILE_CHEST;
              break;
              case 
    FURNACE:
              case 
    BURNING_FURNACE:
              case 
    LIT_FURNACE:
                
    $n TILE_FURNACE;
            }
            if(!
    $n===false){  //If block is tile roll it back.
              
    $tile $this->api->tile->get(new Position($data["target"]->x$data["target"]->y$data["target"]->z$data["target"]->level));
              
    $sql $this->db->prepare("INSERT INTO tiles (class, data, x, y, z, level, time) VALUES (:tile, :data, :x, :y, :z, :level, :time);");
              
    $sql->bindValue(":tile"$n);
              
    $sql->bindValue(":data"serialize($tile->data));
              
    $sql->bindValue(":x"$tile->x);
              
    $sql->bindValue(":y"$tile->y);
              
    $sql->bindValue(":z"$tile->z);
              
    $sql->bindValue(":level"$tile->level->getName());
              
    $sql->bindValue(":time"$time);
              
    $sql->execute();
              unset(
    $sql);
            }
            
    $tool $data["item"];
            if(
    $tool->isTool()){
              
    $tool $tool->getName();
            }else{
              
    $tool "noTool"// noTool is used when no tool is used to break the block.
            
    }
            
    $sql $this->db->prepare("INSERT INTO blocks (id, meta, x, y, z, level, time, player, tool, placed) VALUES (:id, :meta, :x, :y, :z, :level, :time, :username, :tool, 0);"); // Add the broken block to the database.
            
    $sql->bindValue(":id"$data["target"]->getID());
            
    $sql->bindValue(":meta"$data["target"]->getMetadata());
            
    $sql->bindValue(":x"$data["target"]->x);
            
    $sql->bindValue(":y"$data["target"]->y);
            
    $sql->bindValue(":z"$data["target"]->z);
            
    $sql->bindValue(":level"$data["target"]->level->getName());
            
    $sql->bindValue(":time"$time);
            
    $sql->bindValue(":username"$data["player"]->username);
            
    $sql->bindValue(":tool"$tool);
            
    $sql->execute();
            unset(
    $sql);
          break;
        }
      }
     
      public function 
    handleCommand($cmd,$params,$issuer){
        switch(
    $cmd){
          case 
    "dinologger":
            if(!
    $issuer instanceof Player) break;
            
    $subCommand $params[0];
            unset(
    $params[0]);
            switch(
    $subCommand){
              case 
    "i":
              case 
    "inspect":
                
    // Enables or disables the "block inspector"
                
    if(!isset($this->inspect[$issuer->username]) || !$this->inspect[$issuer->username]===true){
                  
    $issuer->sendChat("Block inspector enabled do command again to turn off. Tap a block to see it's log.");
                  
    $this->inspect[$issuer->username] = true;
                }else{
                  
    $this->inspect[$issuer->username] = false;
                  
    $issuer->sendChat("Block inspector disabled.");
                }
              break;
              case 
    "r":
              case 
    "rollback":
                
    // Command for rolling back (this is what I have trouble with).
                
    $params implode(" "$params);
                if(!
    strpos("h:")===false){
                  
    $n = (int)strpos("h:") + 2;
                  
    $i $n;
                  while(!
    $i===strlen($params)){
                    if(
    substr($params$i$i+1)===" "){
                      break;
                    }
                    
    $s .= substr($params$i$i+1);
                    
    $i++;
                  }
                  
    $s = (int)$s;
                  
    $hours $s;
                }
                if(!
    strpos("m:")===false){
                  
    $n = (int)strpos("m:") + 2;
                  
    $i $n;
                  while(!
    $i===strlen($params)){
                    if(!
    substr($params$i$i+1)===" "){
                      break;
                    }
                    
    $s .= substr($params$i$i+1);
                    
    $i++;
                  }
                  
    $s = (int)$s;
                  
    $mins $s;
                }
                if(!
    strpos("s:")===false){
                  
    $n = (int)strpos("s:") + 2;
                  
    $i $n;
                  while(!
    $i===strlen($params)){
                    if(!
    substr($params$i$i+1)===" "){
                      break;
                    }
                    
    $s .= substr($params$i$i+1);
                    
    $i++;
                  }
                  
    $s = (int)$s;
                  
    $secs $s;
                }
                if(!
    strpos("r:")===false){
                  
    $n = (int)strpos("s:") + 2;
                  
    $i $n;
                  while(!
    $i===strlen($params)){
                    if(!
    substr($params$i$i+1)===" "){
                      break;
                    }
                    
    $s .= substr($params$i$i+1);
                    
    $i++;
                  }
                  
    $s = (int)$s;
                  
    $radius $s;
                }
                if(
    $radius $this->config["max-rollback-radius"]){ // If radius is too high.
                  
    $issuer->sendChat("That radius is too high and could cause lag! Radius is set to be between ".$this->config["max-rollback-radius"]."-".$this->config["mix-rollback-radius"]);
                }
                if(
    $radius $this->config["min-rollback-radius"]){ //If radius is too low
                  
    $issuer->sendChat("That radius is too low! Radius is set to be between ".$this->config["max-rollback-radius"]."-".$this->config["mix-rollback-radius"]);
                }
                
    $ms = ($secs 1000) + ($mins 60 1000) + ($hours 60 60 1000);
                
    $rollback $this->rollback($issuer->x,$issuer);
                
    $msg "Rolled back ";
                if(
    $rollback[1]>0){
                  
    $msg .= $rollback[1]." blocks";
                }
                if(
    $rollback[2]>&& $rollback[1]>0){
                  
    $msg .= " and ".$rollback[2]." tiles";
                }elseif(
    $rollback[2]>0){
                  
    $msg .= $rollback[2]." tiles";
                }
                
    $msg.= " in ".$rollback[0]." seconds.";
                
    $issuer->sendChat($msg); // Show how much was rolled back in seconds.
              
    break;
            }
          break;
        }
      }
     
      private function 
    showLog($player$x$y$z){
        
    $log "";
        if(!isset(
    $this->inspect[$player->username]) || !$this->inspect[$player->username]===true || !$player instanceof Player){
          return 
    null;
        }
        
    $time time();
        
    $mintime $time 432000000;
        
    $sql $this->db->prepare("SELECT * FROM blocks WHERE x = :x AND y = :y AND z = :z AND level = :level AND time >= :time");
        
    $sql->bindValue(":x"$x);
        
    $sql->bindValue(":y"$y);
        
    $sql->bindValue(":z"$z);
        
    $sql->bindValue(":level"$player->level->getName());
        
    $sql->bindValue(":time"$mintime);
        
    $sql $sql->execute();
        
    $blocks $sql->fetchArray();
        unset(
    $sql);
        if(
    DINOLOGGER_DEBUG){
          
    var_dump($blocks);
        }
        if(
    $blocks === false){ // If no data is found.
          
    $player->sendChat("No data for this location.");
          return 
    null;
        }
        if(
    $this->countDim($blocks)===1){
          
    $word " placed ";
          
    $tool "noTool";
          if(
    $blocks["placed"]===0){
            
    $word " destroyed ";
            
    $tool $blocks["tool"];
          }
          
    $log .= $player->username .$word.$blocks["id"];
          if(!
    $tool==="noTool"){
            
    $log .= "using ".$tool;
          }
          
    $log .= " at ".date("d/m/Y H:i:s"$blocks["time"]);
        }else{
          
    $i 0;
          while(!
    $i>count($blocks)){
            
    $block $blocks[$i];
            
    $word " placed ";
            
    $tool "noTool";
            if(
    $block["placed"]===0){
              
    $word " destroyed ";
              
    $tool $block["tool"];
            }
            
    $log .= $player->username .$word.$block["id"];
            if(!
    $tool==="noTool"){
              
    $log .= "using ".$tool;
            }
            
    $log .= " at ".date("d/m/Y H:i:s"$block["time"])."\n";
            
    $i++;
          }
        }
        
    $player->sendChat($log);
      }
     
      private function 
    countDim($array){ // Counts array dimmesions.
        
    if(is_array(reset($array)))  {
          
    $return $this->countDim(reset($array)) + 1;
        }else{
          
    $return 1;
        }
        return 
    $return;
      }
     
      public function 
    __destruct(){
      }

    }
    I think this should bring up more answers.
  10. Darunia18
    Offline

    Darunia18 Staff Member Sectional Moderator

    Joined:
    Aug 23, 2013
    Posts:
    755
    Plugins:
    2
    Minecraft User:
    Darunia18
    Ok that definitely makes more sense to me now :p
    From what I can tell, it seems like it should work. The rollback really confuses me though XD I'd have to take quite a bit of time to read through that.
  11. Dinokiller
    Offline

    Dinokiller Notable Member

    Joined:
    Sep 22, 2013
    Posts:
    376
    Minecraft User:
    Dinokiller_
    I'm thinking I used setBlock wrong maybe.
  12. PEMapModder
    Offline

    PEMapModder Notable Member Plugin Developer

    Joined:
    Oct 9, 2013
    Posts:
    7,306
    Plugins:
    11
    Minecraft User:
    PEMapModder
    SimpleAuth puts each player to a single YAML file so it is very fast because you don't have to SEARCH in the file. You just tell PHP to OPEN the file CHOOSE FROM DIRECTORY, and opening a file stream is faster than SEARCHING a name in the SQL, even it is in binary (because it is file system things; in YAML PHP has to pass from Plugin to Config, then to Spyc, and PHP has to interpret the script in each call, and Windows (or any OS) has to operate PHP, which is the main cause of the lag. In SQL it searches the index, but it still needs the OS to operate php.exe. However if you directly ask Windows to open the file stream, just see how fast it counts the number of files at C:\Windows\System32 (3,003 files are displayed as I opened the directory, but it takes time for Windows to count the number of bytes, which is still because of the directory things)

    Anyway if you don't understand what I am saying, I am trying to point out that spliting data to files is faster than everything else, unless you want to tell me you are making a database for each block, because choosing a file (and only get the content in that part related to the block/player) is asking the OS to do it, and using SQL is asking SQL to do it, where SQL asks PHP to do it, where PHP asks the OS to do it. So files are the most direct method.


    Although some may not be keen on knowing that millions of files will be generated.
  13. PEMapModder
    Offline

    PEMapModder Notable Member Plugin Developer

    Joined:
    Oct 9, 2013
    Posts:
    7,306
    Plugins:
    11
    Minecraft User:
    PEMapModder
    So, put the data not in a database/file, but in a folder. You can get part of a folder faster than getting part of a database.
  14. tnpxxsheepdog
    Offline

    tnpxxsheepdog Notable Member

    Joined:
    Dec 31, 2013
    Posts:
    354
    Minecraft User:
    TNPXXSHEEPDOG
    Permissions Plus plugin really needs this as it adds all users to one file. It is what eventually killed my server (kicked players every 5 seconds cause of lag). I deleted the plugin and no more and and there won't be for quite some time.
    PEMapModder likes this.
  15. PEMapModder
    Offline

    PEMapModder Notable Member Plugin Developer

    Joined:
    Oct 9, 2013
    Posts:
    7,306
    Plugins:
    11
    Minecraft User:
    PEMapModder
    How big is your server? (how many players?)

    Just to give one evidence for above, pocketmine uses splited YAML files to save player profiles.
  16. tnpxxsheepdog
    Offline

    tnpxxsheepdog Notable Member

    Joined:
    Dec 31, 2013
    Posts:
    354
    Minecraft User:
    TNPXXSHEEPDOG
    My players folder (which has never caused problems so far and I know it will eventually but not at the moment) http://puu.sh/6sRQ5.png

    So almost 15,000 player files. If I delete them, players get mad because they loose their stuff. Also, my server is too big and has too many players for my staff to know which ones to delete.
  17. PEMapModder
    Offline

    PEMapModder Notable Member Plugin Developer

    Joined:
    Oct 9, 2013
    Posts:
    7,306
    Plugins:
    11
    Minecraft User:
    PEMapModder
    Yeah because the runtime only opens the files containing online players and offline players' files are closed.
  18. tnpxxsheepdog
    Offline

    tnpxxsheepdog Notable Member

    Joined:
    Dec 31, 2013
    Posts:
    354
    Minecraft User:
    TNPXXSHEEPDOG
    Correct.
  19. PEMapModder
    Offline

    PEMapModder Notable Member Plugin Developer

    Joined:
    Oct 9, 2013
    Posts:
    7,306
    Plugins:
    11
    Minecraft User:
    PEMapModder
    And a common misconception thinks that saving plain text is the fastest, but in fact SQL saves in binary and does not need to be converted (its effect significant when working with large strings) from Unicode to the binary PHP uses to analyze strings.
  20. PEMapModder
    Offline

    PEMapModder Notable Member Plugin Developer

    Joined:
    Oct 9, 2013
    Posts:
    7,306
    Plugins:
    11
    Minecraft User:
    PEMapModder
    But in fact the real difference in this argument is negligible, except saving all data in a single YAML and I/O every 5 seconds is most laggy or even crashing.

Share This Page

Advertisement