<?php
/**
 * 房间
 */
namespace Game\Model;

use Framework\Lib\Utils;
use Framework\Log\LogMark;
use Framework\Network\SendMessage;
use Framework\DB\Handler\PlayerDBHandler;
use Game\Constant\ClientErrorCode;
use Game\Constant\ConstTemplate\TemplateConst;
use Game\Constant\DBTableDefine;
use Game\Constant\GameConstantDefine;
use Game\Constant\GameErrorCode;
use Game\Constant\TemplateDefine;
use Game\Data\JoinIdData;
use Game\Data\PaiWei\PaiweiLevelData;
use Game\Data\RoomData;
use Game\Logic\AccountLogic;
use Game\Logic\Pvp\PvpMatchLogic;
use Game\Protobuf\ClothData;
use Game\Protobuf\GCExitRoom;
use Game\Protobuf\GCJoinRoom;
use Game\Protobuf\GCOwnerRemovePlayer;
use Game\Protobuf\GCRankChangeCarExteriorRefit;
use Game\Protobuf\GCRoomInfo;
use Game\Protobuf\GCSyncPlayerInfo;
use Game\Protobuf\GCRankChangeCar;
use Game\Protobuf\GCUpdatePlayerCar;
use Game\Protobuf\GCUpdatePlayerState;
use Game\Protobuf\GCUpdateRoomMapID;
use Game\Protobuf\GCUpdateRoomModeType;
use Game\Protobuf\GCUpdateRoomName;
use Game\Protobuf\GCUpdateRoomPlayer;
use Game\Protobuf\GCUpdateRoomState;
use Game\Protobuf\PacketId;
use Game\Protobuf\RoomPlayer;
use Framework\Logic\PacketCacheLogic;
use Game\Data\AccountData;

class RoomModel {
    use PlayerDBHandler;
    use PacketCacheLogic;
    use PvpMatchLogic;
    use AccountLogic;

    public const MODE_OPEN                 = 0;  // 模式开启
    public const ROOM_STATE_PRIVATE        = 1;  // 私人
    public const ROOM_STATE_PUBLIC         = 2;  // 公开

    public const ROOM_GAME_READY         = 1;  // 准备中 （可加入）
    public const ROOM_GAME_MATCH_OR_PLAY = 2;  // 匹配中 & 游戏中 (不可加入)

    public const ROOM_PLAY_NOT_READY     = 1;  // 玩家未准备
    public const ROOM_PLAY_READY         = 2;  // 玩家已准备

    public const ROOM_NAME_SUFFIX        = "的房间"; // 房间名称后缀

    public const ROOM_ID_MIN             = 100000;
    public const ROOM_ID_MAX             = 999999;
    public const COMMON_CONST_ROOM_ID    = 'RoomID';

    public const CHECK_ROOM_TIME         = 600; // 检测大于五分钟的房间进行处理

    public const ROOM_LUA_PLAYER_EXISTS      = 5001;  // 玩家已存在
    public const ROOM_LUA_ROOM_NOT_EXISTS    = 5002;  // 房间不存在
    public const ROOM_LUA_ROOM_IS_MAX        = 5003;  // 房间已满员
    public const ROOM_LUA_PLAYER_NOT_EXISTS  = 5004;  // 玩家不存在
    public const ROOM_LUA_ROOM_EXISTS        = 5005;  // 房间已存在

    public const MAP_RANDOM              = 1; // 随机地图标识

    public  int                   $playerId;
    public  GCJoinRoom            $joinRoomMsg;
    public  GCOwnerRemovePlayer   $removePlayer;
    public  GCExitRoom            $exitRoom;
    public  GCUpdatePlayerCar     $updateCar;
    public  GCRankChangeCar       $rankChangeCar;
    public  GCRankChangeCarExteriorRefit       $rankChangeCarER;
    private GCUpdateRoomPlayer $updatePlayer;

    public function __construct() {
        $this->joinRoomMsg   = new GCJoinRoom();
        $this->removePlayer  = new GCOwnerRemovePlayer();
        $this->exitRoom      = new GCExitRoom();
        $this->updateCar     = new GCUpdatePlayerCar();
        $this->rankChangeCar = new GCRankChangeCar();
        $this->rankChangeCarER = new GCRankChangeCarExteriorRefit();
        $this->updatePlayer = new GCUpdateRoomPlayer();
    }

    public function setPlayerId($playerId)
    {
        $this->playerId = $playerId;
    }

    public function getPlayerID(): int {
        return $this->playerId;
    }

    public function onLoginExec() {
        if(!$this->searchOldRoomExit($this->playerId)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] login exit room error!'
            );
        }
        // 玩家roomID重置
        $this->savePlayerRoomID(AccountData::DB_ROOM_ID_DEFAULT);
    }

    //生成RoomData
    public function makeRoomData(int $roomId, int $mapID, int $modeType, string $roomName, int $roomState): ?RoomData
    {
        //需要显示的玩家信息和赛车信息
        $showPlayer = $this->getMatchShowPlayer($this->playerId);
        $showCar = $this->getMatchShowCar($showPlayer[RoomData::CAR_TPL_ID]);
        if (empty($showCar)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] create room error!,not found default car',
                array(
                    "carId" => $showPlayer[AccountData::DB_DEFAULT_CAT_TPL]
                )
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ERROR);
            return null;
        }
        $playerInfo = [
            $this->playerId => [
                RoomData::PLAYER_ID   => (string)$this->playerId,
                RoomData::CAR_TPL_ID  => $showPlayer[RoomData::CAR_TPL_ID],
                RoomData::JOIN_TIME   => Utils::getServerTimestamp(),
                RoomData::READY_STATE => self::ROOM_PLAY_NOT_READY,
                //赛车信息
                RoomData::EXTERIOR_REFIT => $showCar[RoomData::EXTERIOR_REFIT],
                RoomData::PROFICIENCY_LV => $showCar[RoomData::PROFICIENCY_LV],
                RoomData::PROFICIENCY => $showCar[RoomData::PROFICIENCY],
                RoomData::CAR_LV => $showCar[RoomData::CAR_LV],
                //玩家信息
                RoomData::GENDER => $showPlayer[RoomData::GENDER],
                RoomData::CLOTH => $showPlayer[RoomData::CLOTH],
                RoomData::HEAD => $showPlayer[RoomData::HEAD],
                RoomData::NICK_NAME => $showPlayer[RoomData::NICK_NAME],
                RoomData::POSITION => $showPlayer[RoomData::POSITION],
                RoomData::SCUFFLE_SCORE => $showPlayer[RoomData::SCUFFLE_SCORE],
                RoomData::CONTINUITY_WIN => $showPlayer[RoomData::CONTINUITY_WIN],
                AccountData::DB_SKIN_COLOR => $showPlayer[AccountData::DB_SKIN_COLOR],
                RoomData::PLAYER_LEVEL => $showPlayer[RoomData::PLAYER_LEVEL],
                RoomData::PLAYER_DAN => $showPlayer[RoomData::PLAYER_DAN],
            ]
        ];
        return new RoomData(
            $roomId,
            $this->playerId,
            $roomName,
            $roomState,
            $mapID,
            $modeType,
            $playerInfo,
            self::ROOM_GAME_READY,
        );
    }

    // 创建房间
    public function createRoom(int $mapID, int $modeType, string $roomName, int $roomState): bool {
        $room = $this->makeRoomData($this->makeRoomID(), $mapID, $modeType, $roomName, $roomState);
        if (is_null($room)) {
            return false;
        }
        $ret = $room->playerCreateRoomDB(
            $this->playerId,
            $room->playerInfo[$this->playerId][RoomData::CAR_TPL_ID]
        );

        if($ret == self::ROOM_LUA_ROOM_EXISTS) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] create room bug room exists error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_EXISTS);
            return false;
        }

        if($ret != 0) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] create room error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ERROR);
            return false;
        }

        // 检测room并退出
        if(!$this->searchOldRoomExit($this->playerId)) {
            $this->GCJoinRoom(ClientErrorCode::ERROR_EXIT_OLD_ROOM_FAILED);
            return false;
        }

        // 保存加入的roomID
        $this->savePlayerRoomID($room->roomID);

        $this->GCJoinRoom(ClientErrorCode::CLIENT_SUCCESS, $room);

        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ROOM, $this->playerId);
        return true;
    }

    public function playerJoinRoom(RoomData $room, int $invitedPlayer = 0): bool {
        //需要显示的玩家信息和赛车信息
        $showPlayer = $this->getMatchShowPlayer($this->playerId);
        $showCar = $this->getMatchShowCar($showPlayer[RoomData::CAR_TPL_ID]);
        if (empty($showCar)) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_CREATE_ERROR,
                '[roomModel] playerJoinRoom error!,not found default car',
                array(
                    "carId" => $showPlayer[AccountData::DB_DEFAULT_CAT_TPL]
                )
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ERROR);
            return false;
        }

        $roomMax = $this->getTerm(TemplateDefine::TYPE_CONST, TemplateConst::Const_Room_Max_Num, "Const_num");

        $ret = $room->playerJoinRoomDB($roomMax, $this->playerId, $showPlayer, $showCar);

        // 房间不存在
        if($ret == self::ROOM_LUA_ROOM_NOT_EXISTS) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[RoomModel] room not exists error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_NOT_EXIST);
            return false;
        }
        // 已经在房间中 同步给其他人 进入时间刷新
        if($ret == self::ROOM_LUA_PLAYER_EXISTS) {
            LogMark::getInstance()->markInfo(
                '[RoomModel] player exists in room error!'
            );
            foreach($room->playerInfo as $pid => $item) {
                if($pid == $this->playerId) {
                    $item[RoomData::JOIN_TIME] = Utils::getServerTimestamp();
                }
            }
            $room->saveDB();
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_PLAYER_IS_EXIST, $room);
            $this->GCSendAddJoinRoom();
            return false;
        }

        if($ret == self::ROOM_LUA_ROOM_IS_MAX) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] room num is max !', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_IS_MAX_NOT_JOIN);
            return false;
        }

        if($ret != 0) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] update room error!', (array)$room
            );
            $this->GCJoinRoom(ClientErrorCode::ERROR_ROOM_JOIN_ERROR);
            return false;
        }

        // 检测room并退出
        if(!$this->searchOldRoomExit($this->playerId)) {
            $this->GCJoinRoom(ClientErrorCode::ERROR_EXIT_OLD_ROOM_FAILED);
            return false;
        }

        // 保存加入的roomID
        $this->savePlayerRoomID($room->roomID);

        $room->searchRoomByRoomID();
        //记录邀请信息
        $this->addInvitedPlayer($room, $invitedPlayer);
        // 通知客户端加入成功
        $this->GCJoinRoom(ClientErrorCode::CLIENT_SUCCESS, $room);
        $this->upRoleStatus(GameConstantDefine::PLAYER_STATUS_ROOM, $this->playerId);
        return true;
    }

    public function searchOldRoomExit($playerID = 0):bool {
        // 检测玩家是否在房间内
        if(isset($this->searchPlayerInfo($playerID)[AccountData::DB_ROOM_ID])) {
            $oldRoomID = $this->searchPlayerInfo($playerID)[AccountData::DB_ROOM_ID];
            $oldRoom = $this->checkPlayerInRoom($oldRoomID);
            if(!is_null($oldRoom)) {
                if(!$this->exitRoom($oldRoom, $playerID)) {
                    LogMark::getInstance()->markError(
                        GameErrorCode::DATA_UPDATE_ERROR,
                        '[roomModel] exit old room  error!', (array)$oldRoom
                    );
                }
                $this->GCSendAddExitRoom($oldRoom->playerInfo, $oldRoomID);
            }
        }
        return true;
    }

    public function checkPlayerInRoom(int $roomID):?RoomData {
        if($roomID != AccountData::DB_ROOM_ID_DEFAULT) {
            // 退出房间
            $oldRoom = $this->newRoom($roomID);
            if(!$oldRoom->searchRoomByRoomID()) {
                return null;
            }
            return $oldRoom;
        }
        return null;
    }

    public function exitRoom(RoomData $room, $playerID = 0):bool {

        if($playerID != 0) {
            $this->playerId = $playerID;
        }
        // 检查移除邀请信息
        $this->removeInvitedPlayer($room, $this->playerId);

        $ret = $room->playerExitRoomDB($room->roomID, $this->playerId);

        if($ret == self::ROOM_LUA_ROOM_NOT_EXISTS) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[room model] lua room not exists error!', (array)$room
            );
            $this->GCExitRoom(ClientErrorCode::ERROR_ROOM_NOT_EXIST, $room->roomID);
            return false;
        }
        if($ret == self::ROOM_LUA_PLAYER_NOT_EXISTS) {
            LogMark::getInstance()->markDebug(
                '[room model] lua player not exists error!', (array)$room
            );
            $this->GCExitRoom(ClientErrorCode::ERROR_ROOM_PLAYER_NOT_EXIST, $room->roomID);
            return false;
        }

        if($ret != 0) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[room model] lua exit room error!', (array)$room
            );
            $this->GCExitRoom(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED, $room->roomID);
            return false;
        }

        // 玩家roomID重置
        $this->savePlayerRoomID(AccountData::DB_ROOM_ID_DEFAULT);        // 通知客户端退出成功
        $this->GCExitRoom(ClientErrorCode::CLIENT_SUCCESS, $room->roomID,$this->playerId);
        // 清空joinId
        $joinIdData = new JoinIdData($this->playerId);
        $joinIdData->joinId = 0;
        $joinIdData->saveJoinId();
        LogMark::getInstance()->markDebug(
            "[RoomModel] exitRoom",
            array(
                "roomId" => $room->roomID,
                "playerId" => $this->playerId
            )
        );
        return true;
    }

    public function removeRoomPlayer(RoomData $room, int $playerID): bool {
        if(!isset($room->playerInfo[$playerID])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_SEARCH_ERROR,
                '[RoomModel] remove player but room not exist!', (array)$room
            );
            $this->GCRemovePlayer(ClientErrorCode::ERROR_ROOM_PLAYER_NOT_EXIST);
            return false;
        }
        unset($room->playerInfo[$playerID]);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] remove player save error!', (array)$room
            );
            $this->GCRemovePlayer(ClientErrorCode::ERROR_ROOM_OPERATE_FAILED);
            return false;
        }
        // 玩家roomID重置
        $this->savePlayerRoomID(AccountData::DB_ROOM_ID_DEFAULT, $playerID);
        $this->GCRemovePlayer(ClientErrorCode::CLIENT_SUCCESS, $playerID);
        // 检查移除邀请信息
        $this->removeInvitedPlayer($room, $playerID);
        return true;
    }

    // 通过分数范围查找房间号
    public function getRoomListByModeAndScore(int $sScore, int $eScore):array {
        $result = array();
        if(!$this->searchRevRankByIndex(DBTableDefine::TABLE_ROOM,0, $sScore, $eScore, $result)) {
            return array();
        }
        return $result;
    }

    //获取当前离线的玩家
    public function getCheckRoomByScore(&$ret): bool
    {
        $now = Utils::getServerTimestamp();
        $start = 1;
        $end = $now - self::CHECK_ROOM_TIME;
        if (false === $this->searchRank(
                DBTableDefine::TABLE_ROOM_TIME,
                0,
                $start,
                $end,
                $ret
            )){
            LogMark::getInstance()->markError(
                GameErrorCode::ROOM_SEARCH_OFFLINE_ERROR,
                "[RoomModel] search off-line error!",
            );
            return false;
        }
        return true;
    }

    // 创建房间id
    private function makeRoomID():int {
        $roomID = 0;
        $this->autoAddTermByRoomID(
            $roomID,
            DBTableDefine::TABLE_COMMON_CONST,
            0,
            self::COMMON_CONST_ROOM_ID,
            1
        );
        if($roomID < self::ROOM_ID_MIN || $roomID > self::ROOM_ID_MAX) {
            $this->updateTitle(
                DBTableDefine::TABLE_COMMON_CONST,
                0,
                0,
                [self::COMMON_CONST_ROOM_ID => self::ROOM_ID_MIN]
            );
            $roomID = self::ROOM_ID_MIN;
        }

        // roomID 判断是否已经存在 存在则查询出房间 退出所有玩家
        $room = $this->newRoom($roomID);
        if($room->searchRoomByRoomID()) {
            $playerList = $room->playerInfo;
            if(!empty($playerList)) {
                foreach($playerList as $pid => $info) {
                    $this->searchOldRoomExit($pid);
                    $this->savePlayerRoomID(AccountData::DB_ROOM_ID_DEFAULT, $pid);
                }
            }
        }
        return $roomID;
    }

    public function newRoom(int $roomID): RoomData {
        return new RoomData($roomID);
    }

    // 保存地图
    public function saveRoomMap(RoomData $room, int $mapID): bool {
        $room->setMapID($mapID);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] update room map error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 房主更换模式
    public function saveRoomMode(RoomData $room, int $mode): bool {
        $room->setModeType($mode);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] update room mode error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 房主更换房间名称
    public function saveRoomName(RoomData $room, string $name): bool {
        $room->setRoomName($name);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] update room name error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 房主更换房间状态 私人/公开
    public function saveRoomState(RoomData $room, int $state): bool {
        $room->setRoomState($state);
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] update room state error!', (array)$room
            );
            return false;
        }

        return true;
    }

    // 玩家换车
    public function savePlayerCar(RoomData $room, int $carID, int $carUID): bool {
        $showCar = $this->getMatchShowCar($carID);
        if (empty($showCar)) {
            LogMark::getInstance()->markDebug(
                '[RoomModel] player car not found!',
                array(
                    "carId" => $carID
                )
            );
            return false;
        }
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] save car but room not found player info!'
            );
            return false;
        }
        $room->setPlayerCar($players[$this->playerId], $carID, $showCar);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] update player car error!', (array)$room
            );
            return false;
        }
        $this->GCAddChangePlayerCar(ClientErrorCode::CLIENT_SUCCESS, $this->playerId, $carID, $showCar);
        return true;
    }

    //保存房间内玩家服装
    public function savePlayerCloth(RoomData $room): bool {
        $showPlayer = $this->getMatchShowPlayer($this->playerId);
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] save car but room not found player info!'
            );
            return false;
        }
        $room->setPlayerCloth($players[$this->playerId], $showPlayer[RoomData::CLOTH]);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] update player cloth error!', (array)$room
            );
            return false;
        }
        return true;
    }

    // 团队排位玩家换车
    public function saveTeamRankCar(RoomData $room, int $carID, int $carUID): bool {
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] save car but room not found player info!'
            );
            return false;
        }
        $showCar = $this->getMatchShowCar($carID);
        if (empty($showCar)) {
            LogMark::getInstance()->markDebug(
                '[RoomModel] player car not found!',
                array(
                    "carId" => $carID
                )
            );
            return false;
        }
        $room->setPlayerCar($players[$this->playerId], $carID, $showCar);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] team rank update player car error!', (array)$room
            );
            return false;
        }
        $this->GCAddRankChangeCar(ClientErrorCode::CLIENT_SUCCESS, $this->playerId, $carID, $showCar);
        return true;
    }

    // 玩家更换状态
    public function savePlayerState(RoomData $room, int $state): bool {
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] save state but room not found player info!'
            );
            return false;
        }
        $room->setPlayerState($players[$this->playerId], $state);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] update player state error!', (array)$room
            );
            return false;
        }
        return true;
    }

    //更新玩家显示信息和状态
    public function updatePlayerShowAndState(RoomData $room, array $showPlayer, int $state): bool {
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] save state but room not found player info!'
            );
            return false;
        }
        $room->setPlayerState($players[$this->playerId], $state);
        $room->setPlayerShow($players[$this->playerId], $showPlayer);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomModel] update player state error!', (array)$room
            );
            return false;
        }
        return true;
    }

    //--------------------------------------- GC MSG ----------------------------------------------------------//

    public function GCJoinRoom(int $code, RoomData $roomData = null) {
        $this->joinRoomMsg->setCode($code);
        if(!is_null($roomData)) {
            $room = new GCRoomInfo();
            $players = [];   // 单条玩家数据
            $room->setRoomID($roomData->roomID)
                ->setRoomName($roomData->roomName)
                ->setMapID($roomData->mapID)
                ->setModeType($roomData->modeType)
                ->setRoomState($roomData->roomState)
                ->setPlayType($roomData->playType)
                ->setChatID($roomData->chatID);
            $this->joinRoomMsg->setRoom($room);
            foreach($roomData->playerInfo as $id => $data) {
                $tmp = new RoomPlayer();
                $clothData = new ClothData();
                $clothData->mergeFromJsonString($data[RoomData::CLOTH]);
                $tmp->setPlayerID($data[RoomData::PLAYER_ID])
                    ->setCarTplID($data[RoomData::CAR_TPL_ID])
                    ->setJoinTime($data[RoomData::JOIN_TIME])
                    ->setState($data[RoomData::READY_STATE])
                    ->setHead($data[RoomData::HEAD])
                    ->setNickName($data[RoomData::NICK_NAME])
                    ->setGender($data[RoomData::GENDER])
                    ->setExteriorRefit($data[RoomData::EXTERIOR_REFIT])
                    ->setCloth($clothData)
                    ->setSkinColor($data[AccountData::DB_SKIN_COLOR])
                    ->setProficiencyLv($data[RoomData::PROFICIENCY_LV])
                    ->setProficiency($data[RoomData::PROFICIENCY])
                    ->setPosition($data[RoomData::POSITION])
                    ->setScuffleScore($data[RoomData::SCUFFLE_SCORE])
                    ->setContinuityWin($data[RoomData::CONTINUITY_WIN])
                    ->setCarLv($data[RoomData::CAR_LV])
                    ->setLevel($data[RoomData::PLAYER_LEVEL])
                    ->setDan($data[RoomData::PLAYER_DAN]);
                $players[] = $tmp;
            }
            $this->joinRoomMsg->setPlayers($players);
            //修改玩家的状态

        }
    }

    public function GCSendJoinRoom() {
        if(!empty($this->joinRoomMsg)) {
            SendMessage::getInstance()->sendClient(PacketId::GC_JoinRoom, $this->joinRoomMsg);
        }
    }

    public function GCSendAddJoinRoom() {
        $players = $this->joinRoomMsg->getPlayers();
        $syncPlayer = new GCSyncPlayerInfo();
        $syncPlayer->setCode(ClientErrorCode::CLIENT_SUCCESS);

        foreach($players as $p) {
            if($p->getPlayerID() == $this->playerId) {
                $syncPlayer->setPlayer($p);
            }
        }
        foreach ($players as $p) {
            $this->addPacket(PacketId::GC_GCSyncPlayerInfo,  $syncPlayer, $p->getPlayerID());
        }
    }

    public function GCRemovePlayer(int $code, int $playerID = 0) {
        $this->removePlayer->setCode($code);
        if($playerID != 0) {
            $this->removePlayer->setPlayerID($playerID);
        }
    }

    public function GCSendRemovePlayer() {
        if(!empty($this->removePlayer)) {
            SendMessage::getInstance()->sendClient(PacketId::GC_OwnerRemovePlayer, $this->removePlayer);
        }
    }

    public function GCSendAddRemovePlayer(array $players) {
        $this->removePlayer->setCode(ClientErrorCode::CLIENT_SUCCESS);
        if(!empty($players)) {
            foreach ($players as $id => $p) {
                if($id == $this->playerId) {
                    continue;
                }
                $this->removePlayer->setPlayerID($this->removePlayer->getPlayerID());
                $this->addPacket(PacketId::GC_OwnerRemovePlayer,  $this->removePlayer, $id);
            }
        }
    }

    public function GCUpdateRoomMap(int $code) {
        $roomMsg = new GCUpdateRoomMapID();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateRoomMapID, $roomMsg);
    }

    public function GCAddUpdateRoomMap(int $code, int $mapID, int $playerID) {
        $roomMsg = new GCUpdateRoomMapID();
        $roomMsg->setCode($code);
        $roomMsg->setMapID($mapID);
        $this->addPacket(PacketId::GC_UpdateRoomMapID, $roomMsg, $playerID);
    }

    public function GCUpdateRoomMode(int $code, $mode = 0) {
        $roomMsg = new GCUpdateRoomModeType();
        $roomMsg->setCode($code);
        $roomMsg->setModeType($mode);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateRoomModeType, $roomMsg);
    }

    public function GCAddUpdateRoomMode(int $code, int $mode, int $playerID) {
        $roomMsg = new GCUpdateRoomModeType();
        $roomMsg->setCode($code);
        $roomMsg->setModeType($mode);
        $this->addPacket(PacketId::GC_UpdateRoomModeType, $roomMsg, $playerID);
    }

    public function GCUpdateRoomName(int $code) {
        $roomMsg = new GCUpdateRoomName();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateRoomName, $roomMsg);
    }

    public function GCAddUpdateRoomName(int $code, string $name, int $playerID) {
        $roomMsg = new GCUpdateRoomName();
        $roomMsg->setCode($code);
        $roomMsg->setRoomName($name);
        $this->addPacket(PacketId::GC_UpdateRoomName, $roomMsg, $playerID);
    }

    public function GCUpdateRoomState(int $code) {
        $roomMsg = new GCUpdateRoomState();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdateRoomState, $roomMsg);
    }

    public function GCAddUpdateRoomState(int $code, int $state, int $playerID) {
        $roomMsg = new GCUpdateRoomState();
        $roomMsg->setCode($code);
        $roomMsg->setState($state);
        $this->addPacket(PacketId::GC_UpdateRoomState, $roomMsg, $playerID);
    }

    public function GCChangePlayerCar(int $code) {
        $roomMsg = new GCUpdatePlayerCar();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdatePlayerCar, $roomMsg);
    }

    public function GCAddChangePlayerCar(int $code, int $changePlayerID,int $carID, array $showCar) {
        $this->updateCar->setCode($code);
        $this->updateCar->setPlayerID($changePlayerID);
        $this->updateCar->setCarTplID($carID);
        $this->updateCar->setExteriorRefit($showCar[RoomData::EXTERIOR_REFIT]);
        $this->updateCar->setProficiencyLv($showCar[RoomData::PROFICIENCY_LV]);
        $this->updateCar->setProficiency($showCar[RoomData::PROFICIENCY]);
        $this->updateCar->setCarLv($showCar[RoomData::CAR_LV]);
    }

    public function GCSendAddChangePlayerCar(int $playerID) {
        if(!empty($this->updateCar)) {
            $this->addPacket(PacketId::GC_UpdatePlayerCar, $this->updateCar, $playerID);
        }
    }

    //通知自己换车状态
    public function GCRankChangeCar(int $code) {
        $roomMsg = new GCRankChangeCar();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_RankChangeCar, $roomMsg);
    }

    public function GCAddRankChangeCar(int $code, int $changePlayerID,int $carID, array $showCar) {
        $this->rankChangeCar->setCode($code);
        $this->rankChangeCar->setPlayerID($changePlayerID);
        $this->rankChangeCar->setCarTplID($carID);
        $this->rankChangeCar->setExteriorRefit($showCar[RoomData::EXTERIOR_REFIT]);
        $this->rankChangeCar->setProficiencyLv($showCar[RoomData::PROFICIENCY_LV]);
        $this->rankChangeCar->setProficiency($showCar[RoomData::PROFICIENCY]);
        $this->rankChangeCar->setCarLv($showCar[RoomData::CAR_LV]);
    }

    //通知其他人换车
    public function GCSendRankCar(int $playerID) {
        if(!empty($this->rankChangeCar)) {
            $this->addPacket(PacketId::GC_RankChangeCar, $this->rankChangeCar, $playerID);
        }
    }

    public function GCRankChangeCarER(int $code) {
        $roomMsg = new GCRankChangeCarExteriorRefit();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_RankChangeCarExteriorRefit, $roomMsg);
    }

    public function GCAddRankChangeCarER(int $code, int $playerID, string $exteriorRefit) {
        $this->rankChangeCarER->setCode($code);
        $this->rankChangeCarER->setPlayerID($playerID);
        $this->rankChangeCarER->setExteriorRefit($exteriorRefit);
    }

    public function GCSendRankCarER(int $playerID) {
        if(!empty($this->rankChangeCarER)) {
            $this->addPacket(PacketId::GC_RankChangeCarExteriorRefit, $this->rankChangeCarER, $playerID);
        }
    }

    public function GCChangePlayerState(int $code) {
        $roomMsg = new GCUpdatePlayerState();
        $roomMsg->setCode($code);
        SendMessage::getInstance()->sendClient(PacketId::GC_UpdatePlayerState, $roomMsg);
    }

    public function GCAddChangePlayerState(int $code, int $changePlayerID, int $state, int $playerID) {
        $roomMsg = new GCUpdatePlayerState();
        $roomMsg->setCode($code);
        $roomMsg->setPlayerID($changePlayerID);
        $roomMsg->setState($state);
        $this->addPacket(PacketId::GC_UpdatePlayerState, $roomMsg, $playerID);
    }

    public function GCExitRoom(int $code, int $roomID = 0, int $exitID = 0) {
        $this->exitRoom->setCode($code);
        $this->exitRoom->setRoomID($roomID);
        if($exitID != 0) {
            $this->exitRoom->setPlayerID($exitID);
        }
    }

    public function GCSendExitRoom() {
        if(!empty($this->exitRoom)) {
            SendMessage::getInstance()->sendClient(PacketId::GC_ExitRoom, $this->exitRoom);
        }
    }

    public function GCSendAddExitRoom(array $players, $roomID) {
        $this->exitRoom->setCode(ClientErrorCode::CLIENT_SUCCESS);
        $this->exitRoom->setRoomID($roomID);
        if(!empty($players)) {
            foreach ($players as $id => $p) {
                if($id == $this->playerId) {
                    continue;
                }
                $this->exitRoom->setPlayerID($this->exitRoom->getPlayerID());
                $this->addPacket(PacketId::GC_ExitRoom,  $this->exitRoom, $id);
            }
        }
    }

    public function getMyInvitedInfo(int $roomId, $playerId): array
    {
        $room = new RoomData($roomId);
        if (!$room->searchRoomByRoomID()) {
            return array();
        }
        $invitedInfo = json_decode($room->invitedInfo1, true);
        return $invitedInfo[(string)$playerId] ?? array();
    }

    public function getMyInvitedInfo2(int $roomId, $playerId): int
    {
        $room = new RoomData($roomId);
        if (!$room->searchRoomByRoomID()) {
            return 0;
        }
        $invitedInfo = json_decode($room->invitedInfo2, true);
        return $invitedInfo[(string)$playerId] ?? 0;
    }

    /**
     * 添加邀请信息
     * @param RoomData $room
     * @param int $invitedPlayerId 发送邀请的人
     * $playerId 被邀请人
     */
    private function addInvitedPlayer(RoomData $room, int $invitedPlayerId)
    {
        if ($invitedPlayerId == 0) {
            return;
        }
        $invited2 = json_decode($room->invitedInfo2, true);
        $invited2[$this->playerId] = $invitedPlayerId;

        $invited1 = json_decode($room->invitedInfo1, true);
        if (isset($invited1[$invitedPlayerId])) {
            array_push($invited1[$invitedPlayerId], $this->playerId);
        } else {
            $invited1[$invitedPlayerId] = array($this->playerId);
        }
        $room->invitedInfo1 = json_encode($invited1);
        $room->invitedInfo2 = json_encode($invited2);
        $room->saveInvitedInfo();
    }

    /**
     * 移除被邀请信息
     * @param RoomData $room
     * @param int $leavePlayerId 离开房间的人
     */
    private function removeInvitedPlayer(RoomData $room, int $leavePlayerId)
    {
        $invited2 = json_decode($room->invitedInfo2, true);
        if (isset($invited2[strval($leavePlayerId)])) {
            $targetPlayerId = strval($invited2[$leavePlayerId]);
            unset($invited2[strval($leavePlayerId)]);
            shuffle($invited2);
            $invited1 = json_decode($room->invitedInfo1, true);
            if (count($invited1[$targetPlayerId]) == 1) {
                unset($invited1[$targetPlayerId]);
                shuffle($invited1);
            } else {
                $idx = array_search($leavePlayerId, $invited1[$targetPlayerId]);
                unset($invited1[$targetPlayerId][$idx]);
                shuffle($invited1[$targetPlayerId]);
            }
            $room->invitedInfo1 = json_encode($invited1);
            $room->invitedInfo2 = json_encode($invited2);
            $room->saveInvitedInfo();
        }
    }

    //更新玩家选择位置
    public function updateRoomPlayerPosition(RoomData $room, int $position): bool
    {
        $players = $room->playerInfo;
        if(!isset($players[$this->playerId])) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomLogic] room not found player info!'
            );
            return false;
        }
        $room->setPlayerPosition($players[$this->playerId], $position);
        $room->playerInfo = $players;
        if(!$room->saveDB()) {
            LogMark::getInstance()->markError(
                GameErrorCode::DATA_UPDATE_ERROR,
                '[RoomLogic] update player error!',
                (array)$room
            );
            return false;
        }
        $this->addGCUpdateRoomPlayerMessage($this->playerId, $players[$this->playerId]);
        return true;
    }

    //更新玩家数据
    private function addGCUpdateRoomPlayerMessage(int $playerId, array $roomPlayer)
    {
        $this->updatePlayer->setPlayerID($playerId);
        $this->updatePlayer->setPosition($roomPlayer[RoomData::POSITION]);
    }

    public function addPacketGCUpdateRoomPlayerMessage(array $players)
    {
        foreach ($players as $pid => $pInfo) {
            if($pid == $this->playerId) {
                continue;
            }
            $this->addPacket(PacketId::GC_UpdateRoomPlayer,  $this->updatePlayer, $pid);
        }
    }
}