/// <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>Fetches a previously indexed object.</summary> /// <returns>Promise resolved when the request has finished.</returns> /// <param name="objectId">ID of the object to look for, as passed when indexing.</param> public Promise<IndexResult> GetObject(string objectId) { UrlBuilder url = new UrlBuilder("/v1/index").Path(Domain).Path(IndexName).Path(objectId); HttpRequest req = Cloud.MakeUnauthenticatedHttpRequest(url); return Common.RunInTask<IndexResult>(req, (response, task) => { task.PostResult(new IndexResult(response.BodyJson)); }); }
/// <summary>Deletes an indexed entry. If you just want to update an entry, simply use IndexObject.</summary> /// <returns>Promise resolved when the request has finished.</returns> /// <param name="objectId">ID of the object to delete, as passed when indexing.</param> public Promise<Done> DeleteObject(string objectId) { UrlBuilder url = new UrlBuilder("/v1/index").Path(Domain).Path(IndexName).Path(objectId); HttpRequest req = Cloud.MakeUnauthenticatedHttpRequest(url); req.Method = "DELETE"; return Common.RunInTask<Done>(req, (response, task) => { task.PostResult(new Done(true, response.BodyJson)); }); }
/// <summary>Deletes a match. Only works if you are the one who created it and it is already finished.</summary> /// <returns>Promise resolved when the operation has completed.</returns> /// <param name="matchId">ID of the match to delete.</param> public Promise<Done> Delete(string matchId) { UrlBuilder url = new UrlBuilder("/v1/gamer/matches").Path(matchId); HttpRequest req = Gamer.MakeHttpRequest(url); req.Method = "DELETE"; return Common.RunInTask<Done>(req, (response, task) => { task.PostResult(new Done(response.BodyJson)); }); }
/// <summary> /// Allows to store arbitrary data for a given achievement and the current player (appears in the /// 'gamerData' node of achievements). /// </summary> /// <returns>Promise resolved when the operation has completed. The attached value contains the updated definition /// of the achievement.</returns> /// <param name="achName">Name of the achievement to update.</param> /// <param name="data">Data to associate with the achievement, merged with the current data (that is, existing keys /// are not affected)</param> public Promise<AchievementDefinition> AssociateData(string achName, Bundle data) { UrlBuilder url = new UrlBuilder("/v1/gamer/achievements").Path(domain).Path(achName).Path("gamerdata"); HttpRequest req = Gamer.MakeHttpRequest(url); req.BodyJson = data; return Common.RunInTask<AchievementDefinition>(req, (response, task) => { task.PostResult(new AchievementDefinition(achName, response.BodyJson["achievement"])); }); }
/// <summary> /// Method to call in order to generate a temporary code that can be passed to another gamer so he can /// add us as a godfather. /// /// The domain as specified by the #Domain method is the domain in which the godfather link should be /// established. "private" means it's local to this game only. /// /// </summary> /// <returns>Promise resolved when the operation has completed. The attached string is the generated code.</returns> public Promise<string> GenerateCode() { UrlBuilder url = new UrlBuilder("/v2.6/gamer/godfather").Path(domain); HttpRequest req = Gamer.MakeHttpRequest(url); req.Method = "PUT"; return Common.RunInTask<string>(req, (response, task) => { task.PostResult(response.BodyJson["godfathercode"]); }); }
/// <summary>Retrieves the rank that a given score would have on the leaderboard, without actually registering the score.</summary> /// <returns>Promise resolved when the operation has completed. The attached value contains the rank that the /// score would have (position in the board).</returns> /// <param name="score">The score (numeric value) to check for ranking.</param> /// <param name="board">The name of the board to check the ranking against. Should match the board where a score has /// already been posted.</param> public Promise<int> GetRank(long score, string board) { UrlBuilder url = new UrlBuilder("/v2.6/gamer/scores").Path(domain).Path(board); HttpRequest req = Gamer.MakeHttpRequest(url); req.BodyJson = Bundle.CreateObject("score", score); req.Method = "PUT"; return Common.RunInTask<int>(req, (response, task) => { task.PostResult(response.BodyJson["rank"]); }); }
/// <summary>This method can be used to retrieve the gamer who have added you as a godfather.</summary> /// <returns>Promise resolved when the operation has completed.</returns> public Promise<NonpagedList<GamerInfo>> GetGodchildren() { UrlBuilder url = new UrlBuilder("/v2.6/gamer/godchildren").Path(domain); HttpRequest req = Gamer.MakeHttpRequest(url); return Common.RunInTask<NonpagedList<GamerInfo>>(req, (response, task) => { var result = new NonpagedList<GamerInfo>(response.BodyJson); foreach (Bundle b in response.BodyJson["godchildren"].AsArray()) { result.Add(new GamerInfo(b)); } task.PostResult(result); }); }
/// <summary>Fetches information about the status of the achievements configured for this game.</summary> /// <returns>Promise resolved when the operation has completed. The attached value is the list of achievements /// with their current state.</returns> public Promise<Dictionary<string, AchievementDefinition>> List() { UrlBuilder url = new UrlBuilder("/v1/gamer/achievements").Path(domain); HttpRequest req = Gamer.MakeHttpRequest(url); return Common.RunInTask<Dictionary<string, AchievementDefinition>>(req, (response, task) => { Dictionary<string, AchievementDefinition> result = new Dictionary<string,AchievementDefinition>(); foreach (var pair in response.BodyJson["achievements"].AsDictionary()) { result[pair.Key] = new AchievementDefinition(pair.Key, pair.Value); } task.PostResult(result); }); }
/// <summary> /// Indexes a new object. /// Use this API to add or update an object in an index. You can have as many indexes as you need: one /// for gamer properties, one for matches, one for finished matches, etc. It only depends on what you /// want to search for. /// </summary> /// <returns>Promise resolved when the request has finished.</returns> /// <param name="objectId">The ID of the object to be indexed. It can be anything; this ID only needs to uniquely /// identify your document. Therefore, using the match ID to index a match is recommended for instance.</param> /// <param name="properties">A freeform object, whose attributes will be indexed and searchable. These properties /// are typed! So if 'age' is once passed as an int, it must always be an int, or an error will be /// thrown upon insertion.</param> /// <param name="payload">Another freeform object. These properties are attached to the document in the same way /// as the properties, however those are not indexed (cannot be looked for in a search request). Its /// content is returned in searches (#CotcSdk.IndexResult.Payload property).</param> public Promise<Done> IndexObject(string objectId, Bundle properties, Bundle payload) { UrlBuilder url = new UrlBuilder("/v1/index").Path(Domain).Path(IndexName); HttpRequest req = Cloud.MakeUnauthenticatedHttpRequest(url); req.BodyJson = Bundle.CreateObject( "id", objectId, "properties", properties, "payload", payload ); return Common.RunInTask<Done>(req, (response, task) => { task.PostResult(new Done(true, response.BodyJson)); }); }
/// <summary> /// Fetch the list of products as configured on the backoffice. Note that this doesn't include any information /// about pricing and so on: the external store plugin is required to do so. /// Note that this call returns the catalog as configured on the CotC server, which may not be exhaustive if /// additional products are configured on iTunes Connect but not reported to the CotC servers. /// </summary> /// <param name="limit">The maximum number of results to return per page.</param> /// <param name="offset">Number of the first result.</param> /// <returns>Promise resolved when the operation has completed. The attached value describes a list of products, /// with pagination functionality.</returns> public Promise<PagedList<ConfiguredProduct>> ListConfiguredProducts(int limit = 30, int offset = 0) { UrlBuilder url = new UrlBuilder("/v1/gamer/store/products").QueryParam("limit", limit).QueryParam("skip", offset); return Common.RunInTask<PagedList<ConfiguredProduct>>(Gamer.MakeHttpRequest(url), (response, task) => { PagedList<ConfiguredProduct> products = new PagedList<ConfiguredProduct>(response.BodyJson, offset, response.BodyJson["count"]); foreach (Bundle b in response.BodyJson["products"].AsArray()) { products.Add(new ConfiguredProduct(b)); } // Handle pagination if (offset > 0) { products.Previous = () => ListConfiguredProducts(limit, offset - limit); } if (offset + products.Count < products.Total) { products.Next = () => ListConfiguredProducts(limit, offset + limit); } task.PostResult(products); }); }
/// <summary> /// Creates a match, available for join by other players. If you would like to make your match private, please read /// the general documentation about matches. /// </summary> /// <returns>Promise resolved when the operation has completed. The attached Match object allows to operate with the /// match.</returns> /// <param name="maxPlayers">The maximum number of players who may be in the game at a time.</param> /// <param name="description">String describing the match (available for other who want to join).</param> /// <param name="customProperties">Freeform object containing the properties of the match, which may be used by other players /// to search for a suited match.</param> /// <param name="shoe">Freeform object containing a list of objects which will be shuffled upon match creation. This offers /// an easy way to make a random generator that is safe, unbiased (since made on the server) and can be verified /// by all players once the game is finished. This bundle needs to be an array (use Bundle.CreateArray).</param> public Promise<Match> Create(int maxPlayers, string description = null, Bundle customProperties = null, Bundle shoe = null) { var task = new Promise<Match>(); if (shoe != null && shoe.Type != Bundle.DataType.Array) { task.PostResult(ErrorCode.BadParameters, "The shoe must be an array"); return task; } UrlBuilder url = new UrlBuilder("/v1/gamer/matches").QueryParam("domain", domain); HttpRequest req = Gamer.MakeHttpRequest(url); Bundle config = Bundle.CreateObject(); config["maxPlayers"] = maxPlayers; config["description"] = description; config["customProperties"] = customProperties; config["shoe"] = shoe; req.BodyJson = config; return Common.RunRequest(req, task, (HttpResponse response) => { task.PostResult(new Match(Gamer, response.BodyJson["match"])); }); }
/// <summary> /// Asks to join the match with a given ID. Do not use this if you are already part of the match. /// This call is not scoped by domain (it uses the Match ID directly). /// </summary> /// <returns>Promise resolved when the operation has completed. In case of success, you get the exact same /// match object that would be returned by a call to Create or Fetch. It can be used to interact with /// the match as the user who just joined.</returns> /// <param name="matchId">The ID of an existing match to join. It can be fetched from the Match object (MatchId).</param> /// <param name="notification">Optional push notification to be sent to inactive players (see class definition).</param> public Promise<Match> Join(string matchId, PushNotification notification = null) { UrlBuilder url = new UrlBuilder("/v1/gamer/matches").Path(matchId).Path("join"); HttpRequest req = Gamer.MakeHttpRequest(url); req.BodyJson = Bundle.CreateObject("osn", notification != null ? notification.Data : null); return Common.RunInTask<Match>(req, (response, task) => { task.PostResult(new Match(Gamer, response.BodyJson["match"])); }); }
/// <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); }); }
/// <summary>Posts a move to other players.</summary> /// <returns>Promise resolved when the operation has completed.</returns> /// <param name="moveData">A freeform object indicating the move data to be posted and transfered to other players. This /// move data will be kept in the events, and new players should be able to use it to reproduce the local game /// state.</param> /// <param name="updatedGameState">A freeform object replacing the global game state, to be used by players who join from /// now on. Passing a non null value clears the pending events in the match.</param> /// <param name="notification">A push notification that can be sent to all players except you.</param> public Promise<Done> PostMove(Bundle moveData, Bundle updatedGameState = null, PushNotification notification = null) { UrlBuilder url = new UrlBuilder("/v1/gamer/matches").Path(MatchId).Path("move").QueryParam("lastEventId", LastEventId); Bundle config = Bundle.CreateObject(); config["move"] = moveData; config["globalState"] = updatedGameState; if (notification != null) config["osn"] = notification.Data; HttpRequest req = Gamer.MakeHttpRequest(url); req.BodyJson = config; return Common.RunInTask<Done>(req, (response, task) => { UpdateWithServerData(response.BodyJson["match"]); // Record event if (updatedGameState != null) { Moves.Clear(); GlobalState = updatedGameState; } Moves.Add(new MatchMove(Gamer.GamerId, moveData)); task.PostResult(new Done(true, response.BodyJson)); }); }
/// <summary>Leaves the match.</summary> /// <returns>Promise resolved when the operation has completed.</returns> /// <param name="notification">A push notification that can be sent to all players except you.</param> public Promise<Done> Leave(PushNotification notification = null) { UrlBuilder url = new UrlBuilder("/v1/gamer/matches").Path(MatchId).Path("leave"); 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"]); task.PostResult(new Done(true, response.BodyJson)); }); }
/// <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)); }); }
/// <summary> /// When you have data about friends from another social network, you can post them to CotC servers using /// these function. /// This will automatically add them as a friend on CotC as they get recognized on our servers. /// The friends get associated to the domain of this object. /// Note: this function was once called PostSocialNetworkFriends but renamed due to it being misleading. /// </summary> /// <returns>Promise resolved when the operation has completed. The attached value is the same list as passed, /// enriched with potential information about the gamer (member #CotcSdk.SocialNetworkFriend.ClanInfo) for /// gamers who are already registered on CotC servers.</returns> /// <param name="network">The network with which these friends are associated.</param> /// <param name="friends">A list of data about the friends fetched on the social network.</param> /// <param name="automatching">If true, synchronizes the CotC friends with the list. That is, the provided /// social network friends become your friends on CotC as well (reported on ListFriends and such).</param> public Promise<SocialNetworkFriendResponse> ListNetworkFriends(LoginNetwork network, List<SocialNetworkFriend> friends, bool automatching = false) { var task = new Promise<SocialNetworkFriendResponse>(); UrlBuilder url = new UrlBuilder("/v2.12/gamer/friends").Path(domain).QueryParam("network", network.Describe()); HttpRequest req = Gamer.MakeHttpRequest(url); Bundle body = Bundle.CreateObject(); Bundle friendData = (body["friends"] = Bundle.CreateObject()); foreach (SocialNetworkFriend f in friends) { friendData[f.Id] = f.ToBundle(); } body["automatching"] = automatching; req.BodyJson = body; return Common.RunRequest(req, task, (HttpResponse response) => { task.PostResult(new SocialNetworkFriendResponse(response.BodyJson)); }); }
/// <summary>Fetch the score list for a given board, restricting to the scores made by the friends of the current user.</summary> /// <returns>Promise resolved when the operation has completed. The attached value describes a list of scores, /// without pagination functionality.</returns> /// <param name="board">The name of the board to fetch scores from.</param> public Promise<NonpagedList<Score>> ListFriendScores(string board) { UrlBuilder url = new UrlBuilder("/v2.6/gamer/scores").Path(domain).Path(board).QueryParam("type", "friendscore"); return Common.RunInTask<NonpagedList<Score>>(Gamer.MakeHttpRequest(url), (response, task) => { var scores = new NonpagedList<Score>(response.BodyJson); foreach (Bundle b in response.BodyJson[board].AsArray()) { scores.Add(new Score(b)); } task.PostResult(scores); }); }
/// <summary>Retrieves the best scores of this gamer, on all board he has posted one score to.</summary> /// <returns>Promise resolved when the operation has completed. The attached value contains information about /// the best scores of the user, indexed by board name. /// *IMPORTANT*: in the results, the gamer information is not provided. GamerInfo is always null.</returns> public Promise<Dictionary<string, Score>> ListUserBestScores() { UrlBuilder url = new UrlBuilder("/v2.6/gamer/bestscores").Path(domain); HttpRequest req = Gamer.MakeHttpRequest(url); return Common.RunInTask<Dictionary<string, Score>>(req, (response, task) => { Dictionary<string, Score> scores = new Dictionary<string, Score>(); foreach (var pair in response.BodyJson.AsDictionary()) { Score s = new Score(pair.Value); s.GamerInfo = null; scores[pair.Key] = s; } task.PostResult(scores); }); }
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> /// Fetches a Match object corresponding to a match which the player already belongs to. /// It can be used either to obtain additional information about a running match (by inspecting the resulting /// match object), or to continue an existing match (by keeping the match object which corresponds to the one /// that was returned by the Create method). /// This call is not scoped by domain (it uses the Match ID directly). /// </summary> /// <returns>Promise resolved when the operation has completed. The attached Match object allows to operate with /// the match.</returns> /// <param name="matchId">The ID of an existing match to resume. It can be fetched from the Match object (MatchId).</param> public Promise<Match> Fetch(string matchId) { UrlBuilder url = new UrlBuilder("/v1/gamer/matches").Path(matchId); HttpRequest req = Gamer.MakeHttpRequest(url); return Common.RunInTask<Match>(req, (response, task) => { task.PostResult(new Match(Gamer, response.BodyJson["match"])); }); }
/// <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> /// Use this method to send a message to another user from your game. /// /// Messages are sent to a specific user, in a specific domain. You can use domains to send messages /// across games (or use private for messages sent to your game only). /// /// </summary> /// <returns>Promise resolved when the operation has completed.</returns> /// <param name="gamerId">ID of the recipient gamer.</param> /// <param name="eventData">JSON object representing the event to be sent. The recipient will receive it as is /// when subscribed to a #CotcSdk.DomainEventLoop (ReceivedEvent property). If the application is not active, /// the message will be queued and transmitted the next time the domain event loop is started.</param> /// <param name="notification">Push notification to send to the recipient player if not currently active.</param> public Promise<Done> SendEvent(string gamerId, Bundle eventData, PushNotification notification = null) { UrlBuilder url = new UrlBuilder("/v1/gamer/event").Path(domain).Path(gamerId); HttpRequest req = Gamer.MakeHttpRequest(url); Bundle config = Bundle.CreateObject(); config["type"] = "user"; config["event"] = eventData; config["from"] = Gamer.GamerId; config["to"] = gamerId; config["name"] = Gamer["profile"]["displayname"]; if (notification != null) config["osn"] = notification.Data; req.BodyJson = config; return Common.RunInTask<Done>(req, (response, task) => { task.PostResult(new Done(true, response.BodyJson)); }); }
/// <summary>Call this to attribute a godfather to the currently logged in user.</summary> /// <returns>Promise resolved when the operation has completed.</returns> /// <param name="code">Is a string as generated by #GenerateCode.</param> /// <param name="rewardTx">A transaction Json rewarding the godfather formed as follows: /// { transaction : { "unit" : amount}, /// description : "reward transaction", /// domain : "com.clanoftcloud.text.DOMAIN" } /// where description and domain are optional.</param> /// <param name="notification">Optional OS notification to be sent to the godfather who generated the code. /// The godfather will reveive an event of type 'godchildren' containing the id of the godchildren /// and the balance/achievements field if rewarded.</param> public Promise<Done> UseCode(string code, Bundle rewardTx = null, PushNotification notification = null) { UrlBuilder url = new UrlBuilder("/v2.6/gamer/godfather").Path(domain); HttpRequest req = Gamer.MakeHttpRequest(url); Bundle config = Bundle.CreateObject(); config["godfather"] = code; config["osn"] = notification != null ? notification.Data : null; config["reward"] = rewardTx; req.BodyJson = config; return Common.RunInTask<Done>(req, (response, task) => { task.PostResult(new Done(response.BodyJson)); }); }
/// <summary>Allows to change the relation of a friendship inside the application.</summary> /// <returns>Promise resolved when the operation has completed.</returns> /// <param name="gamerId">ID of the gamer to change the relationship (fetched using ListFriends for instance).</param> /// <param name="state">The new state to set.</param> /// <param name="notification">Optional OS notification to be sent to indicate the player that the status has changed.</param> public Promise<Done> ChangeRelationshipStatus(string gamerId, FriendRelationshipStatus state, PushNotification notification = null) { UrlBuilder url = new UrlBuilder("/v2.6/gamer/friends").Path(domain).Path(gamerId).QueryParam("status", state.ToString().ToLower()); HttpRequest req = Gamer.MakeHttpRequest(url); req.BodyJson = Bundle.CreateObject("osn", notification != null ? notification.Data : null); return Common.RunInTask<Done>(req, (response, task) => { task.PostResult(new Done(response.BodyJson)); }); }
/// <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>This method can be used to retrieve the godfather of the gamer.</summary> /// <returns>Promise resolved when the operation has completed.</returns> public Promise<GamerInfo> GetGodfather() { UrlBuilder url = new UrlBuilder("/v2.6/gamer/godfather").Path(domain); HttpRequest req = Gamer.MakeHttpRequest(url); return Common.RunInTask<GamerInfo>(req, (response, task) => { task.PostResult(new GamerInfo(response.BodyJson["godfather"])); }); }
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); }