internal static async Task FetchFullLogs(this BotzoneMatch match)
        {
            do
            {
                Logger.Log(LogLevel.Info, "尝试从 Botzone 读取对局 Log……");
                try
                {
                    var str = await client.GetStringAsync(Credentials.BotzoneMatchURL(match.MatchID, true));

                    match.Logs        = JsonConvert.DeserializeObject <List <ILogItem> >(str, logConverter);
                    match.DisplayLogs = (from item in match.Logs.OfType <JudgeLogItem>()
                                         select item.output.display).ToList();
                }
                catch (Exception ex)
                {
                    Logger.Log(LogLevel.Warning, "请求过程中发生错误:" + ex.Message);
                    Logger.Log(LogLevel.InfoTip, "5秒后重试……");
                    await Task.Delay(5000);
                }
            } while (match.Logs == null);

            Logger.Log(LogLevel.OK, "对局 Log 加载成功!");
        }
        internal static async Task <bool> FetchNextMatchRequest(this BotzoneMatch match)
        {
            string raw;

            do
            {
                Logger.Log(LogLevel.InfoTip, "连接 Botzone,并等待新 request");
                var req = new HttpRequestMessage(HttpMethod.Get, Credentials.BotzoneLocalAIURL());
                if (match.Runner.Responses.Count > 0)
                {
                    var last = match.Runner.Responses.Last();
                    if (LocalProgramRunner.IsSimpleIO)
                    {
                        req.Headers.Add("X-Match-" + match.MatchID, last);
                    }
                    else
                    {
                        req.Headers.Add("X-Match-" + match.MatchID, JsonConvert.SerializeObject(last, Formatting.None));
                    }
                }
                var res = await client.SendAsync(req);

                raw = await res.Content.ReadAsStringAsync();

                if (CheckResponse(res, raw))
                {
                    break;
                }

                Logger.Log(LogLevel.InfoTip, "5秒后重试……");
                await Task.Delay(5000);
            } while (true);

            var lines = raw.Split('\n');
            var counts = lines[0].Split(' ');
            int reqCount = int.Parse(counts[0]), finishCount = int.Parse(counts[1]), i, j;

            // Debug.Assert(reqCount + finishCount <= 1);
            for (i = 1, j = 0; j < reqCount; i += 2, j++)
            {
                if (lines[i] != match.MatchID)
                {
                    continue;
                }
                string req = lines[i + 1];
                if (!LocalProgramRunner.IsSimpleIO)
                {
                    match.Runner.Requests.Add(JsonConvert.DeserializeObject(req));
                }
                else
                {
                    match.Runner.Requests.Add(req);
                }
                match.Status = MatchStatus.Running;
                Logger.Log(LogLevel.InfoTip, $"对局 {match.MatchID} 获得一条新 request");
                return(true);
            }
            for (i = 2 * reqCount + 1, j = 0; j < finishCount; i++, j++)
            {
                var parts = lines[i].Split(' ');
                if (parts[0] != match.MatchID)
                {
                    continue;
                }
                Debug.Assert(parts[1] == match.MySlot.ToString());
                if (parts[2] == "0")
                {
                    Logger.Log(LogLevel.Warning, $"对局 {match.MatchID} 中止");
                    await match.OnFinish(true);
                }
                else
                {
                    match.Scores = parts.Skip(3).Select(double.Parse).ToArray();
                    Logger.Log(LogLevel.OK, $"对局 {match.MatchID} 结束,比分为 {string.Join(", ", parts.Skip(3))},本地AI分数 {parts[3 + match.MySlot]}");
                    await match.OnFinish(false);
                }
                return(true);
            }
            return(false);
        }