Exemplo n.º 1
0
        /// <summary>
        /// 辅助子过程。检查异步的网络请求是否成功、对服务器的操作请求是否实现,并返回实现了的操作的结果,或错误信息。
        /// </summary>
        /// <typeparam name="T">代表结果数据的泛型参数。</typeparam>
        /// <param name="respTask">代表异步的网络请求。</param>
        /// <param name="deserializer">对网络请求结果的反序列化器。</param>
        /// <returns>操作的结果。</returns>
        private static async Task <GameOperation <T> > OperationFromResponseAsync <T>(Task <HttpResponseMessage> respTask, Func <string, T> deserializer)
        {
            string msg = "";

            try
            {
                var resp = await respTask.ConfigureAwait(false);

                // 如果是 404 BadRequest,我们最好看看服务器提供了哪些错误信息。
                if (resp.IsSuccessStatusCode || resp.StatusCode == HttpStatusCode.BadRequest)
                {
                    var respStr = await resp.Content.ReadAsStringAsync();

                    if (resp.IsSuccessStatusCode)
                    {
                        return(GameOperation <T> .Succ(deserializer(respStr)));
                    }
                    else
                    {
                        // 404。服务器会将错误信息放在 JSON 数据的 Message 字段里。
                        var deserialized = JsonConvert.DeserializeAnonymousType(respStr, new { Message = "" });
                        msg = deserialized.Message;
                    }
                }
            }
            catch (Exception ex)
            {
                msg = ex.Message;
            }

            return(GameOperation <T> .Fail(msg));
        }
Exemplo n.º 2
0
        /// <summary>
        /// 开启一个新游戏。
        /// </summary>
        /// <param name="roomIdToStart">这个游戏实例要连接到的房间号。若为null或空,则为默认的0号房间。</param>
        /// <param name="userId">参与这个游戏的玩家ID。若为null或空,会生成新ID。</param>
        /// <returns>创建游戏的操作结果。</returns>
        public static async Task <GameOperation <CreateGameResult> > OpenRoomAsync(string roomIdToStart, string userId)
        {
            // ConfigureAwait(false) 可以避免对特定的线程进行调度,比如只能序列化访问的UI线程。
            // 该函数内的操作都不需要 UI 线程。
            var stateOp = await QueryStateAsync(userId, roomIdToStart).ConfigureAwait(false);

            if (!stateOp.Succeeded)
            {
                // 如果查询状态失败了,创建游戏也就失败了。
                return(GameOperation <CreateGameResult> .Fail(stateOp.ErrorMessage));
            }

            var state = stateOp.OperationResult;

            if (string.IsNullOrEmpty(state.UserId))
            {
                // User ID 是必要的。
                return(GameOperation <CreateGameResult> .Fail("No valid User ID."));
            }

            var mode = ConvertNumberMode(state.Numbers);

            if (mode == RoomNumberMode.Unknown)
            {
                // 不支持的游戏模式。
                return(GameOperation <CreateGameResult> .Fail($"Unsupported number mode {mode}."));
            }

            var game = new Game
            {
                UserId     = state.UserId,
                RoomId     = state.RoomId,
                Nickname   = state.NickName,
                NumberMode = mode,
            };

            // 启动游戏主循环,用于倒计时和推进游戏轮数。
            // 由于我们用 async/await的形式包装了主循环,故其返回 Task。不过我们不需要用这个 Task。
            var ignored = game.StartAsync(state.RoundId, state.LeftTime);

            return(GameOperation <CreateGameResult> .Succ(new CreateGameResult(game, TimeSpan.FromSeconds(state.LeftTime), ConvertStateToNewRound(state))));
        }
Exemplo n.º 3
0
        /// <summary>
        /// 创建一个新的游戏房间,并在其中开启新游戏。
        /// </summary>
        /// <param name="mode"></param>
        /// <param name="userId">可选。要继承的玩家ID。</param>
        /// <returns>创建游戏的操作结果。</returns>
        public static async Task <GameOperation <CreateGameResult> > StartInNewRoomAsync(RoomNumberMode mode, string userId = null)
        {
            if (!SupportedNumberModes.ContainsKey(mode))
            {
                throw new ArgumentOutOfRangeException(nameof(mode));
            }

            // 先创建新房间,再在这个房间里,按正常流程启动游戏。
            var url       = string.Format(NewRoomEndpointTemplate, SupportedNumberModes[mode]);
            var newRoomOp = await OperationFromResponseAsync(s_httpClient.GetAsync(url), JsonConvert.DeserializeObject <NewRoom>);

            if (!newRoomOp.Succeeded)
            {
                return(GameOperation <CreateGameResult> .Fail(newRoomOp.ErrorMessage));
            }

            var newRoom = newRoomOp.OperationResult;

            return(await OpenRoomAsync(roomIdToStart : newRoom.RoomId, userId : userId));
        }
Exemplo n.º 4
0
        /// <summary>
        /// 检查并提交这一轮的黄金点。
        /// </summary>
        /// <param name="candidate">要提交的数。</param>
        /// <param name="candidate2">要提交的第二个数。在不支持两个数的游戏里,必须是null。</param>
        /// <returns>提交黄金点的操作结果。</returns>
        public async Task <GameOperation <bool> > SubmitAsync(double candidate, double?candidate2)
        {
            EnsureGameNotClosed();

            // _roundId 可能由于多线程而变动。我们先复制它到局部变量。
            var roundId = _roundId;

            if (!(0 < candidate && candidate < 100))
            {
                return(GameOperation <bool> .Fail("Input must be in (0, 100)"));
            }

            if (candidate2.HasValue)
            {
                if (NumberMode != RoomNumberMode.Two)
                {
                    return(GameOperation <bool> .Fail("Not 2-number room."));
                }

                if (!(0 < candidate2 && candidate2 < 100))
                {
                    return(GameOperation <bool> .Fail("Secondary input must be in (0, 100)"));
                }
            }

            string submitUrl = string.Format(
                SubmitEndpointTemplate,
                Uri.EscapeDataString(UserId),
                Uri.EscapeDataString(roundId),
                Uri.EscapeDataString(candidate.ToString()),
                Uri.EscapeDataString(candidate2?.ToString() ?? "")); // 如果游戏模式不是提交两个数,就将第二个数对应的参数设为空。

            var dummyBody = new StringContent("");

            return(await OperationFromResponseAsync(
                       s_httpClient.PostAsync(submitUrl, dummyBody),
                       unused => true));
        }