/// <summary>Fetch the score list for a given board.</summary> /// <returns>Promise resolved when the operation has completed. The attached value describes a list of scores, and /// provides pagination functionality.</returns> /// <param name="board">The name of the board to fetch scores from.</param> /// <param name="limit">The maximum number of results to return per page.</param> /// <param name="offset">Number of the first result. Needs to be a multiple of `limit`. The special value of -1 can be used /// to auto-select the page where the current logged in user is located, including his score in the result. After /// that, you may use the paged result handler to fetch pages nearby.</param> public Promise<PagedList<Score>> List(string board, int limit = 30, int offset = 0) { UrlBuilder url = new UrlBuilder("/v2.6/gamer/scores").Path(domain).Path(board).QueryParam("count", limit); if (offset == -1) url.QueryParam("page", "me"); else url.QueryParam("page", offset / limit + 1); return Common.RunInTask<PagedList<Score>>(Gamer.MakeHttpRequest(url), (response, task) => { // Pagination computing Bundle boardData = response.BodyJson[board]; int currentItems = boardData["scores"].AsArray().Count; int total = Math.Min(boardData["maxpage"] * limit, offset + currentItems); // Fetch listed scores PagedList<Score> scores = new PagedList<Score>(response.BodyJson, offset, total); int rank = boardData["rankOfFirst"]; foreach (Bundle b in boardData["scores"].AsArray()) { scores.Add(new Score(b, rank++)); } // Handle pagination if (offset > 0) { scores.Previous = () => List(board, limit, offset - limit); } if (offset + scores.Count < scores.Total) { scores.Next = () => List(board, limit, offset + limit); } task.PostResult(scores); }); }
/// <summary>Terminates the match. You need to be the creator of the match to perform this operation.</summary> /// <returns>Promise resolved when the operation has completed.</returns> /// <param name="deleteToo">If true, deletes the match if it finishes successfully or is already finished.</param> /// <param name="notification">A notification that can be sent to all players currently playing the match (except you).</param> public Promise<Done> Finish(bool deleteToo = false, PushNotification notification = null) { UrlBuilder url = new UrlBuilder("/v1/gamer/matches").Path(MatchId).Path("finish"); url.QueryParam("lastEventId", LastEventId); HttpRequest req = Gamer.MakeHttpRequest(url); req.BodyJson = Bundle.CreateObject("osn", notification != null ? notification.Data : null); return Common.RunInTask<Done>(req, (response, task) => { UpdateWithServerData(response.BodyJson["match"]); // Affect match Status = MatchStatus.Finished; // Also delete match if (deleteToo) { Gamer.Matches.Delete(MatchId).ForwardTo(task); } else { task.PostResult(new Done(true, response.BodyJson)); } }); }
/// <summary>Draws an item from the shoe.</summary> /// <returns>Promise resolved when the operation has completed. The attached bundle contains an array of items drawn /// from the shoe. You may do `(int)result.Value[0]` to fetch the first value as integer.</returns> /// <param name="count">The number of items to draw from the shoe.</param> /// <param name="notification">A notification that can be sent to all players currently playing the match (except you).</param> public Promise<DrawnItemsResult> DrawFromShoe(int count = 1, PushNotification notification = null) { UrlBuilder url = new UrlBuilder("/v1/gamer/matches").Path(MatchId).Path("shoe").Path("draw"); url.QueryParam("count", count).QueryParam("lastEventId", LastEventId); HttpRequest req = Gamer.MakeHttpRequest(url); req.BodyJson = Bundle.CreateObject("osn", notification != null ? notification.Data : null); return Common.RunInTask<DrawnItemsResult>(req, (response, task) => { UpdateWithServerData(response.BodyJson["match"]); task.PostResult(new DrawnItemsResult(response.BodyJson)); }); }
private Promise<IndexSearchResult> Search(string query, Bundle jsonData, List<string> sortingProperties, int limit, int offset) { UrlBuilder url = new UrlBuilder("/v1/index").Path(Domain).Path(IndexName).Path("search"); if (query != null) url.QueryParam("q", query); url.QueryParam("from", offset).QueryParam("max", limit); // Build sort property if (sortingProperties != null) { Bundle sort = Bundle.CreateArray(); foreach (string s in sortingProperties) sort.Add(s); url.QueryParam("sort", sort.ToJson()); } var request = Cloud.MakeUnauthenticatedHttpRequest(url); request.Method = "POST"; if (jsonData != null) request.BodyJson = jsonData; return Common.RunInTask<IndexSearchResult>(request, (response, task) => { // Fetch listed scores IndexSearchResult result = new IndexSearchResult(response.BodyJson, offset); foreach (Bundle b in response.BodyJson["hits"].AsArray()) { result.Hits.Add(new IndexResult(b)); } // Handle pagination if (offset > 0) { result.Hits.Previous = () => { var promise = new Promise<PagedList<IndexResult>>(); Search(query, jsonData, sortingProperties, limit, offset - limit) .Then(r => promise.Resolve(r.Hits)) .Catch(e => promise.Reject(e)); return promise; }; } if (offset + result.Hits.Count < result.Hits.Total) { result.Hits.Next = () => { var promise = new Promise<PagedList<IndexResult>>(); Search(query, jsonData, sortingProperties, limit, offset + limit) .Then(r => promise.Resolve(r.Hits)) .Catch(e => promise.Reject(e)); return promise; }; } task.PostResult(result); }); }
/// <summary>Method used to retrieve the application's friends of the currently logged in profile.</summary> /// <returns>Promise resolved when the operation has completed, with the fetched list of friends.</returns> /// <param name="filterBlacklisted">When set to true, restricts to blacklisted friends.</param> public Promise<NonpagedList<GamerInfo>> ListFriends(bool filterBlacklisted = false) { UrlBuilder url = new UrlBuilder("/v2.6/gamer/friends").Path(domain); if (filterBlacklisted) url.QueryParam("status", "blacklist"); HttpRequest req = Gamer.MakeHttpRequest(url); return Common.RunInTask<NonpagedList<GamerInfo>>(req, (response, task) => { var result = new NonpagedList<GamerInfo>(response.BodyJson); foreach (Bundle f in response.BodyJson["friends"].AsArray()) { result.Add(new GamerInfo(f)); } task.PostResult(result); }); }
/// <summary>Post a score.</summary> /// <returns>Promise resolved when the operation has completed. The attached value contains the new rank of the /// player as well as whether the score was saved.</returns> /// <param name="score">The score (numeric value) to record.</param> /// <param name="board">The name of the board to post the score to. You may have as many boards as you like for your /// game, and scores are scoped between them.</param> /// <param name="order">The order for this board. As board are not configured on the server, any client can create a /// board dynamically. This parameter serves as as a description for the board and is used only upon /// creation (that is, the first player posting to the named board).</param> /// <param name="scoreInfo">An optional string used to describe the score made by the user.</param> /// <param name="forceSave">When set to true, the score is saved even if its value is less than the past best score /// for this player.</param> public Promise<PostedGameScore> Post(long score, string board, ScoreOrder order, string scoreInfo = null, bool forceSave = false) { UrlBuilder url = new UrlBuilder("/v2.6/gamer/scores").Path(domain).Path(board); switch (order) { case ScoreOrder.HighToLow: url.QueryParam("order", "hightolow"); break; case ScoreOrder.LowToHigh: url.QueryParam("order", "lowtohigh"); break; } url.QueryParam("mayvary", forceSave); HttpRequest req = Gamer.MakeHttpRequest(url); req.BodyJson = Bundle.CreateObject("score", score, "info", scoreInfo); return Common.RunInTask<PostedGameScore>(req, (response, task) => { task.PostResult(new PostedGameScore(response.BodyJson)); }); }
/// <summary> /// Can be used to list the active matches for this game. In general, it is not recommended to proceed this way /// if your goal is to display the games that may be joined. The indexing API is better suited to this use case /// (index the match along with properties and look for matches matching the desired properties). /// </summary> /// <returns>Promise resolved when the operation has completed. The list of matches filtered according to the /// following parameters is provided.</returns> /// <param name="participating">Set to true to only list matches to which this user is participating.</param> /// <param name="invited">Set to true to filter by matches you are invited to (only include them).</param> /// <param name="finished">Set to true to also include finished matchs (which are filtered out by default).</param> /// <param name="full">Set to true to also include games where the maximum number of players has been reached.</param> /// <param name="limit">For pagination, allows to set a greater or smaller page size than the default 30.</param> /// <param name="offset">For pagination, avoid using it explicitly.</param> public Promise<PagedList<MatchListResult>> List(bool participating = false, bool invited = false, bool finished = false, bool full = false, int limit = 30, int offset = 0) { UrlBuilder url = new UrlBuilder("/v1/gamer/matches"); url.QueryParam("domain", domain).QueryParam("offset", offset).QueryParam("limit", limit); if (participating) url.QueryParam("participating"); if (finished) url.QueryParam("finished"); if (invited) url.QueryParam("invited"); if (full) url.QueryParam("full"); // Request for current results return Common.RunInTask<PagedList<MatchListResult>>(Gamer.MakeHttpRequest(url), (response, task) => { PagedList<MatchListResult> matches = new PagedList<MatchListResult>(response.BodyJson, offset, response.BodyJson["count"]); foreach (Bundle b in response.BodyJson["matches"].AsArray()) { matches.Add(new MatchListResult(b)); } // Handle pagination if (offset > 0) { matches.Previous = () => List(participating, invited, finished, full, limit, offset - limit); } if (offset + matches.Count < matches.Total) { matches.Next = () => List(participating, invited, finished, full, limit, offset + limit); } task.PostResult(matches); }); }
private void Run() { int delay = LoopIterationDuration; string messageToAcknowledge = null; bool lastResultPositive = true; while (!Stopped) { if (!lastResultPositive) { // Last time failed, wait a bit to avoid bombing the Internet. Thread.Sleep(PopEventDelayThreadHold); } UrlBuilder url = new UrlBuilder("/v1/gamer/event"); url.Path(Domain).QueryParam("timeout", delay); if (messageToAcknowledge != null) { url.QueryParam("ack", messageToAcknowledge); } CurrentRequest = Gamer.MakeHttpRequest(url); CurrentRequest.RetryPolicy = HttpRequest.Policy.NonpermanentErrors; CurrentRequest.TimeoutMillisec = delay + 30000; CurrentRequest.DoNotEnqueue = true; Managers.HttpClient.Run(CurrentRequest, (HttpResponse res) => { CurrentRequest = null; try { lastResultPositive = true; if (res.StatusCode == 200) { messageToAcknowledge = res.BodyJson["id"]; ProcessEvent(res); } else if (res.StatusCode != 204) { lastResultPositive = false; // Non retriable error -> kill ourselves if (res.StatusCode >= 400 && res.StatusCode < 500) { Stopped = true; } } } catch (Exception e) { Common.LogError("Exception happened in pop event loop: " + e.ToString()); } SynchronousRequestLock.Set(); }); // Wait for request (synchronous) SynchronousRequestLock.WaitOne(); // Wait if suspended if (Paused) { SynchronousRequestLock.WaitOne(); lastResultPositive = true; } } Common.Log("Finished pop event thread " + Thread.CurrentThread.ManagedThreadId); }