Since I know Virtual Nomad will just move my thread to showcase anyway I'll start here. I did a new thread because with all the extra code snippets posted I didn't want anything getting confused with the new version. Much of it has been restructured and simplified. It's recommended to use this version as it uses the newer API on my server which has more features. You can retrieve dates along with score submissions and set a limit. If you don't want the top 20 scores you can now limit it to retrieve only the top 10. Scores can now also be retrieved in 3 formats: CSV (same as old version), JSON, and XML. Previously, requests could only be made using POST but now you may use GET as well. If rolling your own library instead or building one for another language this can be helpful as you can test your commands with a simple web browser. You can now remotely delete scores as well. This feature is disabled by default and you would need to turn it on in the game settings from purpletoken.com. With the new formats, you no longer need to parse the response string yourself as this library will use json to load the response directly into an array.
The video below shows the attached demo code. The scores are loaded via the library. Press ENTER to retrieve the scores. Press space to send a score, the example will automatically request the scores again (this is through the demo not the library). Use the left mouse button to delete a score. Obviously you want to use your own game key, but I'm providing a dummy one folks can play with.
Of course you can always create your own way of doing it, the API documentation is here:
https://purpletoken.com/api.php
PT Library
/*
*
* Author: Phaelax
* Date: Feb 20, 2024
* Version: 2.0
*
* Description: The Purple Token library was created to make it
* easier for users to incorporate the functionality
* of the Purple Token high-score service from within
* AGK Tier 1.
*
* Score format: No_Of_Entries,User1,Score1,User2,Score2,User3,Score3
*
*/
Type _PurpleToken_Score
id as integer
player as string
score as integer
date as string
EndType
// Internally used variables. You should never need to access
// _PT directly in your application
Type _PurpleToken
key as string
connection as integer
connType as integer
code as integer
scoresRetrieved as integer
scores as _PurpleToken_Score[0]
Endtype
Global _PT as _PurpleToken
// This array is safe to access from your application
// and contains the return codes possible for score submission
// The exception is an error code of -1, which is an unknown error
Global _PT_Codes as string[6]
#CONSTANT _PT_SCORE_GET = 1
#CONSTANT _PT_SCORE_SEND = 2
#CONSTANT _PT_SCORE_DELETE = 3
#CONSTANT _PT_ERROR_UNKNOWN = 0
#CONSTANT _PT_ERROR_SUCCESS = 1
#CONSTANT _PT_ERROR_NAMELEN = 2
#CONSTANT _PT_ERROR_BADKEY = 3
#CONSTANT _PT_ERROR_LOWSCORE = 4
#CONSTANT _PT_ERROR_REQS = 5
#CONSTANT _PT_ERROR_RIGHTS = 6
// Must be called before using any other PT commands
function PT_initialize(gameKey as string)
_PT.key = gameKey
_PT.code = -1
_PT_Codes[_PT_ERROR_UNKNOWN] = "Unknown Error"
_PT_Codes[_PT_ERROR_SUCCESS] = "Success"
_PT_Codes[_PT_ERROR_NAMELEN] = "Name length exceeds 32 characters"
_PT_Codes[_PT_ERROR_BADKEY] = "Gamekey not recognized"
_PT_Codes[_PT_ERROR_LOWSCORE] = "Score too low" // Score was too low to make the top 20
_PT_Codes[_PT_ERROR_REQS] = "Missing requirement" // if you forgot an ID on deletion
_PT_Codes[_PT_ERROR_RIGHTS] = "Insufficient Rights"
endfunction
// Initiate the connection to get list of scores
// Once the request has completed, PT_scoresAvailable() will return 1
//
// limit how many scores to return (decending order by score)
// incDates If 1 then score submission dates are included
// incIDs If 1 then score IDs is included (only necessary if you plan on manually deleting a score)
function PT_requestScores(limit, incDates, incIDs)
local vars$ as string
local extras$ as string
_PT.connType = _PT_SCORE_GET
_PT.scoresRetrieved = 0
_PT.connection = createHTTPConnection()
setHTTPHost(_PT.connection, "purpletoken.com", 1, "", "")
extras$ = "&limit="+str(limit)
if incDates = 1 then extras$ = extras$ + "&dates=yes"
if incIDs = 1 then extras$ = extras$ + "&ids=yes"
vars$ = "format=json&array=yes&gamekey="+_PT.key + extras$
sendHTTPRequestASync(_PT.connection, "update/v2/get_score/index.php", vars$)
endfunction
// Submit a score
function PT_deleteScore(id as integer)
local vars$ as string
_PT.connType = _PT_SCORE_DELETE
_PT.connection = createHTTPConnection()
setHTTPHost(_PT.connection, "purpletoken.com", 1, "", "")
vars$ = "gamekey="+_PT.key+"&score_id="+str(id)
sendHTTPRequestASync(_PT.connection, "update/v2/delete_score/index.php", vars$)
endfunction
// Submit a score
function PT_submitScore(player as string, score as integer)
local vars$ as string
_PT.connType = _PT_SCORE_SEND
//_PT.scoreSubmitted = 0
_PT.connection = createHTTPConnection()
setHTTPHost(_PT.connection, "purpletoken.com", 1, "", "")
vars$ = "player="+player+"&score="+str(score)+"&gamekey="+_PT.key
sendHTTPRequestASync(_PT.connection, "update/v2/submit_score/index.php", vars$)
endfunction
// Handles the http responses for API calls
// It is safe to call this every iteration of your game loop as it
// will not block
function PT_listener()
local ptResponse as integer
if _PT.connection > 0
if _PT.connType = _PT_SCORE_SEND // Score submission
ptResponse = getHTTPResponseReady(_PT.connection)
if ptResponse = -1
_PT.code = _PT_ERROR_UNKNOWN
elseif ptResponse = 1
_PT.code = val(GetHTTPResponse(_PT.connection))
endif
elseif _PT.connType = _PT_SCORE_GET // Score retrieval
local r$ as String
ptResponse = getHTTPResponseReady(_PT.connection)
if ptResponse = -1
_PT.code = _PT_ERROR_UNKNOWN
elseif ptResponse = 1
r$ = GetHTTPResponse(_PT.connection)
if val(r$) = 0
_PT.scores.fromJSON(r$)
_PT.scoresRetrieved = 1
_PT.code = _PT_ERROR_SUCCESS
else
_PT.code = val(r$)
endif
endif
elseif _PT.connType = _PT_SCORE_DELETE
ptResponse = getHTTPResponseReady(_PT.connection)
if ptResponse = -1
_PT.code = _PT_ERROR_UNKNOWN
elseif ptResponse = 1
_PT.code = val(GetHTTPResponse(_PT.connection))
endif
endif
if ptResponse <> 0 // response is completed, success or fail, close the connection
closeHTTPConnection(_PT.connection)
deleteHTTPConnection(_PT.connection)
_PT.connection = 0
endif
endif
endfunction
function PT_getScoreArray()
endfunction _PT.scores
// After a score submission has finished a code is generated (for success or possible errors)
// Once this function is called the code is cleared until a new submission is made
function PT_getCode()
local code as integer
code = _PT.code
_PT.code = -1
endfunction code
// Check if new score data is available after a request is made through PT_requestScores()
// Returns 1 for true, 0 otherwise. This will reset to 0 if a new request is made until
// that request completes.
function PT_scoresAvailable()
endfunction _PT.scoresRetrieved
// Returns a human-friendly string of the error code
function PT_getCodeString(v)
if v < 0 or v > 6 then exitfunction "Invalid Code"
endfunction _PT_Codes[v]
// Returns 1 if there's an active connection, otherwise 0
function PT_ConnectionActive()
if _PT.connection <> 0 then exitfunction 1
endfunction 0
Example demo:
// Project: ptv2_example
// Created: 2024-02-20
#option_explicit
SetErrorMode(2)
SetWindowSize(1024, 768, 0)
SetVirtualResolution(1024,768)
UseNewDefaultFonts( 1 )
SetSyncRate(0, 0)
// Include the Purple Token library
#include 'purpletoken.agc'
// Initialize the library with your unique private key
PT_initialize('40e1f1dc7624289738d4a147dea1770a8d859ab2')
Your_Name as string = "Johnny Bravo"
code as integer
codeText$ as string
/** Start silly demo stuff **/
font as integer
font = loadFont("Courier New")
scores as integer[20]
local i as integer
for i = 0 to 19
scores[i] = createText("")
setTextFont(scores[i], font)
setTextSize(scores[i], 40)
setTextPosition(scores[i], 150, 50+i*24)
next i
local mx, my as integer
j as integer
local a#, x#, y#, amplitude#, freq#, phase# as float
a# = 0
amplitude# = 5 // height of wave
freq# = 0.4 // speed of wave
phase# = 0 // don't worry bout that
r1 as float
g1 as float
b1 as float
r2 as float
g2 as float
b2 as float
rt as float
gt as float
bt as float
delete as integer
local zr1, zg1, zb1, zr2, zg2, zb2 as integer
local r,g,b as float
r# as float = 0
r1 = random(0,255)
g1 = random(0,255)
b1 = random(0,255)
r2 = random(0,255)
g2 = random(0,255)
b2 = random(0,255)
rt = random(0,255)
gt = random(0,255)
bt = random(0,255)
/** End silly demo stuff **/
do
print("Result: " + codeText$)
// ENTER key to retrieve scores
if getRawKeyPressed(13) then PT_requestScores(20, 1, 1)
// Press SPACE key to submit a score
if getRawKeyPressed(32)
codeText$ = "Submitting...."
PT_submitScore(Your_Name, random(5, 999999))
endif
// Get status of response. If no code is available, -1 is returned
code = PT_getCode()
if code > -1
// Automatically request scores after successful score submission
if code = _PT_ERROR_SUCCESS
if codeText$ = "Submitting...."
PT_requestScores(20, 1, 1)
else
if delete = 1
delete = 0
PT_requestScores(20, 1, 1)
endif
// scores retrieved, set strings of our text objects to do stupid silly fancy display
for i = 0 to _PT.scores.length
setTextString(scores[i], fillChar("0",2-len(str(i+1)))+str(i+1)+". "+ _PT.scores[i].player + fillChar(".", 48 - len(_PT.scores[i].player+str(_PT.scores[i].score))) + str(_PT.scores[i].score))
next i
for i = _PT.scores.length+1 to 19
setTextString(scores[i], "")
next i
endif
endif
// Get human readable code string
codeText$ = PT_getCodeString(code)
endif
/** start stupid silly fancy display **/
inc r#, 0.001
if r# > 1
r# = 0
r1 = r2
g1 = g2
b1 = b2
r2 = rt
g2 = gt
b2 = bt
rt = random(0,255)
gt = random(0,255)
bt = random(0,255)
endif
zr1 = r1 + (r2-r1)*r#
zg1 = g1 + (g2-g1)*r#
zb1 = b1 + (b2-b1)*r#
zr2 = r2 + (rt-r2)*r#
zg2 = g2 + (gt-g2)*r#
zb2 = b2 + (bt-b2)*r#
inc a#, 0.2
for i = 0 to _PT.scores.length
for j = 0 to 51
r = zr1 + (zr2-zr1)*(j/52.0)
g = zg1 + (zg2-zg1)*(j/52.0)
b = zb1 + (zb2-zb1)*(j/52.0)
setTextCharColor(scores[i], j, r, g, b, 255)
x# = j * 14
y# = amplitude# * sin(6.28*freq#*(a#+j*10)+phase#)
setTextCharPosition(scores[i], j, x#, y#)
next j
next i
// Use mouse to select score
my = floor((getRawMouseY()-50) / 24.0)
if my >= 0 and my <= 19
drawEllipse(130, my*24+66+getTextCharY(scores[my], 0), 10, 10, 0xFFFFFFFF, 0xFFFFFFFF, 1)
endif
// left click mouse to delete the score
if getRawMouseLeftPressed() = 1 and delete = 0
delete = 1
PT_deleteScore(_PT.scores[my].id)
endif
/** end stupid silly fancy display **/
// Display the scores
`displayScores(PT_getScoreArray())
// Listens for the http request responses
// Connections are automatically closed on completion
PT_listener()
Sync()
loop
function fillChar(c as string, n as integer)
local i as integer
local s as string
for i = 1 to n
s = s + c
next i
endfunction s
function displayScores(scores as _PurpleToken_Score[])
local i as integer
for i = 0 to scores.length
print(str(i+1) + ": " + scores[i].player + " : " + str(scores[i].score) + " : "+ scores[i].date)
next i
endfunction