internal Transaction(Bundle serverData) { Description = serverData["desc"]; Domain = serverData["domain"]; RunDate = Common.ParseHttpDate(serverData["ts"]); TxData = serverData["tx"]; }
internal TransactionResult(Bundle serverData) : base(serverData) { Balance = serverData["balance"]; foreach (var pair in serverData["achievements"].AsDictionary()) { TriggeredAchievements[pair.Key] = new AchievementDefinition(pair.Key, pair.Value); } }
internal Score(Bundle serverData) : base(serverData) { GamerInfo = new GamerInfo(serverData); Info = serverData["score"]["info"]; PostedAt = Common.ParseHttpDate(serverData["score"]["timestamp"]); Rank = serverData["rank"]; Value = serverData["score"]["score"]; }
/// <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"])); }); }
internal Match(Gamer gamer, Bundle serverData) : base(serverData) { Gamer = gamer; CustomProperties = Bundle.Empty; Moves = new List<MatchMove>(); GlobalState = Bundle.Empty; Players = new List<GamerInfo>(); Shoe = Bundle.Empty; UpdateWithServerData(serverData); }
/// <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>To be used when an HTTP request has failed. Will extract a default error code (server error, network error) from the HTTP request.</summary> internal CotcException(HttpResponse response, string failureDescription = null) { Json = response.BodyJson; HttpStatusCode = response.StatusCode; if (response.HasFailed) { ErrorCode = ErrorCode.NetworkError; ErrorInformation = failureDescription; } else if (response.StatusCode < 200 || response.StatusCode >= 300) { ErrorCode = ErrorCode.ServerError; ErrorInformation = failureDescription; } else { throw new InvalidOperationException("Should only call this for a failed request"); } }
internal SocialNetworkFriendResponse(Bundle serverData) : base(serverData) { ByNetwork = new Dictionary<LoginNetwork, List<SocialNetworkFriend>>(); // Parse the response, per network foreach (var pair in serverData.AsDictionary()) { try { LoginNetwork net = (LoginNetwork) Enum.Parse(typeof(LoginNetwork), pair.Key, true); var list = new List<SocialNetworkFriend>(); foreach (var friendPair in pair.Value.AsDictionary()) { list.Add(new SocialNetworkFriend(friendPair.Value)); } ByNetwork[net] = list; } catch (Exception e) { Common.LogError("Unknown network " + pair.Key + ", ignoring. Details: " + e.ToString()); } } }
/// <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"])); }); }
internal MatchLeaveEvent(Gamer gamer, Bundle serverData) : base(gamer, serverData) { foreach (Bundle b in serverData["event"]["playersLeft"].AsArray()) { PlayersLeft.Add(new GamerInfo(b)); } }
internal MatchMove(string playerId, Bundle moveData) { MoveData = moveData; PlayerId = playerId; }
private void UpdateWithServerData(Bundle serverData) { Lock(() => { if (serverData.Has("creator")) Creator = new GamerInfo(serverData["creator"]); if (serverData.Has("customProperties")) CustomProperties = serverData["customProperties"]; if (serverData.Has("domain")) Domain = serverData["domain"]; if (serverData.Has("description")) Description = serverData["description"]; if (serverData.Has("globalState")) GlobalState = serverData["globalState"]; MatchId = serverData["_id"]; if (serverData.Has("maxPlayers")) MaxPlayers = serverData["maxPlayers"]; if (serverData.Has("seed")) Seed = serverData["seed"]; Status = Common.ParseEnum<MatchStatus>(serverData["status"]); if (serverData.Has("shoe")) Shoe = serverData["shoe"]; // Process pending events if (serverData.Has("events")) { Moves.Clear(); foreach (var b in serverData["events"].AsArray()) { if (b["type"] == "match.move") { Moves.Add(new MatchMove(serverData["event"]["player_id"], serverData["event"]["move"])); } } } // Players if (serverData.Has("players")) { Players.Clear(); foreach (var b in serverData["players"].AsArray()) { Players.Add(new GamerInfo(b)); } } // Last event ID (null if 0; =first time) string lastEvent = serverData["lastEventId"]; if (lastEvent != "0") LastEventId = lastEvent; }); }
/// <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)); }); }
internal ConfiguredProduct(Bundle serverData) : base(serverData) { }
protected PropertiesObject(Bundle baseData) { Props = baseData; }
public GotGodchildEvent(Bundle serverData) : base(serverData) { Gamer = new GamerInfo(serverData["event"]["godchildren"]); Reward = serverData["reward"]; }
/// <summary> /// Alternative search function (see #Search for more information) that takes a bundle as a search criteria. /// /// It allows using the full Elastic search capabilities with full query DSL search. The query bundle represents /// the JSON document as documented here: /// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html /// /// </summary> /// <returns>Promise resolved when the operation has finished. The attached object contains various /// information about the results, including a Hits member, which handles the results in a /// paginated way.</returns> /// <param name="query">Search query as described in the summary.</param> /// <param name="limit">The maximum number of results to return per page.</param> /// <param name="offset">Number of the first result.</param> public Promise<IndexSearchResult> SearchExtended(Bundle query, int limit = 30, int offset = 0) { return Search(null, query, null, limit, offset); }
private JsonData ToJson(Bundle bundle) { JsonData target = new JsonData(); if (bundle.Type == DataType.Object) { target.SetJsonType(JsonType.Object); foreach (KeyValuePair<string, Bundle> entry in bundle.Dictionary) { switch (entry.Value.Type) { case DataType.Boolean: target[entry.Key] = entry.Value.AsBool(); break; case DataType.Integer: target[entry.Key] = entry.Value.AsLong(); break; case DataType.Double: target[entry.Key] = entry.Value.AsDouble(); break; case DataType.String: target[entry.Key] = entry.Value.AsString(); break; default: target[entry.Key] = ToJson(entry.Value); break; } } } else { target.SetJsonType(JsonType.Array); foreach (Bundle entry in bundle.Array) { switch (entry.Type) { case DataType.Boolean: target.Add(entry.AsBool()); break; case DataType.Integer: target.Add(entry.AsInt()); break; case DataType.Double: target.Add(entry.AsDouble()); break; case DataType.String: target.Add(entry.AsString()); break; default: target.Add(ToJson(entry)); break; } } } return target; }
internal Score(Bundle serverData, int rank) : this(serverData) { Rank = rank; }
internal MatchShoeDrawnEvent(Gamer gamer, Bundle serverData) : base(gamer, serverData) { Count = serverData["event"]["count"]; }
internal MatchFinishEvent(Gamer gamer, Bundle serverData) : base(gamer, serverData) { Finished = serverData["event"]["finished"]; }
internal FriendStatusChangeEvent(string status, Bundle serverData) { FriendId = serverData["event"]["friend"]; NewStatus = Common.ParseEnum<FriendRelationshipStatus>(status); }
/// <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)); }); }
protected MatchEvent(Gamer gamer, Bundle serverData) : base(serverData) { MatchEventId = serverData["event"]["_id"]; Match = new MatchInfo(gamer, serverData["event"]["match_id"]); }
/// <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)); }); }
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); }); }
internal MatchMoveEvent(Gamer gamer, Bundle serverData) : base(gamer, serverData) { MoveData = serverData["event"]["move"]; PlayerId = serverData["event"]["player_id"]; }
public DrawnItemsResult(Bundle serverData) : base(serverData) { }
internal PurchaseTransaction(Bundle serverData) : base(serverData) { }
internal MatchInviteEvent(Gamer gamer, Bundle serverData) : base(gamer, serverData) { Inviter = new GamerInfo(serverData["event"]["inviter"]); }