public async Task OnHotelBroadCast()
        {
            try
            {
                SendMessage send = new SendMessage
                {
                    UserID = 11,
                    Msg    = 1,
                };
                HotelBroadcast broadcast = new HotelBroadcast()
                {
                    UserID = 11,
                    GameID = 12,
                    RoomID = 13,
                    //CpProto = Google.Protobuf.ByteString.CopyFromUtf8("kickplayer|1588649253385801743,200945,200945"),
                    //CpProto = Google.Protobuf.ByteString.CopyFromUtf8(JsonConvert.SerializeObject(send)),
                    CpProto = Google.Protobuf.ByteString.CopyFromUtf8("getRoomDetail|158864,200945"),
                };
                Package.Types.Frame message = new Package.Types.Frame()
                {
                    Type     = Package.Types.FrameType.HotelServer,
                    Version  = 2,
                    CmdId    = (UInt32)HotelGsCmdID.HotelBroadcastCmdid,
                    UserId   = 1001,
                    Reserved = 100,
                    Message  = ObjectToByteString(broadcast)
                };
                using (var call = client.Stream(new Metadata {
                    { "ctx", "ctx" }, { "userid", "17" }
                }))
                {
                    var responseReaderTask = Task.Run(async() =>
                    {
                        while (await call.ResponseStream.MoveNext())
                        {
                            Package.Types.Frame note       = call.ResponseStream.Current;
                            HotelBroadcastAck broadcastAck = new HotelBroadcastAck();
                            ByteStringToObject(broadcastAck, note.Message);

                            Console.WriteLine("OnHotelBroadCast:receve msg userID:" + broadcastAck.UserID + " status:" + broadcastAck.Status);
                        }
                        Console.WriteLine("OnHotelBroadCast:response over");
                    });

                    Console.WriteLine("OnHotelBroadCast:send OnHotelBroadCast start");

                    await call.RequestStream.WriteAsync(message);

                    await call.RequestStream.CompleteAsync();

                    await responseReaderTask;
                }
            }
            catch (RpcException e)
            {
                Console.WriteLine("RPC failed", e);
                throw;
            }
        }
    public override IMessage OnHotelBroadCast(ByteString msg)
    {
        HotelBroadcast broadcast = new HotelBroadcast();

        ByteUtils.ByteStringToObject(broadcast, msg);
        Logger.Info("HotelBroadcast start, userID:{0} gameID:{1} roomID:{2} cpProto:{3}", broadcast.UserID, broadcast.GameID, broadcast.RoomID, broadcast.CpProto.ToStringUtf8());

        HotelBroadcastAck broadcastAck = new HotelBroadcastAck()
        {
            UserID = broadcast.UserID, Status = (UInt32)ErrorCode.Ok
        };

        PushToHotelMsg pushMsg = new PushToHotelMsg()
        {
            PushType = PushMsgType.UserTypeAll,
            GameID   = broadcast.GameID,
            RoomID   = broadcast.RoomID,
            CpProto  = broadcast.CpProto,
        };

        pushMsg.DstUids.Add(broadcast.UserID);

        PushToHotel(broadcast.RoomID, pushMsg);

        //测试主动推送给MVS的两个消息
        string str = broadcast.CpProto.ToStringUtf8();

        Logger.Info("HotelBroadcast, str = {0}", str);

        String[] result = str.Split("|");
        if (result.Length > 1)
        {
            if (result[0] == "joinover")
            {
                String[] param = result[1].Split(",");
                if (param.Length > 1)
                {
                    UInt64 roomID = UInt64.Parse(param[0]);
                    UInt32 gameID = UInt32.Parse(param[1]);
                    PushJoinOver(roomID, gameID);
                }
            }
            else if (result[0] == "joinopen")
            {
                PushJoinOpen(broadcast.RoomID, broadcast.GameID);
            }
            else if (result[0] == "kickplayer")
            {
                String[] param = result[1].Split(",");
                if (param.Length > 2)
                {
                    UInt64 roomID = UInt64.Parse(param[0]);
                    UInt32 destID = UInt32.Parse(param[1]);
                    PushKickPlayer(roomID, destID);
                }
            }
            else if (result[0] == "getRoomDetail")
            {
                String[] param = result[1].Split(",");
                if (param.Length > 1)
                {
                    UInt32 gameID = UInt32.Parse(param[0]);
                    UInt64 roomID = UInt64.Parse(param[1]);
                    PushGetRoomDetail(roomID, gameID, 2);
                }
            }
            else if (result[0] == "setRoomProperty")
            {
                ByteString roomProperty = Google.Protobuf.ByteString.CopyFromUtf8(result[1]);
                PushSetRoomProperty(broadcast.RoomID, broadcast.GameID, roomProperty);
            }
            else if (result[0] == "createRoom")
            {
                CreateRoom request = new CreateRoom()
                {
                    GameID   = broadcast.GameID,
                    Ttl      = 600,
                    RoomInfo = new RoomInfo()
                    {
                        RoomName     = "game server room",
                        MaxPlayer    = 2,
                        Mode         = 1,
                        CanWatch     = 1,
                        Visibility   = 1,
                        RoomProperty = Google.Protobuf.ByteString.CopyFromUtf8("hello"),
                    },
                    WatchSetting = new WatchSetting()
                    {
                        MaxWatch        = 3,
                        WatchPersistent = false,
                        WatchDelayMs    = 10 * 1000,
                        CacheTime       = 60 * 1000,
                    },
                };
                var reply = CreateRoom(request);
                Logger.Debug("create room request: {0}, reply: {1}", request, reply);
            }
            else if (result[0] == "touchRoom")
            {
                String[]  param   = result[1].Split(",");
                TouchRoom request = new TouchRoom()
                {
                    GameID = broadcast.GameID,
                    RoomID = UInt64.Parse(param[0]),
                    Ttl    = UInt32.Parse(param[1]),
                };
                var reply = TouchRoom(request);
                Logger.Debug("touch room request: {0}, reply: {1}", request, reply);
            }
            else if (result[0] == "destroyRoom")
            {
                DestroyRoom request = new DestroyRoom()
                {
                    GameID = broadcast.GameID,
                    RoomID = UInt64.Parse(result[1]),
                };
                var reply = DestroyRoom(request);
                Logger.Debug("destroy room request: {0}, reply: {1}", request, reply);
            }
            else if (result[0] == "setFrameSyncRate")
            {
                if (result.Length < 3)
                {
                    Logger.Error("set frame sync rate error: no cacheFrameMS");
                    return(new HotelBroadcastAck()
                    {
                        UserID = broadcast.UserID, Status = (UInt32)ErrorCode.BadRequest
                    });
                }

                var rate         = UInt32.Parse(result[1]);
                var cacheFrameMS = Int32.Parse(result[2]);
                SetFrameSyncRate(broadcast.RoomID, broadcast.GameID, rate, 1, cacheFrameMS);
                Logger.Debug("set frame sync rate: {0}", rate);
            }
            else if (result[0] == "getCacheData")
            {
                Int32 cacheFrameMS = Int32.Parse(result[1]);
                GetCacheData(broadcast.RoomID, broadcast.GameID, cacheFrameMS);
                Logger.Debug("get cache frame data: {0}", cacheFrameMS);
            }
            else if (result[0] == "frameBroadcast")
            {
                var cpProto = result[1];
                FrameBroadcast(broadcast.RoomID, broadcast.GameID, ByteString.CopyFromUtf8(cpProto), 2);
                Logger.Info("frame broadcast: {0}", cpProto);
            }
            else if (result[0] == "metric")
            {
                if (result.Length >= 3)
                {
                    string      name  = result[1];
                    double      value = double.Parse(result[2]);
                    MetricPoint point = new MetricPoint()
                    {
                        Name  = name,
                        Value = value,
                        Attr  = MetricAttr.Custom,
                    };
                    ReportAck ack = ReportMetrics(point);
                    Logger.Info("Set matric response: {0}", ack);
                }
            }
        }

        Logger.Info("HotelBroadcast end, userID:{0} gameID:{1} roomID:{2} cpProto:{3}", broadcast.UserID, broadcast.GameID, broadcast.RoomID, broadcast.CpProto.ToStringUtf8());

        return(broadcastAck);
    }
    public override IMessage OnHotelBroadCast(ByteString msg)
    {
        HotelBroadcast broadcast = new HotelBroadcast();

        ByteUtils.ByteStringToObject(broadcast, msg);
        Logger.Info("HotelBroadcast start, userID:{0} gameID:{1} roomID:{2} cpProto:{3}", broadcast.UserID, broadcast.GameID, broadcast.RoomID, broadcast.CpProto.ToStringUtf8());

        HotelBroadcastAck broadcastAck = new HotelBroadcastAck()
        {
            UserID = broadcast.UserID, Status = (UInt32)ErrorCode.Ok
        };

        string cpValue = broadcast.CpProto.ToStringUtf8();
        var    obj     = JObject.Parse(cpValue);
        String action  = obj["action"].ToString();

        Logger.Info("请求数据: {0}", cpValue);
        Logger.Info("请求的action: {0}", action);
        if (action.Equals("gsReadyRsp"))
        {
            Room room = roomMgr.GetRoom(broadcast.RoomID);
            if (room.Ready(broadcast.UserID))
            {
                JObject data = new JObject();
                data["action"] = "gsStart";
                JArray array = new JArray();
                for (int i = 0; i < 3; i++)
                {
                    RewardItem item      = room.CreateRrewardItem();
                    JObject    rewardObj = new JObject();
                    rewardObj["x"] = item.x;
                    rewardObj["y"] = item.y;
                    array.Add(rewardObj);
                }
                data["rewards"] = array;
                ByteString     cpProto = JsonUtils.EncodetoByteString(data);
                PushToHotelMsg pushMsg = new PushToHotelMsg()
                {
                    PushType = PushMsgType.UserTypeExclude,
                    GameID   = broadcast.GameID,
                    RoomID   = broadcast.RoomID,
                    CpProto  = cpProto,
                };

                PushToHotel(broadcast.RoomID, pushMsg);
            }
        }

        if (action.Equals("gsReward"))
        {
            int  rewardID = (int)obj["rewardID"];
            int  userID   = (int)obj["userID"];
            Room room     = roomMgr.GetRoom(broadcast.RoomID);
            bool eated    = room.GetReward(rewardID);
            if (eated)
            {
                JObject data = new JObject();
                data["action"]   = "gsRewardRsp";
                data["rewardID"] = rewardID;
                data["userID"]   = userID;
                ByteString     cpProto = JsonUtils.EncodetoByteString(data);
                PushToHotelMsg pushMsg = new PushToHotelMsg()
                {
                    PushType = PushMsgType.UserTypeExclude,
                    GameID   = broadcast.GameID,
                    RoomID   = broadcast.RoomID,
                    CpProto  = cpProto,
                };

                PushToHotel(broadcast.RoomID, pushMsg);
            }
        }

        if (action.Equals("gsScore"))
        {
            int  score     = (int)obj["score"];
            int  rewardNum = (int)obj["rewardNum"];
            bool roomOwner = (bool)obj["roomOwner"];
            Room room      = roomMgr.GetRoom(broadcast.RoomID);
            if (roomOwner)
            {
                JArray robotScore = (JArray)obj["robotScore"];
                for (int i = 0; i < robotScore.Count; i++)
                {
                    JObject item           = (JObject)robotScore[i];
                    int     robotuserid    = (int)item["userid"];
                    int     robotscore     = (int)item["score"];
                    int     robotrewardNum = (int)item["rewardNum"];
                    room.ReportScore((uint)robotuserid, robotscore, robotrewardNum);
                }
            }

            bool flag = room.ReportScore(broadcast.UserID, score, rewardNum);
            if (flag)
            {
                JObject data = new JObject();
                data["action"] = "gsResult";;
                JArray resultList = new JArray();
                for (int i = 0; i < room.playerResults.Count; i++)
                {
                    Player  player       = room.playerResults[i];
                    JObject playerResult = new JObject();
                    playerResult["userid"]    = player.Uid;
                    playerResult["rewardNum"] = player.Attr_1;
                    resultList.Add(playerResult);
                }
                data["resultList"] = resultList;
                ByteString     cpProto = JsonUtils.EncodetoByteString(data);
                PushToHotelMsg pushMsg = new PushToHotelMsg()
                {
                    PushType = PushMsgType.UserTypeExclude,
                    GameID   = broadcast.GameID,
                    RoomID   = broadcast.RoomID,
                    CpProto  = cpProto,
                };

                PushToHotel(broadcast.RoomID, pushMsg);
            }
        }
        else
        {
            PushToHotelMsg pushMsg = new PushToHotelMsg()
            {
                PushType = PushMsgType.UserTypeExclude,
                GameID   = broadcast.GameID,
                RoomID   = broadcast.RoomID,
                CpProto  = broadcast.CpProto,
            };
            pushMsg.DstUids.Add(broadcast.UserID);

            PushToHotel(broadcast.RoomID, pushMsg);
        }

        //PushToHotel(broadcast.RoomID, pushMsg);

        //测试主动推送给MVS的两个消息
        string str = broadcast.CpProto.ToStringUtf8();

        Logger.Info("HotelBroadcast, str = {0}", str);

        String[] result = str.Split("|");
        if (result.Length > 1)
        {
            if (result[0] == "joinover")
            {
                String[] param = result[1].Split(",");
                if (param.Length > 1)
                {
                    UInt64 roomID = UInt64.Parse(param[0]);
                    UInt32 gameID = UInt32.Parse(param[1]);
                    UInt32 userID = UInt32.Parse(param[2]);
                    PushJoinOver(roomID, gameID, userID);
                }
            }
            else if (result[0] == "kickplayer")
            {
                String[] param = result[1].Split(",");
                if (param.Length > 2)
                {
                    UInt64 roomID = UInt64.Parse(param[0]);
                    UInt32 srcID  = UInt32.Parse(param[1]);
                    UInt32 destID = UInt32.Parse(param[2]);

                    PushKickPlayer(roomID, srcID, destID);
                }
            }
            else if (result[0] == "getRoomDetail")
            {
                String[] param = result[1].Split(",");
                if (param.Length > 1)
                {
                    UInt32 gameID = UInt32.Parse(param[0]);
                    UInt64 roomID = UInt64.Parse(param[1]);
                    PushGetRoomDetail(roomID, gameID);
                }
            }
        }

        Logger.Info("HotelBroadcast end, userID:{0} gameID:{1} roomID:{2} cpProto:{3}", broadcast.UserID, broadcast.GameID, broadcast.RoomID, broadcast.CpProto.ToStringUtf8());

        return(broadcastAck);
    }