<?php

namespace loyalsoft;

require_once __DIR__ . '/../service_call/pay/official/pay_op.php';

/**
 * Description of UserProc
 * 玩家数据处理流程
 *
 */
class UserProc {

    const role_Table = 'tab_rolename';

    /**
     * 逻辑分发
     * 所有的Proc中必须有这样一个方法
     * @param Req $req
     */
    public static function procMain($req) {
        switch ($req->cmd) {
            case CmdCode::cmd_user_getzonelist:                                 # 6000 分区列表
                return UserProc::GetZoneList();
            case CmdCode::cmd_user_loginuserinfo:                               # 6001 登录
                return UserProc::loginUserInfo();
            case CmdCode::cmd_user_gameconstinfo:                               # 6002 下载游戏配置
                return UserProc::downloadConstInfo();
            case CmdCode::cmd_user_acceptcontidaysgift:                         # 6003 领取连续登陆奖励
                return UserProc::acceptContiDaysGift();
            case CmdCode::cmd_user_registerNewRole:                             # 6006 注册新角色
                return UserProc::RegisterNewRole();
            case CmdCode::cmd_user_completeNewbieGuide:                         # 6007 提交新手引导步骤
                return UserProc::completeNewbieGuide();
//            case CmdCode::cmd_user_setNewbieGuideCards:                         # 6008 发放新手引导所需卡牌
//                return self::SetNewbieGuideCards($req);
//            case CmdCode::cmd_user_setNewbieGuideOver:                          # 6009 跳过新手引导
//                return UserProc::setNewbieGuideOver();
            case CmdCode::cmd_user_setNickname:                                 # 6010 设置/修改玩家昵称
                return self::SetUserNickname();
            case CmdCode::cmd_user_SetUserHeadImageBorder:                      # 6011 修改玩家头像框
                return self::SetUserImageBorder();
            case CmdCode::cmd_user_setUserImage:                                # 6012 更换玩家形象
                return self::SetUserImage();
            case CmdCode::cmd_user_changeUserHeadImg:                           # 6013 更换玩家头像
                return self::SetUserHeadImage();

            case CmdCode::cmd_user_ctxBack:                                     # 6015 收集玩家反馈
                return self::UserCtxBack();
            case CmdCode::cmd_user_other_info:                                  # 6016 拉取其他玩家信息cmd_user_deleteUserUId
                return self::UserOtherPlayerInfo();
            case CmdCode::cmd_user_deleteUserUId:                               # 6016 删除角色
                return self::deleteUserUId();
            default:
                Err(ErrCode::cmd_err);
        }
    }

    /*
     * 6016 删除角色
     */

    public static function deleteUserUId() {
        $mem = gMem();

        $uid = req()->uid;
        $zoneid = req()->zoneid;
        $list = array();

        $zoneKey = MemKey_User::Union_PlayedZoneInfo_normal($uid);
        $list[] = $zoneKey;
        $publicKey = MemKey_User::Union_PublicState_hash($uid);
        $list[] = $publicKey;
        $gameInfoKey = MemKey_User::Info_hash($zoneid, $uid);
        $list[] = $gameInfoKey;
        $interactKey = MemKey_User::Interact($zoneid, $uid);
        $list[] = $interactKey;
        $OffensiveLogKey = MemKey_User::OffensiveLog_zset($zoneid, $uid);
        $list[] = $OffensiveLogKey;
        $DefensiveLogKey = MemKey_User::DefensiveLog_zset($zoneid, $uid);
        $list[] = $DefensiveLogKey;
        $PVP_OffensiveLogKey = MemKey_User::PVP_OffensiveLog_zset($zoneid, $uid);
        $list[] = $PVP_OffensiveLogKey;
        $PVP_DefensiveLogKey = MemKey_User::PVP_DefensiveLog_zset($zoneid, $uid);
        $list[] = $PVP_DefensiveLogKey;
        $SigKey = MemKey_User::Sig($zoneid, $uid);
        $list[] = $SigKey;

        $CurIdKey = MemKey_User::Mail_CurId_int($zoneid, $uid);
        $list[] = $CurIdKey;
        $SysRecordKey = MemKey_User::Mail_SysRecord_set($zoneid, $uid);
        $list[] = $SysRecordKey;
        $QueueKey = MemKey_User::Mail_Queue_hash($zoneid, $uid);
        $list[] = $QueueKey;

        foreach ($list as $key) {
            if ($mem->exists($key)) {
                $mem->delete($key);
            }
        }

        return Resp::ok();
    }

    /**
     * 检测遗漏订单
     */
    static function checkMissOrder() {
        $tableName = "tpl_order_tab";

        if (daoInst()->tableExist($tableName)) {
            $arr = daoInst()->select("*")->from($tableName)
                    ->where('uid')->eq(req()->uid)
                    ->andWhere('zoneid')->eq(req()->zoneid)
                    ->andWhere('status')->eq(1)
                    ->andWhere('drawed_ts')->eq(0)
                    ->fetchAll();

            if (count($arr) != null) {
                foreach ($arr as $item) {
                    $result = pay_op::CheckAndDrawOrder(req()->uid, $item->cpOrderId, array(new PayProc, 'distributePayGoods'));
                }
            }
        }
    }

    /**
     * 6016 拉取其他玩家的信息.
     */
    public static function UserOtherPlayerInfo() {
        $zoneId = req()->zoneid;
        list($other_uid) = req()->paras;
        $g = UserProc::getUserGame($zoneId, $other_uid);
        my_Assert(null != $g, ErrCode::user_no_err);                            # 找不到指定的玩家数据
        return Resp::ok($g);
    }

    /**
     * 6015 收集玩家反馈
     */
    public static function UserCtxBack() {
        $userID = req()->uid;
        $zoneid = req()->zoneid;
        $name = ctx()->baseInfo->name;
        list($ctx, $type, $phoneId) = req()->paras;

        $ret = self::insertUserCtxBack($userID, $name, $zoneid, $ctx, $phoneId, $type);
        return Resp::ok();
    }

    /**
     * 收集玩家反馈
     * @param type $userID
     * @param type $zoneid
     * @param type $ctx
     * @param type $phoneId
     * @return type
     */
    static function insertUserCtxBack($userID, $name, $zoneid, $ctx, $phoneId, $type) {
        $paydb = daoInst();
        $sql = "INSERT INTO `tab_userctxback` (tid,uid,name,zoneid,ctx,phoneId,type) VALUES (null,'%s','%s',%d,'%s','%s','%s')";
        $query = sprintf($sql, $userID, $name, $zoneid, $ctx, $phoneId, $type);
        $result = $paydb->exec($query);
        return $result;
    }

//
// <editor-fold defaultstate="collapsed" desc="玩家信息 修改">

    /**
     * [6013]修改玩家头像
     */
    static function SetUserHeadImage() {
        list($headImage) = req()->paras;                                         # 参数, 新头像
        ctx()->baseInfo->headImg = $headImage;
        UserProc::updateUserInfo();
        return Resp::ok(array('ret' => 'ok'));
    }

    /**
     * [6012]修改玩家形象
     */
    static function SetUserImage() {
        list($image) = req()->paras;                                             # 参数, 新形象
        ctx()->baseInfo->img = $image;
        UserProc::updateUserInfo();
        return Resp::ok(array('ret' => 'ok'));
    }

    /**
     * [6011]修改玩家头像框
     */
    static function SetUserImageBorder() {
        list($imgborderId) = req()->paras;                                       # 参数, 新头像框ID
        ctx()->baseInfo->imgBorderId = $imgborderId;
        UserProc::updateUserInfo();
        return Resp::ok(array('ret' => 'ok'));
    }

    /**
     * [6010] 设置/修改玩家昵称
     */
    static function SetUserNickname() {
        list($newname) = req()->paras;                                          # 参数: 新昵称, 头像
        my_Assert(strlen($newname) >= 3, "名字太短了, 换个长点的吧!");                # 防御字符串长度太短.
        my_Assert(isset(glc()->User_SetNickname_Cost), "全局变量中未找到改名消耗钻石数量的配置字段");
        $amt = glc()->User_SetNickname_Cost;                                    # 改名需要消耗钻石
        $ok = ctx()->base()->Consume_Cash($amt);                # 扣除钻石
        my_Assert($ok, ErrCode::notenough_cash_msg);
        my_Assert(self::checkRoleNameNotExist($newname), ErrCode::user_nicknameexist); # 昵称已存在
        ctx()->baseInfo->name = $newname;
        daoInst()->update(self::role_Table)
                ->data(array('roleName' => $newname))
                ->where('userID')->eq(req()->uid)
                ->andWhere('zoneid')->eq(req()->zoneid)
                ->exec();
        UserProc::updateUserInfo();
        return Resp::ok(array('ret' => 'ok'));
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="新手引导">
//    /**
//     * [6009] 新手引导 发送n张碎片到玩家身上
//     */
//    static function SetNewbieGuideCards() {
//        Err(ErrCode::function_notopen_msg);                                     # 功能已经废弃 -wg
//        $private = ctx()->privateState;
//        if (!property_exists($private, 'newbieguideCards')) {                   # 逻辑检查, 是否已经发放过
//            $private->newbieguideCards = true;
//            ctx()->privateState = $private;                      # 回写私有数据
//            $sp = explode(';', glc()->User_SetNewbieGuideCards);                # 碎片
//            foreach ($sp as $cardinfo) {
//                $arr = explode(',', $cardinfo);
//                $itemid = $arr[0];
//                $num = $arr[1];
//                StoreProc::addSegmentIntoStore(ctx()->store, $itemid, $num);
//            }
//            $exp = explode(';', glc()->User_SetNewbieGuideExps);                # 经验卡
//            foreach ($exp as $expinfo) {
//                $arr = explode(',', $expinfo);
//                $itemid = $arr[0];
//                $num = $arr[1];
//                StoreProc::PutOverlyingItemInStore($itemid, $num);
//            }
//            UserProc::updateUserInfo();                                         # 回存玩家数据
//            return Resp::ok(array('store' => ctx()->store));
//        }
//        return Resp::err(ErrCode::active_hasgetted);
//    }

    /**
     * [6009] 增加 设置引导结束的标志位
     */
    public static function setNewbieGuideOver() {
        Err(ErrCode::function_notopen_msg);                                     # 功能已经废弃 -wg
//        $user = new Data_UserGame(ctx());                        # user
//        if (!CommUtil::isPropertyExists($user, "NewbieGuideOver")) {            # 防御: 变量未初始化
//            $user->NewbieGuideOver = 1;
//        }
//        $user->NewbieGuideOver = 1;
//        ctx($user);
//        UserProc::updateUserInfo();                                         # 回写数据
//        return Resp::ok(array(#                                                 # 返回值
//                    'result' => "succeed"
//        ));
    }

    /**
     * [6007] 更新初始的强制的新手引导阶段步骤
     * 第一阶段的引导
     */
    public static function completeNewbieGuide() {
        $guideIndex = req()->paras[0];                                          # 参数: 新手引导步骤
        $user = new Data_UserGame(ctx());                       # user
        my_default_Obj($user->NewbieGuide);                                     # 防御: 变量未初始化
        $NewbieGuide = $user->NewbieGuide;
        if (!CommUtil::isPropertyExists($NewbieGuide, "guideStep")) {           # 防御: 变量未初始化
            $NewbieGuide->guideStep = 0;
        }
//        my_Assert($guideIndex >= $NewbieGuide->guideStep, ErrCode::user_settutorialscompletedfail_err);

        $NewbieGuide->guideStep = $guideIndex;
        $user->NewbieGuide = $NewbieGuide;
        if ($NewbieGuide->guideStep == 2) {
            StatisticsProc::TargetStatistics(Enum_TargetStatistics::comNewGuide_UserNum);
        }

        ctx($user);
        UserProc::updateUserInfo();                                             # 回写数据
        return Resp::ok(array(
                    "store" => $user->store, #                                  # 目前来看只涉及到items变化
                    "heros" => $user->heros, #                                  # 英雄数据
        ));
    }

// </editor-fold>
//

    /**
     * 6006 注册新角色
     */
    public static function RegisterNewRole() {
        $userID = req()->uid;
        list($rolename, $gender, $profile_img) = req()->paras;                   # 参数: 昵称,性别,头像
        $id = gMem()->increment(MemKey_GameRun::Stat_UserCountByZone_int(req()->zoneid));   # 增加玩家数量计数
        $rolename = "No." . sprintf("%03d", req()->zoneid) . sprintf("%07d", $id); # 生成编号
        if (self::checkRoleNameNotExist($rolename)) {                           # 记录玩家
            $game = self::createUser($rolename, $gender, $profile_img);
            self::newFieldSupplement($game);
            if (1 == self::regRole(req()->zoneid, $userID, $rolename, $gender, $profile_img, req()->getPlatStr())) {
                StatisticsProc::TargetStatistics(Enum_TargetStatistics::registerUserNum); //注册人数统计
                StatisticsProc::dailyTaskInit();

                $resp = Resp::ok($game);
                self::updtateUserZoneInfo();
            } else {
                $resp = Resp::err(ErrCode::err_db);
            }
//            StatProc::UserGuidStep($userinfo->user->firstLogOn, $req->zoneid, -2, 1);
        } else {                                                                # 昵称已存在
            $resp = Resp::ok(array('ret' => '用户已存在.'));
        }
        return $resp;
    }

    /**
     * 6000 【移动端】 获取分区列表
     */
    public static function GetZoneList() {
        $defaultZone = new Ins_ZoneInfo(1, 0, "");                             # 新用户默认分区
        $bGetRecommended = false;
        if (count(req()->paras) > 0) {                                           # 是否只拉取推荐分区
            $bGetRecommended = req()->paras[0];
        }
        $zoneList = array();
        $ts = now();
        foreach (GameConfig::zonelist() as $zoneid => $zone) {
            isEditor() and $zone = new \sm_zonelist();
            if ($zone->publicTs > $ts) {
                continue;
            }
            $zone->zoneid = $zoneid;                                            # 把zoneid塞进zone数据结构中
            if ($bGetRecommended) {
                if ($zone->isRecommended > 0 && $zone->status == 1) {
                    $zoneList[] = $zone;
                } else {

                }
            } else {
                $zoneList[] = $zone;
            }
            unset($zone->isRecommended);
        }
        UserProc::_AddTesterZonelist($zoneList);                                # 添加测试分区
#
// <editor-fold defaultstate="collapsed" desc="    取玩家分区记录    ">
        $userZoneInfo = self::getUserZoneInfo();                                # 玩家分区记录
        $isNewUser = false;
        if ($userZoneInfo == null) {           // 这里使用推荐分区的数据，推荐分区信息，在后台编辑，编辑器可用
            $userZoneInfo = new Data_UserZoneInfo();
            $userZoneInfo->lastZone = $defaultZone;                             # 新用户导向默认分区
            $isNewUser = true;

//            $err = self::RegisterNewUser($req);  # 新用户添加到总表中
//            if ($err != ErrCode::succeed) {
//                return ResponseVo::myErrResponse($req, $err);
//            }
# 统计模块添加记录
//            StatProc::UserGuidStep(CommUtil::tsCurrent(), $req->zoneid, -1, 1); // 第一次进入游戏
        } else { # 转换一下格式，去掉key，只保留value的集合
//            $userZoneInfo->playedZones = ArrayInit();
//            array_merge($userZoneInfo->playedZones, array_values((array) $userZoneInfo->playedZones));
        }

// </editor-fold>

        $ret = array(
            'isNewUser' => $isNewUser,
            'zonelist' => json_decode(json_encode($zoneList)),
            'userZoneInfo' => $userZoneInfo
        );

        return Resp::ok($ret);                                                  # 返回值
    }

    private static function _AddTesterZonelist(&$zoneList) {
        if (config::Inst()->isTester(req()->uid)) {                              # 添加测试分区
            $zoneList[] = array('zoneid' => 999, 'name' => '内测专区', 'status' => 2, 'publicTs' => 0);
        }
    }

    /**
     * 6002 客户端下载常量配置信息
     * @return type
     */
    public static function downloadConstInfo() {
        list($clientDataVer) = req()->paras;                                     # 客户端数据版本号,程序版本号
        $serverVer = GameConfig::ver();                                         # 最新数据版本号
        my_Assert($serverVer, ErrCode::err_const_no);                           # 找不到常量数据
        $url = config::CDN_host() . "/cfg/" . req()->CV . "/Client.bytes?" . $serverVer;

        $ret = array(
            'version' => $serverVer,
            'url' => $clientDataVer == $serverVer ? "" : $url, #                # 如果版本一致，url传空，只传回版本号
            'data' => null);
        return Resp::ok($ret);

//                $md5 = md5(json_encode($constInfo));                          # 计算MD5值，多余计算md5
//        $constInfo = GameConfig::client();                       # 取出来的已经是base64过的压缩数据
//        my_Assert($constInfo, ErrCode::err_const_no);                           # 找不到配置数据
    }

    /**
     * 6001 客户端登录并返还玩家信息
     * @return Resp
     */
    public static function loginUserInfo() {
        $game = UserProc::getUserGame(req()->zoneid, req()->uid);
        if ($game == null) {                                                # 新用户, -> 6006创建账号
            $ret = array(
                'isNewUser' => true
            );
            return Resp::ok($ret);
        } else {                                                                # 2.如果玩家已存在，则处理普通登录流程
            self::newFieldSupplement($game);
            MapProc::checkGatePassPositionInfo($game);

            req()->game = $game;                                                # 给Req挂载玩家数据
            UserProc::checkContidays();                                         # 连续登录,状态检查
            PayProc::m_refreshChargeOrders();                                   # 刷新订单, 多平台版本
            PayProc::checkDeltest();                                            # 检查内侧充值记录(函数内部会只检查一次)
            //PayProc::SuitGiftTsRecord();
            $college = new Info_College(ctx()->college);
            $college->initTaskCard();
            ctx()->college = $college;
            ctx()->privateState->bornPlace = 0;
            ShopProc::resetDaliySpecialPackages();                              #每日特惠领奖每日重置
            PayProc::suitGiftCheck();
            self::checkMissOrder();                                             #校验是否有漏单
            ActiveProc::ChangeTili(0);
//TaskProc::checkHandOverDailyTask();

            ctx()->RegenNewToken();
            UserProc::updateUserInfo();                                         # 这一步回存操作只有在 userInfo正常存在的情况下才进行

            $resp = Resp::ok($game);                                            # 设置返回值
//            self::backupUserInfo();                                             # 数据回写

            AuctionProc::TriggerSettlement();                                   #结算流拍信息
            self::updtateUserZoneInfo();                                        # 1. 更新玩家分区记录
        }
        return $resp;
    }

    /*
     * 上线账号里的任务数据的修改
     */

    public static function userDataUpdate() {
        $dic = GameConfig::taskJianRong();
        $arr = (array) $dic;
        ksort($arr);
//ctx()->privateState->taskJianRong_index;
        if (!StlUtil::dictHasProperty(ctx()->privateState, 'taskJianRong_index')) {
            ctx()->privateState->taskJianRong_index = 0;
        }

        $startIndex = ctx()->privateState->taskJianRong_index;
        $taskcards = ctx()->store->taskcards;
        foreach ($arr as $index => $val) {
            if ($index <= $startIndex) {
                continue;
            }

            $cmd = $val->cmd1;

            $tempUId = 999999;

            $tag = true;
            switch ($cmd) {
                case 100://该任务存在即重置,不存在就算了 参数：任务id
                    $typeId = $val->paras1;
                    $tag = false;
//$tempUId = 0;
                    foreach ($taskcards as $taskUid => $task) {
                        if ($task->typeId == $typeId) {
                            $tag = true;
//$tempUid = $taskUid;
                            break;
                        }
                    }

//                    if($tag){
//                        $task = new Ins_TaskCard($val->taskId_Modified);
//                        $task->uid = $tempUid;
//                        $task->state = 2;
//                        $taskcards->$tempUid = $task;
//                    }

                    break;
                case 200://多个武器拥有其中一个即不需要重置,都没有要重置 参数:多个武器id,号分开
                    $list = explode(',', $val->paras1);
                    $tag = true;
                    $equip = ctx()->store->equipment;

                    foreach ($equip as $eUid => $eDic) {
                        if (in_array($eDic->typeId, $list)) {
                            $tag = false;
                            break;
                        }
                    }

                    break;
                case 201://多个武器任一一个等级到10级即不重置,都没有则重置
                    $list = explode(',', $val->paras1);
                    $lv = $val->parasCtx1;
                    $equip = ctx()->store->equipment;
                    $tag = true;

                    foreach ($list as $eTypeId) {
                        foreach ($equip as $eUid => $eDic) {
                            if ($eTypeId == $eDic->typeId && $eDic->level >= $lv) {
                                $tag = false;
                                break 2;
                            }
                        }
                    }

                    break;
                case 202://多个武器任一一个突破到1星即不重置,都没有则重置
                    $list = explode(',', $val->paras1);
                    $starLv = $val->parasCtx1;
                    $equip = ctx()->store->equipment;
                    $tag = true;

                    foreach ($list as $eTypeId) {
                        foreach ($equip as $eUid => $eDic) {
                            if ($eTypeId == $eDic->typeId && $eDic->starLevel >= $starLv) {
                                $tag = false;
                                break 2;
                            }
                        }
                    }

//                    if($tag){//一个都没有
//                        foreach ($taskcards as $uid => $dic) {
//                            if($dic->typeId == $val->taskId){
//                                $tempUId = $uid;
//                                break;
//                            }
//                        }
//
//                        $task = new Ins_TaskCard($val->taskId_Modified);
//                        $task->uid = $tempUId;
//                        $task->state = 2;
//                        $taskcards->$tempUId = $task;
//                    }
                    break;
                case 300://唤灵师等级升到X级,不满足则重置***
                    $lv = $val->paras1;
                    $heros = ctx()->heros->collectHeros;
                    $tag = true;
                    foreach ($heros as $heroUid => $val) {
                        if ($val->level >= $lv) {
                            $tag = false;
                            break;
                        }
                    }

                    break;
                case 301://唤灵师有过突破,不满足则重置
                    $sLv = $val->paras1;
                    $heros = ctx()->heros->collectHeros;
                    $tag = true;
                    foreach ($heros as $heroUid => $val) {
                        if ($val->curStar >= $sLv) {
                            $tag = false;
                            break;
                        }
                    }

                    break;
                case 302://是否拥有某言灵,有其中一个则不重置,全没有则重置
                    $list = explode(',', $val->paras1);
                    $tag = true;
                    $yanling = ctx()->store->yanling;

                    foreach ($yanling as $eUid => $eDic) {
                        if (in_array($eDic->typeId, $list)) {
                            $tag = false;
                            break;
                        }
                    }

                    break;
                case 303://items 背包里是否有某道具(是否获得召唤卡)
                    $items = ctx()->store->items;
                    $itemId = $val->paras1;
                    $tag = true;
                    if (StlUtil::dictHasProperty($items, $itemId)) {
                        $tag = false;
                    }

                    break;
                case 304://任一言灵等级到20级,都没有则重置
                    $lv = $val->paras1;
                    $yanling = ctx()->store->yanling;
                    $tag = true;

                    foreach ($yanling as $eUid => $eDic) {
                        if ($eDic->level >= $lv) {
                            $tag = false;
                            break;
                        }
                    }

                    break;
                case 305://任一言灵突破到1星,都没有则重置
                    $slv = $val->paras1;
                    $yanling = ctx()->store->yanling;
                    $tag = true;
                    foreach ($yanling as $eUid => $eDic) {
                        if ($eDic->starLv >= $slv) {
                            $tag = false;
                            break;
                        }
                    }

                    break;

                default:
                    break;
            }
            if ($tag) {//一个都没有
                foreach ($taskcards as $eUid => $eDic) {
                    if ($eDic->typeId == $val->taskId) {
                        $tempUId = $eUid;
                        break;
                    }
                }


                if ($tempUId != 999999) {
                    $task = new Ins_TaskCard($val->taskId_Modified);
                    $task->uid = $tempUId;
                    $task->state = 2;
                    $taskcards->$tempUId = $task;
                }
            }

            ctx()->privateState->taskJianRong_index = $index;
        }
        ctx()->store->taskcards = $taskcards;
    }

    public static function newFieldSupplement(&$game) {
//        if (!StlUtil::dictHasProperty($game->store, 'weaponRecord')) {
//            $game->store->weaponRecord = array();
//        }
//
//        if (!StlUtil::dictHasProperty($game->store, 'yanlingRecord')) {
//            $game->store->yanlingRecord = array();
//        }

        if (!StlUtil::dictHasProperty($game->shopdata, 'rechargeRecordList')) {
            $game->shopdata->rechargeRecordList = array();
        }


        if (!StlUtil::dictHasProperty($game->shopdata, 'fRechargeTime')) {
            $game->shopdata->fRechargeTime = new \stdClass();
        }
        if (!StlUtil::dictHasProperty($game->shopdata, 'fRechargeRecord')) {
            $game->shopdata->fRechargeRecord = new \stdClass();
        }

        if ($game->shopdata->fRechargeTime == null) {
            $game->shopdata->fRechargeTime = new \stdClass();
        }
        if ($game->shopdata->fRechargeRecord == null) {
            $game->shopdata->fRechargeRecord = new \stdClass();
        }

        if (!StlUtil::dictHasProperty($game->privateState, 'battleReviveNum')) {
            $game->privateState->battleReviveNum = 0;
        }

        $shopdata = $game->shopdata;
        if (!StlUtil::dictHasProperty($shopdata, 'suitGiftStartTs')) {
            $shopdata->suitGiftStartTs = 0;
        }

//        if($shopdata->suitGiftStartTs == 0){
//            $shopdata->suitGiftStartTs = now();
//        }
        $game->shopdata = $shopdata;

        //$game->privateState->onlineGiftClearTs = now();
//        if (!StlUtil::dictHasProperty($game->store, 'storage')) {
//            $key = 1;
//            $key2 = 2;
//
//            $game->store->storage = ObjectInit();
//            $game->store->storage->$key = new Ins_storage();
//            $game->store->storage->$key2 = new Ins_storage();
//        }
//
//        if ($game->store->storage == null) {
//            $key = 1;
//            $key2 = 2;
//
//            $game->store->storage = ObjectInit();
//            $game->store->storage->$key = new Ins_storage();
//            $game->store->storage->$key2 = new Ins_storage();
//        }
//
//        if (!StlUtil::dictHasProperty($game->store, 'battleItem')) {
//            $game->store->battleItem = ObjectInit();
//        }
//
//        if ($game->store->battleItem == null) {
//            $game->store->battleItem = ObjectInit();
//        }
//
//        if (!StlUtil::dictHasProperty($game->newMap, 'huichengquanRecord')) {
//            $game->newMap->huichengquanRecord = ObjectInit();
//        }
//
//        if (is_array($game->newMap->huichengquanRecord)) {
//            $game->newMap->huichengquanRecord = ObjectInit();
//        }
//
//        $dixiachengId = 503001;
//        if (!StlUtil::dictHasProperty($game->newMap->unlockedFootholds, $dixiachengId)) {
//            $mo = GameConfig::gate_getItem($dixiachengId);
//            $footHold = new Ins_FootHold();
//            $footHold->mapId = $mo->gateId;
//            $footHold->curMapType = $mo->mapType;
//            $game->newMap->unlockedFootholds->$dixiachengId = $footHold;
//        }
//
//        foreach ($game->newMap->unlockedFootholds as $mapid => $val) {
//            if ($val->mapId != $mapid) {
//                $game->newMap->unlockedFootholds->$mapid->mapId = $mapid;
//                break;
//            }
//        }
//
//        if (!StlUtil::dictHasProperty($game->newMap, 'curFootholdId')) {
//            $game->newMap->curFootholdId = $game->newMap->curMapId;
//        }
//
//        if ($game->newMap->lastMapId == 0) {
//            $game->newMap->lastMapId = 503099;
//        }
//
//        if (!CommUtil::isPropertyExists($game->privateState, "buyTiliNum")) {
//            $game->privateState->buyTiliNum = 0;
//        }
//
//        if (!StlUtil::dictHasProperty($game->store, 'dailyTask_HandOver')) {
//            $game->store->dailyTask_HandOver = 0;
//        }
    }

    /**
     * 6003 领取连续登录奖励
     */
    public static function acceptContiDaysGift() {
        $resp = Resp::err(ErrCode::err_method_notimplement);

        return $resp;
    }

//
// <editor-fold defaultstate="collapsed" desc="    辅助方法    ">

    /**
     * 检查昵称是否已经存在
     * @param string $roleName
     * @return boolean
     */
    static function checkRoleNameNotExist($roleName) {
        return true;                                                            # 不再检查昵称重复
        static $sqlFormat = "SELECT count(*) as rows FROM `tab_rolename` WHERE roleName='%s';";
        $sql = sprintf($sqlFormat, $roleName);
        $n = daoInst()->query($sql)->fetch();
        return $n->rows <= 0;
    }

    /**
     * 插入玩家新角色
     *
     * @param string $zoneid
     * @param string $userID
     * @param string $nickname
     * @param string $gender
     * @param string $profile_img
     * @param string $plat
     */
    static function regRole($zoneid, $userID, $nickname, $gender, $profile_img, $plat) {
        return daoInst()->insert('tab_rolename')
                        ->data(array(
                            'zoneid' => $zoneid,
                            'userID' => $userID,
                            'roleName' => $nickname,
                            'gender' => $gender,
                            'profile' => $profile_img,
                            'plat' => $plat
                        ))->exec();
    }

    /**
     * 检测连续登录状态，重置必要字段[时间戳自动记录]
     */
    static function checkContidays($isnew = 0) {
        $ret = TimeUtil::totalDays() - TimeUtil::totalDays(ctx()->baseInfo->lastLogin); // 对比登录日期
        if ($ret > 0 || $isnew) {                                               # 当天第一次登录
            self::OnNewDay($isnew);
        } else {                                                                # 更新下登录次数记录(任务计数器)
        }
        if ($ret == 1) {                                                        # 连续登录
        } else if ($ret >= 2) {                                                 # 隔天登录
        }
        ctx()->baseInfo->lastLogin = now();                                     # 更新下访问时间

        TapDBUtil::SOnUserLogin();                                              # 向tapdb上报玩家登录 2023.5.10
        TaskProc::OnUserLogin();
        return $ret;
    }

    /**
     *  处理当天第一次登录
     * @param bool $isnew Description
     */
    static function OnNewDay($isnew) {
        self::updatePlatUserRecord($isnew);                                     # 添加到到当天活跃玩家记录集合
        ActiveProc::DailyReset();                                               # 抽奖、每日任务状态,计数器...
        FightProc::ClearByDay();                                                # 战斗相关状态每日清理
        ShopProc::DailyCheck();
        TaskProc::ResetDailyTaskCards();                                        # 重置每日任务卡
        StoreProc::DailyRefreshWeapon();                                        # 每日登录刷新武器商店
        //RankProc::paihangReward_fPower();                                       # 战力榜的排名定时发送奖励邮件
        //RankProc::paihangReward_level();                                        # 等级榜的排名定时发送奖励邮件
    }

// <editor-fold defaultstate="collapsed" desc="创建新用户">

    /**
     * 创建用户
     * @return Data_UserGame
     */
    static function createUser($rolename, $gender, $profile_img) {
        $game = new Data_UserGame();
        req()->game = $game;                                                    # 更新Req挂载的玩家数据,
        $game->initialize();                                                    # 初始化玩家数据
        $game->baseInfo->name = $rolename;
        $game->baseInfo->gender = $gender;
        $game->baseInfo->headImg = $profile_img;
        $game->baseInfo->firstLogin = now();

#Ps 6006是没有获得到Userinfo到Req中的
        UserProc::checkContidays(1);                                            # 每日状态检查
        UserProc::dataUpdate();
//        UserProc::fetchFromInteract($mem, $req);  # 从interact拉取数据，Ps.初始化的过程应该还拉取不到什么有效数据
        UserProc::updateUserInfo();                                             # 回存用户数据

        return $game;
    }

    /**
     * 整理平台玩家记录集
     * @param int $isnew
     */
    private static function updatePlatUserRecord($isnew = 0) {
        $zoneid = req()->zoneid;
        $uid = req()->uid;
        $user = ctx()->baseInfo;
        $day = totalDays();
        $level = $user->level;
        $platUser = ObjectInit();
        $platUser->uid = $uid;                                                  #
        $platUser->name = $user->name;                                          #
        $platUser->level = $level;
        $platUser->img = $user->headImg;                                            # 头像字段
        $platUser->cash = $user->cash;
        $platUser->gold = $user->gold;
        $platUser->tili = $user->tili;
        $platUser->ts = now();
        $platUser->isnew = $isnew;
        gMem()->delete(MemKey_GameRun::DailyLoginUser_byUID_hash($zoneid, $day - 108));
        gMem()->hset(MemKey_GameRun::DailyLoginUser_byUID_hash($zoneid), $uid, $platUser);
        gMem()->delete(MemKey_GameRun::DailyLoginUser_byLevel_hash($zoneid, $level, $day - 108));
        gMem()->hset(MemKey_GameRun::DailyLoginUser_byLevel_hash($zoneid, $level), $uid, $platUser);
    }

    private static function dataUpdate() {
        ctx()->privateState->bornPlace = 1;
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="读写玩家数据">

    /**
     * 取玩家数据
     * @param type $zoneid
     * @param type $uid
     * @return Data_UserGame
     */
    public static function getUserGame($zoneid, $uid) {
        $key = MemKey_User::Info_hash($zoneid, $uid);
//        $pf = req()->getPlatStr();
//        if ($pf == "tap") {                                        # taptap平台
//            $oldkey = MemKey_User::Info_hash($zoneid, req()->getPlatOid());
//            if (gMem()->exists($oldkey)) {
//                gMem()->rename($oldkey, $key);                                  # 做下数据迁移
//            }
//        }
        $a = new Data_UserGame();
        if (null == $a->readDataFromMem($key)) {                                # ps.下面这一段代码和经常删号会有冲突,因此关闭了 --gwang 2022.2.28
            $collection = "userInfoBack";
            $cursor = gMongo()->find($collection, ['key' => $key], ['sort' => array('ts' => -1), 'limit' => 1]); # 提取备份数据
            $cursor->rewind();
            if ($cursor->valid()) {
                $v = $cursor->current();
                $a->LoadFrom($v->value);                                    # 加载
                $a->updateDataFull($key);                                   # 反向写回redis
            } else {
                return null;
            }
        }
        return new Data_UserGame($a);
    }

    /**
     * 更新用户数据(设置标志位,最后统一更新-gwang 2017.07.18)
     */
    public static function updateUserInfo() {
        my_Assert(req(), "req()为空");
        my_Assert(req()->game, "[" . req()->cmd . "] 玩家数据正在被清空!" . req()->uid);
        req()->userInfoChanged = TRUE;                                          # 设置回写标志位
    }

    /**
     * 回写玩家数据
     * @param Data_UserGame $game
     */
    public static function setUserInfo($game) {
        $OK = false;
        if ($game) {
            $zoneid = req()->zoneid;
            $uid = req()->uid;
            $game->baseInfo->lastSaveTs = now();
            $key = MemKey_User::Info_hash($zoneid, $uid);
            $OK = $game->updateDataFull($key);                                  # 向Redis回写玩家数据
            if ($OK) {
//                CLog::info($msg);
                self::backupUserInfoMongo();                                    # 向MongoDB备份数据
                gMem()->expire($key, 3600);                                     # 设置过期时间1小时
            } else {
                // redo Logic
                CLog::err("写入数据时版本已过期!!!");
            }
        }
        return $OK;
    }

// </editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="玩家分区记录">

    /**
     * 读取玩家的分区记录
     * @return Data_UserZoneInfo Description
     */
    public static function getUserZoneInfo() {
        $ret = gMem()->get(MemKey_User::Union_PlayedZoneInfo_normal(req()->uid));
        return $ret;
    }

    /**
     * 更新玩家分区记录
     */
    public static function updtateUserZoneInfo() {
        $req = req();
        $userZoneInfo = self::getUserZoneInfo();                                # 取玩家分区记录
        if (!$userZoneInfo) {
            $userZoneInfo = new Data_UserZoneInfo;
        }
        $level = 0;
        $zoneid = $req->zoneid;
        $playerName = "";
        $headImg = "";
        if (null != ctx()) {                                                     # 防御确保玩家数据不为空
            $level = ctx()->baseInfo->level;
            $playerName = ctx()->baseInfo->name;
            $headImg = ctx()->baseInfo->headImg;
        } else {
            Err('玩家数据为空!' . __CLASS__ . '.' . __FUNCTION__);
        }
        if (is_null($level)) {
            $level = 0;
        }
        $userZoneInfo->lastZone = new Ins_ZoneInfo($zoneid, $level, $playerName, $headImg); # 更新玩家分区记录
        $userZoneInfo->playedZones->$zoneid = $userZoneInfo->lastZone;          # 玩过的分区集合
        gMem()->set(MemKey_User::Union_PlayedZoneInfo_normal($req->uid), $userZoneInfo); # 回写数据
    }

// </editor-fold>
// <editor-fold defaultstate="collapsed" desc=" 用户数据备份 ">

    /**
     * 备份玩家数据,(玩家数据落地),一天一份,当天记为最后一次登录时的状态而非最后一次操作的状态
     * @history
     *           version 3.0.13 mysql版备份玩家数据, (性能优化后表现还不错)
     *                      除非用脚本在redis中实现备份,否则直接写入mysql.
     *                      主要是mysql版本利用存储过程之后性能已经和redis版相差不多.
     *           version 2.0.0 Redis storage 从MySQL转到Redis中存储. 这个备份数据没有大规模导出硬盘的需求
     *                      因为mysql版本实在是负载太大了, 上百毫秒. 所以改在redis中了.
     *           version 1.0.0 Mysql storagef 1. 分表不做了, 2. 存储过程不用了 3. 先能用再说
     */
    public static function backupUserInfo() {
        $tsday = TimeUtil::totalDays();                                         # 精确到天,保留10个最近记录.
        $value = base64_encode(gzdeflate(JsonUtil::encode(ctx())));             # blob数据,序列化下
        $sql = sprintf("call insertUserInfo('%s', %d, %d, '%s');", #            # 所以直接将序列化后的结果存进去吧,
                req()->uid, req()->zoneid, $tsday, $value);                     # zoneid, uid, tsday
        daoInst()->exec($sql);                                                  # 也可以用exec($sql)
    }

    /**
     * 备份玩家数据,(玩家数据落地),一天一份,当天记为最后一次登录时的状态而非最后一次操作的状态
     * @history
     *           version 4.0.0 切换到MongoDB存储
     *           version 3.0.13 mysql版备份玩家数据, (性能优化后表现还不错)
     *                      除非用脚本在redis中实现备份,否则直接写入mysql.
     *                      主要是mysql版本利用存储过程之后性能已经和redis版相差不多.
     *           version 2.0.0 Redis storage 从MySQL转到Redis中存储. 这个备份数据没有大规模导出硬盘的需求
     *                      因为mysql版本实在是负载太大了, 上百毫秒. 所以改在redis中了.
     *           version 1.0.0 Mysql storagef 1. 分表不做了, 2. 存储过程不用了 3. 先能用再说
     */
    public static function backupUserInfoMongo() {
        $collectionName = "userInfoBack";                                       # 表名
        $key = MemKey_User::Info_hash(req()->zoneid, req()->uid);
        $doc = array('key' => $key, #                                           # 最新文档
            'ts' => TimeUtil::dtCurrent(), #                                    # 更新时间
            'stVer' => ctx()->stVer, #                                          # 增加版本号
            'value' => ctx());                                                  # 玩家数据
        $bok = gMongo()->insert($collectionName, $doc);                                # 插入备份
//        CLog::err($collectionName . "备份玩家数据" . req()->uid . ($bok ? "成功" : "失败"));
        if (ctx()->stVer % 100 == 1) {                                          # 每100条记录清理一次(delete耗时比较长-gwang.2023年3月10日)
            $delFilter = array('key' => $key, 'stVer' => ['$lt' => ctx()->stVer - 100]); # 保留最后一百次变更记录
            gMongo()->delete($collectionName, $delFilter);
        }
//        $filter = array('key' => $key);                                         # 指定条件
//        gMongo()->update($collectionName, $filter, $doc, true);                 # 更新
    }

// </editor-fold>
//</editor-fold>
//
// <editor-fold defaultstate="collapsed" desc="    统计信息    ">

    /**
     * 经验 金币 钻石 统计
     * @param type $cmd
     * @param type $cmdName
     * @param type $type
     * @param type $cur
     * @param type $change
     * @param type $end
     * @param type $desc
     */
    static function CollectUserBaseParam($cmd, $type, $curVal, $changeVal, $endVal, $desc) {
        $item = new \stdClass();
        $item->uid = req()->uid;
        $item->cmd = $cmd;
        $item->type = $type;
        $item->curVal = $curVal;
        $item->changeVal = $changeVal;
        $item->endVal = $endVal;
        $item->desc = $desc;
        $item->ts = time();

        $arr = array();
        $arr[] = $item;

        //$num = gMem()->rpush("userbaseParams-" . req()->uid . "-" . req()->zoneid, $arr);
        $num = gMem()->rpush("userbaseParams-" . req()->zoneid, $arr);
    }

    static function CollectUserLevelParam() {
        $uid = req()->uid;
        $level = ctx()->baseInfo->level;

        if ($level < 20) {
            return;
        }

        $rediskey = "userlevelLimitRecord";
        if (!gMem()->exists($rediskey)) {
            gMem()->lpush($rediskey, $uid);
        }

        $length = gMem()->llen($rediskey);
        if ($length > 20) {
            return;
        }
        $arr = gMem()->lrange($rediskey, 0, $length - 1);
        if (in_array($uid, $arr)) {
            return;
        }
        gMem()->lpush($rediskey, $uid);
    }

    static function CollectHeroLevelParam() {
        $dic = ctx()->heros->collectHeros;

        $tag = false;
        foreach ($dic as $uid => $val) {
            if ($val->level >= 20) {
                $tag = true;
                break;
            }
        }

        if (!$tag) {
            return;
        }

        $rediskey = "herolevelLimitRecord";
        $userUid = req()->uid;

        if (!gMem()->exists($rediskey)) {
            gMem()->lpush($rediskey, $userUid);
        }

        $length = gMem()->llen($rediskey);
        if ($length > 20) {
            return;
        }

        $arr = gMem()->lrange($rediskey, 0, $length - 1);
        if (in_array($userUid, $arr)) {
            return;
        }

        gMem()->lpush($rediskey, $userUid);
    }

//</editor-fold>
}
