Jump to content

REST API experts?


Josh
 Share

Recommended Posts

http://restapidocs.gameanalytics.com/#routes

 

Do the postman thing they are talking about. They have a button (in the link above) that look like it'll set it up for you to see how it works.

 

From command line via curl it looks like posts are done via:

 

curl -i -X POST -H "Content-Type:application/json" http://localhost:8888/demo-rest-jersey-spring/podcasts/ -d '{"title":"- The Naked Scientists Podcast - Stripping Down Science","linkOnPodcastpedia":"http://www.podcastpedia.org/podcasts/792/-The-Naked-Scientists-Podcast-Stripping-Down-Science","feed":"feed_placeholder","description":"The Naked Scientists flagship science show brings you a lighthearted look at the latest scientific breakthroughs, interviews with the world top scientists, answers to your science questions and science experiments to try at home."}'

 

 

The bit in '{}' is json where you are filling out fields that the api is expecting and should be in the docs.

Link to comment
Share on other sites

So it looks like you have to init the API first.

 

POST /v2/<game_key>/init

 

Your body data needs the following fields: platform, os_version, sdk_version

 

The Docs ask for JSON to be submitted, but I'd imagine URLEncoding should work.

 

Once you do that, you should be able to create different events here:

 

POST /v2/<game_key>/events

 

Depending on the category in your data, your fields change.

 

For example, a Category of business appears to support the following fields:

 

category, event_id, amount, currency, transaction_num, cart_type, receipt_info

 

A Category even of type Resource requires the following fields:

category, event_id, amount.

Link to comment
Share on other sites

I fail to see how the stuff in PostMan translates into CURL commands. I'm trying this but I just get "not found":

curl.setWriteString()
curl.setOptInt(CURLOPT_VERBOSE, 0)
curl.setOptInt(CURLOPT_FOLLOWLOCATION, 1)
curl.setOptString(CURLOPT_URL,"http://sandbox-api.gameanalytics.com/v2/5c6bcb5402204249437fb5a7a80a4959/init")
curl.setOptString(CURLOPT_BODY,"{~qplatform~q: ~qios~q, ~qsdk_version~q: ~qrest api v2~q, ~qos_version~q: ~qios 8.2~q}")
curl.perform()

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

What you're setting the body to isn't valid json.

 

What I would do is see if you can use CURLOPT_POSTFIELDS and pass it like this:

 

curl.setOptString(CURLOPT_CURLOPT_POSTFIELDS, "platform=Windows&os_version=10&sdk_version=rest api v2");

 

What library of CURL are you using? I can try to get a quick example tomorrow morning of each of the different requests.

Link to comment
Share on other sites

Two Questions

 

1. Are you using the sandbox API keys for the sandbox API?

 

2. Are you creating the Authorization header correctly? From what I can tell, it's a hmac hash of your POST data using your secret key, then base64 encoding it

 

I looked into CURL a little bit further. Here is my text example thanks to libcurl's example.

 

/***************************************************************************
*				 _  _ ____ _
* Project		   ___| | | | _ \| |
*			   / __| | | | |_) | |
*			  | (__| |_| | _ <| |___
*			   \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
/* <DESC>
* simple HTTP POST using the easy interface
* </DESC>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

struct MemoryStruct {
char *memory;
size_t size;
};
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
if(mem->memory == NULL)
{
 /* out of memory! */
 printf("not enough memory (realloc returned NULL)\n");
 return 0;
}
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}

int main(void)
{
CURL *curl;
CURLcode res;
struct MemoryStruct chunk;
chunk.memory = malloc(1);
chunk.size = 0;
char* postData = "{\"platform\":\"Windows\", \"os_version\":\"10\", \"sdk_version\":\"api v2\"}";
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
if(curl)
{
 struct curl_slist *headers=NULL;
 headers = curl_slist_append(headers, "Content-Type: text/xml");
 headers = curl_slist_append(headers, "Authorization: <token_from_hmac_sha256_hash_with_secret>");
 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
 /* First set the URL that is about to receive our POST. This URL can
   just as well be a https:// URL if that is what should receive the
   data. */
 curl_easy_setopt(curl, CURLOPT_URL, "http://api.gameanalytics.com/v2/<game_key_here>/init");
 /* Now specify the POST data */
 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData);
 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(postData));
 /* Perform the request, res will get the return code */
 res = curl_easy_perform(curl);

 /* Check for errors */
 if(res != CURLE_OK)
 {
  fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
 }
 else
 {
  printf("%s\n",chunk.memory);
 }

 /* always cleanup */
 curl_easy_cleanup(curl);
}
curl_global_cleanup();
return 0;
}

 

To generate an Hmac hash I'd check out this code: https://github.com/unixpickle/LibOrange/blob/master/LibOrange/hmac-sha256.c

 

Then all you have to do is to base64 encode that for your Authorization token.

  • Upvote 1
Link to comment
Share on other sites

While you are in LE adding curl can you make it so we can call out to any API? When it's a get call pass the string data received back to a callback function we specify and put this into the game launcher exe as this would be very handy.

You know I can't do that in the Game Launcher. :)

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Got this compiling. I really do not understand the authorization stuff.

	struct MemoryStruct

{

char *memory;

size_t size;

};

 

curl_global_init(CURL_GLOBAL_DEFAULT);

CURL* curl = curl_easy_init();

if (curl)

{

char* postData = "{\"platform\":\"Windows\", \"os_version\":\"10\", \"sdk_version\":\"api v2\"}";

 

struct curl_slist *headers = NULL;

headers = curl_slist_append(headers, "Content-Type: text/xml");

headers = curl_slist_append(headers, "Authorization: <token_from_hmac_sha256_hash_with_secret>");// wtf?

curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

 

curl_easy_setopt(curl, CURLOPT_URL, "http://sandbox-api.gameanalytics.com/v2/5c6bcb5402204249437fb5a7a80a4959/init");

curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData);

curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)strlen(postData));

 

CURLcode res = curl_easy_perform(curl);

 

struct MemoryStruct chunk;

chunk.memory = (char*)malloc(1);

chunk.size = 0;

 

/* Check for errors */

if (res != CURLE_OK)

{

fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

}

else

{

printf("%s\n", chunk.memory);

}

 

curl_easy_cleanup(curl);

}

curl_global_cleanup();

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

They have stored on their side your key so you had to register with them to get one I assume. This is unique per person who registered and tells the API provider that you're ok to use their API. I'm kind of shocked the URL isn't https though.

 

Also I would assume each game dev would need their own key or else we'd all be using yours which you don't want. That would mean we have to sign up, get a key, and tell LE to use our key.

Link to comment
Share on other sites

I think their python example gives the best use case of how their auth works.

 

http://restapidocs.gameanalytics.com/#python-example

 

# sandbox game keys
game_key = "5c6bcb5402204249437fb5a7a80a4959"
secret_key = "16813a12f718bc5c620f56944e1abc3ea13ccbac"
# sandbox API urls
url_init = 'http://sandbox-api.gameanalytics.com/v2/' + game_key + '/init'
init_payload = {
'platform': platform,
'os_version': os_version,
'sdk_version': sdk_version
}
init_payload_json = json.dumps(init_payload)
headers = {
'Authorization': hmac_hash_with_secret(init_payload_json, secret_key),
'Content-Type': 'application/json'
}

try:
init_response = requests.post(url_init, data=init_payload_json, headers=headers)
except:
print "Init request failed!"
sys.exit()

 

Notice the line 'Authorization': hmac_hash_with_secret(event_list_json, secret_key),

 

Makes use of this function

 

def hmac_hash_with_secret(message, key):
   return base64.b64encode(hmac.new(key, message, digestmod=hashlib.sha256).digest())

 

Create hmac hash based upon key and message.

 

Base64 encode hmac hash.

Link to comment
Share on other sites

I think the token is a combination of the secret key (which I have) and the post body. How are these combined?

//Public and private keys

std::string gamekey = "bacdf164822d96d83b4d185357e633f5";

std::string secretkey = "xxxxxxxxxx";

 

//Post data

std::string postData = "{\"platform\":\"Windows\", \"os_version\":\"10\", \"sdk_version\":\"api v2\"}";

 

//Construct authentication token

std::string token = "";//hash_some_stuff_or_something(secretkey,postData);

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

That would be great. I've got the class written and integrated in the editor and Lua:

#include "../Leadwerks.h"

 

namespace Leadwerks

{

bool Analytics::disabled = false;

bool Analytics::initialized = false;

long Analytics::starttime = 0;

std::string Analytics::publickey;

std::string Analytics::privatekey;

 

std::string Analytics::GetUserID()

{

if (Steamworks::initialized) return String(Steamworks::GetUserID());

return "";

#ifdef _WIN32

/*GUID guid;

CoCreateGuid(&guid);

std::string userid = String(long(guid.Data1)) + String(guid.Data2) + String(guid.Data3);

for (int i = 0; i < 8; ++i)

{

userid += String(guid.Data4);

}

return userid;*/

#elif __linux__

//void uuid_generate();

#endif

}

 

int Analytics::GetUserTeam()

{

std::string userid = GetUserID();

if ((int(userid[userid.length() - 1]) & 2) == 1) return 1;

return 0;

}

 

bool Analytics::SetKeys(const std::string& gamekey, const std::string& secretkey)

{

if (initialized) return false;

publickey = gamekey;

privatekey = secretkey;

return Initialize();

}

 

void Analytics::Disable()

{

disabled = true;

}

 

void Analytics::Shutdown()

{

if (initialized)

{

SendEvent("session_end", "", "\"length\" = \""+String((Time::Millisecs() - starttime) / 1000)+"\"");

curl_global_cleanup();

initialized = false;

}

}

 

bool Analytics::Initialize()

{

if (disabled) return false;

 

struct MemoryStruct

{

char *memory;

size_t size;

};

 

CURL* curl = NULL;

CURLcode res;

std::string postData;

std::string token;

struct curl_slist *headers = NULL;

std::string userID = GetUserID();

 

if (initialized) return true;

 

curl_global_init(CURL_GLOBAL_DEFAULT);

curl = curl_easy_init();

 

//Post data

postData = std::string("{\"platform\":\"") + System::GetPlatformName() + "\", \"os_version\":\"0\", \"sdk_version\":\"api v2\"}";

 

//Construct authentication token

token = "";//hash_some_stuff_or_something(privatekey,postData)???;

 

//Construct header

headers = NULL;

headers = curl_slist_append(headers, "Content-Type: text/xml");

headers = curl_slist_append(headers, (std::string("Authorization: ") + token).c_str());

curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

 

//Set URL

curl_easy_setopt(curl, CURLOPT_URL, (std::string("http://sandbox-api.gameanalytics.com/v2/") + publickey + std::string("/init")).c_str());

 

//Set body

curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());

curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)postData.length());

 

//Send

res = curl_easy_perform(curl);

 

//Get the result

if (res != CURLE_OK)

{

fprintf(stderr, "Error: curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

curl_easy_cleanup(curl);

return false;

}

else

{

struct MemoryStruct chunk;

chunk.memory = (char*)malloc(1);

chunk.size = 0;

printf("%s\n", chunk.memory);

initialized = true;

}

 

curl_easy_cleanup(curl);

 

initialized = true;

starttime = Time::Millisecs();

SendEvent("user","");

 

return true;

}

 

bool Analytics::SendEvent(const std::string& category, const std::string& eventid, const std::string& params)

{

if (disabled) return false;

if (!initialized) return false;

if (publickey == "") return false;

if (privatekey == "") return false;

 

struct MemoryStruct

{

char *memory;

size_t size;

};

 

CURL* curl = NULL;

CURLcode res;

std::string postData;

std::string token;

struct curl_slist *headers = NULL;

std::string userID = GetUserID();

 

curl = curl_easy_init();

 

//Post data

postData = "{";

postData += "\"session_id\": \"0\"";

postData += ", ";

postData += std::string("\"user_id\": \"" + userID + "\"");

 

if (eventid != "")

{

postData += ", ";

postData += std::string("\"event_id\": \"" + eventid + "\"");

}

 

if (params != "")

{

postData += ", ";

postData += params;

}

 

postData += "}";

 

//Construct authentication token

token = "";//hash_some_stuff_or_something(privatekey,postData)???;

 

//Construct header

headers = NULL;

headers = curl_slist_append(headers, "Content-Type: text/xml");

headers = curl_slist_append(headers, (std::string("Authorization: ") + token).c_str());

curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

 

//Set URL

curl_easy_setopt(curl, CURLOPT_URL, (std::string("http://api.gameanalytics.com/v2/") + publickey + "/" + category).c_str());

 

//Set body

curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());

curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)postData.length());

 

//Send

res = curl_easy_perform(curl);

 

//Get the result

if (res != CURLE_OK)

{

fprintf(stderr, "Error: curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

curl_easy_cleanup(curl);

return false;

}

else

{

struct MemoryStruct chunk;

chunk.memory = (char*)malloc(1);

chunk.size = 0;

printf("%s\n", chunk.memory);

}

 

curl_easy_cleanup(curl);

return true;

}

}

 

Usage is very easy:

static void SetKeys(const std::string& gamekey, const std::string& secretkey)

static bool SendEvent(category, const std::string& eventid, params="")

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Here's why you guys should have this:

post-1-0-39455200-1480624314_thumb.jpg

 

Setting a new funnel up in a single level would be super useful. You could see how many people can find the blue key, can find the blue door, move on to the next step, etc. And instead of just listening to one person who you aren't sure is really representative of everyone, you will actually be able to see how people play your game.

  • Upvote 1

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

I was honestly very curious how many people got to the last level of my game and how many beat it.

I think you would have seen a huge dropoff at that first hard part...I think it was level 3. But this will let you know whether the game is too hard if I just suck at jumping. :)

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

This is really cool. I've added scripts for the types of events that are allowed:

Script.status=0--choice "Status" "Start, Complete, Fail"
Script.levelname=""--string "Level name"

function Script:Send(score, attempt_num)--in
Analytics:SendProgressionEvent(self.status,levelname,score,attempt_num)
end

 

You can have an end-of-level trigger activate an object to send an event for the level being completed, and if the player dies you can have them send an event for them to fail the level.

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...