Explorar o código

Merge pull request #339 from rocco27/master

Various updates
rocco27 %!s(int64=8) %!d(string=hai) anos
pai
achega
ef2ca257eb

BIN=BIN
images/icons/theforest.png


+ 3 - 3
install.php

@@ -219,7 +219,7 @@ function install() {
         }
 		
         echo "</table>";
-        echo "<h3>".get_lang('php_version_check')."</h3>\n";
+        echo "<h3>".get_lang('php_version_check').":</h3>\n";
         echo "<table class='install'>";
         echo "<tr><td>PHP Version >= ".REQUIRED_PHP_VERSION."</td><td>";
         if ( version_compare(PHP_VERSION, REQUIRED_PHP_VERSION, ">=") )
@@ -246,7 +246,7 @@ function install() {
 			array( "name" => "PHP BCMath Extension", "type" => "f", "value" => "bcadd" ),
 		);
 		
-        echo "<h3>".get_lang('checking_required_modules')."</h3>\n<table class='install'>";
+        echo "<h3>".get_lang('checking_required_modules').":</h3>\n<table class='install'>";
 
         foreach ( $properties_to_check as $propertie ) {
 			if(preReqInstalled($propertie)){
@@ -328,7 +328,7 @@ function install() {
 
         echo "</table>\n";
         
-        echo "<h3>".get_lang('checking_optional_modules')."</h3>\n<table class='install'>";
+        echo "<h3>".get_lang('checking_optional_modules').":</h3>\n<table class='install'>";
         
         foreach ( $optional_properties_to_check as $propertie ) {
 			if(preReqInstalled($propertie)){

+ 4 - 4
lang/English/install.php

@@ -23,7 +23,7 @@
  */
 
 define('OGP_LANG_install_lang', "Select your preferred language");
-define('OGP_LANG_install_welcome', "Welcome to the Open Game Panel Setup");
+define('OGP_LANG_install_welcome', "Welcome to the Open Game Panel Installer");
 define('OGP_LANG_file_permission_check', "Checking required file permissions");
 define('OGP_LANG_OK', "OK");
 define('OGP_LANG_write_permission_required', "Write permission required");
@@ -33,8 +33,8 @@ define('OGP_LANG_found', "Found");
 define('OGP_LANG_not_found', "Not found");
 define('OGP_LANG_pear_xxtea_info', "Pear Crypt_XXTEA is required for OGP usage. In the most of the Linux distributions this module can be installed with the following Pear command 'pear install Crypt_XXTEA-beta'.");
 define('OGP_LANG_refresh', "Refresh");
-define('OGP_LANG_checking_required_modules', "Checking required modules:");
-define('OGP_LANG_checking_optional_modules', "Checking optional modules:");
+define('OGP_LANG_checking_required_modules', "Checking required modules");
+define('OGP_LANG_checking_optional_modules', "Checking optional modules");
 define('OGP_LANG_database_type', "Database type");
 define('OGP_LANG_database_settings', "Database access settings");
 define('OGP_LANG_database_hostname', "Database Hostname");
@@ -55,7 +55,7 @@ define('OGP_LANG_repeat_password', "Repeat password");
 define('OGP_LANG_email', "E-mail address");
 define('OGP_LANG_back', "Back");
 define('OGP_LANG_database_setup_failure', "Setup was unable to create the database. Please recheck your database configs.");
-define('OGP_LANG_php_version_check', "Checking PHP version:");
+define('OGP_LANG_php_version_check', "Checking PHP version");
 define('OGP_LANG_invalid_username', "You entered invalid username.");
 define('OGP_LANG_password_too_short', "Your password is too short. It must be at least '%d' characters long.");
 define('OGP_LANG_password_contains_invalid_characters', "Your password contains invalid characters.");

+ 1 - 1
lang/English/modules/config_games.php

@@ -23,7 +23,7 @@
  */
 
 define('OGP_LANG_resetting_configs', "Resetting all configs");
-define('OGP_LANG_updating_configs', "Updating old configs.");
+define('OGP_LANG_updating_configs', "Updating old configs");
 define('OGP_LANG_configs_updated_ok', "Configurations reread successfully.");
 define('OGP_LANG_reset_old_configs', "Reset old configs");
 define('OGP_LANG_update_configs', "Update Configs");

+ 0 - 1
lang/English/modules/ftp.php

@@ -22,7 +22,6 @@
  *
  */
 
-define('OGP_LANG_pure-ftpd_accounts_for_remote_server_named', "FTP accounts for remote server named %s");
 define('OGP_LANG_ftp_address', "FTP address: %s:%s");
 define('OGP_LANG_change_account_details', "Change account details");
 define('OGP_LANG_remove_account', "Delete account");

+ 2 - 2
modules/config_games/config_servers.php

@@ -55,12 +55,12 @@ function exec_ogp_module() {
 
         if ( isset( $_REQUEST['clear_old']) && $_REQUEST['clear_old'] === 'yes' )
         {
-            echo "<p class='info'>".get_lang('resetting_configs')."</p>";
+            echo "<p class='info'>".get_lang('resetting_configs').":</p>";
             $clear_old = TRUE;
         }
         else
         {
-            echo "<p class='info'>".get_lang('updating_configs')."</p>";
+            echo "<p class='info'>".get_lang('updating_configs').":</p>";
         }
         
         $oldStructure = $db->getCurrentHomeConfigMods();

+ 77 - 0
modules/config_games/server_configs/theforest_win32.xml

@@ -0,0 +1,77 @@
+<game_config>
+  <game_key>theforest_win32</game_key>
+  <protocol>lgsl</protocol>
+  <lgsl_query_name>theforest</lgsl_query_name>
+  <installer>steamcmd</installer>
+  <game_name>The Forest (Oxide mod incl.)</game_name>
+  <server_exec_name>TheForestDedicatedServer.exe</server_exec_name>
+  <cli_template>-batchmode %IP% %PORT% %QUERY_PORT% %STEAM_PORT% %HOSTNAME% %PLAYERS% %CONTROL_PASSWORD% %IT% %SLOT% %SSA% %VAC% -nographics</cli_template>
+  <cli_params>
+    <cli_param id="IP" cli_string="-serverip" options="s" />
+    <cli_param id="PORT" cli_string="-servergameport" options="s" />
+    <cli_param id="PLAYERS" cli_string="-serverplayers" options="s" />
+    <cli_param id="HOSTNAME" cli_string="-servername" options="sq" />
+    <cli_param id="CONTROL_PASSWORD" cli_string="-serverpassword_admin" options="sq" />
+  </cli_params>
+  <reserve_ports>
+    <port type="add" id="QUERY_PORT" cli_string="-serverqueryport" options="s">1</port>
+  </reserve_ports>
+  <console_log>logs/output_log.txt</console_log>
+  <max_user_amount>100</max_user_amount>
+  <mods>
+    <mod key="default">
+      <name>None</name>
+      <installer_name>556450</installer_name>
+      <installer_login>anonymous</installer_login>
+    </mod>
+  </mods>
+  <server_params>
+    <param key="-serversteamport" type="text" id="STEAM_PORT">
+      <default>8766</default>
+      <options>s</options>
+      <access>admin</access>
+      <desc>This is used to authenticate Steam accounts.</desc>
+    </param>
+    <param key="-serversteamaccount" type="text" id="SSA">
+      <caption>Steam Account Login Token</caption>
+      <desc>Manage your Steam tokens &lt;a href=&quot;https://steamcommunity.com/dev/managegameservers&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.</desc>
+    </param>
+    <param key="-inittype" type="select" id="IT">
+      <option value="New">New</option>
+      <option value="Continue">Continue</option>
+      <options>s</options>
+      <desc>New or continue a game.</desc>
+    </param>
+    <param key="-slot" type="select" id="SLOT">
+      <option value="1">1</option>
+      <option value="2">2</option>
+      <option value="3">3</option>
+      <option value="4">4</option>
+      <option value="5">5</option>
+      <options>s</options>
+      <desc>Slot to save the game.</desc>
+    </param>
+    <param key="-serverautosaveinterval" type="text" id="SAVEINTERNAL">
+      <default>15</default>
+      <options>s</options>
+      <desc>Set the autosave interval in minutes.</desc>
+    </param>
+    <param key="-enableVAC" type="checkbox_key_value" id="VAC">
+      <desc>Enable Valve Anti-Cheat.</desc>
+    </param>
+  </server_params>
+  <post_install>
+    if [ -s "TheForestDedicatedServer_Data/Managed/Oxide.TheForest.dll" ]
+      then
+        echo "Oxide mod found. OK!"
+      else
+        echo "Oxide mod not found."
+        echo "Installing Oxide mod!"
+        wget https://github.com/OxideMod/Oxide/releases/download/latest/Oxide-TheForest.zip
+        unzip -o Oxide-TheForest.zip
+    fi
+  </post_install>
+  <configuration_files>
+    <file description="Server Configurations">Server.cfg</file>
+  </configuration_files>
+</game_config>

+ 44 - 1
protocol/TeamSpeak3/Adapter/ServerQuery/Exception.php

@@ -26,4 +26,47 @@
  * @class TeamSpeak3_Adapter_ServerQuery_Exception
  * @brief Enhanced exception class for TeamSpeak3_Adapter_ServerQuery objects.
  */
-class TeamSpeak3_Adapter_ServerQuery_Exception extends TeamSpeak3_Adapter_Exception {}
+class TeamSpeak3_Adapter_ServerQuery_Exception extends TeamSpeak3_Adapter_Exception
+{
+  /**
+   * Stores the optional return code for ServerQuery errors.
+   *
+   * @var string
+   */
+  protected $return_code = null;
+
+  /**
+   * The TeamSpeak3_Adapter_ServerQuery_Exception constructor.
+   *
+   * @param  string  $mesg
+   * @param  integer $code
+   * @param  string  $return_code
+   * @return TeamSpeak3_Adapter_ServerQuery_Exception
+   */
+  public function __construct($mesg, $code = 0x00, $return_code = null)
+  {
+    parent::__construct($mesg, $code);
+
+    $this->return_code = $return_code;
+  }
+
+  /**
+   * Returns TRUE if the exception provides a return code for ServerQuery errors.
+   *
+   * @return boolean
+   */
+  public function hasReturnCode()
+  {
+    return $this->return_code !== null;
+  }
+
+  /**
+   * Returns the optional return code for ServerQuery errors.
+   *
+   * @return string
+   */
+  public function getReturnCode()
+  {
+    return $this->return_code;
+  }
+}

+ 1 - 1
protocol/TeamSpeak3/Adapter/ServerQuery/Reply.php

@@ -313,7 +313,7 @@ class TeamSpeak3_Adapter_ServerQuery_Reply
         $suffix = "";
       }
       
-      throw new TeamSpeak3_Adapter_ServerQuery_Exception($this->getErrorProperty("msg") . $suffix, $this->getErrorProperty("id"));
+      throw new TeamSpeak3_Adapter_ServerQuery_Exception($this->getErrorProperty("msg") . $suffix, $this->getErrorProperty("id"), $this->getErrorProperty("return_code"));
     }
   }
 

+ 37 - 0
protocol/TeamSpeak3/Exception.php

@@ -28,6 +28,20 @@
  */
 class TeamSpeak3_Exception extends Exception
 {
+  /**
+   * Stores the original error code.
+   *
+   * @var integer
+   */
+  protected $raw_code = 0x00;
+
+  /**
+   * Stores the original error message.
+   *
+   * @var string
+   */
+  protected $raw_mesg = null;
+
   /**
    * Stores custom error messages.
    *
@@ -46,6 +60,9 @@ class TeamSpeak3_Exception extends Exception
   {
     parent::__construct($mesg, $code);
 
+    $this->raw_code = $code;
+    $this->raw_mesg = $mesg;
+
     if(array_key_exists((int) $code, self::$messages))
     {
       $this->message = $this->prepareCustomMessage(self::$messages[intval($code)]);
@@ -112,6 +129,26 @@ class TeamSpeak3_Exception extends Exception
     unset(self::$messages[(int) $code]);
   }
 
+  /**
+   * Returns the original error code.
+   *
+   * @return integer
+   */
+  public function getRawCode()
+  {
+    return $this->raw_code;
+  }
+
+  /**
+   * Returns the original error message.
+   *
+   * @return integer
+   */
+  public function getRawMessage()
+  {
+    return $this->raw_mesg;
+  }
+
   /**
    * Returns the class from which the exception was thrown.
    *

+ 7 - 1
protocol/TeamSpeak3/Helper/Convert.php

@@ -36,11 +36,14 @@ class TeamSpeak3_Helper_Convert
    */
   public static function bytes($bytes)
   {
+    // @todo: Fix precision lost from multiple rounding
     $kbytes = sprintf("%.02f", $bytes/1024);
     $mbytes = sprintf("%.02f", $kbytes/1024);
     $gbytes = sprintf("%.02f", $mbytes/1024);
     $tbytes = sprintf("%.02f", $gbytes/1024);
-
+  
+    // @todo: Fix assuming non-negative $bytes value, without validation
+    // Recommend something like: if( (float)$xbytes != 0 )
     if($tbytes >= 1)
       return $tbytes . " TB";
     if($gbytes >= 1)
@@ -55,6 +58,9 @@ class TeamSpeak3_Helper_Convert
 
   /**
    * Converts seconds/milliseconds to a human readable value.
+   * 
+   * Note: Assumes non-negative integer, but no validation
+   * @todo: Handle negative integer $seconds, or invalidate
    *
    * @param  integer $seconds
    * @param  boolean $is_ms

+ 2 - 0
protocol/TeamSpeak3/Helper/Crypt.php

@@ -23,6 +23,8 @@
  */
 
 /**
+ * @todo: Replace this class with native encryption
+ *
  * @class TeamSpeak3_Helper_Crypt
  * @brief Helper class for data encryption.
  */

+ 6 - 3
protocol/TeamSpeak3/Helper/Signal.php

@@ -44,6 +44,9 @@ class TeamSpeak3_Helper_Signal
 
   /**
    * Emits a signal with a given set of parameters.
+   * 
+   * @todo: Confirm / fix $return is set to last $slot->call() return value.
+   *      It appears all previous calls before last are lost / ignored.
    *
    * @param  string $signal
    * @param  mixed  $params
@@ -75,7 +78,7 @@ class TeamSpeak3_Helper_Signal
    *
    * @param  mixed  $callback
    * @param  string
-   * @return void
+   * @return string
    */
   public function getCallbackHash($callback)
   {
@@ -171,7 +174,7 @@ class TeamSpeak3_Helper_Signal
    */
   public function getHandlers($signal)
   {
-    if(!$this->hasHandlers($signal))
+    if($this->hasHandlers($signal))
     {
       return $this->sigslots[$signal];
     }
@@ -187,7 +190,7 @@ class TeamSpeak3_Helper_Signal
    */
   public function clearHandlers($signal)
   {
-    if(!$this->hasHandlers($signal))
+    if($this->hasHandlers($signal))
     {
       unset($this->sigslots[$signal]);
     }

+ 6 - 0
protocol/TeamSpeak3/Helper/Uri.php

@@ -322,7 +322,10 @@ class TeamSpeak3_Helper_Uri
   /**
    * Returns TRUE if the host is valid.
    *
+   * @todo: Implement check for host URI segment
+   *
    * @param string $host
+   *
    * @return boolean
    */
   public function checkHost($host = null)
@@ -359,7 +362,10 @@ class TeamSpeak3_Helper_Uri
   /**
    * Returns TRUE if the port is valid.
    *
+   * @todo: Implement check for port URI segment
+   *
    * @param  integer $port
+   *
    * @return boolean
    */
   public function checkPort($port = null)

+ 5 - 0
protocol/TeamSpeak3/Node/Abstract.php

@@ -206,6 +206,11 @@ abstract class TeamSpeak3_Node_Abstract implements RecursiveIterator, ArrayAcces
       $html .= $viewer->fetchObject($node, $siblings);
     }
 
+    if(empty($html) && method_exists($viewer, "toString"))
+    {
+      return $viewer->toString();
+    }
+
     return $html;
   }
 

+ 53 - 1
protocol/TeamSpeak3/Node/Client.php

@@ -164,6 +164,29 @@ class TeamSpeak3_Node_Client extends TeamSpeak3_Node_Abstract
     return $this->getParent()->customInfo($this["client_database_id"]);
   }
 
+  /**
+   * Creates or updates a custom property for the client.
+   *
+   * @param  string $ident
+   * @param  string $value
+   * @return void
+   */
+  public function customSet($ident, $value)
+  {
+    $this->getParent()->customSet($this["client_database_id"], $ident, $value);
+  }
+
+  /**
+   * Removes a custom property from the client.
+   *
+   * @param  string $ident
+   * @return void
+   */
+  public function customDelete($ident)
+  {
+    $this->getParent()->customDelete($this["client_database_id"], $ident);
+  }
+
   /**
    * Returns an array containing the permission overview of the client.
    *
@@ -300,6 +323,36 @@ class TeamSpeak3_Node_Client extends TeamSpeak3_Node_Abstract
     return $this->execute("clientgetids", array("cluid" => $this["client_unique_identifier"]))->toAssocArray("clid");
   }
 
+  /**
+   * Returns TRUE if the client is using Overwolf.
+   *
+   * @return boolean
+   */
+  public function hasOverwolf()
+  {
+    return strstr($this["client_badges"], "overwolf=1") !== FALSE;
+  }
+
+  /**
+   * Returns a list of equipped badges for this client.
+   *
+   * @return array
+   */
+  public function getBadges()
+  {
+    $badges = array();
+
+    foreach(explode(":", $this["client_badges"]) as $set)
+    {
+      if(substr($set, 0, 7) == "badges=")
+      {
+        $badges[] = array_map("trim", explode(",", substr($set, 7)));
+      }
+    }
+
+    return $badges;
+  }
+
   /**
    * Returns the revision/build number from the clients version string.
    *
@@ -438,4 +491,3 @@ class TeamSpeak3_Node_Client extends TeamSpeak3_Node_Abstract
     return (string) $this["client_nickname"];
   }
 }
-

+ 6 - 5
protocol/TeamSpeak3/Node/Host.php

@@ -362,16 +362,17 @@ class TeamSpeak3_Node_Host extends TeamSpeak3_Node_Abstract
    * Stops the virtual server specified by ID.
    *
    * @param  integer $sid
+   * @param  string  $msg
    * @return void
    */
-  public function serverStop($sid)
+  public function serverStop($sid, $msg = null)
   {
     if($sid == $this->serverSelectedId())
     {
       $this->serverDeselect();
     }
 
-    $this->execute("serverstop", array("sid" => $sid));
+    $this->execute("serverstop", array("sid" => $sid, "reasonmsg" => $msg));
     $this->serverListReset();
 
     TeamSpeak3_Helper_Signal::getInstance()->emit("notifyServerstopped", $this, $sid);
@@ -380,13 +381,14 @@ class TeamSpeak3_Node_Host extends TeamSpeak3_Node_Abstract
   /**
    * Stops the entire TeamSpeak 3 Server instance by shutting down the process.
    *
+   * @param  string $msg
    * @return void
    */
-  public function serverStopProcess()
+  public function serverStopProcess($msg = null)
   {
     TeamSpeak3_Helper_Signal::getInstance()->emit("notifyServershutdown", $this);
 
-    $this->execute("serverprocessstop");
+    $this->execute("serverprocessstop", array("reasonmsg" => $msg));
   }
 
   /**
@@ -1162,4 +1164,3 @@ class TeamSpeak3_Node_Host extends TeamSpeak3_Node_Abstract
     return (string) $this->getAdapterHost();
   }
 }
-

+ 31 - 5
protocol/TeamSpeak3/Node/Server.php

@@ -638,7 +638,7 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
    * Returns an array filled with TeamSpeak3_Node_Client objects.
    *
    * @param  array $filter
-   * @return array | TeamSpeak3_Node_Client[]
+   * @return array|TeamSpeak3_Node_Client[]
    */
   public function clientList(array $filter = array())
   {
@@ -1036,7 +1036,7 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
    * Returns a list of server groups available.
    *
    * @param  filter $filter
-   * @return array | TeamSpeak3_Node_Servergroup[]
+   * @return array|TeamSpeak3_Node_Servergroup[]
    */
   public function serverGroupList(array $filter = array())
   {
@@ -1364,7 +1364,7 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
    * Returns a list of channel groups available.
    *
    * @param  array $filter
-   * @return array | TeamSpeak3_Node_Channelgroup[]
+   * @return array|TeamSpeak3_Node_Channelgroup[]
    */
   public function channelGroupList(array $filter = array())
   {
@@ -2108,6 +2108,31 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
     return $this->execute("custominfo", array("cldbid" => $cldbid))->toArray();
   }
 
+  /**
+   * Creates or updates a custom property for the client specified by $cldbid.
+   *
+   * @param  integer $cldbid
+   * @param  string  $ident
+   * @param  string  $value
+   * @return void
+   */
+  public function customSet($cldbid, $ident, $value)
+  {
+    $this->execute("customset", array("cldbid" => $cldbid, "ident" => $ident, "value" => $value));
+  }
+
+  /**
+   * Removes a custom property from the client specified by $cldbid.
+   *
+   * @param  integer $cldbid
+   * @param  string  $ident
+   * @return void
+   */
+  public function customDelete($cldbid, $ident)
+  {
+    $this->execute("customdelete", array("cldbid" => $cldbid, "ident" => $ident));
+  }
+
   /**
    * Returns a list of active bans on the selected virtual server.
    *
@@ -2325,11 +2350,12 @@ class TeamSpeak3_Node_Server extends TeamSpeak3_Node_Abstract
   /**
    * Stops the virtual server.
    *
+   * @param  string $msg
    * @return void
    */
-  public function stop()
+  public function stop($msg = null)
   {
-    $this->getParent()->serverStop($this->getId());
+    $this->getParent()->serverStop($this->getId(), $msg);
   }
   
   /**

+ 1 - 1
protocol/TeamSpeak3/TeamSpeak3.php

@@ -66,7 +66,7 @@ class TeamSpeak3
   /**
    * TeamSpeak 3 PHP Framework version.
    */
-  const LIB_VERSION = "1.1.32";
+  const LIB_VERSION = "1.1.33";
 
   /*@
    * TeamSpeak 3 protocol separators.

+ 20 - 8
protocol/TeamSpeak3/Viewer/Html.php

@@ -43,7 +43,7 @@ class TeamSpeak3_Viewer_Html implements TeamSpeak3_Viewer_Interface
   protected $currObj = null;
 
   /**
-   * An array filled with siblingsfor the  TeamSpeak3_Node_Abstract object which is currently
+   * An array filled with siblings for the TeamSpeak3_Node_Abstract object which is currently
    * processed.
    *
    * @var array
@@ -274,6 +274,10 @@ class TeamSpeak3_Viewer_Html implements TeamSpeak3_Viewer_Interface
           break;
       }
     }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client && $this->currObj->client_is_recording)
+    {
+      $extras .= " recording";
+    }
 
     return "corpus " . $this->currObj->getClass(null) . $extras;
   }
@@ -344,17 +348,25 @@ class TeamSpeak3_Viewer_Html implements TeamSpeak3_Viewer_Interface
       $before = array();
       $behind = array();
 
-      foreach($this->currObj->memberOf() as $group)
+      if(!$this->currObj->client_is_recording)
       {
-        if($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEFORE)
+        foreach($this->currObj->memberOf() as $group)
         {
-          $before[] = "[" . htmlspecialchars($group["name"]) . "]";
-        }
-        elseif($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEHIND)
-        {
-          $behind[] = "[" . htmlspecialchars($group["name"]) . "]";
+          if($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEFORE)
+          {
+            $before[] = "[" . htmlspecialchars($group["name"]) . "]";
+          }
+          elseif($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEHIND)
+          {
+            $behind[] = "[" . htmlspecialchars($group["name"]) . "]";
+          }
         }
       }
+      else
+      {
+        $before[] = "***";
+        $behind[] = "*** [RECORDING]";
+      }
 
       return implode("", $before) . " " . htmlspecialchars($this->currObj) . " " . implode("", $behind);
     }

+ 494 - 0
protocol/TeamSpeak3/Viewer/Json.php

@@ -0,0 +1,494 @@
+<?php
+
+/**
+ * @file
+ * TeamSpeak 3 PHP Framework
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package   TeamSpeak3
+ * @author    Sven 'ScP' Paulsen
+ * @copyright Copyright (c) Planet TeamSpeak. All rights reserved.
+ */
+
+/**
+ * @class TeamSpeak3_Viewer_Json
+ * @brief Generates a JSON struct used in JS-based TeamSpeak 3 viewers.
+ */
+class TeamSpeak3_Viewer_Json implements TeamSpeak3_Viewer_Interface
+{
+  /**
+   * Stores an array of data parsed from TeamSpeak3_Node_Abstract objects.
+   *
+   * @var array
+   */
+  protected $data = null;
+
+  /**
+   * The TeamSpeak3_Node_Abstract object which is currently processed.
+   *
+   * @var TeamSpeak3_Node_Abstract
+   */
+  protected $currObj = null;
+
+  /**
+   * An array filled with siblings for the TeamSpeak3_Node_Abstract object which is currently
+   * processed.
+   *
+   * @var array
+   */
+  protected $currSib = null;
+
+  /**
+   * An internal counter indicating the depth of the TeamSpeak3_Node_Abstract object previously
+   * processed.
+   *
+   * @var integer
+   */
+  protected $lastLvl = 0;
+
+  /**
+   * The TeamSpeak3_Viewer_Json constructor.
+   *
+   * @param  array $data
+   * @return TeamSpeak3_Viewer_Json
+   */
+  public function __construct(array &$data = array())
+  {
+    $this->data = &$data;
+  }
+
+  /**
+   * Assembles an stdClass object for the current element.
+   *
+   * @param  TeamSpeak3_Node_Abstract $node
+   * @param  array $siblings
+   * @return void
+   */
+  public function fetchObject(TeamSpeak3_Node_Abstract $node, array $siblings = array())
+  {
+    $this->currObj = $node;
+    $this->currSib = $siblings;
+
+    $obj = new stdClass();
+
+    $obj->ident    = $this->getId();
+    $obj->parent   = $this->getParent();
+    $obj->children = $node->count();
+    $obj->level    = $this->getLevel();
+    $obj->first    = (bool) ($obj->level != $this->lastLvl);
+    $obj->last     = (bool) array_pop($siblings);
+    $obj->siblings = array_map("boolval", $siblings);
+    $obj->class    = $this->getType();
+    $obj->name     = $this->getName();
+    $obj->image    = $this->getImage();
+    $obj->props    = $this->getProps();
+
+    $this->data[]  = $obj;
+    $this->lastLvl = $obj->level;
+  }
+
+  /**
+   * Returns the ID of the current element.
+   *
+   * @return mixed
+   */
+  protected function getId()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Server)
+    {
+      return "ts3_s" . $this->currObj->virtualserver_id;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      return "ts3_c" . $this->currObj->cid;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      return "ts3_u" . $this->currObj->clid;
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Returns the parent ID of the current element.
+   *
+   * @return mixed
+   */
+  protected function getParent()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      return $this->currObj->pid ? "ts3_c" . $this->currObj->pid : "ts3_s" . $this->currObj->getParent()->getId();
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      return $this->currObj->cid ? "ts3_c" . $this->currObj->cid : "ts3_s" . $this->currObj->getParent()->getId();
+    }
+
+    return "ts3";
+  }
+
+  /**
+   * Returns the level of the current element.
+   *
+   * @return integer
+   */
+  protected function getLevel()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      return $this->currObj->getLevel()+2;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      return $this->currObj->channelGetById($this->currObj->cid)->getLevel()+3;
+    }
+
+    return 1;
+  }
+
+  /**
+   * Returns a single type identifier for the current element.
+   *
+   * @return string
+   */
+  protected function getType()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Server)
+    {
+      return "server";
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      return "channel";
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      return "client";
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Servergroup || $this->currObj instanceof TeamSpeak3_Node_Channelgroup)
+    {
+      return "group";
+    }
+
+    return "host";
+  }
+
+  /**
+   * Returns a string for the current corpus element which can be used as a HTML class
+   * property. If the current node is a channel spacer the class string will contain
+   * additional class names to allow further customization of the content via CSS.
+   *
+   * @return string
+   */
+  protected function getClass()
+  {
+    $extras = "";
+
+    if($this->currObj instanceof TeamSpeak3_Node_Channel && $this->currObj->isSpacer())
+    {
+      switch($this->currObj->spacerGetType())
+      {
+        case (string) TeamSpeak3::SPACER_SOLIDLINE:
+          $extras .= " solidline";
+          break;
+
+        case (string) TeamSpeak3::SPACER_DASHLINE:
+          $extras .= " dashline";
+          break;
+
+        case (string) TeamSpeak3::SPACER_DASHDOTLINE:
+          $extras .= " dashdotline";
+          break;
+
+        case (string) TeamSpeak3::SPACER_DASHDOTDOTLINE:
+          $extras .= " dashdotdotline";
+          break;
+
+        case (string) TeamSpeak3::SPACER_DOTLINE:
+          $extras .= " dotline";
+          break;
+      }
+
+      switch($this->currObj->spacerGetAlign())
+      {
+        case TeamSpeak3::SPACER_ALIGN_REPEAT:
+          $extras .= " repeat";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_CENTER:
+          $extras .= " center";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_RIGHT:
+          $extras .= " right";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_LEFT:
+          $extras .= " left";
+          break;
+      }
+    }
+
+    return $this->currObj->getClass(null) . $extras;
+  }
+
+  /**
+   * Returns an individual type for a spacer.
+   *
+   * @return string
+   */
+  protected function getSpacerType()
+  {
+    $type = "";
+
+    if(!$this->currObj instanceof TeamSpeak3_Node_Channel || !$this->currObj->isSpacer())
+    {
+      return "none";
+    }
+
+    switch($this->currObj->spacerGetType())
+    {
+      case (string) TeamSpeak3::SPACER_SOLIDLINE:
+        $type .= "solidline";
+        break;
+
+      case (string) TeamSpeak3::SPACER_DASHLINE:
+        $type .= "dashline";
+        break;
+
+      case (string) TeamSpeak3::SPACER_DASHDOTLINE:
+        $type .= "dashdotline";
+        break;
+
+      case (string) TeamSpeak3::SPACER_DASHDOTDOTLINE:
+        $type .= "dashdotdotline";
+        break;
+
+      case (string) TeamSpeak3::SPACER_DOTLINE:
+        $type .= "dotline";
+        break;
+
+      default:
+        $type .= "custom";
+    }
+
+    if($type == "custom")
+    {
+      switch($this->currObj->spacerGetAlign())
+      {
+        case TeamSpeak3::SPACER_ALIGN_REPEAT:
+          $type .= "repeat";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_CENTER:
+          $type .= "center";
+          break;
+
+        case TeamSpeak3::SPACER_ALIGN_RIGHT:
+          $type .= "right";
+          break;
+
+        default:
+          $type .= "left";
+      }
+    }
+
+    return $type;
+  }
+
+  /**
+   * Returns a string for the current corpus element which contains the display name
+   * for the current TeamSpeak_Node_Abstract object.
+   *
+   * @return string
+   */
+  protected function getName()
+  {
+    if($this->currObj instanceof TeamSpeak3_Node_Channel && $this->currObj->isSpacer())
+    {
+      return $this->currObj["channel_name"]->section("]", 1, 99)->toString();
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      $before = array();
+      $behind = array();
+
+      foreach($this->currObj->memberOf() as $group)
+      {
+        if($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEFORE)
+        {
+          $before[] = "[" . $group["name"] . "]";
+        }
+        elseif($group->getProperty("namemode") == TeamSpeak3::GROUP_NAMEMODE_BEHIND)
+        {
+          $behind[] = "[" . $group["name"] . "]";
+        }
+      }
+
+      return trim(implode("", $before) . " " . $this->currObj . " " . implode("", $behind));
+    }
+
+    return $this->currObj->toString();
+  }
+
+  /**
+   * Returns the parent ID of the current element.
+   *
+   * @return stdClass
+   */
+  protected function getProps()
+  {
+    $props = new stdClass();
+
+    if($this->currObj instanceof TeamSpeak3_Node_Host)
+    {
+      $this->id        = 0;
+      $this->icon      = 0;
+      $props->version  = $this->currObj->version("version")->toString();
+      $props->platform = $this->currObj->version("platform")->toString();
+      $props->users    = $this->currObj->virtualservers_total_clients_online;
+      $props->slots    = $this->currObj->virtualservers_total_maxclients;
+      $props->flags    = 0;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Server)
+    {
+      $props->id       = $this->currObj->getId();
+      $props->icon     = $this->currObj->virtualserver_icon_id < 0 ? pow(2, 32)-($this->currObj->virtualserver_icon_id*-1) : $this->currObj->virtualserver_icon_id;
+      $props->welcmsg  = strlen($this->currObj->virtualserver_welcomemessage) ? trim($this->currObj->virtualserver_welcomemessage) : null;
+      $props->hostmsg  = strlen($this->currObj->virtualserver_hostmessage) ? trim($this->currObj->virtualserver_hostmessage) : null;
+      $props->version  = TeamSpeak3_Helper_Convert::versionShort($this->currObj->virtualserver_version)->toString();
+      $props->platform = $this->currObj->virtualserver_platform->toString();
+      $props->country  = null;
+      $props->users    = $this->currObj->clientCount();
+      $props->slots    = $this->currObj->virtualserver_maxclients;
+      $props->flags    = 0;
+
+      $props->flags += $this->currObj->virtualserver_status == "online"   ? 1  : 0;
+      $props->flags += $this->currObj->virtualserver_flag_password        ? 2  : 0;
+      $props->flags += $this->currObj->virtualserver_autostart            ? 4  : 0;
+      $props->flags += $this->currObj->virtualserver_weblist_enabled      ? 8  : 0;
+      $props->flags += $this->currObj->virtualserver_ask_for_privilegekey ? 16 : 0;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Channel)
+    {
+      $props->id       = $this->currObj->getId();
+      $props->icon     = $this->currObj->isSpacer() ? 0 : $this->currObj->channel_icon_id < 0 ? pow(2, 32)-($this->currObj->channel_icon_id*-1) : $this->currObj->channel_icon_id;
+      $props->path     = trim($this->currObj->getPathway());
+      $props->topic    = strlen($this->currObj->channel_topic) ? trim($this->currObj->channel_topic) : null;
+      $props->codec    = $this->currObj->channel_codec;
+      $props->users    = $this->currObj->total_clients == -1 ? 0 : $this->currObj->total_clients;
+      $props->slots    = $this->currObj->channel_maxclients == -1 ? $this->currObj->getParent()->virtualserver_maxclients : $this->currObj->channel_maxclients;
+      $props->famusers = $this->currObj->total_clients_family == -1 ? 0 : $this->currObj->total_clients_family;
+      $props->famslots = $this->currObj->channel_maxfamilyclients == -1 ? $this->currObj->getParent()->virtualserver_maxclients : $this->currObj->channel_maxfamilyclients;
+      $props->spacer   = $this->getSpacerType();
+      $props->flags    = 0;
+
+      $props->flags += $this->currObj->channel_flag_default           ? 1   : 0;
+      $props->flags += $this->currObj->channel_flag_password          ? 2   : 0;
+      $props->flags += $this->currObj->channel_flag_permanent         ? 4   : 0;
+      $props->flags += $this->currObj->channel_flag_semi_permanent    ? 8   : 0;
+      $props->flags += ($props->codec == 3 || $props->codec == 5)     ? 16  : 0;
+      $props->flags += $this->currObj->channel_needed_talk_power != 0 ? 32  : 0;
+      $props->flags += $this->currObj->total_clients != -1            ? 64  : 0;
+      $props->flags += $this->currObj->isSpacer()                     ? 128 : 0;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Client)
+    {
+      $props->id       = $this->currObj->getId();
+      $props->icon     = $this->currObj->client_icon_id < 0 ? pow(2, 32)-($this->currObj->client_icon_id*-1) : $this->currObj->client_icon_id;
+      $props->version  = TeamSpeak3_Helper_Convert::versionShort($this->currObj->client_version)->toString();
+      $props->platform = $this->currObj->client_platform->toString();
+      $props->country  = strlen($this->currObj->client_country) ? trim($this->currObj->client_country) : null;
+      $props->awaymesg = strlen($this->currObj->client_away_message) ? trim($this->currObj->client_away_message) : null;
+      $props->memberof = array();
+      $props->badges   = $this->currObj->getBadges();
+      $props->flags    = 0;
+
+      foreach($this->currObj->memberOf() as $num => $group)
+      {
+        $props->memberof[$num] = new stdClass();
+
+        $props->memberof[$num]->name  = trim($group->name);
+        $props->memberof[$num]->icon  = $group->iconid < 0 ? pow(2, 32)-($group->iconid*-1) : $group->iconid;
+        $props->memberof[$num]->order = $group->sortid;
+        $props->memberof[$num]->flags = 0;
+
+        $props->memberof[$num]->flags += $group->namemode;
+        $props->memberof[$num]->flags += $group->type == 2                             ? 4  : 0;
+        $props->memberof[$num]->flags += $group->type == 0                             ? 8  : 0;
+        $props->memberof[$num]->flags += $group->savedb                                ? 16 : 0;
+        $props->memberof[$num]->flags += $group instanceof TeamSpeak3_Node_Servergroup ? 32 : 0;
+      }
+
+      $props->flags += $this->currObj->client_away                                                                                                                             ? 1   : 0;
+      $props->flags += $this->currObj->client_is_recording                                                                                                                     ? 2   : 0;
+      $props->flags += $this->currObj->client_is_channel_commander                                                                                                             ? 4   : 0;
+      $props->flags += $this->currObj->client_is_priority_speaker                                                                                                              ? 8   : 0;
+      $props->flags += $this->currObj->client_is_talker                                                                                                                        ? 16  : 0;
+      $props->flags += $this->currObj->channelGetById($this->currObj->cid)->channel_needed_talk_power > $this->currObj->client_talk_power && !$this->currObj->client_is_talker ? 32  : 0;
+      $props->flags += $this->currObj->client_input_muted || !$this->currObj->client_input_hardware                                                                            ? 64  : 0;
+      $props->flags += $this->currObj->client_output_muted || !$this->currObj->client_output_hardware                                                                          ? 128 : 0;
+    }
+    elseif($this->currObj instanceof TeamSpeak3_Node_Servergroup || $this->currObj instanceof TeamSpeak3_Node_Channelgroup)
+    {
+      $props->id     = $this->currObj->getId();
+      $props->icon   = $this->currObj->iconid < 0 ? pow(2, 32)-($this->currObj->iconid*-1) : $this->currObj->iconid;
+      $props->order  = $this->currObj->sortid;
+      $props->n_map  = $this->currObj->n_member_addp;
+      $props->n_mrp  = $this->currObj->n_member_removep;
+      $props->flags  = 0;
+
+      $props->flags += $this->currObj->namemode;
+      $props->flags += $this->currObj->type == 2                             ? 4  : 0;
+      $props->flags += $this->currObj->type == 0                             ? 8  : 0;
+      $props->flags += $this->currObj->savedb                                ? 16 : 0;
+      $props->flags += $this->currObj instanceof TeamSpeak3_Node_Servergroup ? 32 : 0;
+    }
+
+    return $props;
+  }
+
+  /**
+   * Returns the status icon URL of the current element.
+   *
+   * @return string
+   */
+  protected function getImage()
+  {
+    return str_replace("_", "-", $this->currObj->getIcon());
+  }
+
+  /**
+   * Returns a string representation of this node.
+   *
+   * @return string
+   */
+  public function toString()
+  {
+    return $this->__toString();
+  }
+
+  /**
+   * Returns a string representation of this node.
+   *
+   * @return string
+   */
+  public function __toString()
+  {
+    return json_encode($this->data);
+  }
+}

+ 6 - 2
protocol/lgsl/lgsl_protocol.php

@@ -140,6 +140,7 @@ if (!function_exists('lgsl_version')) { // START OF DOUBLE LOAD PROTECTION
 		"swat4"			=> "SWAT 4",
 		"test"			=> "Test ( For PHP Developers )",
 		"teeworlds"		=> "Teeworlds",
+		"theforest"		=> "The Forest",
 		"tribes"		=> "Tribes ( Starsiege )",
 		"tribes2"		=> "Tribes 2",
 		"tribesv"		=> "Tribes Vengeance",
@@ -200,7 +201,7 @@ if (!function_exists('lgsl_version')) { // START OF DOUBLE LOAD PROTECTION
 		"crysiswars"	=> "06",
 		"cs2d"			=> "29",
 		"cube"			=> "24",
-		"dayzmod"  => "05",
+		"dayzmod"		=> "05",
 		"doomskulltag"	=> "27",
 		"doomzdaemon"	=> "28",
 		"doom3"			=> "10",
@@ -291,6 +292,7 @@ if (!function_exists('lgsl_version')) { // START OF DOUBLE LOAD PROTECTION
 		"swat4"			=> "03",
 		"test"			=> "01",
 		"teeworlds"		=> "21",
+		"theforest"		=> "05",
 		"tribes"		=> "23",
 		"tribes2"		=> "25",
 		"tribesv"		=> "09",
@@ -354,7 +356,7 @@ if (!function_exists('lgsl_version')) { // START OF DOUBLE LOAD PROTECTION
 		"crysiswars"	=> "qtracker://{IP}:{S_PORT}?game=CrysisWars&action=show",
 		"cs2d"			=> "http://www.cs2d.com",
 		"cube"			=> "http://cubeengine.com",
-		"dayzmod"          => "steam://connect/{IP}:{S_PORT}",
+		"dayzmod"		=> "steam://connect/{IP}:{S_PORT}",
 		"doomskulltag"	=> "http://skulltag.com",
 		"doomzdaemon"	=> "http://www.zdaemon.org",
 		"doom3"			=> "qtracker://{IP}:{S_PORT}?game=Doom3&action=show",
@@ -439,6 +441,7 @@ if (!function_exists('lgsl_version')) { // START OF DOUBLE LOAD PROTECTION
 		"swat4"			=> "qtracker://{IP}:{S_PORT}?game=SWAT4&action=show",
 		"test"			=> "http://www.greycube.com",
 		"teeworlds"		=> "http://www.teeworlds.com",
+		"theforest"		=> "steam://connect/{IP}:{Q_PORT}",
 		"tribes"		=> "qtracker://{IP}:{S_PORT}?game=Tribes&action=show",
 		"tribes2"		=> "qtracker://{IP}:{S_PORT}?game=Tribes2&action=show",
 		"tribesv"		=> "qtracker://{IP}:{S_PORT}?game=TribesVengeance&action=show",
@@ -532,6 +535,7 @@ if (!function_exists('lgsl_version')) { // START OF DOUBLE LOAD PROTECTION
 			case "stationeers"		: $c_to_q = -485;	$c_def = 27500;	$q_def = 27015;	$c_to_s = 0;	break;
 			case "squad"			: $c_to_q = 19378;	$c_def = 7787;	$q_def = 27165;	$c_to_s = 0;	break;
 			case "swat4"			: $c_to_q = 1;		$c_def = 10780;	$q_def = 10781;	$c_to_s = 0;	break;
+			case "theforest"		: $c_to_q = 1;		$c_def = 27015;	$q_def = 27016;	$c_to_s = 0;	break;
 			case "tribesv"			: $c_to_q = 1;		$c_def = 7777;	$q_def = 7778;	$c_to_s = 0;	break;
 			case "ut"				: $c_to_q = 1;		$c_def = 7777;	$q_def = 7778;	$c_to_s = 0;	break;
 			case "ut2003"			: $c_to_q = 1;		$c_def = 7757;	$q_def = 7758;	$c_to_s = 10;	break;