Scoreoid for Monkey

Monkey Forums/User Modules/Scoreoid for Monkey

c.k.(Posted 2012) [#1]
I've started a module to help integrate Scoreoid functionality into monkey games.

Check out Scoreoid here: http://www.scoreoid.net

The Google Code repo (in hg) is here: http://code.google.com/p/scoreoid-for-monkey/

Why Scoreoid? No SDKs. It's all RESTful via http/https. This means it can be done in pure monkey. And since my first game project is pretty simple, I don't need anything more complicated than Scoreoid can provide right now.

UPDATES:

2013.03.04.01

Added *.java POST() function for mnet to this article for Android compatibility.

2012.08.06.02

All API calls (22) are now available. I'm building a test app now to test all of this. I don't expect any problems... FLW.

2012.08.06 - The module is incomplete, but the skeletal structure is ready. I suspect it will be complete in a few days, since there are only 22 API calls. If anybody is interested in helping out, please get in touch. I will accept changes/updates to the code here in this thread (which I'll then commit), or, if you'd like, I can give you commit permission.

At the moment, the Scoreoid module utilizes mnet, but I'm probably going to switch it to the NativeHTTP, since NativeHTTP has implementations for all platforms that monkey supports. Current problem with NativeHTTP: can't make HTTP POST work in HTML5. :-/

Oh! Almost forgot! You have to make the following changes in your code for this to work:

In mnet.monkey, add HTTP POST functionality:
[monkeycode]
'Http stuff
Class Http = "MNet_Http"
Method Get:String( url:String, timeoutConnection:Int = 3000, timeoutSocket:Int = 5000 )
Method AGet:String( url:String, timeoutConnection:Int = 3000, timeoutSocket:Int = 5000 )
Method GetResult:String()
Method Post:String(url:String, param:String, timeoutConnection:Int = 3000, timeoutSocket:Int = 5000) '<<--- add this
End Class
[/monkeycode]

In mnet.html5.js, add this entire function:
[monkeycode]

MNet_Http.prototype.Post = function( url, params, timeoutConnection, timeoutSocket )
{
try
{
var client = new XMLHttpRequest();
client.open( "POST", url, false );
client.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
client.setRequestHeader( "Content-length", params.length );
client.setRequestHeader( "Connection", "close" );
client.send( params );
var serverResponse = client.responseText;
return serverResponse;
}
catch( e )
{
return "";
}
}
[/monkeycode]

For Android compatibility, make sure you have POST() functionality in your mnet *.java file. It should look something like this:

[monkeycode]
public String Post( String url, String param, int timeoutConnection, int timeoutSocket )
{
HttpURLConnection con = null;
InputStream is = null;
try
{
URL connectToURL = new URL( url + "?" + param );
con = ( HttpURLConnection )connectToURL.openConnection();
con.setReadTimeout( timeoutSocket );
con.setConnectTimeout( timeoutConnection );
con.setRequestMethod( "POST" );
con.setDoInput( true );
// Start the query
con.connect();
is = con.getInputStream();
}
catch( IOException e )
{
_result = "ERR_HttpGet " + e.getMessage();
return "";
}
return convertStreamToString( is );
}
[/monkeycode]

That should be it, though I wouldn't be surprised if I left something critical out of this post. If you have any questions, let me know. :-)


Neuro(Posted 2012) [#2]
That would be awesome to get this working. I've been meaning to look into getting monkey to work with Scoreoid for a while now. Glad to be seeing this :).


c.k.(Posted 2012) [#3]
I've tested it with createPlayer() and deletePlayer(), and it works with those. It should work with all functions now. If not, let me know.

I'll be adding functionality to my game as the days go by, so I will be putting it through the ringer and will be exploring more efficient algorithms for it. In particular, the createPlayer() and editPlayer() functions have many many optional parameters, so I'm wondering what the best way to handle that is (right now, you have to pass it a parameters string that you build, as opposed to passing parameters and letting the module build the string for you). If you have ideas, let me know.


Neuro(Posted 2012) [#4]
Gonna give this a test later on today.

At the moment, the Scoreoid module utilizes mnet, but I'm probably going to switch it to the NativeHTTP, since NativeHTTP has implementations for all platforms that monkey supports. Current problem with NativeHTTP: can't make HTTP POST work in HTML5. :-/


I do agree with making it with NativeHTTP. But for just HTML5, couldn't you just check the current target for HTML5 and use MNET only for that?


c.k.(Posted 2012) [#5]
If @Xaron wants to get on the ball and finish mnet, then it won't be a problem... :-)

One of the issues with NativeHTTP (or maybe the issue), is that it is asynchronous (mnet is synchronous, meaning it doesn't return until it gets the result from the server). That means, I think, that it has to be constantly queried until it finishes. I guess that would be done in the OnUpdate() method of the scoreoid module, right?

I did try making the call to NativeHTTP synchronous, but didn't get it working yet...


c.k.(Posted 2012) [#6]
I'm changing up the internals so that each function returns a boolean, and you can get the results with Scoreoid.result. Seems to make it easier to use.

Example:

[monkeycode]
If Not Scoreoid.getPlayer("", playername) Then
' player does not exist so create him
If Scoreoid.createPlayer(playername, "&password=" + password) Then
lblMessage.SetText("Your profile has been created!")
Else
lblMessage.SetText("Could not create you. :-/")
EndIf
Else ' player exists, so are we logging in?
If Not Scoreoid.getPlayer("", playername, password) Then
lblMessage.SetText("ERROR: Bad username or password.")
Else 'successful login?
lblMessage.SetText("You are now signed in!")
EndIf
EndIf
[/monkeycode]

How does that work for y'all?

I'm wondering if I should create a player object, a game object, and a scores object... That way, when you do getPlayer(), you can then access a Scoreoid.player object to get/set attributes. We'll see...

Any input welcome! :-)


Neuro(Posted 2012) [#7]
I'm wondering if I should create a player object, a game object, and a scores object... That way, when you do getPlayer(), you can then access a Scoreoid.player object to get/set attributes.

Agrees with this :)


dmaz(Posted 2012) [#8]
hey c.k., before I start hacking away, is this still active?


c.k.(Posted 2012) [#9]
Somewhat. You are welcome to do with it as you please. If you want commit rights to the current repo, let me know.


Moggy(Posted 2013) [#10]
Has anyone got this running on android? for some reason i get "Error invalid API Key" on android, but it works fine on html5.


caffeinekid(Posted 2013) [#11]
Is the google code repository empty? I can't find any downloads there.

I also would like to use this with Android if it's working?


Moggy(Posted 2013) [#12]
You need to click on source, then browse
http://code.google.com/p/scoreoid-for-monkey/source/browse/

as for working on android, it should work, but for some reason it does not add the api key to the url string(i think lol)

you also have to add the post function to mnet.android.java, c.k was nice enough to send me his as its not in the main post,

public String Post( String url, String param, int timeoutConnection, int timeoutSocket )
  {
    HttpURLConnection con = null;
    InputStream is = null;
    try 
    {
      URL connectToURL = new URL( url );
      con = ( HttpURLConnection )connectToURL.openConnection();
      con.setReadTimeout( timeoutSocket );
      con.setConnectTimeout( timeoutConnection );
      con.setRequestMethod( "POST" );
      con.setDoInput( true );
      // Start the query
      con.connect();
      is = con.getInputStream();
    }
    catch( IOException e )
    {
      _result = "ERR_HttpGet " + e.getMessage();
      return "";
    }
    return convertStreamToString( is );
  }


i am honestly not sure why it doesn't work on android, or even if its something simple my end, someone with more coding experience and brain power than me will have to figure that out lol


c.k.(Posted 2013) [#13]
You need to use a mercurial client to grab the code, or you can grab it via the source/browse like Moggy said.

The Android code is in the main post, as of a few days ago I think.

Moggy, as I'm looking over the code, nowhere does the params variable get used. Am I missing something? I suspect there's more that should be there than there is.


Moggy(Posted 2013) [#14]
in your mnet *.java file.change
URL connectToURL = new URL( url );

to this
URL connectToURL = new URL( url + "?" + param );


fixed my issue on android, all works fine now :)


Aymes(Posted 2013) [#15]
Hey guys wonder if I could get some help on this.

Scoreoid looks perfect as a solution for high score stuff but I'm having trouble getting it to work.

Firstly, for HTML5, I can't seem to download the php file you mention in the readme.txt - I can see the link in the extra security page but it doesn't seem to work for me. Anyone else got this problem?

But I've tried on Android and can't seem to get a high score posted. This is what I've done:

scoreEngine = New BaaSEngine		
scoreEngine.setAPIKey("myAPIKey")
scoreEngine.setGameID("gameID")
scoreEngine.createScore("20", "testUser")


Any thoughts?


Moggy(Posted 2013) [#16]
i think you have to set the url before you call it

scoreEngine = New BaaSEngine		
scoreEngine.setAPIKey("myAPIKey")
scoreEngine.setGameID("gameID")
scoreEngine.setURL("https://www.scoreoid.com/api/createScore")
scoreEngine.createScore("20", "testUser")



c.k.(Posted 2013) [#17]
The URL should be the base. When you call createScore(), it should append "createScore" to the URL. The URL is defaulted to "https://www.scoreoid.com/", so you don't have to call it for non-HTML5 apps.

I think. :-D

For HTML5, the base is going to be a local PHP file that acts as a proxy. On my system, it's called "scoreoid_proxy.php". THIS WILL NOT WORK WITH MSERVER! You have to use Apache, or some other server.

I think. :-D


c.k.(Posted 2013) [#18]
I've updated the repo. Primarily, I added a test program that you can use to make sure you can connect to scoreoid. Requires your own custom scoreoid.txt in /data, so check it out and see if it's helpful.

It works for me with HTML and my own scoreoid_proxy.php file being served by Apache. Doesn't seem to work with MSERVER.


Aymes(Posted 2013) [#19]
Thanks for your help guys!

Unfortunately still couldn't download the php file from scoreoid. Android version still doesn't work despite the ping and other commands returning true. I can't create a new score from within the game :/

Ah well, I think I'll leave it for this project and look at it again when I can focus on just getting this aspect to work.


c.k.(Posted 2013) [#20]
Andrew, contact Scoreoid. They are pretty responsive and will help you get your file if you need it.

In fact, now that you mention it, I think they have to send it to you because that part of their process is broke right now... Oops.

I'll try running some Android tests tonight. I haven't had my PC at home because of a move, but that should be restored today.


Aymes(Posted 2013) [#21]
Righto, dropped them a line about it. I would be great to have something as simple as this to use in future for sure, plus with all the other functionality that comes with it other than just the high score stuff :)

If you do get anywhere with the Android version please let me know!


David Casual Box(Posted 2013) [#22]
Hi c.k.,

We're trying to use your module but we get this error when importing scoroidnet.monkey:

Monkey Runtime Error : TypeError: Cannot call method 'call' of undefined

Any idea?


c.k.(Posted 2013) [#23]
You get that when just importing the module?

What version of monkey are y'all using?


David Casual Box(Posted 2013) [#24]
Thanks for helping!
We're using v69 and mongoose as web server.


David Casual Box(Posted 2013) [#25]
C.K. can you try to help us?
We'll try today with Apache.


David Casual Box(Posted 2013) [#26]
We try under Apache, same result.

Monkey Runtime Error : TypeError: Cannot call method 'call' of undefined

The issue in not in your code, but with mnet. The mnet lib is very old (2012!!), do we have to use the version from https://code.google.com/p/mnet or do you use a modified version?


David Casual Box(Posted 2013) [#27]
We did a lot of tried, and managed to have the module working. It was, perhaps, the way we inserted your changes in mnet.html5.js. We restarted our project from scratch and it compile now!
We'll let you know when the game will be online :)


c.k.(Posted 2013) [#28]
Awesome! Sorry I was unresponsive. I was out of town over the weekend and not able to get here.

Good luck! Keep us up to date. :-)


Capn Lee(Posted 2014) [#29]
I recently used this module and it's been a great help, but it's kind of broken now due to changes at scoreoid's end.

I changed the POST method to:
	Method POST:String(apiCall:String, param:String = "")
		Local URL:String = ScoreoidURL + "api/" + apiCall
		#If TARGET="html5" Then
			Local params:String = "game_id=" + game_id + "&api_key=" + api_key + "&response=" + response
			Local skipCheck:Bool = True
		#Else
			Local params:String = "game_id=" + game_id + "&api_key=" + api_key + "&response=" + response
			Local skipCheck:Bool = False
		#End
		Local res:String = ""

		If api_key.Length() > 0 Or skipCheck Then
			If param.Length() > 0 Then
				params += "&" + param
			EndIf
			res = httpPost(URL, params)
		Else
			#If TARGET="html5" Then
				res = "[{~qerror~q:~qBaaSEngine Problem!~q}]"
			#Else
				res = "[{~qerror~q:~qBaaSEngine NOT INITIALIZED!~q}]"
			#End
		EndIf
		Return res
	End

I changed the format to fit with the way scoreoid works now.

Note i also removed the https:// as I found http:// was much faster and for my purposes security isn't much of a worry, you may want to keep it how it is, though

and i changed the CountBestScores method to
	Method getBestScores:Bool(order_by:String = "", order:String = "", limit:String = "", start_date:String = "", end_date:String = "", platform:String = "", difficulty:String = "")
		Local params:String = ""
		
		params = add_param(params, "order_by", order_by)
		params = add_param(params, "order", order)
		params = add_param(params, "limit", limit)
		params = add_param(params, "start_date", start_date)
		params = add_param(params, "end_date", end_date)
		params = add_param(params, "platform", platform)
		params = add_param(params, "difficulty", difficulty)
		
		resultText = POST("getBestScores", params)
		success = resultText.Contains("success")

		resultMap.Clear()
		Return success
	End

it's actually far dirtier than the original code but the original would only post the single top score. This will reply back with the json feed as a single string and you'll need to edit the important information out. Sorry, but I struggled with the json module for ages without really knowing what I was doing and decided this was easier for me.

Notice that I've changed success to be determined by reading success from the file rather than not finding an error because I found that not having a connection would make the resultText just read ""

Finally, there are no checks in the module's end for non net compatible characters in the fields and this meant usernames with a space were being truncated and possibly usernames with a quotation mark or comma to fail so I wrote a little function that would make the username net compatible before feeding it to the scoreoid module

Function NetChars:String(str:String)
	str = str.Replace("%", "%25")
	str = str.Replace("$", "%24")
	str = str.Replace("&", "%26")
	str = str.Replace("+", "%2B")
	str = str.Replace(",", "%2C")
	str = str.Replace("/", "%2F")
	str = str.Replace(":", "%3A")
	str = str.Replace(";", "%3B")
	str = str.Replace("=", "%3D")
	str = str.Replace("?", "%3F")
	str = str.Replace("@", "%40")
	str = str.Replace(" ", "%20")
	str = str.Replace("~q", "%22")
	str = str.Replace("<", "%3C")
	str = str.Replace(">", "%3E")
	str = str.Replace("#", "%23")
	str = str.Replace("{", "%7B")
	str = str.Replace("}", "%7D")
	str = str.Replace("|", "%7C")
	str = str.Replace("\", "%5C") 'this line totally contains a backslash but the forum doesn't like it
	str = str.Replace("^", "%5E")
	str = str.Replace("~~", "%7E")
	str = str.Replace("[", "%5B")
	str = str.Replace("]", "%5D")
	str = str.Replace("`", "%60")
	Return str
End


hope this helps someone


Sammy(Posted 2014) [#30]
Well done, although I have not used Scoreoid with Monkey, I have used it in the past. It's a very nice little feature to add to any-ones games. It's nice to see that this option is now (re)open for Monkey users again.


Lugato(Posted 2014) [#31]
Hi Capn Lee,

I tried to use the lib an have some problems :(

Some question:
First: The MNET Lib that you used is this: https://code.google.com/p/mnet without changes ?
Second: The json lib that the scoroid lib use is the one that made for warpy ? (/bananas/warpy/json)

And finally the error that i get:
"expecting class member declaration: when the field playerParams will be set to string

Can you give a little help here please ?

PS: I has adjusted the scoroid lib with your code ;)

thanks


Capn Lee(Posted 2014) [#32]
I did just download the newest mnet and made the changes in the initial post (replaced the http class etc)
the json module i think it uses is this one https://code.google.com/p/monkey-json/
what function is being called that causes this problem, the only thing I've really tested is GetBestScores() and CreateScore() as I'm not using much of the advanced stuff so it could be that changes I've made muck up other areas of the module I don't use.

because this thread is a little bit lost with changes and such I'll upload the modules as I know them to be working. Mnet has been altered as stated in the 1st post in this thread, json is the old monkey-json project unaltered and scoreoid has my changes in there
https://dl.dropboxusercontent.com/u/2021720/modules.zip


Capn Lee(Posted 2014) [#33]
scratch all that, I tried to build my app for iOS and found that the code this module is reliant on (mnet) will not work with newer xcode.
I've written my own little class for this instead that uses brl.httprequest instead and it's much nicer than the previous.
I'm a terrible coder so feel free to correct anything here but I can assure you this works on Android, iOS and HTML5 without the messing around that the module needs.

this is the module:
Import brl.httprequest

Class Scoreoid Implements IOnHttpRequestComplete
	
	Field game_id:String
	Field api_key:String
	
	Field responseText:String
	Field complete:Bool = True
	
	Field scoreoidURL:String = "https://api.scoreoid.com/"
	
	Method New(api_key:String = "", game_id:String = "")
		Self.game_id = game_id
		Self.api_key = api_key
	End
	
	Method Complete:Bool()
		If complete = True
	'		Print("true")
			Return True
		Else
	'		Print("false")
			Return False
		EndIf
	End
	
	Method SetApiKey(api_key:String)
		Self.api_key = api_key
	End

	Method SetGameID(game_id:String)
		Self.game_id = game_id
	End
	
	Method Update()
		If complete = False
			UpdateAsyncEvents()
		EndIf
	End
	
	Method Response:String()
		Return responseText
	End
	
	Method OnHttpRequestComplete:Void(req:HttpRequest)
		responseText = req.ResponseText()
		complete = True
	End
	
	Method CreateScore(score:String, username:String, unique_id:String = "", platform:String = "", difficulty:String = "", data:String = "")
		complete = False
		Local req:HttpRequest = New HttpRequest("GET", scoreoidURL + "v1/createScore?api_key=" + api_key + "&game_id=" + game_id + "&score=" + score + "&username=" + username + "&unique_id=" + unique_id + "&platform=" + platform + "&difficulty=" + difficulty + "&data=" + data + "&response=json", Self)
		req.Send()
	End
	
	Method GetBestScores(order_by:String = "score", order:String = "desc", limit:String = "25", start_date:String = "", end_date:String = "", platform:String = "", difficulty:String = "", usernames:String = "")
		complete = False
		Local req:HttpRequest = New HttpRequest("GET", scoreoidURL + "v1/getBestScores?api_key=" + api_key + "&game_id=" + game_id + "&order_by=" + order_by + "&order=" + order + "&limit=" + limit + "&start_date=" + start_date + "&end_date=" + end_date + "&platform=" + platform + "&difficulty=" + difficulty + "&usernames=" + usernames + "&response=json", Self)
		req.Send()
	End
End


and here is example code on how to use it:
Import mojo
Import scoreoid

Global g:ScoreoidTest
Function Main()
	g = New ScoreoidTest
End

Class ScoreoidTest Extends App

Field scoreoid:Scoreoid

	Method OnCreate()
		SetUpdateRate(60)
		scoreoid = New Scoreoid("0000000000a000a00a000a00a0", "000a000a00a") 'obviously this needs to be a correct key and id
		scoreoid.CreateScore("99999", "capnlee")
	End

	Method OnUpdate()
		scoreoid.Update()
	End
End