浏览代码

fixed: 言灵卸载时的bug导致武器出现2个同时装备的现象.

gwang 5 年之前
父节点
当前提交
29cb3ee4ba
共有 100 个文件被更改,包括 6690 次插入16 次删除
  1. 13 0
      Gameserver/Amfphp/main.php
  2. 2 2
      Gameserver/Amfphp/process/HeroProc.php
  3. 6 14
      Gameserver/Amfphp/process/StoreProc.php
  4. 16 0
      Gameserver/gamesys/api.php
  5. 49 0
      Gameserver/gamesys/app/Autoloader.php
  6. 2 0
      Gameserver/gamesys/app/const.php
  7. 258 0
      Gameserver/gamesys/app/main.php
  8. 35 0
      Gameserver/gamesys/app/models/Err.php
  9. 63 0
      Gameserver/gamesys/app/models/Resp.php
  10. 76 0
      Gameserver/gamesys/app/providers/action.php
  11. 43 0
      Gameserver/gamesys/app/providers/actionc32.php
  12. 461 0
      Gameserver/gamesys/app/providers/actionone.php
  13. 18 0
      Gameserver/gamesys/app/providers/default.php
  14. 18 0
      Gameserver/gamesys/app/providers/ganggetest.php
  15. 88 0
      Gameserver/gamesys/app/providers/level.php
  16. 34 0
      Gameserver/gamesys/app/providers/login.php
  17. 41 0
      Gameserver/gamesys/app/providers/order.php
  18. 51 0
      Gameserver/gamesys/app/providers/orderChart.php
  19. 16 0
      Gameserver/gamesys/app/providers/pay.php
  20. 84 0
      Gameserver/gamesys/app/providers/retention.php
  21. 60 0
      Gameserver/gamesys/app/providers/retention2.php
  22. 14 0
      Gameserver/gamesys/app/providers/sys.php
  23. 17 0
      Gameserver/gamesys/app/utils/CMongo.php
  24. 76 0
      Gameserver/gamesys/app/utils/CRedis.php
  25. 150 0
      Gameserver/gamesys/app/utils/Comm.php
  26. 81 0
      Gameserver/gamesys/app/utils/Date.php
  27. 24 0
      Gameserver/gamesys/app/utils/GZip.php
  28. 41 0
      Gameserver/gamesys/app/utils/Json.php
  29. 66 0
      Gameserver/gamesys/app/utils/Log.php
  30. 124 0
      Gameserver/gamesys/app/utils/MySql.php
  31. 91 0
      Gameserver/gamesys/app/utils/PRedis.php
  32. 62 0
      Gameserver/gamesys/app/utils/Predis/Autoloader.php
  33. 523 0
      Gameserver/gamesys/app/utils/Predis/Client.php
  34. 189 0
      Gameserver/gamesys/app/utils/Predis/ClientContextInterface.php
  35. 21 0
      Gameserver/gamesys/app/utils/Predis/ClientException.php
  36. 230 0
      Gameserver/gamesys/app/utils/Predis/ClientInterface.php
  37. 398 0
      Gameserver/gamesys/app/utils/Predis/Cluster/ClusterStrategy.php
  38. 82 0
      Gameserver/gamesys/app/utils/Predis/Cluster/Distributor/DistributorInterface.php
  39. 21 0
      Gameserver/gamesys/app/utils/Predis/Cluster/Distributor/EmptyRingException.php
  40. 270 0
      Gameserver/gamesys/app/utils/Predis/Cluster/Distributor/HashRing.php
  41. 71 0
      Gameserver/gamesys/app/utils/Predis/Cluster/Distributor/KetamaRing.php
  42. 72 0
      Gameserver/gamesys/app/utils/Predis/Cluster/Hash/CRC16.php
  43. 30 0
      Gameserver/gamesys/app/utils/Predis/Cluster/Hash/HashGeneratorInterface.php
  44. 79 0
      Gameserver/gamesys/app/utils/Predis/Cluster/PredisStrategy.php
  45. 58 0
      Gameserver/gamesys/app/utils/Predis/Cluster/RedisStrategy.php
  46. 53 0
      Gameserver/gamesys/app/utils/Predis/Cluster/StrategyInterface.php
  47. 191 0
      Gameserver/gamesys/app/utils/Predis/Collection/Iterator/CursorBasedIterator.php
  48. 56 0
      Gameserver/gamesys/app/utils/Predis/Collection/Iterator/HashKey.php
  49. 43 0
      Gameserver/gamesys/app/utils/Predis/Collection/Iterator/Keyspace.php
  50. 176 0
      Gameserver/gamesys/app/utils/Predis/Collection/Iterator/ListKey.php
  51. 47 0
      Gameserver/gamesys/app/utils/Predis/Collection/Iterator/SetKey.php
  52. 60 0
      Gameserver/gamesys/app/utils/Predis/Collection/Iterator/SortedSetKey.php
  53. 129 0
      Gameserver/gamesys/app/utils/Predis/Command/Command.php
  54. 81 0
      Gameserver/gamesys/app/utils/Predis/Command/CommandInterface.php
  55. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/ConnectionAuth.php
  56. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/ConnectionEcho.php
  57. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/ConnectionPing.php
  58. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/ConnectionQuit.php
  59. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/ConnectionSelect.php
  60. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/HashDelete.php
  61. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/HashExists.php
  62. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/HashGet.php
  63. 42 0
      Gameserver/gamesys/app/utils/Predis/Command/HashGetAll.php
  64. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/HashGetMultiple.php
  65. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/HashIncrementBy.php
  66. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/HashIncrementByFloat.php
  67. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/HashKeys.php
  68. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/HashLength.php
  69. 85 0
      Gameserver/gamesys/app/utils/Predis/Command/HashScan.php
  70. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/HashSet.php
  71. 48 0
      Gameserver/gamesys/app/utils/Predis/Command/HashSetMultiple.php
  72. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/HashSetPreserve.php
  73. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/HashStringLength.php
  74. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/HashValues.php
  75. 44 0
      Gameserver/gamesys/app/utils/Predis/Command/HyperLogLogAdd.php
  76. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/HyperLogLogCount.php
  77. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/HyperLogLogMerge.php
  78. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyDelete.php
  79. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyDump.php
  80. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyExists.php
  81. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyExpire.php
  82. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyExpireAt.php
  83. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyKeys.php
  84. 50 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyMigrate.php
  85. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyMove.php
  86. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyPersist.php
  87. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyPreciseExpire.php
  88. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyPreciseExpireAt.php
  89. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyPreciseTimeToLive.php
  90. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyRandom.php
  91. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyRename.php
  92. 36 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyRenamePreserve.php
  93. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyRestore.php
  94. 66 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyScan.php
  95. 83 0
      Gameserver/gamesys/app/utils/Predis/Command/KeySort.php
  96. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyTimeToLive.php
  97. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/KeyType.php
  98. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/ListIndex.php
  99. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/ListInsert.php
  100. 28 0
      Gameserver/gamesys/app/utils/Predis/Command/ListLength.php

+ 13 - 0
Gameserver/Amfphp/main.php

@@ -15,6 +15,19 @@ $GLOBALS['OS'] = "win32";
 
 defined("ROOTDIR") or define("ROOTDIR", __DIR__);                               # 定义项目根目录
 
+/**
+ * 获取项目根路径URL(限制条件,本方法所在的文件也必须在项目根路径下wg)
+ * @author gwang
+ * @return string
+ */
+function getRootURL() {
+    $self = substr(__FILE__, strlen($_SERVER['DOCUMENT_ROOT']));
+    $self = str_replace('\\', '/', $self);
+    $uri = substr($self, 0, strrpos($self, '/') + 1);
+    $url = "http://" . $_SERVER['HTTP_HOST'] . $uri;
+    return $url;
+}
+
 /**
  * 一种优化方案, 如果部署环境可以支持动态代码生成,将会减少与redis网络通信的次数. 
  * (在云服务器实验中,开了不如不开效率高 ̄□ ̄||)

+ 2 - 2
Gameserver/Amfphp/process/HeroProc.php

@@ -1073,10 +1073,10 @@ class HeroProc {
                 $targetHero->level += 1;
                 $lvs = GameConfig::hero_levelexp_getItem($targetHero->level);
                 if ($targetHero->level >= $maxLevel) {
-                    echoLine("maxlevel");
+//                    echoLine("maxlevel");
                     $targetHero->xp = $lvs->needExp;
                 } else {
-                    echoLine("targetHero->xp -= $lvs->needExp");
+//                    echoLine("targetHero->xp -= $lvs->needExp");
                     $targetHero->xp -= $lvs->needExp;
                 }
 //                $targetHero->maxXp = $lvs->needExp;

+ 6 - 14
Gameserver/Amfphp/process/StoreProc.php

@@ -568,22 +568,14 @@ class StoreProc {
         list($itemtype, $yanling_uid, $herouid) = $req->paras;                  # 提取参数: 装备类型, 装备的UID, 拥有该装备的英雄的UID 
 
         $collectHeros = $user->heros->collectHeros;
-        if (!$collectHeros) {
-            Err(ErrCode::err_innerfault);
-        }
-        if (!CommUtil::isPropertyExists($collectHeros, $herouid)) {             # 检测是否存在拥有该装备的英雄
-            Err(ErrCode::hero_no);
-        }
-        if (!CommUtil::isPropertyExists($user->store->yanling, $yanling_uid)) { # 检测是否存在该装备
-            Err(ErrCode::store_itemno_err);
-        }
+        my_Assert(null != $collectHeros, ErrCode::err_innerfault);              # 防御
+        my_Assert(CommUtil::isPropertyExists($collectHeros, $herouid), ErrCode::hero_no); # 检测是否存在拥有该装备的英雄
+        my_Assert(CommUtil::isPropertyExists($user->store->yanling, $yanling_uid), ErrCode::store_itemno_err); # 检测是否存在该装备
         if ($user->store->yanling->$yanling_uid->herouid == $herouid) {         # 取装备对象
-            $user->store->yanling->$yanling_uid->herouid = 0;
-        }
-        if ($collectHeros->$herouid->yanling->$itemtype->itemuid != $yanling_uid) {
-            Err(ErrCode::store_noequip_err);
+            $user->store->yanling->$yanling_uid->herouid = 0;                   # 清理反向引用
         }
-        $collectHeros->$herouid->equip->weapon->itemuid = 0;                    # 卸下
+        my_Assert($collectHeros->$herouid->yanling->$itemtype->itemuid == $yanling_uid, ErrCode::store_noequip_err); # 防御
+        $collectHeros->$herouid->yanling->$itemtype->itemuid = 0;               # 卸下
 
         UserProc::updateUserInfo();                                             # 回写数据
         $resp = Resp::ok(array('resp' => "succeed!"));                          // 返回 

+ 16 - 0
Gameserver/gamesys/api.php

@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of api
+ *
+ * @author jgao
+ */
+class api {
+    //put your code here
+}

+ 49 - 0
Gameserver/gamesys/app/Autoloader.php

@@ -0,0 +1,49 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Autoloader
+ * 自动加载器
+ * @author jgao
+ */
+class Autoloader
+{
+    private $directorys;
+
+    public function __construct() {
+        $this->directorys = array(
+            __DIR__."/utils/",
+            __DIR__."/models/",
+            __DIR__."/providers/",
+        );
+    }
+
+    /**
+     * Registers the autoloader class with the PHP SPL autoloader.
+     *
+     * @param bool $prepend Prepend the autoloader on the stack instead of appending it.
+     */
+    public static function register($prepend = false){
+        spl_autoload_register(array(new self(), 'autoload'), true, $prepend);
+    }
+
+    /**
+     * Loads a class from a file using its fully qualified name.
+     *
+     * @param string $className Fully qualified name of a class.
+     */
+    public function autoload($className) {
+        foreach ($this->directorys as $dir){
+            $filepath = $dir.$className.'.php';
+            if (is_file($filepath)) {
+                require $filepath;
+                break;
+            }
+        }
+    }
+}

+ 2 - 0
Gameserver/gamesys/app/const.php

@@ -0,0 +1,2 @@
+<?php
+

+ 258 - 0
Gameserver/gamesys/app/main.php

@@ -0,0 +1,258 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of main
+ * 主头文件及全局方法
+ * @author jgao
+ */
+require __DIR__ . '/../globals.php';
+require __DIR__ . '/Autoloader.php';
+Autoloader::register();
+
+// <editor-fold defaultstate="collapsed">
+
+function gzip_encode($data) {
+    return GZip::encode($data);
+}
+
+function gzip_decode($data) {
+    return GZip::decode($data);
+}
+
+function console($obj) {
+    Log::console($obj);
+}
+
+function debug_stack_trace() {
+    if ($GLOBALS['API_TEST']) {
+        $array = debug_backtrace();
+        unset($array[0]);
+        foreach ($array as $row) {
+            $html .= $row['file'] . ':' . $row['line'] . '行,调用方法:' . $row['function'] . "<p>";
+        }
+        echo $html;
+    }
+}
+
+function random($min, $max) {
+    return Rand::random($min, $max);
+}
+
+function random_percent($percent) {
+    return Rand::randomPercent($percent);
+}
+
+function random_prob($probs) {
+    return Rand::randomProb($probs);
+}
+
+function dict_contains_key($dict, $property) {
+    return STL::dictContainsKey($dict, $property);
+}
+
+function dict_remove($dict, $property) {
+    STL::dictRemove($dict, $property);
+}
+
+function dict_insert($dict, $property, $value) {
+    STL::dictInsert($dict, $property, $value);
+}
+
+function dict_value($dict, $property) {
+    return STL::dictValue($dict, $property);
+}
+
+function dict_count($dict) {
+    return STL::count($dict);
+}
+
+function dict_propertys($dict) {
+    return STL::dictPropertys($dict);
+}
+
+function dict_values($dict) {
+    return STL::dictValues($dict);
+}
+
+function dict_random($dict) {
+    return STL::dictRandom($dict);
+}
+
+function dict_randoms($dict, $nums) {
+    return STL::dictRandoms($dict, $nums);
+}
+
+function dict_to_array($dict, $mod, $propertyList = null) {
+    return STL::dictToArray($dict, $mod, $propertyList);
+}
+
+function array_pushs(&$array, $items) {
+    $array = array_merge($array, $items);
+}
+
+function array_get(&$array, $index) {
+    return STL::arrayGet($array, $index);
+}
+
+function array_set(&$array, $index, $item) {
+    STL::arraySet($array, $index, $item);
+}
+
+function array_insert(&$array, $index, $item) {
+    STL::arrayInsert($array, $index, $item);
+}
+
+function array_remove(&$array, $item) {
+    STL::arrayRemove($array, $item);
+}
+
+function array_remove_at(&$array, $index) {
+    STL::arrayRemoveAt($array, $index);
+}
+
+function array_contains(&$array, $item) {
+    return STL::arrayContains($array, $item);
+}
+
+function array_index_of(&$array, $item) {
+    return STL::arrayIndexOf($array, $item);
+}
+
+function array_clear(&$array) {
+    return STL::arrayClear($array);
+}
+
+function array_clone(&$array) {
+    return STL::arrayClone($array);
+}
+
+function array_sort(&$array) {
+    return STL::arraySort($array);
+}
+
+function array_rsort(&$array) {
+    return STL::arrayRsort($array);
+}
+
+function array_count(&$array) {
+    return STL::count($array);
+}
+
+function array_random(&$array) {
+    return STL::arrayRandom($array);
+}
+
+function array_randoms(&$array, $num) {
+    return STL::arrayRandoms($array, $num);
+}
+
+function array_uniqueness(&$array) {
+    return STL::arrayUnique($array);
+}
+
+function str_contains($str, $sub) {
+    return Strings::contains($str, $sub);
+}
+
+function strsplit($str, $char = ',') {
+    return Strings::split($str, $char);
+}
+
+function strsplit_raw($str, $char = ',') {
+    return Strings::splitRaw($str, $char);
+}
+
+function now($offset = 0) {
+    return Time::now($offset);
+}
+
+function daytotime($day = 0) {
+    if ($day == 0) {
+        $day = day();
+    }
+    return $day * 86400 - 28800;
+}
+
+function millisecond() {
+    list($s1, $s2) = explode(' ', microtime());
+    return (float) sprintf('%.0f', (floatval($s1) + floatval($s2)) * 1000);
+}
+
+function hours($time = -1) {
+    return Time::hours($time);
+}
+
+function days($time = -1) {
+    return Time::days($time);
+}
+
+function weeks($time = -1) {
+    return Time::weeks($time);
+}
+
+function datetime($time = 0) {
+    return Time::date($time);
+}
+
+function request($url, $data) {
+    return Web::request($url, $data);
+}
+
+function query_string() {
+    return Web::getQueryString();
+}
+
+function query_paras() {
+    return Web::getQueryParas();
+}
+
+// </editor-fold>
+
+function db() {
+    $db = new MySql();
+    $db->conn(CDB_HOST, CDB_PORT, CDB_USER, CDB_PASS, CDB_NAME);
+    return $db;
+}
+
+/**
+ * get dao instance,
+ * @version 2019年12月23日 经过梦幻星工场2一年多的使用, 未发现特别明显的bug. 决定侧重使用. -- 王刚
+ *           2017.06.23     第一版 学习自禅道的开源框架.    -- 王刚
+ * @staticvar type $a
+ * @return \dao  注意: dao并非什么好东西,只是一些改进尝试, 稳定性, 性能, 效率尚未得到证明.-gwang 2017.06.23
+ */
+function daoInst() {
+    static $a = null;
+    if (is_null($a)) {
+        $cfg = array(
+            'persistant' => false,
+            'driver' => 'mysql',
+            'encoding' => 'UTF8',
+            'strictMode' => false,
+//            'emulatePrepare' => true,
+//            'bufferQuery' => true,
+            'prefix' => '',
+            'host' => CDB_HOST,
+            'port' => CDB_PORT,
+            'name' => CDB_NAME,
+            'user' => CDB_USER,
+            'password' => CDB_PASS);
+        $a = new dao();                                                         # 结束请求的时候自动回收,无需主动写关闭代码
+        $a->connectDB(json_decode(json_encode(array('paydb' => $cfg))));        # 建立链接,传入配置文件
+    }
+    return $a;
+}
+
+function getPageUrl($tag) {
+    if ($tag == "login" || $tag == "index") {
+        return ROOT_URL . "home/" . $tag . ".html";
+    } else {
+        return ROOT_URL . "home/pages/" . $tag . "/" . $tag . ".html";
+    }
+}

+ 35 - 0
Gameserver/gamesys/app/models/Err.php

@@ -0,0 +1,35 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Err
+ * 错误码
+ * @author jgao
+ */
+class Err {
+    //put your code here
+    
+    /**
+     * 成功
+     */
+    const succeed = 0;
+    
+    /**
+     * 默认错误
+     */
+    const err_default = 1;
+    
+    // --------------服务端内部错误----------------
+    
+    // --------------业务逻辑错误----------------
+    
+    /**
+     * 用户身份错误
+     */
+    const err_usr_ill = 6001;
+}

+ 63 - 0
Gameserver/gamesys/app/models/Resp.php

@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Resp
+ * 应答体
+ * @author jgao
+ */
+class Resp {
+    //put your code here
+    
+    /**
+     * 应答数据
+     * @var type 
+     */
+    public $data = null;
+    
+    /**
+     * 错误码
+     * @var type 
+     */
+    public $err = 0;
+    
+    /**
+     * 错误消息
+     * @var type 
+     */
+    public $errMsg = "";
+    
+    
+    function stringify(){
+        return json_encode($this);
+    }
+    
+    /**
+     * 
+     * @param type $data
+     * @return \Resp
+     */
+    static function ret($data){
+        $resp = new Resp();
+        $resp->data = $data;
+        return $resp;
+    }
+    
+    /**
+     * 
+     * @param type $err
+     * @param type $msg
+     * @return \Resp
+     */
+    static function err($err, $msg){
+        $resp = new Resp();
+        $resp->err = $err;
+        $resp->errMsg = $msg;
+        return $resp;
+    }
+}

+ 76 - 0
Gameserver/gamesys/app/providers/action.php

@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+include_once dirname(__FILE__).'/../main.php';
+/**
+ * Description of action
+ *
+ * @author jgao
+ */
+
+define("SELECT_BASE", "select `uid`,`plat`,`area`,`insertDatetime`,`lastUpdatetime` from tab_rolename where `insertDatetime`>='%s 00:00:00' and `insertDatetime`<='%s 23:59:59' limit %s,%s");
+define("SELECT_BASE_OLD", "select `uid`,`plat`,`area`,`insertDatetime`,`lastUpdatetime` from tab_rolename where `lastUpdatetime`>='%s 00:00:00' and `lastUpdatetime`<='%s 23:59:59' limit %s,%s");
+define("SELECT_BASE_LAST", "select `uid`,`plat`,`area`,`insertDatetime`,`lastUpdatetime` from tab_rolename where `lastUpdatetime`>='%s 00:00:00' and `lastUpdatetime`<='%s 23:59:59' and TO_DAYS(`lastUpdatetime`) = TO_DAYS(`insertDatetime`) limit %s,%s");
+define("SELECT_BASE_COUNT", "select count(*) from tab_rolename where `insertDatetime`>='%s 00:00:00' and `insertDatetime`<='%s 23:59:59'");
+define("SELECT_BASE_OLD_COUNT", "select count(*) from tab_rolename where `lastUpdatetime`>='%s 00:00:00' and `lastUpdatetime`<='%s 23:59:59'");
+define("SELECT_BASE_LAST_COUNT", "select count(*) from tab_rolename where `lastUpdatetime`>='%s 00:00:00' and `lastUpdatetime`<='%s 23:59:59' and TO_DAYS(`lastUpdatetime`) = TO_DAYS(`insertDatetime`)");
+
+$GET = query_paras();
+$date = $GET['date'];
+$date1 = $GET['date1'];
+$isnew = $GET['isnew'];
+$isold = $GET['isold'];
+$islost = $GET['islost'];
+$pageNumber = $GET['pageNumber'];
+$pageSize = $GET['pageSize'];
+
+$plat = "uc"; // 后续改成前端控制参数
+
+
+$start = ($pageNumber-1) * $pageSize;
+
+$count = 0;
+$arr = array();
+
+
+
+$db = db();
+if($isnew > 0){
+    // 计算新增人数
+    $sql = sprintf(SELECT_BASE_COUNT, $date, $date1);
+    $count = $db->fetch_row($sql);
+    $count = (int)$count[0][0];
+    $sql = sprintf(SELECT_BASE, $date, $date1,$start,$pageSize);
+    $arr = $db->fetch_array($sql);
+}
+else if($isold > 0){
+    // 计算新增人数
+    $sql = sprintf(SELECT_BASE_OLD_COUNT, $date, $date1);
+    $count = $db->fetch_row($sql);
+    $count = (int)$count[0][0];
+    $sql = sprintf(SELECT_BASE_OLD, $date, $date1,$start,$pageSize);
+    $arr = $db->fetch_array($sql);
+}
+else if($islost > 0){
+    // 计算新增人数
+    $sql = sprintf(SELECT_BASE_LAST_COUNT, $date, $date1);
+    $count = $db->fetch_row($sql);
+    $count = (int)$count[0][0];
+    $sql = sprintf(SELECT_BASE_LAST, $date, $date1,$start,$pageSize);
+    $arr = $db->fetch_array($sql);
+}
+$db->close();
+
+
+$result = new stdClass();
+$result->total = $count;
+$result->rows = $arr;
+
+
+echo json_encode($result);
+
+

+ 43 - 0
Gameserver/gamesys/app/providers/actionc32.php

@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+include_once dirname(__FILE__).'/../main.php';
+/**
+ * Description of action
+ *
+ * @author jgao
+ */
+
+define("SELECT_BASE_C32", "select `uid`,`plat`,`area`,`insertDatetime`,`lastUpdatetime` from tab_rolename where `name`='%s' or `c32`='%s' or `uid`='%s'");
+
+$GET = query_paras();
+$name = urldecode($GET['name']);
+$c32 = $GET['c32'];
+$oid = $GET['oid'];
+
+
+$plat = "uc"; // 后续改成前端控制参数
+
+
+
+$db = db();
+
+    // 计算新增人数
+    $sql = sprintf(SELECT_BASE_C32, $name, $c32, $oid);
+    $arr = $db->fetch_array($sql);
+
+
+$db->close();
+
+$count = count($arr);
+$result = new stdClass();
+$result->total = $count;
+$result->rows = $arr;
+
+echo json_encode($result);
+
+

+ 461 - 0
Gameserver/gamesys/app/providers/actionone.php

@@ -0,0 +1,461 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+include_once dirname(__FILE__).'/../main.php';
+/**
+ * Description of actionone
+ *
+ * @author jgao
+ */
+
+define("CHECK_ONE", "SELECT table_name FROM information_schema.TABLES WHERE table_name ='tab_sys_%s' AND `TABLE_SCHEMA`= '%s'");
+define("SELECT_ONE", "select `cmd`,`ts`,`dump`,`prs`,`ret` from tab_sys_%s where `oid`='%s'");
+
+define("CHECK_ONE_UC", "SELECT table_name FROM information_schema.TABLES WHERE table_name ='tab_sys_%s_%s' AND `TABLE_SCHEMA`= '%s'");
+define("SELECT_ONE_UC", "select `cmd`,`ts`,`dump`,`prs`,`ret` from tab_sys_%s_%s where `oid`='%s'");
+
+$GET = query_paras();
+$date = $GET['date'];
+$date1 = $GET['date1'];
+
+$plat = "uc"; // 后续改成前端控制参数
+$oid = $GET['oid'];
+
+$ret = array();
+
+$db = db();
+$endDate = strtotime("$date1");
+// 计算新增人数
+for($i=0;$i<=999;$i++){
+    $ucday = strtotime("2017-09-28 00:00:00");
+    $iday = strtotime("$date   +$i   day");
+    $day = date("Ymd", $iday);
+    if($iday > $endDate){
+        break;
+    }
+    if($iday <= $ucday){
+        $sql = sprintf(CHECK_ONE_UC, $plat, $day, CDB_NAME);
+        $exist = $db->fetch_array($sql);
+    }else{
+        $sql = sprintf(CHECK_ONE, $day, CDB_NAME);
+        $exist = $db->fetch_array($sql);
+    }
+
+    if(count($exist) <= 0){
+        continue;
+    }
+    
+    if($iday <= $ucday){
+        $sql = sprintf(SELECT_ONE_UC, $plat, $day, $oid);
+    }else{
+        $sql = sprintf(SELECT_ONE, $day, $oid);
+    }
+
+    $arr = $db->fetch_array($sql);
+    array_pushs($ret, $arr);
+}
+$db->close();
+
+
+
+// <editor-fold defaultstate="collapsed">
+
+function getCmdName($cmd){
+    switch ($cmd){
+        case '6001':
+            return "玩家登录";
+        case '6002':
+            return "创建角色";
+        case '6007':
+            return "设定头像";
+        case '6008':
+            return "设置昵称";
+        case '6012':
+            return "结束引导";
+            
+            
+        case '6101':
+            return "建造房间";
+        case '6102':
+            return "升级房间";
+        case '6103':
+            return "拆除房间";
+        case '6104':
+            return "移除石头";
+        case '6105':
+            return "人员调配";
+        case '6106':
+            return "收取资源";
+        case '6107':
+            return "加速收取";    
+        case '6108':
+            return "楼层扩建";
+        case '6109':
+            return "房间移动";
+        case '6110':
+            return "房间完成";    
+        case '6111':
+            return "购买建造队列";
+        case '6112':
+            return "房间加速";
+        case '6113':
+            return "完成楼层";    
+        case '6114':
+            return "加速楼层";
+        case '6115':
+            return "新建筑标志取消";
+        
+        case '6201':
+            return "角色创建";    
+        case '6202':
+            return "穿戴装备";
+        case '6203':
+            return "英雄升阶";
+        case '6204':
+            return "英雄升星";    
+        case '6205':
+            return "技能升级";
+        case '6206':
+            return "购买技能点";
+        case '6207':
+            return "放逐角色";    
+            
+        
+        case '6301':
+            return "出售物品";
+        case '6302':
+            return "使用经验道具";
+        case '6303':
+            return "道具合成";    
+        case '6304':
+            return "使用宝箱道具";
+        case '6305':
+            return "一键进阶";
+        case '6306':
+            return "一键升级";    
+        case '6307':
+            return "英雄图鉴奖励";
+        case '6308':
+            return "英雄三选一礼包";
+        case '6309':
+            return "道具随机礼包";    
+        case '6310':
+            return "一键穿戴";
+            
+        case '6501':
+            return "战斗开始";
+        case '6502':
+            return "战斗结束";
+        case '6503':
+            return "关卡扫荡";
+        case '6504':
+            return "买精英关卡次数";
+        case '6505':
+            return "新增战队";
+        case '6506':
+            return "更新战队";
+        case '6507':
+            return "删除战队";
+            
+        case '6602':
+            return "完成任务";
+            
+        case '6702':
+            return "竞技场防守配置";
+        case '6703':
+            return "竞技场挑战";
+        case '6706':
+            return "竞技场刷CD";
+        case '6707':
+            return "竞技场买挑战数";
+        case '6708':
+            return "竞技场刷对手";
+        case '6711':
+            return "竞技场锁定对手";
+        
+        case '6803':
+            return "收取邮件附件";
+        case '6804':
+            return "收取全部邮件";
+        case '6805':
+            return "读旧消息";
+        case '6806':
+            return "月签到";
+        
+        case '7002':
+            return "掠夺其他人";
+        case '7005':
+            return "掠夺拔旗";
+        case '7006':
+            return "掠夺防守配置";
+        case '7009':
+            return "掠夺胜利宣言";
+        case '7010':
+            return "掠夺目标锁定";
+            
+        case '7102':
+            return "领取活动奖励";
+        case '7201':
+            return "居民回收";
+            
+        case '7401':
+            return "穿戴神装";
+        case '7402':
+            return "卸载神装";
+        case '7403':
+            return "一键穿戴";
+        case '7404':
+            return "一键换装";
+        
+        case "7451":
+            return "神装强化";
+        case "7452":
+            return "一键强化";
+        case "7453":
+            return "全部强化";
+        case "7454":
+            return "神装升星";
+            
+        case '8601':
+            return "抽奖";
+        case '8603':
+            return "使用兑换码";
+        
+        case '8001':
+            return "买商城物品";
+        case '8003':
+            return "刷商店商品";
+    }
+    return $cmd;
+}
+
+/**
+ * 组合提示信息
+ * @param type $info
+ */
+function getCmdDes($info){
+    $cmd = $info['cmd'];
+    switch ($cmd){
+        case '1002':
+            $prs = json_decode($info['prs']);
+            $evn = $prs->evn;
+            return "自定义事件: $evn; ";
+        case '6001':
+            $str = "";
+            $ret = json_decode($info['ret']);
+            $str .= "昵称:".$ret->name;
+            $str .= "; 等级:".$ret->lv;
+            $str .= "; 宝石:".$ret->gem;
+            $str .= "; 电:".$ret->elec;
+            $str .= "; 水:".$ret->wat;
+            $str .= "; 食物:".$ret->fod;
+            $str .= "; 治疗针:".$ret->inj;
+            $str .= "; 技能点:".$ret->skp;
+            $str .= "; 英雄数:".$ret->hnum;
+            $str .= "; 英雄列表:[";
+            foreach ($ret->heros as $item){
+                $str .= " id:".$item->tid;
+                $str .= " 等级:".$item->lv;
+                $str .= " 星级:".$item->star;
+                $str .= " 品质:".$item->qual;
+            }
+            $str .= "]; 居民数:".$ret->rnum;
+            $str .= "; 建筑数:".$ret->bnum;
+            $str .= "; 建筑列表:[";
+            foreach ($ret->bds as $item){
+                $str .= " id:".$item->tid;
+                $str .= " 等级:".$item->lv;
+                $str .= " 合并:".$item->mlv;
+            }
+            $str .= "]; 普通关:".$ret->cgt;
+            $str .= "; 精英关:".$ret->agt;
+            return $str;
+        case '6002':
+            $prs = json_decode($info['prs']);
+            $name = base64_decode($prs->name);
+            $sex = $prs->sex == 2 ? "女" : "男";
+            return "姓名:$name; 性别: $sex; ";
+        case '6007':
+            $prs = json_decode($info['prs']);
+            $icon = $prs->iconId;
+            return "图标: $icon; ";
+        case '6008':
+            $prs = json_decode($info['prs']);
+            $name = base64_decode($prs->name);
+            return "姓名: $name; ";
+            
+        case '6602':
+            $prs = json_decode($info['prs']);
+            $id = $prs->id;
+            return "ID: $id; ";
+            
+        case '6703':
+            $ret = json_decode($info['ret']);
+            $rank = $ret->ranking;
+            $top = $ret->topranking;
+            return "当前名次: $rank; 最高排名: $top; ";
+            
+        case '6711':
+            $prs = json_decode($info['prs']);
+            $otheroid = $prs->otheroid;
+            return "Oid: $otheroid; ";
+
+        case '7009':
+            $prs = json_decode($info['prs']);
+            $says = $prs->says;
+            return "ID: $says; ";
+            
+        case '7010':
+            $prs = json_decode($info['prs']);
+            $other = $prs->other;
+            return "Oid: $other; ";
+            
+        case '7102':
+            $prs = json_decode($info['prs']);
+            $actId = $prs->actId;
+            $prog = $prs->prog;
+            return "ID: $actId; 奖项: $prog; ";
+            
+        case '8601':
+            $prs = json_decode($info['prs']);
+            $id = $prs->id;
+            if($id == 2){
+                return "食物十连抽; ";
+            }else if($id == 3){
+                return "宝石一连抽; ";
+            }else if($id == 4){
+                return "宝石十连抽; ";
+            }
+            return "食物一连抽; ";
+            
+        case '8603':
+            $prs = json_decode($info['prs']);
+            $code = $prs->code;
+            return "码: $code; ";
+        
+    }
+    return "";
+}
+
+/**
+ * 敏感数据提示信息
+ * @param type $info
+ */
+function getDumpDes($info){
+    $des = "";
+    $dump = json_decode($info['dump']);
+    foreach ($dump as $key=>$val){
+        if($key == "ulv"){
+            $des .= "玩家等级: $val; ";
+        }else if($key == "gem"){
+            $des .= "当前宝石: $val; ";
+        }else if($key == "agem"){
+            $des .= "宝石增加: $val; ";
+        }else if($key == "dgem"){
+            $des .= "宝石减少: $val; ";
+        }else if($key == "elec"){
+            $des .= "当前电力: $val; ";
+        }else if($key == "alec"){
+            $des .= "电力增加: $val; ";
+        }else if($key == "dlec"){
+            $des .= "电力减少: $val; ";
+        }else if($key == "wat"){
+            $des .= "当前水: $val; ";
+        }else if($key == "awat"){
+            $des .= "水增加: $val; ";
+        }else if($key == "dwat"){
+            $des .= "水减少: $val; ";
+        }else if($key == "food"){
+            $des .= "当前食物: $val; ";
+        }else if($key == "afod"){
+            $des .= "食物增加: $val; ";
+        }else if($key == "dfod"){
+            $des .= "食物减少: $val; ";
+        }else if($key == "inj"){
+            $des .= "当前治疗针: $val; ";
+        }else if($key == "ainj"){
+            $des .= "治疗针增加: $val; ";
+        }else if($key == "dinj"){
+            $des .= "治疗针减少: $val; ";
+        }else if($key == "skp"){
+            $des .= "当前技能点: $val; ";
+        }else if($key == "askp"){
+            $des .= "技能点增加: $val; ";
+        }else if($key == "dskp"){
+            $des .= "技能点减少: $val; ";
+        }else if($key == "pkg"){
+            $des .= "当前决斗币: $val; ";
+        }else if($key == "apkg"){
+            $des .= "决斗币增加: $val; ";
+        }else if($key == "dpkg"){
+            $des .= "决斗币减少: $val; ";
+        }else if($key == "btid"){
+            $des .= "建筑类型: $val; ";
+        }else if($key == "bnum"){
+            $des .= "建筑数量: $val; ";
+        }else if($key == "blv"){
+            $des .= "建筑等级: $val; ";
+        }else if($key == "bmlv"){
+            $des .= "合并等级: $val; ";
+        }else if($key == "flr"){
+            $des .= "楼层数: $val; ";
+        }else if($key == "hnum"){
+            $des .= "英雄数: $val; ";
+        }else if(str_contains ($key, "hlv")){
+            $strs = strsplit($key, "_");
+            $id = $strs[1];
+            $des .= "英雄[$id]等级: $val; ";
+        }else if($key == "htid"){
+            $des .= "英雄: $val; ";
+        }else if($key == "hsid"){
+            $des .= "技能: $val; ";
+        }else if($key == "hqu"){
+            $des .= "英雄品阶: $val; ";
+        }else if($key == "hstr"){
+            $des .= "英雄星级: $val; ";
+        }else if($key == "hskv"){
+            $des .= "技能等级: $val; ";
+        }else if($key == "bfid"){
+            $des .= "关卡: $val; ";
+        }else if($key == "bfst"){
+            $des .= "首次通关: $val; ";
+        }else if(str_contains ($key, "inum")){
+            $strs = strsplit($key, "_");
+            $id = $strs[1];
+            $des .= "道具[$id]数目: $val; ";
+        }else if(str_contains ($key, "iadd")){
+            $strs = strsplit($key, "_");
+            $id = $strs[1];
+            $des .= "道具[$id]增加: $val; ";
+        }else if(str_contains ($key, "idel")){
+            $strs = strsplit($key, "_");
+            $id = $strs[1];
+            $des .= "道具[$id]减少: $val; ";
+        }else if($key == "arpk"){
+            $des .= "竞技场次数: $val; ";
+        }else if($key == "seid"){
+            $des .= "神装: $val; ";
+        }else if($key == "selv"){
+            $des .= "神装强化: $val; ";
+        }else if($key == "sesr"){
+            $des .= "神装升星: $val; ";
+        }
+    }
+    return $des;
+}
+
+// </editor-fold>
+
+$count = count($ret);
+
+for ($i=0;$i<$count;$i++){
+    $ret[$i]['name'] = getCmdName($ret[$i]['cmd']);
+    $ret[$i]['des'] = getCmdDes($ret[$i]);
+    $ret[$i]['des'] .= getDumpDes($ret[$i]);
+}
+
+echo json_encode($ret);

+ 18 - 0
Gameserver/gamesys/app/providers/default.php

@@ -0,0 +1,18 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+
+/**
+ * Description of default
+ * 主页面业务逻辑
+ * @author jgao
+ */
+
+
+
+

+ 18 - 0
Gameserver/gamesys/app/providers/ganggetest.php

@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * 测试 --王刚
+ *  1. 测试新加页面
+ *  2. 测试MySQL连接引擎(pdo)dao
+ *  3. 测试前后端通讯JQuery.post()
+ *  4. 测试H5chart 
+ */
+include_once dirname(__FILE__) . '/../main.php';
+
+
+
+$users = daoInst()->select()->from("tab_rolename")->fetchAll();
+//daoInst()->select()fromm('tab_rolename')->fields()
+//var_dump(Json::encode($users));
+
+exit(Json::encode(array('datas' => $users)));

+ 88 - 0
Gameserver/gamesys/app/providers/level.php

@@ -0,0 +1,88 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+include_once dirname(__FILE__) . '/../main.php';
+/**
+ * Description of retention
+ * 关卡统计业务逻辑
+ * @author jgao
+ */
+define("CHECK_LEVEL", "SELECT table_name FROM information_schema.TABLES WHERE table_name ='tab_sys_%s' AND `TABLE_SCHEMA`= '%s'");
+define("SELECT_LEVEL", "SELECT tab_sys_%s.dump FROM `tab_sys_%s` left join `tab_rolename` on tab_sys_%s.oid = tab_rolename.uid WHERE tab_sys_%s.cmd='6502' and tab_rolename.insertDatetime>='%s 00:00:00' and tab_rolename.insertDatetime<='%s 23:59:59' and tab_sys_%s.dump like '%%bfst%%'");
+define("SELECT_GUIDE", "SELECT count(*) FROM `tab_sys_%s` left join `tab_rolename` on tab_sys_%s.oid = tab_rolename.uid WHERE tab_sys_%s.cmd='6012' and tab_rolename.insertDatetime>='%s 00:00:00' and tab_rolename.insertDatetime<='%s 23:59:59'");
+
+define("CHECK_LEVEL_UC", "SELECT table_name FROM information_schema.TABLES WHERE table_name ='tab_sys_%s_%s' AND `TABLE_SCHEMA`= '%s'");
+define("SELECT_LEVEL_UC", "SELECT tab_sys_%s_%s.dump FROM `tab_sys_%s_%s` left join `tab_rolename` on tab_sys_%s_%s.oid = tab_rolename.uid WHERE tab_sys_%s_%s.cmd='6502' and tab_rolename.insertDatetime>='%s 00:00:00' and tab_rolename.insertDatetime<='%s 23:59:59' and tab_sys_%s_%s.dump like '%%bfst%%'");
+define("SELECT_GUIDE_UC", "SELECT count(*) FROM `tab_sys_%s_%s` left join `tab_rolename` on tab_sys_%s_%s.oid = tab_rolename.uid WHERE tab_sys_%s_%s.cmd='6012' and tab_rolename.insertDatetime>='%s 00:00:00' and tab_rolename.insertDatetime<='%s 23:59:59'");
+$date = $_POST['date'];
+
+$plat = is_null($_POST['plat']) ? "uc" : $_POST['plat']; // 后续改成前端控制参数
+
+$array = array();
+$array2 = array();
+
+$db = db();
+
+
+
+// 计算新增人数
+for ($i = 0; $i <= 0; $i++) {
+    $ucday = strtotime("2017-09-28 00:00:00");
+    $iday = strtotime("$date   +$i   day");
+    $day = date("Ymd", $iday);
+    $day_format = date("Y-m-d", $iday);
+//    if ($iday <= $ucday) {
+    $sql = sprintf(CHECK_LEVEL_UC, $plat, $day, CDB_NAME);
+    $exist = $db->fetch_array($sql);
+//    } else {
+//        $sql = sprintf(CHECK_LEVEL, $day, CDB_NAME);
+//        $exist = $db->fetch_array($sql);
+//    }
+
+    if (count($exist) <= 0) {
+        continue;
+    }
+
+//    if ($iday <= $ucday) {
+    $sql = sprintf(SELECT_LEVEL_UC, $plat, $day, $plat, $day, $plat, $day, $plat, $day, $day_format, $day_format, $plat, $day, $plat);
+//    } else {
+//        $sql = sprintf(SELECT_LEVEL, $day, $day, $day, $day, $day_format, $day_format, $day);
+//    }
+
+    $arr = $db->fetch_array($sql);
+
+//    if ($iday <= $ucday) {
+    $sql = sprintf(SELECT_GUIDE_UC, $plat, $day, $plat, $day, $plat, $day, $day_format, $day_format);
+//    } else {
+//        $sql = sprintf(SELECT_GUIDE, $day, $day, $day, $day_format, $day_format);
+//    }
+
+    $count = $db->fetch_row($sql);
+    $count = (int) $count[0][0];
+}
+
+
+$db->close();
+
+$datas = array();
+$datas[] = $count;
+
+foreach ($arr as $value) {
+    $dump = $value['dump'];
+    $dump = json_decode($dump);
+    if ((int) $dump->bfst <= 100) {
+        if (is_integer($datas[(int) $dump->bfst]) == true) {
+            $datas[(int) $dump->bfst]++;
+        } else {
+            $datas[(int) $dump->bfst] = 1;
+        }
+    }
+}
+
+$result = new stdClass();
+$result->datas = $datas;
+echo json_encode($result);

+ 34 - 0
Gameserver/gamesys/app/providers/login.php

@@ -0,0 +1,34 @@
+<?php
+session_start();
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+include_once dirname(__FILE__).'/../main.php';
+/**
+ * Description of login
+ * 登录页面业务逻辑
+ * @author jgao
+ */
+
+$paras = query_paras();
+$username = $paras["username"];
+$password = $paras["password"];
+
+
+if($username == "admin" && $password == "123456"){
+    // 同时写入cookie和session,用作后续前后端身份验证判定
+    setcookie("account", $username, time() + 3600, "/");
+    $_SESSION["account"] = $username;
+    // 如果账号密码都正确,重定向浏览器到登录页面
+    header("Location:" . getPageUrl("index"));
+    // 确保重定向后,后续代码不会被执行 
+    exit;
+}else{
+    // 否则认为是非法请求
+    header("Location:" . getPageUrl("err"));
+    // 确保重定向后,后续代码不会被执行 
+    exit;
+}
+

+ 41 - 0
Gameserver/gamesys/app/providers/order.php

@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+include_once dirname(__FILE__) . '/../main.php';
+
+
+
+$platform = $_GET["platform"];
+$startTime = $_GET["startTime"];
+$endTime = $_GET["endTime"];
+$pageNumber = $_GET['pageNumber'];
+$pageSize = $_GET['pageSize'];
+
+$start = ($pageNumber - 1) * $pageSize;
+
+
+$db = db();
+$sql = "select * from `tab_order` where 1=1";
+if ($platform != "") {
+    $sql = $sql . " and `plat`='" . $platform . "'";
+}
+if ($startTime != "" && $endTime != "") {
+    $sql = $sql . " and `payTime` >= '" . $startTime . " 00:00:00" . "' and `payTime` <= '" . $endTime . " 00:00:00" . "'";
+}
+$count = count($db->fetch_array($sql));
+
+
+$db->close();
+$db = db();
+$sql = $sql . " limit " . $start . "," . $pageSize;
+$list = $db->fetch_array($sql);
+$db->close();
+$array = new stdClass();
+$array->total = $count;
+$array->rows = $list;
+
+echo json_encode($array);

+ 51 - 0
Gameserver/gamesys/app/providers/orderChart.php

@@ -0,0 +1,51 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+include_once dirname(__FILE__) . '/../main.php';
+
+$platform = $_POST["platform"];
+$startTime = $_POST["startTime"];
+$endTime = $_POST["endTime"];
+
+
+$endDate = strtotime("$endTime");
+
+$db = db();
+$array = array();
+$labels = array();
+$totalPrize = 0;
+for ($i = 0; $i <= 999; $i++) {
+    $iday = strtotime("$startTime   +$i   day");
+    $day = date("Y-m-d", $iday);
+    if ($iday > $endDate) {
+        break;
+    }
+    $sql = "select sum(`amount`) from `tab_order` where `payTime` >= '" . $day . " 00:00:00" . "' and `payTime` <= '" . $day . " 23:59:59" . "'";
+    if ($platform != "") {
+        $sql = $sql . " and `plat`='" . $platform . "'";
+    }
+
+    $prize = $db->fetch_row($sql);
+
+    $prize = ((int)$prize[0][0])/100;
+    $prize = (float)sprintf("%.2f",$prize);
+    $array[] = $prize;
+    $labels[] = $day;
+    $totalPrize += $prize;
+}
+
+$db->close();
+
+
+$result = new stdClass();
+$result->data = $array;
+$result->labels = $labels;
+$result->totalPrize = $totalPrize;
+
+
+echo json_encode($result);

+ 16 - 0
Gameserver/gamesys/app/providers/pay.php

@@ -0,0 +1,16 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of pay
+ * 付费统计
+ * @author jgao
+ */
+
+define("SELECT_PAY_ORDERS", "");
+define("SELECT_PAY_USERS", "");

+ 84 - 0
Gameserver/gamesys/app/providers/retention.php

@@ -0,0 +1,84 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+include_once dirname(__FILE__) . '/../main.php';
+/**
+ * Description of retention
+ * 留存统计业务逻辑
+ * @author jgao
+ */
+define("SELECT_BASE", "select count(uid) as total from tab_rolename where `insertDatetime`>='%s 00:00:00' and `insertDatetime`<='%s 23:59:59'");
+
+define("SELECT_ONE", "select count(uid) as total from tab_rolename where `insertDatetime`>='%s 00:00:00' and `insertDatetime`<='%s 23:59:59' and `lastUpdatetime`>='%s 00:00:00'");
+
+
+$date = $_POST['date'];
+
+$array = array();
+$array2 = array();
+
+$db = db();
+// 计算新增人数
+$sql = sprintf(SELECT_BASE, $date, $date);
+$arr = $db->fetch_array($sql);
+$new = $arr[0]['total'];
+$array[] = $new;
+// 计算次留
+$iday = strtotime("$date   +1   day");
+$day = date("Y-m-d", $iday);
+$sql = sprintf(SELECT_ONE, $date, $date, $day);
+$arr = $db->fetch_array($sql);
+$total = $arr[0]['total'];
+$array[] = $total;
+$array2[] = (int)($total/$new * 100);
+// 计算3日留存
+$iday = strtotime("$date   +3   day");
+$day = date("Y-m-d", $iday);
+$sql = sprintf(SELECT_ONE, $date, $date, $day);
+$arr = $db->fetch_array($sql);
+$total = $arr[0]['total'];
+$array[] = $total;
+$array2[] = (int)($total/$new * 100);
+// 计算5日留存
+$iday = strtotime("$date   +5   day");
+$day = date("Y-m-d", $iday);
+$sql = sprintf(SELECT_ONE, $date, $date, $day);
+$arr = $db->fetch_array($sql);
+$total = $arr[0]['total'];
+$array[] = $total;
+$array2[] = (int)($total/$new * 100);
+// 计算7日留存
+$iday = strtotime("$date   +7   day");
+$day = date("Y-m-d", $iday);
+$sql = sprintf(SELECT_ONE, $date, $date, $day);
+$arr = $db->fetch_array($sql);
+$total = $arr[0]['total'];
+$array[] = $total;
+$array2[] = (int)($total/$new * 100);
+// 计算15日留存
+$iday = strtotime("$date   +15   day");
+$day = date("Y-m-d", $iday);
+$sql = sprintf(SELECT_ONE, $date, $date, $day);
+$arr = $db->fetch_array($sql);
+$total = $arr[0]['total'];
+$array[] = $total;
+$array2[] = (int)($total/$new * 100);
+// 计算30日留存
+$iday = strtotime("$date   +30   day");
+$day = date("Y-m-d", $iday);
+$sql = sprintf(SELECT_ONE, $date, $date, $day);
+$arr = $db->fetch_array($sql);
+$total = $arr[0]['total'];
+$array[] = $total;
+$array2[] = (int)($total/$new * 100);
+
+$db->close();
+
+$result = new stdClass();
+$result->data1 = $array;
+$result->data2 = $array2;
+echo json_encode($result);

+ 60 - 0
Gameserver/gamesys/app/providers/retention2.php

@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+include_once dirname(__FILE__) . '/../main.php';
+/**
+ * Description of retention
+ * 留存统计业务逻辑
+ * @author jgao
+ */
+define("SELECT_BASE", "select count(uid) as total from tab_rolename where `insertDatetime`>='%s 00:00:00' and `insertDatetime`<='%s 23:59:59'");
+
+define("SELECT_ONE", "select count(uid) as total from tab_rolename where `insertDatetime`>='%s 00:00:00' and `insertDatetime`<='%s 23:59:59' and `lastUpdatetime`>='%s 00:00:00'");
+
+
+$startTime = $_POST['date1'];
+$endTime = $_POST['date2'];
+$type = $_POST['type'];
+
+$endDate = strtotime("$endTime");
+
+$array = array();
+$labels = array();
+
+$db = db();
+for ($i = 0; $i <= 999; $i++) {
+    $iday = strtotime("$startTime   +$i   day");
+    $day = date("Y-m-d", $iday);
+    if ($iday > $endDate) {
+        break;
+    }
+    $sql = sprintf(SELECT_BASE, $day, $day);
+    $arr = $db->fetch_array($sql);
+    $new = $arr[0]['total'];
+    $labels[] = $day;
+    
+
+    
+    if ($type == 0) {
+        $array[] = $new;
+    } 
+    else {
+        $iday2 = strtotime("$day   +$type   day");
+        $day2 = date("Y-m-d", $iday2);
+        $sql = sprintf(SELECT_ONE, $day, $day, $day2);
+        $arr = $db->fetch_array($sql);
+        $total = $arr[0]['total'];
+        $array[] = (int) ($total / $new * 100);
+    }
+}
+
+$db->close();
+
+$result = new stdClass();
+$result->data = $array;
+$result->labels = $labels;
+echo json_encode($result);

+ 14 - 0
Gameserver/gamesys/app/providers/sys.php

@@ -0,0 +1,14 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of sys
+ *
+ * @author jgao
+ */
+

+ 17 - 0
Gameserver/gamesys/app/utils/CMongo.php

@@ -0,0 +1,17 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of MongoDB
+ *
+ * @author jgao
+ */
+class Mongo {
+    //put your code here
+    
+}

+ 76 - 0
Gameserver/gamesys/app/utils/CRedis.php

@@ -0,0 +1,76 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Redis
+ *
+ * @author jgao
+ */
+class CRedis {
+    //put your code here
+    var $redis = null;
+    
+    public function conn($host, $port, $db=0) {
+        $this->redis = new Redis();
+        $ret = $this->redis->connect($host, $port);
+        if($ret==null || !$ret){
+            $this->halt("connect failed!");
+        }
+        $this->redis->select($db);
+        return $ret;
+    }
+    
+    public function get($key){
+        $result = $this->redis->get($key);
+        if (!$result || $result == "" || $result == null){
+            return null;
+        }
+        return JsonUtil::decode($result);
+    }
+  
+    public function set($key, $val) {
+        if($val == null || $val == ""){ 
+            return false;
+        }
+        $ret = $this->redis->set($key, JsonUtil::encode($val));
+        return "OK" == $ret;
+    }
+    
+    public function delete($key) {
+        $infos = array(); $infos[] = $key;
+        return $this->redis->del($infos);
+    }
+    
+    public function cas($key, $value) {
+        return $this->set($key, $value);
+    }
+    
+    public function copy($surKey, $desKey) {
+        return $this->set($desKey, $this->get($surKey));
+    }
+    
+    public function close() {
+        $this->redis->close();
+    }
+    
+    /**
+     * 获得Redis扩展客户端
+     * @return Redis 
+     */
+    public function getRedisClient(){
+        return $this->redis;
+    }
+    
+    /**
+     * 错误中断
+     * @param type $msg
+     */
+    function halt($msg=null){
+        exit($msg.'<br /> '.$this->redis->getLastError());
+    }
+}

+ 150 - 0
Gameserver/gamesys/app/utils/Comm.php

@@ -0,0 +1,150 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Com
+ *
+ * @author jgao
+ */
+class Comm {
+    //put your code here
+    
+    /**
+     * 对象克隆
+     * @param type $obj
+     */
+    static function cloneObject($obj, $type=null){
+        if($type != null){
+            $clone = new $type();
+        }else{
+            $clone = new stdClass();
+        }
+        foreach ($obj as $key => $value) {
+            $clone->$key = $value;
+        }
+        return $clone;
+    }
+    
+    /**
+     * 对象装箱
+     * @param type $surObj 原始obj
+     * @param type $desObj 具体obj
+     */
+    static function loadObject($surObj, &$desObj) {
+        if($surObj==null||$desObj==null) return;
+        foreach ($desObj as $key => $value) {
+            if (property_exists($surObj, $key)) {
+                $desObj->$key = $surObj->$key;
+            }
+        }
+    }
+    
+    /**
+     * 对象装箱
+     * @param type $obj
+     * @param type $type
+     * @return type 
+     */
+    static function loadClass($obj, $type){
+        $newObj = new $type();
+        self::loadObject($obj, $newObj);
+        return $obj;
+    }
+    
+    /**
+     * 数组装箱
+     * @param type $obj
+     * @param type $type
+     * @return type 
+     */
+    static function loadArray($obj, $type){
+        $array = array();
+        foreach($obj as $item){
+            $array[] = self::loadClass($item, $type);
+        }
+        return $array;
+    }
+    
+    /**
+     * 字典装箱
+     * @param type $obj
+     * @param type $type
+     * @return \stdClass 
+     */
+    static function loadDict($obj, $type){
+        $dict = new stdClass();
+        foreach($obj as $key=>$val){
+            $dict->$key = self::loadClass($val, $type);
+        }
+        return $dict;
+    }
+    
+    /**
+     * 方法调用
+     * @param type $func
+     * @param type $paras
+     * @param type $class 
+     */
+    static function callFunction($func, $paras, $class=""){
+        if($class == "" || $class == null){
+            call_user_func_array($func, $paras);
+        }else{
+            call_user_func_array(array($class, $func), $paras);
+        }
+    }
+
+    /**
+     * 浮点数转整形
+     * @param type $value
+     * @return type 
+     */
+    static function floatToInt($value){
+        return round($value - 0.4999);
+    }
+
+    /**
+     * 浮点数转上整形
+     * @param type $value
+     * @return type 
+     */
+    static function floatToCeil($value){
+        return round($value + 0.4999);
+    }
+
+    /**
+     * 解析异常描述信息
+     * @param type $e
+     * @return type 
+     */
+    static function getExceptMsg($e) {
+        return 'Msg:'.$e->getMessage().' Code:'.$e->getCode().' File:'.$e->getFile().' Line:'.$e->getLine();
+    }
+    
+    /**
+     * 提取url参数
+     * @param type $querryStr
+     * @return type 
+     */
+    static function getQuerryParas($querryStr) {
+        $arr = explode("&", $querryStr);
+        $querryParas = array();
+        foreach ($arr as $value) {
+            $paras = explode("=", $value);
+            $querryParas[$paras[0]] = $paras[1];
+        }
+        return $querryParas;
+    }
+
+    /**
+     * 获取客户端ip地址及端口
+     * @return type 
+     */
+    static function getClientEP(){
+        return $_SERVER['REMOTE_ADDR'].":".$_SERVER['REMOTE_PORT'];
+    }
+}

+ 81 - 0
Gameserver/gamesys/app/utils/Date.php

@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of DateTimeEx
+ *
+ * @author jgao
+ */
+class Date {
+    //put your code here
+    
+    /**
+     * 年
+     * @var type 
+     */
+    public $year;
+    /**
+     * 月
+     * @var type 
+     */
+    public $month;
+    /**
+     * 日
+     * @var type 
+     */
+    public $day;
+    /**
+     * 星期
+     * @var type 
+     */
+    public $dayofweek;
+    /**
+     * 时
+     * @var type 
+     */
+    public $hours;
+    /**
+     * 分
+     * @var type 
+     */
+    public $minutes;
+    /**
+     * 秒
+     * @var type 
+     */
+    public $seconds;
+    /**
+     * 时间戳
+     * @var type 
+     */
+    public $time;
+    
+    function __construct($time=0){
+        if($time==0){
+            $this->time = time();
+        }else{
+            $this->time = $time;
+        }
+        $date_reg = date("Y:m:d:H:i:s:N",$this->time);
+        $date_info = explode(":",$date_reg);
+        $i = 0;
+        $this->year = intval($date_info[$i++]);
+        $this->month = intval($date_info[$i++]);
+        $this->day = intval($date_info[$i++]);
+        $this->hours = intval($date_info[$i++]);
+        $this->minutes = intval($date_info[$i++]);
+        $this->seconds = intval($date_info[$i++]);
+        $this->dayofweek = intval($date_info[$i++]);
+    }
+    
+    function toString(){
+        return date("Y-m-d H:i:s", $this->time);
+    }
+    
+}
+
+?>

+ 24 - 0
Gameserver/gamesys/app/utils/GZip.php

@@ -0,0 +1,24 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of GZip
+ *
+ * @author jgao
+ */
+class GZip {
+    //put your code here
+    
+    static function encode($data){
+        return gzencode($data);
+    }
+    
+    static function decode($data){
+        return gzinflate(substr($data,10,-8));  // 高版本的php已经拥有名为【gzdecode】的api
+    }
+}

+ 41 - 0
Gameserver/gamesys/app/utils/Json.php

@@ -0,0 +1,41 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of Json
+ *
+ * @author jgao
+ */
+class Json {
+
+    //put your code here
+
+    public static function encode($obj) {
+        $json = json_encode($obj);
+        if ($GLOBALS['OS'] == "linux") {                                        # 还原中文字符串
+            return preg_replace_callback('#\\\u([0-9a-f]{4})#i', function ($matches) {
+                return iconv('UCS-2LE', 'UTF-8', pack('H4', $matches[1]));
+            }, $json);
+        } else if ($GLOBALS['OS'] == "win32") {                                 # 还原中文字符串
+            return preg_replace_callback('#\\\u([0-9a-f]{4})#i', function ($matches) {
+                return iconv('UCS-2BE', 'UTF-8', pack('H4', $matches[1]));
+            }, $json);
+        }
+    }
+
+    public static function decode($data) {
+        // 检测是否为json数据,如果不是则尝试解压缩
+        $obj = json_decode($data);
+        if (is_null($obj)) {
+            $str = gzip_decode($data);
+            $obj = json_decode($str);
+        }
+        return $obj;
+    }
+
+}

+ 66 - 0
Gameserver/gamesys/app/utils/Log.php

@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+$GLOBALS['API_TEST'] = true;
+/**
+ * Description of Log
+ *
+ * @author jgao
+ */
+class Log {
+    //put your code here
+    
+    static function dtCurrent(){
+        return date('Y-m-d ').(date('H') + 8).date(':i:s');
+    }
+    
+    /**
+     * 浏览器打印调试信息
+     * @param type $msg 
+     */
+    static function debug($msg){
+        if($GLOBALS['API_TEST']){
+            echo('['.self::dtCurrent().']|'.$msg.'</br>');
+        }
+    }
+
+    /**
+     * 浏览器输出对象信息
+     * @param type $obj 
+     */
+    static function console($obj){
+        self::debug(json_encode($obj));
+    }
+    
+    /**
+     * 浏览器弹窗提示
+     * @param type $msg 
+     */
+    static function alert($msg){
+        if($GLOBALS['API_TEST']){
+            echo "<Script language=\"javascript\">alert(\"".$msg."\");</Script>";
+        }
+    }
+    
+    /**
+     * 写日志文件
+     * 注意:这里必须写入CEE指定的日志目录!!
+     * Ps.CEE 目录: dirname(__FILE__)."/../../../../logs/".date("Ynj").".log";
+     * @param type $msg 
+     */
+    static function output($msg){
+        $fileName = "";
+        if(GAME_ONLINE){    
+            $fileName = dirname(__FILE__)."/../../../../logs/".date("Ynj").".log";  //cee目录
+        }else{
+            $fileName = dirname(__FILE__)."/../AppLog/".date("Ynj").".log";
+        }
+        $fd = fopen($fileName,"a+");
+        fputs($fd,"[".self::dtCurrent()."]|".json_encode($msg)."\r\n");
+        fclose($fd);
+    }
+}

+ 124 - 0
Gameserver/gamesys/app/utils/MySql.php

@@ -0,0 +1,124 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+/**
+ * Description of MySql
+ *
+ * @author jgao
+ */
+class MySql {
+
+    //put your code here
+
+    var $conn = 0;
+
+    function conn($dbhost, $dbport, $dbuser, $dbpw, $dbname) {
+        $this->conn = mysql_connect($dbhost . ":" . $dbport, $dbuser, $dbpw);
+        if ($this->conn == null || !$this->conn) {
+            $this->halt("Connect to MySQL failed");
+        }
+        $serverinfo = mysql_get_server_info($this->conn);
+        if ($serverinfo > '4.1' && $GLOBALS['charset']) {
+            mysql_query("SET character_set_connection=" . $GLOBALS['charset'] . ",character_set_results=" . $GLOBALS['charset'] . ",character_set_client=binary", $this->conn);
+        }
+        if ($serverinfo > '5.0') {
+            mysql_query("SET sql_mode=''", $this->conn);
+        }
+        if ($dbname && !@mysql_select_db($dbname, $this->conn)) {
+            $this->halt('Cannot use database');
+        }
+    }
+
+    function select_db($dbname) {
+        if (!@mysql_select_db($dbname, $this->conn)) {
+            $this->halt('Cannot use database');
+        }
+    }
+
+    function server_info() {
+        return mysql_get_server_info($this->conn);
+    }
+
+    function insert_id() {
+        $arr = $this->fetch_array('SELECT LAST_INSERT_ID() as id');
+        return $arr["id"];
+    }
+
+    function get_value($SQL, $offset = 0, $field = 0) {
+        $rt = $this->fetch_result($SQL);
+        if (isset($rt[$offset][$field])) {
+            return $rt[$offset][$field];
+        }
+        return false;
+    }
+
+    function query($SQL, $method = null, $error = true) {
+        $query = mysql_query($SQL, $this->conn);
+        !$query && $error && $this->halt('Query Error: ' . $SQL);
+        return $query;
+    }
+
+    function fetch_row($SQL) {
+        return $this->fetch_result($SQL, MYSQL_NUM);
+    }
+
+    function fetch_array($SQL) {
+        return $this->fetch_result($SQL, MYSQL_ASSOC);
+    }
+
+    function fetch_result($SQL, $result_type = MYSQL_BOTH) {
+        $arr = array();
+        $query = $this->query($SQL);
+        while ($data = mysql_fetch_array($query, $result_type)) {
+            $arr[] = $data;
+        }
+        $this->free_result();
+        return $arr;
+    }
+
+    function affected_rows() {
+        return mysql_affected_rows($this->conn);
+    }
+
+    function num_rows($SQL) {
+        $query = $this->query($SQL);
+        if (!is_bool($query)) {
+            return mysql_num_rows($query);
+        }
+        return 0;
+    }
+
+    function num_fields($SQL) {
+        $query = $this->query($SQL);
+        return mysql_num_fields($query);
+    }
+
+    function escape_string($str) {
+        return mysql_escape_string($str);
+    }
+
+    function free_result() {
+        $void = func_get_args();
+        foreach ($void as $query) {
+            if (is_resource($query) && get_resource_type($query) === 'mysql result') {
+                mysql_free_result($query);
+            }
+        }
+        unset($void);
+    }
+
+    function close() {
+        $this->free_result();
+        return @mysql_close($this->conn);
+    }
+
+    function halt($msg = null) {
+        exit($msg . '<br /><br />' . $sql . '<br /> ' . mysql_error());
+    }
+
+}

+ 91 - 0
Gameserver/gamesys/app/utils/PRedis.php

@@ -0,0 +1,91 @@
+<?php
+
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+require __DIR__.'/Predis/Autoloader.php';
+Predis\Autoloader::register();
+/**
+ * Description of Predis
+ *
+ * @author jgao
+ */
+class PRedis {
+    //put your code here
+    var $redis = null;
+   
+    public function conn($host, $port, $pwd, $db=0){
+        $this->redis = new Predis\Client(array(
+            'scheme'=>'tcp',
+            'host'=>$host,
+            'port'=>$port,
+            'password'=>$pwd,
+        ));
+        $this->redis->select($db);
+        return $this;
+    }
+    
+    public function get($key){
+        $result = $this->redis->get($key);
+        if (!$result || $result == "" || $result == null){
+            return null;
+        }
+        return JsonUtil::decode($result);
+    }
+    
+    public function getMulti($keys) {
+        $ctx = $this->redis->mget($keys);
+        $count = count($ctx);
+        for($i = 0; $i < $count; $i++) {
+            $ctx[$i] = JsonUtil::decode($ctx[$i]);
+        }
+        return $ctx;
+    }
+    
+    public function set($key, $val) {
+         if($val == null || $val == ""){
+            return false;
+        }
+        $ret = $this->redis->set($key, JsonUtil::encode($val));
+        return "OK" == $ret;
+    }
+    
+    public function cas($key, $value){
+        return $this->set($key, $value);
+    }
+    
+    public function delete($key) {
+        $infos = array(); $infos[] = $key;
+        return $this->redis->del($infos) == 1;
+    }
+    
+    public function increment($key) {
+        return $this->redis->incr($key);
+    }
+    
+    public function copy($surKey, $desKey) {
+        return $this->set($desKey, $this->get($surKey));
+    }
+
+    public function close() {
+        $this->redis->quit();
+    }
+    
+    /**
+     * 获得Redis扩展客户端
+     * @return Predis\Client 
+     */
+    public function getRedisClient(){
+        return $this->redis;
+    }
+    
+    /**
+     * 错误中断
+     * @param type $msg
+     */
+    function halt($msg=null){
+        exit($msg.'<br /> '.$this->redis->getLastError());
+    }
+}

+ 62 - 0
Gameserver/gamesys/app/utils/Predis/Autoloader.php

@@ -0,0 +1,62 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis;
+
+/**
+ * Implements a lightweight PSR-0 compliant autoloader for Predis.
+ *
+ * @author Eric Naeseth <eric@thumbtack.com>
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class Autoloader
+{
+    private $directory;
+    private $prefix;
+    private $prefixLength;
+
+    /**
+     * @param string $baseDirectory Base directory where the source files are located.
+     */
+    public function __construct($baseDirectory = __DIR__)
+    {
+        $this->directory = $baseDirectory;
+        $this->prefix = __NAMESPACE__.'\\';
+        $this->prefixLength = strlen($this->prefix);
+    }
+
+    /**
+     * Registers the autoloader class with the PHP SPL autoloader.
+     *
+     * @param bool $prepend Prepend the autoloader on the stack instead of appending it.
+     */
+    public static function register($prepend = false)
+    {
+        spl_autoload_register(array(new self(), 'autoload'), true, $prepend);
+    }
+
+    /**
+     * Loads a class from a file using its fully qualified name.
+     *
+     * @param string $className Fully qualified name of a class.
+     */
+    public function autoload($className)
+    {
+        if (0 === strpos($className, $this->prefix)) {
+            $parts = explode('\\', substr($className, $this->prefixLength));
+            $filepath = $this->directory.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $parts).'.php';
+
+            if (is_file($filepath)) {
+                require $filepath;
+            }
+        }
+    }
+}

+ 523 - 0
Gameserver/gamesys/app/utils/Predis/Client.php

@@ -0,0 +1,523 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis;
+
+use Predis\Command\CommandInterface;
+use Predis\Command\RawCommand;
+use Predis\Command\ScriptCommand;
+use Predis\Configuration\Options;
+use Predis\Configuration\OptionsInterface;
+use Predis\Connection\AggregateConnectionInterface;
+use Predis\Connection\ConnectionInterface;
+use Predis\Connection\ParametersInterface;
+use Predis\Monitor\Consumer as MonitorConsumer;
+use Predis\Pipeline\Pipeline;
+use Predis\PubSub\Consumer as PubSubConsumer;
+use Predis\Response\ErrorInterface as ErrorResponseInterface;
+use Predis\Response\ResponseInterface;
+use Predis\Response\ServerException;
+use Predis\Transaction\MultiExec as MultiExecTransaction;
+
+/**
+ * Client class used for connecting and executing commands on Redis.
+ *
+ * This is the main high-level abstraction of Predis upon which various other
+ * abstractions are built. Internally it aggregates various other classes each
+ * one with its own responsibility and scope.
+ *
+ * {@inheritdoc}
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class Client implements ClientInterface
+{
+    const VERSION = '1.0.3';
+
+    protected $connection;
+    protected $options;
+    private $profile;
+
+    /**
+     * @param mixed $parameters Connection parameters for one or more servers.
+     * @param mixed $options    Options to configure some behaviours of the client.
+     */
+    public function __construct($parameters = null, $options = null)
+    {
+        $this->options = $this->createOptions($options ?: array());
+        $this->connection = $this->createConnection($parameters ?: array());
+        $this->profile = $this->options->profile;
+    }
+
+    /**
+     * Creates a new instance of Predis\Configuration\Options from different
+     * types of arguments or simply returns the passed argument if it is an
+     * instance of Predis\Configuration\OptionsInterface.
+     *
+     * @param mixed $options Client options.
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return OptionsInterface
+     */
+    protected function createOptions($options)
+    {
+        if (is_array($options)) {
+            return new Options($options);
+        }
+
+        if ($options instanceof OptionsInterface) {
+            return $options;
+        }
+
+        throw new \InvalidArgumentException('Invalid type for client options.');
+    }
+
+    /**
+     * Creates single or aggregate connections from different types of arguments
+     * (string, array) or returns the passed argument if it is an instance of a
+     * class implementing Predis\Connection\ConnectionInterface.
+     *
+     * Accepted types for connection parameters are:
+     *
+     *  - Instance of Predis\Connection\ConnectionInterface.
+     *  - Instance of Predis\Connection\ParametersInterface.
+     *  - Array
+     *  - String
+     *  - Callable
+     *
+     * @param mixed $parameters Connection parameters or connection instance.
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return ConnectionInterface
+     */
+    protected function createConnection($parameters)
+    {
+        if ($parameters instanceof ConnectionInterface) {
+            return $parameters;
+        }
+
+        if ($parameters instanceof ParametersInterface || is_string($parameters)) {
+            return $this->options->connections->create($parameters);
+        }
+
+        if (is_array($parameters)) {
+            if (!isset($parameters[0])) {
+                return $this->options->connections->create($parameters);
+            }
+
+            $options = $this->options;
+
+            if ($options->defined('aggregate')) {
+                $initializer = $this->getConnectionInitializerWrapper($options->aggregate);
+                $connection = $initializer($parameters, $options);
+            } else {
+                if ($options->defined('replication') && $replication = $options->replication) {
+                    $connection = $replication;
+                } else {
+                    $connection = $options->cluster;
+                }
+
+                $options->connections->aggregate($connection, $parameters);
+            }
+
+            return $connection;
+        }
+
+        if (is_callable($parameters)) {
+            $initializer = $this->getConnectionInitializerWrapper($parameters);
+            $connection = $initializer($this->options);
+
+            return $connection;
+        }
+
+        throw new \InvalidArgumentException('Invalid type for connection parameters.');
+    }
+
+    /**
+     * Wraps a callable to make sure that its returned value represents a valid
+     * connection type.
+     *
+     * @param mixed $callable
+     *
+     * @return \Closure
+     */
+    protected function getConnectionInitializerWrapper($callable)
+    {
+        return function () use ($callable) {
+            $connection = call_user_func_array($callable, func_get_args());
+
+            if (!$connection instanceof ConnectionInterface) {
+                throw new \UnexpectedValueException(
+                    'The callable connection initializer returned an invalid type.'
+                );
+            }
+
+            return $connection;
+        };
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getProfile()
+    {
+        return $this->profile;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getOptions()
+    {
+        return $this->options;
+    }
+
+    /**
+     * Creates a new client instance for the specified connection ID or alias,
+     * only when working with an aggregate connection (cluster, replication).
+     * The new client instances uses the same options of the original one.
+     *
+     * @param string $connectionID Identifier of a connection.
+     *
+     * @throws \InvalidArgumentException
+     *
+     * @return Client
+     */
+    public function getClientFor($connectionID)
+    {
+        if (!$connection = $this->getConnectionById($connectionID)) {
+            throw new \InvalidArgumentException("Invalid connection ID: $connectionID.");
+        }
+
+        return new static($connection, $this->options);
+    }
+
+    /**
+     * Opens the underlying connection and connects to the server.
+     */
+    public function connect()
+    {
+        $this->connection->connect();
+    }
+
+    /**
+     * Closes the underlying connection and disconnects from the server.
+     */
+    public function disconnect()
+    {
+        $this->connection->disconnect();
+    }
+
+    /**
+     * Closes the underlying connection and disconnects from the server.
+     *
+     * This is the same as `Client::disconnect()` as it does not actually send
+     * the `QUIT` command to Redis, but simply closes the connection.
+     */
+    public function quit()
+    {
+        $this->disconnect();
+    }
+
+    /**
+     * Returns the current state of the underlying connection.
+     *
+     * @return bool
+     */
+    public function isConnected()
+    {
+        return $this->connection->isConnected();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getConnection()
+    {
+        return $this->connection;
+    }
+
+    /**
+     * Retrieves the specified connection from the aggregate connection when the
+     * client is in cluster or replication mode.
+     *
+     * @param string $connectionID Index or alias of the single connection.
+     *
+     * @throws NotSupportedException
+     *
+     * @return Connection\NodeConnectionInterface
+     */
+    public function getConnectionById($connectionID)
+    {
+        if (!$this->connection instanceof AggregateConnectionInterface) {
+            throw new NotSupportedException(
+                'Retrieving connections by ID is supported only by aggregate connections.'
+            );
+        }
+
+        return $this->connection->getConnectionById($connectionID);
+    }
+
+    /**
+     * Executes a command without filtering its arguments, parsing the response,
+     * applying any prefix to keys or throwing exceptions on Redis errors even
+     * regardless of client options.
+     *
+     * It is possibile to indentify Redis error responses from normal responses
+     * using the second optional argument which is populated by reference.
+     *
+     * @param array $arguments Command arguments as defined by the command signature.
+     * @param bool  $error     Set to TRUE when Redis returned an error response.
+     *
+     * @return mixed
+     */
+    public function executeRaw(array $arguments, &$error = null)
+    {
+        $error = false;
+
+        $response = $this->connection->executeCommand(
+            new RawCommand($arguments)
+        );
+
+        if ($response instanceof ResponseInterface) {
+            if ($response instanceof ErrorResponseInterface) {
+                $error = true;
+            }
+
+            return (string) $response;
+        }
+
+        return $response;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __call($commandID, $arguments)
+    {
+        return $this->executeCommand(
+            $this->createCommand($commandID, $arguments)
+        );
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function createCommand($commandID, $arguments = array())
+    {
+        return $this->profile->createCommand($commandID, $arguments);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function executeCommand(CommandInterface $command)
+    {
+        $response = $this->connection->executeCommand($command);
+
+        if ($response instanceof ResponseInterface) {
+            if ($response instanceof ErrorResponseInterface) {
+                $response = $this->onErrorResponse($command, $response);
+            }
+
+            return $response;
+        }
+
+        return $command->parseResponse($response);
+    }
+
+    /**
+     * Handles -ERR responses returned by Redis.
+     *
+     * @param CommandInterface       $command  Redis command that generated the error.
+     * @param ErrorResponseInterface $response Instance of the error response.
+     *
+     * @throws ServerException
+     *
+     * @return mixed
+     */
+    protected function onErrorResponse(CommandInterface $command, ErrorResponseInterface $response)
+    {
+        if ($command instanceof ScriptCommand && $response->getErrorType() === 'NOSCRIPT') {
+            $eval = $this->createCommand('EVAL');
+            $eval->setRawArguments($command->getEvalArguments());
+
+            $response = $this->executeCommand($eval);
+
+            if (!$response instanceof ResponseInterface) {
+                $response = $command->parseResponse($response);
+            }
+
+            return $response;
+        }
+
+        if ($this->options->exceptions) {
+            throw new ServerException($response->getMessage());
+        }
+
+        return $response;
+    }
+
+    /**
+     * Executes the specified initializer method on `$this` by adjusting the
+     * actual invokation depending on the arity (0, 1 or 2 arguments). This is
+     * simply an utility method to create Redis contexts instances since they
+     * follow a common initialization path.
+     *
+     * @param string $initializer Method name.
+     * @param array  $argv        Arguments for the method.
+     *
+     * @return mixed
+     */
+    private function sharedContextFactory($initializer, $argv = null)
+    {
+        switch (count($argv)) {
+            case 0:
+                return $this->$initializer();
+
+            case 1:
+                return is_array($argv[0])
+                    ? $this->$initializer($argv[0])
+                    : $this->$initializer(null, $argv[0]);
+
+            case 2:
+                list($arg0, $arg1) = $argv;
+
+                return $this->$initializer($arg0, $arg1);
+
+            default:
+                return $this->$initializer($this, $argv);
+        }
+    }
+
+    /**
+     * Creates a new pipeline context and returns it, or returns the results of
+     * a pipeline executed inside the optionally provided callable object.
+     *
+     * @param mixed ... Array of options, a callable for execution, or both.
+     *
+     * @return Pipeline|array
+     */
+    public function pipeline(/* arguments */)
+    {
+        return $this->sharedContextFactory('createPipeline', func_get_args());
+    }
+
+    /**
+     * Actual pipeline context initializer method.
+     *
+     * @param array $options  Options for the context.
+     * @param mixed $callable Optional callable used to execute the context.
+     *
+     * @return Pipeline|array
+     */
+    protected function createPipeline(array $options = null, $callable = null)
+    {
+        if (isset($options['atomic']) && $options['atomic']) {
+            $class = 'Predis\Pipeline\Atomic';
+        } elseif (isset($options['fire-and-forget']) && $options['fire-and-forget']) {
+            $class = 'Predis\Pipeline\FireAndForget';
+        } else {
+            $class = 'Predis\Pipeline\Pipeline';
+        }
+
+        /*
+         * @var ClientContextInterface
+         */
+        $pipeline = new $class($this);
+
+        if (isset($callable)) {
+            return $pipeline->execute($callable);
+        }
+
+        return $pipeline;
+    }
+
+    /**
+     * Creates a new transaction context and returns it, or returns the results
+     * of a transaction executed inside the optionally provided callable object.
+     *
+     * @param mixed ... Array of options, a callable for execution, or both.
+     *
+     * @return MultiExecTransaction|array
+     */
+    public function transaction(/* arguments */)
+    {
+        return $this->sharedContextFactory('createTransaction', func_get_args());
+    }
+
+    /**
+     * Actual transaction context initializer method.
+     *
+     * @param array $options  Options for the context.
+     * @param mixed $callable Optional callable used to execute the context.
+     *
+     * @return MultiExecTransaction|array
+     */
+    protected function createTransaction(array $options = null, $callable = null)
+    {
+        $transaction = new MultiExecTransaction($this, $options);
+
+        if (isset($callable)) {
+            return $transaction->execute($callable);
+        }
+
+        return $transaction;
+    }
+
+    /**
+     * Creates a new publis/subscribe context and returns it, or starts its loop
+     * inside the optionally provided callable object.
+     *
+     * @param mixed ... Array of options, a callable for execution, or both.
+     *
+     * @return PubSubConsumer|null
+     */
+    public function pubSubLoop(/* arguments */)
+    {
+        return $this->sharedContextFactory('createPubSub', func_get_args());
+    }
+
+    /**
+     * Actual publish/subscribe context initializer method.
+     *
+     * @param array $options  Options for the context.
+     * @param mixed $callable Optional callable used to execute the context.
+     *
+     * @return PubSubConsumer|null
+     */
+    protected function createPubSub(array $options = null, $callable = null)
+    {
+        $pubsub = new PubSubConsumer($this, $options);
+
+        if (!isset($callable)) {
+            return $pubsub;
+        }
+
+        foreach ($pubsub as $message) {
+            if (call_user_func($callable, $pubsub, $message) === false) {
+                $pubsub->stop();
+            }
+        }
+    }
+
+    /**
+     * Creates a new monitor consumer and returns it.
+     *
+     * @return MonitorConsumer
+     */
+    public function monitor()
+    {
+        return new MonitorConsumer($this);
+    }
+}

+ 189 - 0
Gameserver/gamesys/app/utils/Predis/ClientContextInterface.php

@@ -0,0 +1,189 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis;
+
+use Predis\Command\CommandInterface;
+
+/**
+ * Interface defining a client-side context such as a pipeline or transaction.
+ *
+ * @method $this del(array $keys)
+ * @method $this dump($key)
+ * @method $this exists($key)
+ * @method $this expire($key, $seconds)
+ * @method $this expireat($key, $timestamp)
+ * @method $this keys($pattern)
+ * @method $this move($key, $db)
+ * @method $this object($subcommand, $key)
+ * @method $this persist($key)
+ * @method $this pexpire($key, $milliseconds)
+ * @method $this pexpireat($key, $timestamp)
+ * @method $this pttl($key)
+ * @method $this randomkey()
+ * @method $this rename($key, $target)
+ * @method $this renamenx($key, $target)
+ * @method $this scan($cursor, array $options = null)
+ * @method $this sort($key, array $options = null)
+ * @method $this ttl($key)
+ * @method $this type($key)
+ * @method $this append($key, $value)
+ * @method $this bitcount($key, $start = null, $end = null)
+ * @method $this bitop($operation, $destkey, $key)
+ * @method $this decr($key)
+ * @method $this decrby($key, $decrement)
+ * @method $this get($key)
+ * @method $this getbit($key, $offset)
+ * @method $this getrange($key, $start, $end)
+ * @method $this getset($key, $value)
+ * @method $this incr($key)
+ * @method $this incrby($key, $increment)
+ * @method $this incrbyfloat($key, $increment)
+ * @method $this mget(array $keys)
+ * @method $this mset(array $dictionary)
+ * @method $this msetnx(array $dictionary)
+ * @method $this psetex($key, $milliseconds, $value)
+ * @method $this set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
+ * @method $this setbit($key, $offset, $value)
+ * @method $this setex($key, $seconds, $value)
+ * @method $this setnx($key, $value)
+ * @method $this setrange($key, $offset, $value)
+ * @method $this strlen($key)
+ * @method $this hdel($key, array $fields)
+ * @method $this hexists($key, $field)
+ * @method $this hget($key, $field)
+ * @method $this hgetall($key)
+ * @method $this hincrby($key, $field, $increment)
+ * @method $this hincrbyfloat($key, $field, $increment)
+ * @method $this hkeys($key)
+ * @method $this hlen($key)
+ * @method $this hmget($key, array $fields)
+ * @method $this hmset($key, array $dictionary)
+ * @method $this hscan($key, $cursor, array $options = null)
+ * @method $this hset($key, $field, $value)
+ * @method $this hsetnx($key, $field, $value)
+ * @method $this hvals($key)
+ * @method $this blpop(array $keys, $timeout)
+ * @method $this brpop(array $keys, $timeout)
+ * @method $this brpoplpush($source, $destination, $timeout)
+ * @method $this lindex($key, $index)
+ * @method $this linsert($key, $whence, $pivot, $value)
+ * @method $this llen($key)
+ * @method $this lpop($key)
+ * @method $this lpush($key, array $values)
+ * @method $this lpushx($key, $value)
+ * @method $this lrange($key, $start, $stop)
+ * @method $this lrem($key, $count, $value)
+ * @method $this lset($key, $index, $value)
+ * @method $this ltrim($key, $start, $stop)
+ * @method $this rpop($key)
+ * @method $this rpoplpush($source, $destination)
+ * @method $this rpush($key, array $values)
+ * @method $this rpushx($key, $value)
+ * @method $this sadd($key, array $members)
+ * @method $this scard($key)
+ * @method $this sdiff(array $keys)
+ * @method $this sdiffstore($destination, array $keys)
+ * @method $this sinter(array $keys)
+ * @method $this sinterstore($destination, array $keys)
+ * @method $this sismember($key, $member)
+ * @method $this smembers($key)
+ * @method $this smove($source, $destination, $member)
+ * @method $this spop($key)
+ * @method $this srandmember($key, $count = null)
+ * @method $this srem($key, $member)
+ * @method $this sscan($key, $cursor, array $options = null)
+ * @method $this sunion(array $keys)
+ * @method $this sunionstore($destination, array $keys)
+ * @method $this zadd($key, array $membersAndScoresDictionary)
+ * @method $this zcard($key)
+ * @method $this zcount($key, $min, $max)
+ * @method $this zincrby($key, $increment, $member)
+ * @method $this zinterstore($destination, array $keys, array $options = null)
+ * @method $this zrange($key, $start, $stop, array $options = null)
+ * @method $this zrangebyscore($key, $min, $max, array $options = null)
+ * @method $this zrank($key, $member)
+ * @method $this zrem($key, $member)
+ * @method $this zremrangebyrank($key, $start, $stop)
+ * @method $this zremrangebyscore($key, $min, $max)
+ * @method $this zrevrange($key, $start, $stop, array $options = null)
+ * @method $this zrevrangebyscore($key, $min, $max, array $options = null)
+ * @method $this zrevrank($key, $member)
+ * @method $this zunionstore($destination, array $keys, array $options = null)
+ * @method $this zscore($key, $member)
+ * @method $this zscan($key, $cursor, array $options = null)
+ * @method $this zrangebylex($key, $start, $stop, array $options = null)
+ * @method $this zremrangebylex($key, $min, $max)
+ * @method $this zlexcount($key, $min, $max)
+ * @method $this pfadd($key, array $elements)
+ * @method $this pfmerge($destinationKey, array $sourceKeys)
+ * @method $this pfcount(array $keys)
+ * @method $this pubsub($subcommand, $argument)
+ * @method $this publish($channel, $message)
+ * @method $this discard()
+ * @method $this exec()
+ * @method $this multi()
+ * @method $this unwatch()
+ * @method $this watch($key)
+ * @method $this eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
+ * @method $this evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
+ * @method $this script($subcommand, $argument = null)
+ * @method $this auth($password)
+ * @method $this echo($message)
+ * @method $this ping($message = null)
+ * @method $this select($database)
+ * @method $this bgrewriteaof()
+ * @method $this bgsave()
+ * @method $this client($subcommand, $argument = null)
+ * @method $this config($subcommand, $argument = null)
+ * @method $this dbsize()
+ * @method $this flushall()
+ * @method $this flushdb()
+ * @method $this info($section = null)
+ * @method $this lastsave()
+ * @method $this save()
+ * @method $this slaveof($host, $port)
+ * @method $this slowlog($subcommand, $argument = null)
+ * @method $this time()
+ * @method $this command()
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+interface ClientContextInterface
+{
+    /**
+     * Sends the specified command instance to Redis.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return mixed
+     */
+    public function executeCommand(CommandInterface $command);
+
+    /**
+     * Sends the specified command with its arguments to Redis.
+     *
+     * @param string $method    Command ID.
+     * @param array  $arguments Arguments for the command.
+     *
+     * @return mixed
+     */
+    public function __call($method, $arguments);
+
+    /**
+     * Starts the execution of the context.
+     *
+     * @param mixed $callable Optional callback for execution.
+     *
+     * @return array
+     */
+    public function execute($callable = null);
+}

+ 21 - 0
Gameserver/gamesys/app/utils/Predis/ClientException.php

@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis;
+
+/**
+ * Exception class that identifies client-side errors.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class ClientException extends PredisException
+{
+}

+ 230 - 0
Gameserver/gamesys/app/utils/Predis/ClientInterface.php

@@ -0,0 +1,230 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis;
+
+use Predis\Command\CommandInterface;
+use Predis\Configuration\OptionsInterface;
+use Predis\Connection\ConnectionInterface;
+use Predis\Profile\ProfileInterface;
+
+/**
+ * Interface defining a client able to execute commands against Redis.
+ *
+ * All the commands exposed by the client generally have the same signature as
+ * described by the Redis documentation, but some of them offer an additional
+ * and more friendly interface to ease programming which is described in the
+ * following list of methods:
+ *
+ * @method int    del(array $keys)
+ * @method string dump($key)
+ * @method int    exists($key)
+ * @method int    expire($key, $seconds)
+ * @method int    expireat($key, $timestamp)
+ * @method array  keys($pattern)
+ * @method int    move($key, $db)
+ * @method mixed  object($subcommand, $key)
+ * @method int    persist($key)
+ * @method int    pexpire($key, $milliseconds)
+ * @method int    pexpireat($key, $timestamp)
+ * @method int    pttl($key)
+ * @method string randomkey()
+ * @method mixed  rename($key, $target)
+ * @method int    renamenx($key, $target)
+ * @method array  scan($cursor, array $options = null)
+ * @method array  sort($key, array $options = null)
+ * @method int    ttl($key)
+ * @method mixed  type($key)
+ * @method int    append($key, $value)
+ * @method int    bitcount($key, $start = null, $end = null)
+ * @method int    bitop($operation, $destkey, $key)
+ * @method int    decr($key)
+ * @method int    decrby($key, $decrement)
+ * @method string get($key)
+ * @method int    getbit($key, $offset)
+ * @method string getrange($key, $start, $end)
+ * @method string getset($key, $value)
+ * @method int    incr($key)
+ * @method int    incrby($key, $increment)
+ * @method string incrbyfloat($key, $increment)
+ * @method array  mget(array $keys)
+ * @method mixed  mset(array $dictionary)
+ * @method int    msetnx(array $dictionary)
+ * @method mixed  psetex($key, $milliseconds, $value)
+ * @method mixed  set($key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
+ * @method int    setbit($key, $offset, $value)
+ * @method int    setex($key, $seconds, $value)
+ * @method int    setnx($key, $value)
+ * @method int    setrange($key, $offset, $value)
+ * @method int    strlen($key)
+ * @method int    hdel($key, array $fields)
+ * @method int    hexists($key, $field)
+ * @method string hget($key, $field)
+ * @method array  hgetall($key)
+ * @method int    hincrby($key, $field, $increment)
+ * @method string hincrbyfloat($key, $field, $increment)
+ * @method array  hkeys($key)
+ * @method int    hlen($key)
+ * @method array  hmget($key, array $fields)
+ * @method mixed  hmset($key, array $dictionary)
+ * @method array  hscan($key, $cursor, array $options = null)
+ * @method int    hset($key, $field, $value)
+ * @method int    hsetnx($key, $field, $value)
+ * @method array  hvals($key)
+ * @method array  blpop(array $keys, $timeout)
+ * @method array  brpop(array $keys, $timeout)
+ * @method array  brpoplpush($source, $destination, $timeout)
+ * @method string lindex($key, $index)
+ * @method int    linsert($key, $whence, $pivot, $value)
+ * @method int    llen($key)
+ * @method string lpop($key)
+ * @method int    lpush($key, array $values)
+ * @method int    lpushx($key, $value)
+ * @method array  lrange($key, $start, $stop)
+ * @method int    lrem($key, $count, $value)
+ * @method mixed  lset($key, $index, $value)
+ * @method mixed  ltrim($key, $start, $stop)
+ * @method string rpop($key)
+ * @method string rpoplpush($source, $destination)
+ * @method int    rpush($key, array $values)
+ * @method int    rpushx($key, $value)
+ * @method int    sadd($key, array $members)
+ * @method int    scard($key)
+ * @method array  sdiff(array $keys)
+ * @method int    sdiffstore($destination, array $keys)
+ * @method array  sinter(array $keys)
+ * @method int    sinterstore($destination, array $keys)
+ * @method int    sismember($key, $member)
+ * @method array  smembers($key)
+ * @method int    smove($source, $destination, $member)
+ * @method string spop($key)
+ * @method string srandmember($key, $count = null)
+ * @method int    srem($key, $member)
+ * @method array  sscan($key, $cursor, array $options = null)
+ * @method array  sunion(array $keys)
+ * @method int    sunionstore($destination, array $keys)
+ * @method int    zadd($key, array $membersAndScoresDictionary)
+ * @method int    zcard($key)
+ * @method string zcount($key, $min, $max)
+ * @method string zincrby($key, $increment, $member)
+ * @method int    zinterstore($destination, array $keys, array $options = null)
+ * @method array  zrange($key, $start, $stop, array $options = null)
+ * @method array  zrangebyscore($key, $min, $max, array $options = null)
+ * @method int    zrank($key, $member)
+ * @method int    zrem($key, $member)
+ * @method int    zremrangebyrank($key, $start, $stop)
+ * @method int    zremrangebyscore($key, $min, $max)
+ * @method array  zrevrange($key, $start, $stop, array $options = null)
+ * @method array  zrevrangebyscore($key, $min, $max, array $options = null)
+ * @method int    zrevrank($key, $member)
+ * @method int    zunionstore($destination, array $keys, array $options = null)
+ * @method string zscore($key, $member)
+ * @method array  zscan($key, $cursor, array $options = null)
+ * @method array  zrangebylex($key, $start, $stop, array $options = null)
+ * @method int    zremrangebylex($key, $min, $max)
+ * @method int    zlexcount($key, $min, $max)
+ * @method int    pfadd($key, array $elements)
+ * @method mixed  pfmerge($destinationKey, array $sourceKeys)
+ * @method int    pfcount(array $keys)
+ * @method mixed  pubsub($subcommand, $argument)
+ * @method int    publish($channel, $message)
+ * @method mixed  discard()
+ * @method array  exec()
+ * @method mixed  multi()
+ * @method mixed  unwatch()
+ * @method mixed  watch($key)
+ * @method mixed  eval($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
+ * @method mixed  evalsha($script, $numkeys, $keyOrArg1 = null, $keyOrArgN = null)
+ * @method mixed  script($subcommand, $argument = null)
+ * @method mixed  auth($password)
+ * @method string echo($message)
+ * @method mixed  ping($message = null)
+ * @method mixed  select($database)
+ * @method mixed  bgrewriteaof()
+ * @method mixed  bgsave()
+ * @method mixed  client($subcommand, $argument = null)
+ * @method mixed  config($subcommand, $argument = null)
+ * @method int    dbsize()
+ * @method mixed  flushall()
+ * @method mixed  flushdb()
+ * @method array  info($section = null)
+ * @method int    lastsave()
+ * @method mixed  save()
+ * @method mixed  slaveof($host, $port)
+ * @method mixed  slowlog($subcommand, $argument = null)
+ * @method array  time()
+ * @method array  command()
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+interface ClientInterface
+{
+    /**
+     * Returns the server profile used by the client.
+     *
+     * @return ProfileInterface
+     */
+    public function getProfile();
+
+    /**
+     * Returns the client options specified upon initialization.
+     *
+     * @return OptionsInterface
+     */
+    public function getOptions();
+
+    /**
+     * Opens the underlying connection to the server.
+     */
+    public function connect();
+
+    /**
+     * Closes the underlying connection from the server.
+     */
+    public function disconnect();
+
+    /**
+     * Returns the underlying connection instance.
+     *
+     * @return ConnectionInterface
+     */
+    public function getConnection();
+
+    /**
+     * Creates a new instance of the specified Redis command.
+     *
+     * @param string $method    Command ID.
+     * @param array  $arguments Arguments for the command.
+     *
+     * @return CommandInterface
+     */
+    public function createCommand($method, $arguments = array());
+
+    /**
+     * Executes the specified Redis command.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return mixed
+     */
+    public function executeCommand(CommandInterface $command);
+
+    /**
+     * Creates a Redis command with the specified arguments and sends a request
+     * to the server.
+     *
+     * @param string $method    Command ID.
+     * @param array  $arguments Arguments for the command.
+     *
+     * @return mixed
+     */
+    public function __call($method, $arguments);
+}

+ 398 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/ClusterStrategy.php

@@ -0,0 +1,398 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster;
+
+use Predis\Command\CommandInterface;
+use Predis\Command\ScriptCommand;
+
+/**
+ * Common class implementing the logic needed to support clustering strategies.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+abstract class ClusterStrategy implements StrategyInterface
+{
+    protected $commands;
+
+    /**
+     *
+     */
+    public function __construct()
+    {
+        $this->commands = $this->getDefaultCommands();
+    }
+
+    /**
+     * Returns the default map of supported commands with their handlers.
+     *
+     * @return array
+     */
+    protected function getDefaultCommands()
+    {
+        $getKeyFromFirstArgument = array($this, 'getKeyFromFirstArgument');
+        $getKeyFromAllArguments = array($this, 'getKeyFromAllArguments');
+
+        return array(
+            /* commands operating on the key space */
+            'EXISTS' => $getKeyFromFirstArgument,
+            'DEL' => $getKeyFromAllArguments,
+            'TYPE' => $getKeyFromFirstArgument,
+            'EXPIRE' => $getKeyFromFirstArgument,
+            'EXPIREAT' => $getKeyFromFirstArgument,
+            'PERSIST' => $getKeyFromFirstArgument,
+            'PEXPIRE' => $getKeyFromFirstArgument,
+            'PEXPIREAT' => $getKeyFromFirstArgument,
+            'TTL' => $getKeyFromFirstArgument,
+            'PTTL' => $getKeyFromFirstArgument,
+            'SORT' => $getKeyFromFirstArgument, // TODO
+            'DUMP' => $getKeyFromFirstArgument,
+            'RESTORE' => $getKeyFromFirstArgument,
+
+            /* commands operating on string values */
+            'APPEND' => $getKeyFromFirstArgument,
+            'DECR' => $getKeyFromFirstArgument,
+            'DECRBY' => $getKeyFromFirstArgument,
+            'GET' => $getKeyFromFirstArgument,
+            'GETBIT' => $getKeyFromFirstArgument,
+            'MGET' => $getKeyFromAllArguments,
+            'SET' => $getKeyFromFirstArgument,
+            'GETRANGE' => $getKeyFromFirstArgument,
+            'GETSET' => $getKeyFromFirstArgument,
+            'INCR' => $getKeyFromFirstArgument,
+            'INCRBY' => $getKeyFromFirstArgument,
+            'INCRBYFLOAT' => $getKeyFromFirstArgument,
+            'SETBIT' => $getKeyFromFirstArgument,
+            'SETEX' => $getKeyFromFirstArgument,
+            'MSET' => array($this, 'getKeyFromInterleavedArguments'),
+            'MSETNX' => array($this, 'getKeyFromInterleavedArguments'),
+            'SETNX' => $getKeyFromFirstArgument,
+            'SETRANGE' => $getKeyFromFirstArgument,
+            'STRLEN' => $getKeyFromFirstArgument,
+            'SUBSTR' => $getKeyFromFirstArgument,
+            'BITOP' => array($this, 'getKeyFromBitOp'),
+            'BITCOUNT' => $getKeyFromFirstArgument,
+
+            /* commands operating on lists */
+            'LINSERT' => $getKeyFromFirstArgument,
+            'LINDEX' => $getKeyFromFirstArgument,
+            'LLEN' => $getKeyFromFirstArgument,
+            'LPOP' => $getKeyFromFirstArgument,
+            'RPOP' => $getKeyFromFirstArgument,
+            'RPOPLPUSH' => $getKeyFromAllArguments,
+            'BLPOP' => array($this, 'getKeyFromBlockingListCommands'),
+            'BRPOP' => array($this, 'getKeyFromBlockingListCommands'),
+            'BRPOPLPUSH' => array($this, 'getKeyFromBlockingListCommands'),
+            'LPUSH' => $getKeyFromFirstArgument,
+            'LPUSHX' => $getKeyFromFirstArgument,
+            'RPUSH' => $getKeyFromFirstArgument,
+            'RPUSHX' => $getKeyFromFirstArgument,
+            'LRANGE' => $getKeyFromFirstArgument,
+            'LREM' => $getKeyFromFirstArgument,
+            'LSET' => $getKeyFromFirstArgument,
+            'LTRIM' => $getKeyFromFirstArgument,
+
+            /* commands operating on sets */
+            'SADD' => $getKeyFromFirstArgument,
+            'SCARD' => $getKeyFromFirstArgument,
+            'SDIFF' => $getKeyFromAllArguments,
+            'SDIFFSTORE' => $getKeyFromAllArguments,
+            'SINTER' => $getKeyFromAllArguments,
+            'SINTERSTORE' => $getKeyFromAllArguments,
+            'SUNION' => $getKeyFromAllArguments,
+            'SUNIONSTORE' => $getKeyFromAllArguments,
+            'SISMEMBER' => $getKeyFromFirstArgument,
+            'SMEMBERS' => $getKeyFromFirstArgument,
+            'SSCAN' => $getKeyFromFirstArgument,
+            'SPOP' => $getKeyFromFirstArgument,
+            'SRANDMEMBER' => $getKeyFromFirstArgument,
+            'SREM' => $getKeyFromFirstArgument,
+
+            /* commands operating on sorted sets */
+            'ZADD' => $getKeyFromFirstArgument,
+            'ZCARD' => $getKeyFromFirstArgument,
+            'ZCOUNT' => $getKeyFromFirstArgument,
+            'ZINCRBY' => $getKeyFromFirstArgument,
+            'ZINTERSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
+            'ZRANGE' => $getKeyFromFirstArgument,
+            'ZRANGEBYSCORE' => $getKeyFromFirstArgument,
+            'ZRANK' => $getKeyFromFirstArgument,
+            'ZREM' => $getKeyFromFirstArgument,
+            'ZREMRANGEBYRANK' => $getKeyFromFirstArgument,
+            'ZREMRANGEBYSCORE' => $getKeyFromFirstArgument,
+            'ZREVRANGE' => $getKeyFromFirstArgument,
+            'ZREVRANGEBYSCORE' => $getKeyFromFirstArgument,
+            'ZREVRANK' => $getKeyFromFirstArgument,
+            'ZSCORE' => $getKeyFromFirstArgument,
+            'ZUNIONSTORE' => array($this, 'getKeyFromZsetAggregationCommands'),
+            'ZSCAN' => $getKeyFromFirstArgument,
+            'ZLEXCOUNT' => $getKeyFromFirstArgument,
+            'ZRANGEBYLEX' => $getKeyFromFirstArgument,
+            'ZREMRANGEBYLEX' => $getKeyFromFirstArgument,
+            'ZREVRANGEBYLEX' => $getKeyFromFirstArgument,
+
+            /* commands operating on hashes */
+            'HDEL' => $getKeyFromFirstArgument,
+            'HEXISTS' => $getKeyFromFirstArgument,
+            'HGET' => $getKeyFromFirstArgument,
+            'HGETALL' => $getKeyFromFirstArgument,
+            'HMGET' => $getKeyFromFirstArgument,
+            'HMSET' => $getKeyFromFirstArgument,
+            'HINCRBY' => $getKeyFromFirstArgument,
+            'HINCRBYFLOAT' => $getKeyFromFirstArgument,
+            'HKEYS' => $getKeyFromFirstArgument,
+            'HLEN' => $getKeyFromFirstArgument,
+            'HSET' => $getKeyFromFirstArgument,
+            'HSETNX' => $getKeyFromFirstArgument,
+            'HVALS' => $getKeyFromFirstArgument,
+            'HSCAN' => $getKeyFromFirstArgument,
+            'HSTRLEN' => $getKeyFromFirstArgument,
+
+            /* commands operating on HyperLogLog */
+            'PFADD' => $getKeyFromFirstArgument,
+            'PFCOUNT' => $getKeyFromAllArguments,
+            'PFMERGE' => $getKeyFromAllArguments,
+
+            /* scripting */
+            'EVAL' => array($this, 'getKeyFromScriptingCommands'),
+            'EVALSHA' => array($this, 'getKeyFromScriptingCommands'),
+        );
+    }
+
+    /**
+     * Returns the list of IDs for the supported commands.
+     *
+     * @return array
+     */
+    public function getSupportedCommands()
+    {
+        return array_keys($this->commands);
+    }
+
+    /**
+     * Sets an handler for the specified command ID.
+     *
+     * The signature of the callback must have a single parameter of type
+     * Predis\Command\CommandInterface.
+     *
+     * When the callback argument is omitted or NULL, the previously associated
+     * handler for the specified command ID is removed.
+     *
+     * @param string $commandID Command ID.
+     * @param mixed  $callback  A valid callable object, or NULL to unset the handler.
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function setCommandHandler($commandID, $callback = null)
+    {
+        $commandID = strtoupper($commandID);
+
+        if (!isset($callback)) {
+            unset($this->commands[$commandID]);
+
+            return;
+        }
+
+        if (!is_callable($callback)) {
+            throw new \InvalidArgumentException(
+                'The argument must be a callable object or NULL.'
+            );
+        }
+
+        $this->commands[$commandID] = $callback;
+    }
+
+    /**
+     * Extracts the key from the first argument of a command instance.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return string
+     */
+    protected function getKeyFromFirstArgument(CommandInterface $command)
+    {
+        return $command->getArgument(0);
+    }
+
+    /**
+     * Extracts the key from a command with multiple keys only when all keys in
+     * the arguments array produce the same hash.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return string|null
+     */
+    protected function getKeyFromAllArguments(CommandInterface $command)
+    {
+        $arguments = $command->getArguments();
+
+        if ($this->checkSameSlotForKeys($arguments)) {
+            return $arguments[0];
+        }
+    }
+
+    /**
+     * Extracts the key from a command with multiple keys only when all keys in
+     * the arguments array produce the same hash.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return string|null
+     */
+    protected function getKeyFromInterleavedArguments(CommandInterface $command)
+    {
+        $arguments = $command->getArguments();
+        $keys = array();
+
+        for ($i = 0; $i < count($arguments); $i += 2) {
+            $keys[] = $arguments[$i];
+        }
+
+        if ($this->checkSameSlotForKeys($keys)) {
+            return $arguments[0];
+        }
+    }
+
+    /**
+     * Extracts the key from BLPOP and BRPOP commands.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return string|null
+     */
+    protected function getKeyFromBlockingListCommands(CommandInterface $command)
+    {
+        $arguments = $command->getArguments();
+
+        if ($this->checkSameSlotForKeys(array_slice($arguments, 0, count($arguments) - 1))) {
+            return $arguments[0];
+        }
+    }
+
+    /**
+     * Extracts the key from BITOP command.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return string|null
+     */
+    protected function getKeyFromBitOp(CommandInterface $command)
+    {
+        $arguments = $command->getArguments();
+
+        if ($this->checkSameSlotForKeys(array_slice($arguments, 1, count($arguments)))) {
+            return $arguments[1];
+        }
+    }
+
+    /**
+     * Extracts the key from ZINTERSTORE and ZUNIONSTORE commands.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return string|null
+     */
+    protected function getKeyFromZsetAggregationCommands(CommandInterface $command)
+    {
+        $arguments = $command->getArguments();
+        $keys = array_merge(array($arguments[0]), array_slice($arguments, 2, $arguments[1]));
+
+        if ($this->checkSameSlotForKeys($keys)) {
+            return $arguments[0];
+        }
+    }
+
+    /**
+     * Extracts the key from EVAL and EVALSHA commands.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return string|null
+     */
+    protected function getKeyFromScriptingCommands(CommandInterface $command)
+    {
+        if ($command instanceof ScriptCommand) {
+            $keys = $command->getKeys();
+        } else {
+            $keys = array_slice($args = $command->getArguments(), 2, $args[1]);
+        }
+
+        if ($keys && $this->checkSameSlotForKeys($keys)) {
+            return $keys[0];
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlot(CommandInterface $command)
+    {
+        $slot = $command->getSlot();
+
+        if (!isset($slot) && isset($this->commands[$cmdID = $command->getId()])) {
+            $key = call_user_func($this->commands[$cmdID], $command);
+
+            if (isset($key)) {
+                $slot = $this->getSlotByKey($key);
+                $command->setSlot($slot);
+            }
+        }
+
+        return $slot;
+    }
+
+    /**
+     * Checks if the specified array of keys will generate the same hash.
+     *
+     * @param array $keys Array of keys.
+     *
+     * @return bool
+     */
+    protected function checkSameSlotForKeys(array $keys)
+    {
+        if (!$count = count($keys)) {
+            return false;
+        }
+
+        $currentSlot = $this->getSlotByKey($keys[0]);
+
+        for ($i = 1; $i < $count; ++$i) {
+            $nextSlot = $this->getSlotByKey($keys[$i]);
+
+            if ($currentSlot !== $nextSlot) {
+                return false;
+            }
+
+            $currentSlot = $nextSlot;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns only the hashable part of a key (delimited by "{...}"), or the
+     * whole key if a key tag is not found in the string.
+     *
+     * @param string $key A key.
+     *
+     * @return string
+     */
+    protected function extractKeyTag($key)
+    {
+        if (false !== $start = strpos($key, '{')) {
+            if (false !== ($end = strpos($key, '}', $start)) && $end !== ++$start) {
+                $key = substr($key, $start, $end - $start);
+            }
+        }
+
+        return $key;
+    }
+}

+ 82 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/Distributor/DistributorInterface.php

@@ -0,0 +1,82 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster\Distributor;
+
+use Predis\Cluster\Hash\HashGeneratorInterface;
+
+/**
+ * A distributor implements the logic to automatically distribute keys among
+ * several nodes for client-side sharding.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+interface DistributorInterface
+{
+    /**
+     * Adds a node to the distributor with an optional weight.
+     *
+     * @param mixed $node   Node object.
+     * @param int   $weight Weight for the node.
+     */
+    public function add($node, $weight = null);
+
+    /**
+     * Removes a node from the distributor.
+     *
+     * @param mixed $node Node object.
+     */
+    public function remove($node);
+
+    /**
+     * Returns the corresponding slot of a node from the distributor using the
+     * computed hash of a key.
+     *
+     * @param mixed $hash
+     *
+     * @return mixed
+     */
+    public function getSlot($hash);
+
+    /**
+     * Returns a node from the distributor using its assigned slot ID.
+     *
+     * @param mixed $slot
+     *
+     * @return mixed|null
+     */
+    public function getBySlot($slot);
+
+    /**
+     * Returns a node from the distributor using the computed hash of a key.
+     *
+     * @param mixed $hash
+     *
+     * @return mixed
+     */
+    public function getByHash($hash);
+
+    /**
+     * Returns a node from the distributor mapping to the specified value.
+     *
+     * @param string $value
+     *
+     * @return mixed
+     */
+    public function get($value);
+
+    /**
+     * Returns the underlying hash generator instance.
+     *
+     * @return HashGeneratorInterface
+     */
+    public function getHashGenerator();
+}

+ 21 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/Distributor/EmptyRingException.php

@@ -0,0 +1,21 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster\Distributor;
+
+/**
+ * Exception class that identifies empty rings.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class EmptyRingException extends \Exception
+{
+}

+ 270 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/Distributor/HashRing.php

@@ -0,0 +1,270 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster\Distributor;
+
+use Predis\Cluster\Hash\HashGeneratorInterface;
+
+/**
+ * This class implements an hashring-based distributor that uses the same
+ * algorithm of memcache to distribute keys in a cluster using client-side
+ * sharding.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ * @author Lorenzo Castelli <lcastelli@gmail.com>
+ */
+class HashRing implements DistributorInterface, HashGeneratorInterface
+{
+    const DEFAULT_REPLICAS = 128;
+    const DEFAULT_WEIGHT = 100;
+
+    private $ring;
+    private $ringKeys;
+    private $ringKeysCount;
+    private $replicas;
+    private $nodeHashCallback;
+    private $nodes = array();
+
+    /**
+     * @param int   $replicas         Number of replicas in the ring.
+     * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
+     */
+    public function __construct($replicas = self::DEFAULT_REPLICAS, $nodeHashCallback = null)
+    {
+        $this->replicas = $replicas;
+        $this->nodeHashCallback = $nodeHashCallback;
+    }
+
+    /**
+     * Adds a node to the ring with an optional weight.
+     *
+     * @param mixed $node   Node object.
+     * @param int   $weight Weight for the node.
+     */
+    public function add($node, $weight = null)
+    {
+        // In case of collisions in the hashes of the nodes, the node added
+        // last wins, thus the order in which nodes are added is significant.
+        $this->nodes[] = array(
+            'object' => $node,
+            'weight' => (int) $weight ?: $this::DEFAULT_WEIGHT,
+        );
+
+        $this->reset();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function remove($node)
+    {
+        // A node is removed by resetting the ring so that it's recreated from
+        // scratch, in order to reassign possible hashes with collisions to the
+        // right node according to the order in which they were added in the
+        // first place.
+        for ($i = 0; $i < count($this->nodes); ++$i) {
+            if ($this->nodes[$i]['object'] === $node) {
+                array_splice($this->nodes, $i, 1);
+                $this->reset();
+
+                break;
+            }
+        }
+    }
+
+    /**
+     * Resets the distributor.
+     */
+    private function reset()
+    {
+        unset(
+            $this->ring,
+            $this->ringKeys,
+            $this->ringKeysCount
+        );
+    }
+
+    /**
+     * Returns the initialization status of the distributor.
+     *
+     * @return bool
+     */
+    private function isInitialized()
+    {
+        return isset($this->ringKeys);
+    }
+
+    /**
+     * Calculates the total weight of all the nodes in the distributor.
+     *
+     * @return int
+     */
+    private function computeTotalWeight()
+    {
+        $totalWeight = 0;
+
+        foreach ($this->nodes as $node) {
+            $totalWeight += $node['weight'];
+        }
+
+        return $totalWeight;
+    }
+
+    /**
+     * Initializes the distributor.
+     */
+    private function initialize()
+    {
+        if ($this->isInitialized()) {
+            return;
+        }
+
+        if (!$this->nodes) {
+            throw new EmptyRingException('Cannot initialize an empty hashring.');
+        }
+
+        $this->ring = array();
+        $totalWeight = $this->computeTotalWeight();
+        $nodesCount = count($this->nodes);
+
+        foreach ($this->nodes as $node) {
+            $weightRatio = $node['weight'] / $totalWeight;
+            $this->addNodeToRing($this->ring, $node, $nodesCount, $this->replicas, $weightRatio);
+        }
+
+        ksort($this->ring, SORT_NUMERIC);
+        $this->ringKeys = array_keys($this->ring);
+        $this->ringKeysCount = count($this->ringKeys);
+    }
+
+    /**
+     * Implements the logic needed to add a node to the hashring.
+     *
+     * @param array $ring        Source hashring.
+     * @param mixed $node        Node object to be added.
+     * @param int   $totalNodes  Total number of nodes.
+     * @param int   $replicas    Number of replicas in the ring.
+     * @param float $weightRatio Weight ratio for the node.
+     */
+    protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
+    {
+        $nodeObject = $node['object'];
+        $nodeHash = $this->getNodeHash($nodeObject);
+        $replicas = (int) round($weightRatio * $totalNodes * $replicas);
+
+        for ($i = 0; $i < $replicas; ++$i) {
+            $key = crc32("$nodeHash:$i");
+            $ring[$key] = $nodeObject;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getNodeHash($nodeObject)
+    {
+        if (!isset($this->nodeHashCallback)) {
+            return (string) $nodeObject;
+        }
+
+        return call_user_func($this->nodeHashCallback, $nodeObject);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hash($value)
+    {
+        return crc32($value);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getByHash($hash)
+    {
+        return $this->ring[$this->getSlot($hash)];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getBySlot($slot)
+    {
+        $this->initialize();
+
+        if (isset($this->ring[$slot])) {
+            return $this->ring[$slot];
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlot($hash)
+    {
+        $this->initialize();
+
+        $ringKeys = $this->ringKeys;
+        $upper = $this->ringKeysCount - 1;
+        $lower = 0;
+
+        while ($lower <= $upper) {
+            $index = ($lower + $upper) >> 1;
+            $item = $ringKeys[$index];
+
+            if ($item > $hash) {
+                $upper = $index - 1;
+            } elseif ($item < $hash) {
+                $lower = $index + 1;
+            } else {
+                return $item;
+            }
+        }
+
+        return $ringKeys[$this->wrapAroundStrategy($upper, $lower, $this->ringKeysCount)];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function get($value)
+    {
+        $hash = $this->hash($value);
+        $node = $this->getByHash($hash);
+
+        return $node;
+    }
+
+    /**
+     * Implements a strategy to deal with wrap-around errors during binary searches.
+     *
+     * @param int $upper
+     * @param int $lower
+     * @param int $ringKeysCount
+     *
+     * @return int
+     */
+    protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
+    {
+        // Binary search for the last item in ringkeys with a value less or
+        // equal to the key. If no such item exists, return the last item.
+        return $upper >= 0 ? $upper : $ringKeysCount - 1;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getHashGenerator()
+    {
+        return $this;
+    }
+}

+ 71 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/Distributor/KetamaRing.php

@@ -0,0 +1,71 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster\Distributor;
+
+/**
+ * This class implements an hashring-based distributor that uses the same
+ * algorithm of libketama to distribute keys in a cluster using client-side
+ * sharding.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ * @author Lorenzo Castelli <lcastelli@gmail.com>
+ */
+class KetamaRing extends HashRing
+{
+    const DEFAULT_REPLICAS = 160;
+
+    /**
+     * @param mixed $nodeHashCallback Callback returning a string used to calculate the hash of nodes.
+     */
+    public function __construct($nodeHashCallback = null)
+    {
+        parent::__construct($this::DEFAULT_REPLICAS, $nodeHashCallback);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function addNodeToRing(&$ring, $node, $totalNodes, $replicas, $weightRatio)
+    {
+        $nodeObject = $node['object'];
+        $nodeHash = $this->getNodeHash($nodeObject);
+        $replicas = (int) floor($weightRatio * $totalNodes * ($replicas / 4));
+
+        for ($i = 0; $i < $replicas; ++$i) {
+            $unpackedDigest = unpack('V4', md5("$nodeHash-$i", true));
+
+            foreach ($unpackedDigest as $key) {
+                $ring[$key] = $nodeObject;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hash($value)
+    {
+        $hash = unpack('V', md5($value, true));
+
+        return $hash[1];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function wrapAroundStrategy($upper, $lower, $ringKeysCount)
+    {
+        // Binary search for the first item in ringkeys with a value greater
+        // or equal to the key. If no such item exists, return the first item.
+        return $lower < $ringKeysCount ? $lower : 0;
+    }
+}

+ 72 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/Hash/CRC16.php

@@ -0,0 +1,72 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster\Hash;
+
+/**
+ * Hash generator implementing the CRC-CCITT-16 algorithm used by redis-cluster.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class CRC16 implements HashGeneratorInterface
+{
+    private static $CCITT_16 = array(
+        0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+        0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+        0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+        0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+        0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+        0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+        0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+        0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+        0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+        0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+        0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+        0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+        0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+        0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+        0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+        0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+        0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+        0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+        0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+        0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+        0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+        0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+        0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+        0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+        0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+        0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+        0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+        0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+        0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+        0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+        0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+        0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,
+    );
+
+    /**
+     * {@inheritdoc}
+     */
+    public function hash($value)
+    {
+        // CRC-CCITT-16 algorithm
+        $crc = 0;
+        $CCITT_16 = self::$CCITT_16;
+        $strlen = strlen($value);
+
+        for ($i = 0; $i < $strlen; ++$i) {
+            $crc = (($crc << 8) ^ $CCITT_16[($crc >> 8) ^ ord($value[$i])]) & 0xFFFF;
+        }
+
+        return $crc;
+    }
+}

+ 30 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/Hash/HashGeneratorInterface.php

@@ -0,0 +1,30 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster\Hash;
+
+/**
+ * An hash generator implements the logic used to calculate the hash of a key to
+ * distribute operations among Redis nodes.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+interface HashGeneratorInterface
+{
+    /**
+     * Generates an hash from a string to be used for distribution.
+     *
+     * @param string $value String value.
+     *
+     * @return int
+     */
+    public function hash($value);
+}

+ 79 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/PredisStrategy.php

@@ -0,0 +1,79 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster;
+
+use Predis\Cluster\Distributor\DistributorInterface;
+use Predis\Cluster\Distributor\HashRing;
+
+/**
+ * Default cluster strategy used by Predis to handle client-side sharding.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class PredisStrategy extends ClusterStrategy
+{
+    protected $distributor;
+
+    /**
+     * @param DistributorInterface $distributor Optional distributor instance.
+     */
+    public function __construct(DistributorInterface $distributor = null)
+    {
+        parent::__construct();
+
+        $this->distributor = $distributor ?: new HashRing();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlotByKey($key)
+    {
+        $key = $this->extractKeyTag($key);
+        $hash = $this->distributor->hash($key);
+        $slot = $this->distributor->getSlot($hash);
+
+        return $slot;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function checkSameSlotForKeys(array $keys)
+    {
+        if (!$count = count($keys)) {
+            return false;
+        }
+
+        $currentKey = $this->extractKeyTag($keys[0]);
+
+        for ($i = 1; $i < $count; ++$i) {
+            $nextKey = $this->extractKeyTag($keys[$i]);
+
+            if ($currentKey !== $nextKey) {
+                return false;
+            }
+
+            $currentKey = $nextKey;
+        }
+
+        return true;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDistributor()
+    {
+        return $this->distributor;
+    }
+}

+ 58 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/RedisStrategy.php

@@ -0,0 +1,58 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster;
+
+use Predis\Cluster\Hash\CRC16;
+use Predis\Cluster\Hash\HashGeneratorInterface;
+use Predis\NotSupportedException;
+
+/**
+ * Default class used by Predis to calculate hashes out of keys of
+ * commands supported by redis-cluster.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class RedisStrategy extends ClusterStrategy
+{
+    protected $hashGenerator;
+
+    /**
+     * @param HashGeneratorInterface $hashGenerator Hash generator instance.
+     */
+    public function __construct(HashGeneratorInterface $hashGenerator = null)
+    {
+        parent::__construct();
+
+        $this->hashGenerator = $hashGenerator ?: new CRC16();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlotByKey($key)
+    {
+        $key = $this->extractKeyTag($key);
+        $slot = $this->hashGenerator->hash($key) & 0x3FFF;
+
+        return $slot;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getDistributor()
+    {
+        throw new NotSupportedException(
+            'This cluster strategy does not provide an external distributor'
+        );
+    }
+}

+ 53 - 0
Gameserver/gamesys/app/utils/Predis/Cluster/StrategyInterface.php

@@ -0,0 +1,53 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Cluster;
+
+use Predis\Cluster\Distributor\DistributorInterface;
+use Predis\Command\CommandInterface;
+
+/**
+ * Interface for classes defining the strategy used to calculate an hash out of
+ * keys extracted from supported commands.
+ *
+ * This is mostly useful to support clustering via client-side sharding.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+interface StrategyInterface
+{
+    /**
+     * Returns a slot for the given command used for clustering distribution or
+     * NULL when this is not possible.
+     *
+     * @param CommandInterface $command Command instance.
+     *
+     * @return int
+     */
+    public function getSlot(CommandInterface $command);
+
+    /**
+     * Returns a slot for the given key used for clustering distribution or NULL
+     * when this is not possible.
+     *
+     * @param string $key Key string.
+     *
+     * @return int
+     */
+    public function getSlotByKey($key);
+
+    /**
+     * Returns a distributor instance to be used by the cluster.
+     *
+     * @return DistributorInterface
+     */
+    public function getDistributor();
+}

+ 191 - 0
Gameserver/gamesys/app/utils/Predis/Collection/Iterator/CursorBasedIterator.php

@@ -0,0 +1,191 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Collection\Iterator;
+
+use Predis\ClientInterface;
+use Predis\NotSupportedException;
+
+/**
+ * Provides the base implementation for a fully-rewindable PHP iterator that can
+ * incrementally iterate over cursor-based collections stored on Redis using the
+ * commands in the `SCAN` family.
+ *
+ * Given their incremental nature with multiple fetches, these kind of iterators
+ * offer limited guarantees about the returned elements because the collection
+ * can change several times during the iteration process.
+ *
+ * @see http://redis.io/commands/scan
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+abstract class CursorBasedIterator implements \Iterator
+{
+    protected $client;
+    protected $match;
+    protected $count;
+
+    protected $valid;
+    protected $fetchmore;
+    protected $elements;
+    protected $cursor;
+    protected $position;
+    protected $current;
+
+    /**
+     * @param ClientInterface $client Client connected to Redis.
+     * @param string          $match  Pattern to match during the server-side iteration.
+     * @param int             $count  Hint used by Redis to compute the number of results per iteration.
+     */
+    public function __construct(ClientInterface $client, $match = null, $count = null)
+    {
+        $this->client = $client;
+        $this->match = $match;
+        $this->count = $count;
+
+        $this->reset();
+    }
+
+    /**
+     * Ensures that the client supports the specified Redis command required to
+     * fetch elements from the server to perform the iteration.
+     *
+     * @param ClientInterface $client    Client connected to Redis.
+     * @param string          $commandID Command ID.
+     *
+     * @throws NotSupportedException
+     */
+    protected function requiredCommand(ClientInterface $client, $commandID)
+    {
+        if (!$client->getProfile()->supportsCommand($commandID)) {
+            throw new NotSupportedException("The current profile does not support '$commandID'.");
+        }
+    }
+
+    /**
+     * Resets the inner state of the iterator.
+     */
+    protected function reset()
+    {
+        $this->valid = true;
+        $this->fetchmore = true;
+        $this->elements = array();
+        $this->cursor = 0;
+        $this->position = -1;
+        $this->current = null;
+    }
+
+    /**
+     * Returns an array of options for the `SCAN` command.
+     *
+     * @return array
+     */
+    protected function getScanOptions()
+    {
+        $options = array();
+
+        if (strlen($this->match) > 0) {
+            $options['MATCH'] = $this->match;
+        }
+
+        if ($this->count > 0) {
+            $options['COUNT'] = $this->count;
+        }
+
+        return $options;
+    }
+
+    /**
+     * Fetches a new set of elements from the remote collection, effectively
+     * advancing the iteration process.
+     *
+     * @return array
+     */
+    abstract protected function executeCommand();
+
+    /**
+     * Populates the local buffer of elements fetched from the server during
+     * the iteration.
+     */
+    protected function fetch()
+    {
+        list($cursor, $elements) = $this->executeCommand();
+
+        if (!$cursor) {
+            $this->fetchmore = false;
+        }
+
+        $this->cursor = $cursor;
+        $this->elements = $elements;
+    }
+
+    /**
+     * Extracts next values for key() and current().
+     */
+    protected function extractNext()
+    {
+        ++$this->position;
+        $this->current = array_shift($this->elements);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rewind()
+    {
+        $this->reset();
+        $this->next();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function current()
+    {
+        return $this->current;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function key()
+    {
+        return $this->position;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function next()
+    {
+        tryFetch: {
+            if (!$this->elements && $this->fetchmore) {
+                $this->fetch();
+            }
+
+            if ($this->elements) {
+                $this->extractNext();
+            } elseif ($this->cursor) {
+                goto tryFetch;
+            } else {
+                $this->valid = false;
+            }
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function valid()
+    {
+        return $this->valid;
+    }
+}

+ 56 - 0
Gameserver/gamesys/app/utils/Predis/Collection/Iterator/HashKey.php

@@ -0,0 +1,56 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Collection\Iterator;
+
+use Predis\ClientInterface;
+
+/**
+ * Abstracts the iteration of fields and values of an hash by leveraging the
+ * HSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * @link http://redis.io/commands/scan
+ */
+class HashKey extends CursorBasedIterator
+{
+    protected $key;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct(ClientInterface $client, $key, $match = null, $count = null)
+    {
+        $this->requiredCommand($client, 'HSCAN');
+
+        parent::__construct($client, $match, $count);
+
+        $this->key = $key;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function executeCommand()
+    {
+        return $this->client->hscan($this->key, $this->cursor, $this->getScanOptions());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function extractNext()
+    {
+        $this->position = key($this->elements);
+        $this->current = array_shift($this->elements);
+    }
+}

+ 43 - 0
Gameserver/gamesys/app/utils/Predis/Collection/Iterator/Keyspace.php

@@ -0,0 +1,43 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Collection\Iterator;
+
+use Predis\ClientInterface;
+
+/**
+ * Abstracts the iteration of the keyspace on a Redis instance by leveraging the
+ * SCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * @link http://redis.io/commands/scan
+ */
+class Keyspace extends CursorBasedIterator
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct(ClientInterface $client, $match = null, $count = null)
+    {
+        $this->requiredCommand($client, 'SCAN');
+
+        parent::__construct($client, $match, $count);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function executeCommand()
+    {
+        return $this->client->scan($this->cursor, $this->getScanOptions());
+    }
+}

+ 176 - 0
Gameserver/gamesys/app/utils/Predis/Collection/Iterator/ListKey.php

@@ -0,0 +1,176 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Collection\Iterator;
+
+use Predis\ClientInterface;
+use Predis\NotSupportedException;
+
+/**
+ * Abstracts the iteration of items stored in a list by leveraging the LRANGE
+ * command wrapped in a fully-rewindable PHP iterator.
+ *
+ * This iterator tries to emulate the behaviour of cursor-based iterators based
+ * on the SCAN-family of commands introduced in Redis <= 2.8, meaning that due
+ * to its incremental nature with multiple fetches it can only offer limited
+ * guarantees on the returned elements because the collection can change several
+ * times (trimmed, deleted, overwritten) during the iteration process.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * @link http://redis.io/commands/lrange
+ */
+class ListKey implements \Iterator
+{
+    protected $client;
+    protected $count;
+    protected $key;
+
+    protected $valid;
+    protected $fetchmore;
+    protected $elements;
+    protected $position;
+    protected $current;
+
+    /**
+     * @param ClientInterface $client Client connected to Redis.
+     * @param string          $key    Redis list key.
+     * @param int             $count  Number of items retrieved on each fetch operation.
+     *
+     * @throws \InvalidArgumentException
+     */
+    public function __construct(ClientInterface $client, $key, $count = 10)
+    {
+        $this->requiredCommand($client, 'LRANGE');
+
+        if ((false === $count = filter_var($count, FILTER_VALIDATE_INT)) || $count < 0) {
+            throw new \InvalidArgumentException('The $count argument must be a positive integer.');
+        }
+
+        $this->client = $client;
+        $this->key = $key;
+        $this->count = $count;
+
+        $this->reset();
+    }
+
+    /**
+     * Ensures that the client instance supports the specified Redis command
+     * required to fetch elements from the server to perform the iteration.
+     *
+     * @param ClientInterface $client    Client connected to Redis.
+     * @param string          $commandID Command ID.
+     *
+     * @throws NotSupportedException
+     */
+    protected function requiredCommand(ClientInterface $client, $commandID)
+    {
+        if (!$client->getProfile()->supportsCommand($commandID)) {
+            throw new NotSupportedException("The current profile does not support '$commandID'.");
+        }
+    }
+
+    /**
+     * Resets the inner state of the iterator.
+     */
+    protected function reset()
+    {
+        $this->valid = true;
+        $this->fetchmore = true;
+        $this->elements = array();
+        $this->position = -1;
+        $this->current = null;
+    }
+
+    /**
+     * Fetches a new set of elements from the remote collection, effectively
+     * advancing the iteration process.
+     *
+     * @return array
+     */
+    protected function executeCommand()
+    {
+        return $this->client->lrange($this->key, $this->position + 1, $this->position + $this->count);
+    }
+
+    /**
+     * Populates the local buffer of elements fetched from the server during the
+     * iteration.
+     */
+    protected function fetch()
+    {
+        $elements = $this->executeCommand();
+
+        if (count($elements) < $this->count) {
+            $this->fetchmore = false;
+        }
+
+        $this->elements = $elements;
+    }
+
+    /**
+     * Extracts next values for key() and current().
+     */
+    protected function extractNext()
+    {
+        ++$this->position;
+        $this->current = array_shift($this->elements);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rewind()
+    {
+        $this->reset();
+        $this->next();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function current()
+    {
+        return $this->current;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function key()
+    {
+        return $this->position;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function next()
+    {
+        if (!$this->elements && $this->fetchmore) {
+            $this->fetch();
+        }
+
+        if ($this->elements) {
+            $this->extractNext();
+        } else {
+            $this->valid = false;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function valid()
+    {
+        return $this->valid;
+    }
+}

+ 47 - 0
Gameserver/gamesys/app/utils/Predis/Collection/Iterator/SetKey.php

@@ -0,0 +1,47 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Collection\Iterator;
+
+use Predis\ClientInterface;
+
+/**
+ * Abstracts the iteration of members stored in a set by leveraging the SSCAN
+ * command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * @link http://redis.io/commands/scan
+ */
+class SetKey extends CursorBasedIterator
+{
+    protected $key;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct(ClientInterface $client, $key, $match = null, $count = null)
+    {
+        $this->requiredCommand($client, 'SSCAN');
+
+        parent::__construct($client, $match, $count);
+
+        $this->key = $key;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function executeCommand()
+    {
+        return $this->client->sscan($this->key, $this->cursor, $this->getScanOptions());
+    }
+}

+ 60 - 0
Gameserver/gamesys/app/utils/Predis/Collection/Iterator/SortedSetKey.php

@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Collection\Iterator;
+
+use Predis\ClientInterface;
+
+/**
+ * Abstracts the iteration of members stored in a sorted set by leveraging the
+ * ZSCAN command (Redis >= 2.8) wrapped in a fully-rewindable PHP iterator.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * @link http://redis.io/commands/scan
+ */
+class SortedSetKey extends CursorBasedIterator
+{
+    protected $key;
+
+    /**
+     * {@inheritdoc}
+     */
+    public function __construct(ClientInterface $client, $key, $match = null, $count = null)
+    {
+        $this->requiredCommand($client, 'ZSCAN');
+
+        parent::__construct($client, $match, $count);
+
+        $this->key = $key;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function executeCommand()
+    {
+        return $this->client->zscan($this->key, $this->cursor, $this->getScanOptions());
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function extractNext()
+    {
+        if ($kv = each($this->elements)) {
+            $this->position = $kv[0];
+            $this->current = $kv[1];
+
+            unset($this->elements[$this->position]);
+        }
+    }
+}

+ 129 - 0
Gameserver/gamesys/app/utils/Predis/Command/Command.php

@@ -0,0 +1,129 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * Base class for Redis commands.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+abstract class Command implements CommandInterface
+{
+    private $slot;
+    private $arguments = array();
+
+    /**
+     * Returns a filtered array of the arguments.
+     *
+     * @param array $arguments List of arguments.
+     *
+     * @return array
+     */
+    protected function filterArguments(array $arguments)
+    {
+        return $arguments;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setArguments(array $arguments)
+    {
+        $this->arguments = $this->filterArguments($arguments);
+        unset($this->slot);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setRawArguments(array $arguments)
+    {
+        $this->arguments = $arguments;
+        unset($this->slot);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getArguments()
+    {
+        return $this->arguments;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getArgument($index)
+    {
+        if (isset($this->arguments[$index])) {
+            return $this->arguments[$index];
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function setSlot($slot)
+    {
+        $this->slot = $slot;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function getSlot()
+    {
+        if (isset($this->slot)) {
+            return $this->slot;
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return $data;
+    }
+
+    /**
+     * Normalizes the arguments array passed to a Redis command.
+     *
+     * @param array $arguments Arguments for a command.
+     *
+     * @return array
+     */
+    public static function normalizeArguments(array $arguments)
+    {
+        if (count($arguments) === 1 && is_array($arguments[0])) {
+            return $arguments[0];
+        }
+
+        return $arguments;
+    }
+
+    /**
+     * Normalizes the arguments array passed to a variadic Redis command.
+     *
+     * @param array $arguments Arguments for a command.
+     *
+     * @return array
+     */
+    public static function normalizeVariadic(array $arguments)
+    {
+        if (count($arguments) === 2 && is_array($arguments[1])) {
+            return array_merge(array($arguments[0]), $arguments[1]);
+        }
+
+        return $arguments;
+    }
+}

+ 81 - 0
Gameserver/gamesys/app/utils/Predis/Command/CommandInterface.php

@@ -0,0 +1,81 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * Defines an abstraction representing a Redis command.
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+interface CommandInterface
+{
+    /**
+     * Returns the ID of the Redis command. By convention, command identifiers
+     * must always be uppercase.
+     *
+     * @return string
+     */
+    public function getId();
+
+    /**
+     * Assign the specified slot to the command for clustering distribution.
+     *
+     * @param int $slot Slot ID.
+     */
+    public function setSlot($slot);
+
+    /**
+     * Returns the assigned slot of the command for clustering distribution.
+     *
+     * @return int|null
+     */
+    public function getSlot();
+
+    /**
+     * Sets the arguments for the command.
+     *
+     * @param array $arguments List of arguments.
+     */
+    public function setArguments(array $arguments);
+
+    /**
+     * Sets the raw arguments for the command without processing them.
+     *
+     * @param array $arguments List of arguments.
+     */
+    public function setRawArguments(array $arguments);
+
+    /**
+     * Gets the arguments of the command.
+     *
+     * @return array
+     */
+    public function getArguments();
+
+    /**
+     * Gets the argument of the command at the specified index.
+     *
+     * @param int $index Index of the desired argument.
+     *
+     * @return mixed|null
+     */
+    public function getArgument($index);
+
+    /**
+     * Parses a raw response and returns a PHP object.
+     *
+     * @param string $data Binary string containing the whole response.
+     *
+     * @return mixed
+     */
+    public function parseResponse($data);
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/ConnectionAuth.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/auth
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class ConnectionAuth extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'AUTH';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/ConnectionEcho.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/echo
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class ConnectionEcho extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'ECHO';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/ConnectionPing.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/ping
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class ConnectionPing extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'PING';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/ConnectionQuit.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/quit
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class ConnectionQuit extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'QUIT';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/ConnectionSelect.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/select
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class ConnectionSelect extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'SELECT';
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashDelete.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hdel
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashDelete extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HDEL';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        return self::normalizeVariadic($arguments);
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashExists.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hexists
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashExists extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HEXISTS';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashGet.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hget
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashGet extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HGET';
+    }
+}

+ 42 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashGetAll.php

@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hgetall
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashGetAll extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HGETALL';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        $result = array();
+
+        for ($i = 0; $i < count($data); ++$i) {
+            $result[$data[$i]] = $data[++$i];
+        }
+
+        return $result;
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashGetMultiple.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hmget
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashGetMultiple extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HMGET';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        return self::normalizeVariadic($arguments);
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashIncrementBy.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hincrby
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashIncrementBy extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HINCRBY';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashIncrementByFloat.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hincrbyfloat
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashIncrementByFloat extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HINCRBYFLOAT';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashKeys.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hkeys
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashKeys extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HKEYS';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashLength.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hlen
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashLength extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HLEN';
+    }
+}

+ 85 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashScan.php

@@ -0,0 +1,85 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hscan
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashScan extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HSCAN';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        if (count($arguments) === 3 && is_array($arguments[2])) {
+            $options = $this->prepareOptions(array_pop($arguments));
+            $arguments = array_merge($arguments, $options);
+        }
+
+        return $arguments;
+    }
+
+    /**
+     * Returns a list of options and modifiers compatible with Redis.
+     *
+     * @param array $options List of options.
+     *
+     * @return array
+     */
+    protected function prepareOptions($options)
+    {
+        $options = array_change_key_case($options, CASE_UPPER);
+        $normalized = array();
+
+        if (!empty($options['MATCH'])) {
+            $normalized[] = 'MATCH';
+            $normalized[] = $options['MATCH'];
+        }
+
+        if (!empty($options['COUNT'])) {
+            $normalized[] = 'COUNT';
+            $normalized[] = $options['COUNT'];
+        }
+
+        return $normalized;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        if (is_array($data)) {
+            $fields = $data[1];
+            $result = array();
+
+            for ($i = 0; $i < count($fields); ++$i) {
+                $result[$fields[$i]] = $fields[++$i];
+            }
+
+            $data[1] = $result;
+        }
+
+        return $data;
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashSet.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hset
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashSet extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HSET';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 48 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashSetMultiple.php

@@ -0,0 +1,48 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hmset
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashSetMultiple extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HMSET';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        if (count($arguments) === 2 && is_array($arguments[1])) {
+            $flattenedKVs = array($arguments[0]);
+            $args = $arguments[1];
+
+            foreach ($args as $k => $v) {
+                $flattenedKVs[] = $k;
+                $flattenedKVs[] = $v;
+            }
+
+            return $flattenedKVs;
+        }
+
+        return $arguments;
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashSetPreserve.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hsetnx
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashSetPreserve extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HSETNX';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashStringLength.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hstrlen
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashStringLength extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HSTRLEN';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/HashValues.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/hvals
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HashValues extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'HVALS';
+    }
+}

+ 44 - 0
Gameserver/gamesys/app/utils/Predis/Command/HyperLogLogAdd.php

@@ -0,0 +1,44 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/pfadd
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HyperLogLogAdd extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'PFADD';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        return self::normalizeVariadic($arguments);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/HyperLogLogCount.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/pfcount
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HyperLogLogCount extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'PFCOUNT';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        return self::normalizeArguments($arguments);
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/HyperLogLogMerge.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/pfmerge
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class HyperLogLogMerge extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'PFMERGE';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        return self::normalizeArguments($arguments);
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyDelete.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/del
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyDelete extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'DEL';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        return self::normalizeArguments($arguments);
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyDump.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/dump
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyDump extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'DUMP';
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyExists.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/exists
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyExists extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'EXISTS';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyExpire.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/expire
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyExpire extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'EXPIRE';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyExpireAt.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/expireat
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyExpireAt extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'EXPIREAT';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyKeys.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/keys
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyKeys extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'KEYS';
+    }
+}

+ 50 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyMigrate.php

@@ -0,0 +1,50 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/migrate
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyMigrate extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'MIGRATE';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        if (is_array(end($arguments))) {
+            foreach (array_pop($arguments) as $modifier => $value) {
+                $modifier = strtoupper($modifier);
+
+                if ($modifier === 'COPY' && $value == true) {
+                    $arguments[] = $modifier;
+                }
+
+                if ($modifier === 'REPLACE' && $value == true) {
+                    $arguments[] = $modifier;
+                }
+            }
+        }
+
+        return $arguments;
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyMove.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/move
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyMove extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'MOVE';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyPersist.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/persist
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyPersist extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'PERSIST';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyPreciseExpire.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/pexpire
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyPreciseExpire extends KeyExpire
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'PEXPIRE';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyPreciseExpireAt.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/pexpireat
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyPreciseExpireAt extends KeyExpireAt
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'PEXPIREAT';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyPreciseTimeToLive.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/pttl
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyPreciseTimeToLive extends KeyTimeToLive
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'PTTL';
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyRandom.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/randomkey
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyRandom extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'RANDOMKEY';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return $data !== '' ? $data : null;
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyRename.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/rename
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyRename extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'RENAME';
+    }
+}

+ 36 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyRenamePreserve.php

@@ -0,0 +1,36 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/renamenx
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyRenamePreserve extends KeyRename
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'RENAMENX';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function parseResponse($data)
+    {
+        return (bool) $data;
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyRestore.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/restore
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyRestore extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'RESTORE';
+    }
+}

+ 66 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyScan.php

@@ -0,0 +1,66 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/scan
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyScan extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'SCAN';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        if (count($arguments) === 2 && is_array($arguments[1])) {
+            $options = $this->prepareOptions(array_pop($arguments));
+            $arguments = array_merge($arguments, $options);
+        }
+
+        return $arguments;
+    }
+
+    /**
+     * Returns a list of options and modifiers compatible with Redis.
+     *
+     * @param array $options List of options.
+     *
+     * @return array
+     */
+    protected function prepareOptions($options)
+    {
+        $options = array_change_key_case($options, CASE_UPPER);
+        $normalized = array();
+
+        if (!empty($options['MATCH'])) {
+            $normalized[] = 'MATCH';
+            $normalized[] = $options['MATCH'];
+        }
+
+        if (!empty($options['COUNT'])) {
+            $normalized[] = 'COUNT';
+            $normalized[] = $options['COUNT'];
+        }
+
+        return $normalized;
+    }
+}

+ 83 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeySort.php

@@ -0,0 +1,83 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/sort
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeySort extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'SORT';
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function filterArguments(array $arguments)
+    {
+        if (count($arguments) === 1) {
+            return $arguments;
+        }
+
+        $query = array($arguments[0]);
+        $sortParams = array_change_key_case($arguments[1], CASE_UPPER);
+
+        if (isset($sortParams['BY'])) {
+            $query[] = 'BY';
+            $query[] = $sortParams['BY'];
+        }
+
+        if (isset($sortParams['GET'])) {
+            $getargs = $sortParams['GET'];
+
+            if (is_array($getargs)) {
+                foreach ($getargs as $getarg) {
+                    $query[] = 'GET';
+                    $query[] = $getarg;
+                }
+            } else {
+                $query[] = 'GET';
+                $query[] = $getargs;
+            }
+        }
+
+        if (isset($sortParams['LIMIT']) &&
+            is_array($sortParams['LIMIT']) &&
+            count($sortParams['LIMIT']) == 2) {
+            $query[] = 'LIMIT';
+            $query[] = $sortParams['LIMIT'][0];
+            $query[] = $sortParams['LIMIT'][1];
+        }
+
+        if (isset($sortParams['SORT'])) {
+            $query[] = strtoupper($sortParams['SORT']);
+        }
+
+        if (isset($sortParams['ALPHA']) && $sortParams['ALPHA'] == true) {
+            $query[] = 'ALPHA';
+        }
+
+        if (isset($sortParams['STORE'])) {
+            $query[] = 'STORE';
+            $query[] = $sortParams['STORE'];
+        }
+
+        return $query;
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyTimeToLive.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/ttl
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyTimeToLive extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'TTL';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/KeyType.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/type
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class KeyType extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'TYPE';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/ListIndex.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/lindex
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class ListIndex extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'LINDEX';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/ListInsert.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/linsert
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class ListInsert extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'LINSERT';
+    }
+}

+ 28 - 0
Gameserver/gamesys/app/utils/Predis/Command/ListLength.php

@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Predis package.
+ *
+ * (c) Daniele Alessandri <suppakilla@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Predis\Command;
+
+/**
+ * @link http://redis.io/commands/llen
+ *
+ * @author Daniele Alessandri <suppakilla@gmail.com>
+ */
+class ListLength extends Command
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function getId()
+    {
+        return 'LLEN';
+    }
+}

部分文件因为文件数量过多而无法显示