Achievements

One potential downside to the client-side string-hash method is that it doesn’t allow for achievements like “attempt to kiss every NPC in the game” if the game itself does not track this, or other sweet cheevos involving external state tracking. Other than that I like the idea & would be stoked to add achievements to Dinner Bell.

And once in the game site, you just have to open the console and start executing “saveAchievement()” function calls to get all achievements you want.

There is no “story file” to unpack in most javascript engines. Even if this is implemented for Inform using a Glk opcode, it would be easy to generate them all for all Inform games just with a bit of hacking on the glkote.js implementation in Quixe. Same goes with the native interpreters, you can easilty modify the glk implementation, recompile glulxe and fake them all. The problem is in the end there is a common entry point for all achievements in interpreter code, and if the interpreter is open source (as most IF engines terps are) or just the game itself is open source by default (like all html/javascript engines) you don’t need to hack the authentication, you just hack that common entry point.

And having the game online doesn’t help, you can just go to the site, open javascript console, and start launching the function that you know generates achievements just after authentication is made.

The dummy achievement may be a good a idea for a “Hall of Shame” list though :smiling_imp:

How does this solution manage to report achievements completed to server?

I do. :slight_smile: Not for extended play, but for a while it’s fun. It’s pretty much exactly what I was talking about, so I knew I wasn’t being original.

Although I prefer the space-invaders-type one which is less about achievements and more about unlocking. Unlock the “play game” option. Unlock graphics. Unlock music. Unlock everything.

In my mind, achievements would have a unique Id, like a Guid.

{1ce2c571-3433-429f-88e9-e353cb038195}

This is embedded within the Glulx file itself. The only time it would be emitted for achievement unlocking is when the game emits it. The interpreter (JS or native) would make the call to the achievement server. Sure you’d know the code at that point, but you wouldn’t know the code until then.

And before you say, “Someone will post all of the codes!”, that’s easily managed by using a public-private key hash with your credentials. So any given achievement key will only work for you and no one else.

There are ways to make this really hard for a hacker.

Of course that’s usually incentive, I know.

Making achivement ids have that format will only make easier for hackers to find the achievement ids. In a Twine, ngpaws of fi.js game, that are pure javascript engines, all the strings are visible in plain text (not 100% sure about Twine, but almost).

Of course you can minimize the code, even obfuscate it, but there are as many javascript code minimizers as de-minimizers, and de-obfuscators are also around. With that taken into consideration, I bet is better to use non-consecutive integer ids than UUID style ones, but still it would be easier to just find the “saveAchievement” calls :wink:

For a glulx file, that is run by an interpreter (wether it is javascript or native) it may work for a while, but I’m sure is easy to use deform to decompile the ULX file once it is extracted from the gblorb file with blorb utilities. And though deform produces a quite unreadable I6 source code, I’m sure it won’t be difficult to find some UUID-like IDs in there, even integer Ids would be easy to find as you would just have to find the proper glk call.

In the end I’m just detailing what zarf summarized in something like “if the client code is open, then there is no way to hide the achievement codes”.

Anyway, I’m noting down everything, maybe if we finally have a spec we may consider using UUIDs, integer ids, auth tokens… or maybe the spec just doesn’t define authentication and only achievement communication, leaving authentication work and decisions to each interpreter/engine author. While they are able to provice a unique userid that the server can recognize as a player, it really doesn’t matter :slight_smile:

This rings a bell… any achievement system should have some kind of sub-achievements, or achievement parts, that may also be defined, so in some cases instead of:

saveAchievement(userid,gameid,achievementid)

there may be

saveAchievement(userid,gameid,achievementid, stepid)

Which would be succesful only if all steps for an achievement has already been achieved.

So if there is a game where finding Dr. Livingston all along river Nile is main objective, there may be optional missions built as achievements like “get to lake Victoria” (simple achievement) or “find the seven clues of Moses presence in Egypt” (stepped achievement).

Achievement IDs aren’t going to be secret at all, since we’re talking about a reporting API. (“Tell me all the achievements for game X, and which ones player P has gotten.” This is the typical request for a trophy page for a game.)

I suppose each achievement could have a public ID and a “secret” unlock code, but as we’ve been saying, it wouldn’t be very secret and it’s not worth the extra complexity.

Similarly – I see no point to using OAuth for this. Simple web password-and-cookie (over HTTPS) will do the same job simpler.

True. The author would have to mark the achievement entry as having N steps (when setting it up).

In the iOS model (the only one I’ve worked with), you can mark an achievement “complete” or “x% complete”. (That is, you always describe it as X out of 100, rather than specifying a number of steps.) This is more straightforward, but you lose the ability to say “found 23 out of 35 spells in the game” or whatever.

You’re right, that only solves half the problem. But as you all were saying, something with authentication or cookies?

Another possibility I had considered (and this may be out of the scope of this conversation?) was having all the computations/interpreting happening on the server, and only sending commands and responses between server and client; essentially, having a dumb client. This would solve the problem of reporting achievements and preventing cheating, since everything is on the server side.
I think that’s what TADS (or is it htmlTADS?) does, but it’s not the current trend for Inform (Parchement and Quixe vs ClubFloyd, essentially). So the same website recording the achievements would also be the place where you play them, which may or may not be a good idea. And it’s way harder than what we’re talking about here: your server has to perform all the computations, you need to find a way to send music and pictures (JSON?) and support timed events, etc. On the other hand, maybe you can do some precomputation/batching that keeps the load manageable and makes big games faster (since the client side is dumb). And you could even record transcripts for authors, maybe even sell games on there and… woops, too far, that’s almost Steam.

Built it already (Glulx->Zifmia). It was nice. It worked. Never sure how to get adoption and I had a few ratholes I spent way too much time on (branching mechanism to allow the player to go back to any point in a tree of play). Client-Server has uses. I was also using it to build an education/classroom IF platform. Would have worked too. Guy killed me with a sword, Mal.

I think that’s the way achievements work in Battle.net or Steam, server side, but those companies invest millions in avoiding cheating, cause is an important part of their core busines (players won’t play if other players can cheat easily). Building something like that it’s quite complicated, specially if you want to be able to run games server side (and when I mean server side I mean really server side, that is a javascript game should not be served by the server so the client executes it, but run in the server using node.js or similar and just send output text and multimedia to player). All that would be a major change in the way games run and is really out of the scope of this thread :slight_smile:

Of course, when you start doing something centralized like achievements is easy to come to a lot of “and why don’t we also …?”; I had a chat on IRC with the guy that is mantainig the CAAD server (spanish IF site) and a lot of this posibilities appeared, but we both agreed (and he can correct me if I’m wrong cause I’m sure he is reading) that while creating the achievement system in a way that could be used for more features in the future was right, trying to do it all at the same time was doomed to fail. I rather prefer attainable goals, than biting more than I can chew :wink:

On the other hand, while having a centralized server for gaming is nice (and I like the way textadventures.co.uk/ is built so much), it’s not for everybody, and not for every game. I mean that there are few reasons why a game that may be playable ofline should become a server side game, even if it contains achievements, it should be playable (you just won’t get any achievement if you are offline).

Regarding authentication, I think it would be good to use a username/password that people already use, rather that creating a new one, but of course that means interacting with some other site (i.e. this forum).

Do you think using this forum’s login is possible or should we go for another solution? (or just another site like ifwiki, ifdb…)

I agree with zarf and Uto that contrived anti-cheating measures that complicate the protocol are not worth it. No matter how authentication is implemented, this has the same base problem as DRM: the key is in the attacker’s machine from the beginning. Huge companies have tried again and again to design security systems around this problem, and they have failed. Furthermore, there is also what dfabulich said, a bot can be programmed to finish the game and achieve everything.

In fact, in AGE (apart from the fact that the source code of games is in plain text, I have never cared much about preventing cheating as there has been no demand for encryption or other anti-cheating mechanics) you can save a game in log form, send the log to a friend and zoom! Your friend can run the log and automatically do everything you did. So uploading your achievements for everyone and their dog to reproduce them would be trivial.

So to sum up, I love the idea of the achievement system (as I already told Uto in the CAAD forums) but I don’t care much for anti-cheating and I don’t think it’s worth the time to define and implement it. If that is included in the protocol at all, I hope it is made optional.

This is a draft of what the spec may be, plus the data structures required to keep data in server. Any comments are welcome.

Finally I decided to make it simple, and include the authentication server in the service itself (that is, not using third party login methods), user would be able to register and get their achievement server own username.

The spec in no way defines how the interpreters or game engines should handle the achievements, or show them on success, is up to terp/engine to show them the way they prefer.


Data structures
===============


Game object properties
----------------------

- A owner (userID)
- A title
- A description
- A picture
- A game id (IFID)



Achievment object properties
----------------------------

- A game id (IFID)
- A title
- A description
- isPublic: a boolean defining if the server will list this achievement when list of achievements for a game is requested
- An optional parent achievement. If defined, parent achievement will only be completed if all child achievements are completed.
- An UUID


User object properties
----------------------

- A nickname
- A username
- A password
- An e-mail address (optional), used to recover password in case of need
- A userID
- A user token (generated on login)

User-achievement properties
---------------------------

- A userID
- An achievement UUID
- A timestamp





Server methods
==============


Login method
------------

ifas.php?action=login&username=XXXXX&pasword=yyyyyy

Returns:

On success:

{"success":"1","usertoken":"<a user token>"}

On failure:

{"success":"0"}



Achievement method
------------------

ifas.php?action=achieve&usertoken=<a token>&achievementID=<an achievement UUID>


Returns:

On success:

{"success":"1", "title":"<achievement short description>", "description":"<a description>"}

On failure:

{"success":"0", "errocode":"<an error code>"}

Error codes:

0: Achievement already completed
1: Invalid user token
2: Invalid achivementID 



Get User Achievements method
----------------------------

ifas.php?action=userchievements&usertoken=<a token>
ifas.php?action=userchievements&userid=<a userID>
ifas.php?action=userchievements&nick=<a nickname>


Returns an array of arrays of games together with achievements on those games

Sample:

[
{"gametitle":"18 Rooms to Home"}, "achievements":[{"title":"some title","description":"some description","timestamp":"some unix timestamp"},{"title":"some other title","description":"some other description","timestamp":"some other unix timestamp"}]}, {"gametitle":"Leadlight Gamma"}, "achievements":[{"title":"some title","description":"some description","timestamp":"some unix timestamp"}]}]

Accepts an optional parameter: ifid=xxxxx that will return only achievements for that game, please notice that if IFDB is wrong it will just return an empt array.


Get Game Achievements method
----------------------------

ifas.php?action=gameachievements&ifid=<an IFID>

Retuns an array of achievements together with nicknames and dates of achievers

Sample:

[{"title":"an achievement title", "description":"an achievement description", achievers:[{"nickname":"a nickname", "timestamp":"a unix timestamp"}, {"nickname":"other nickname", "timestamp":"some other unix timestamp"}]} , {"title":"some other achievement title", "description":"some other achievement description", achievers:[{"nickname":"some other nickname", "timestamp":"some other unix timestamp"}, {"nickname":"yet another nickname", "timestamp":"yet another unix timestamp"}]}]


Q&A
===

- Why there is no game id in the achivement method anymore?

The achievement UUID is unique among all games, so there is no need to provide gameid.

- Why does the achivement method return title and description on success?

To be used when showing the achievement to player, and ensure the same text visible in server is shown in game.

- How do you handle achievements that have several parts?

Game author will call the achievement method per each of those parts, using the part UUID, and that call will only be successful when the server determines all child achievements have been completed. If that happens, parent title and description will be returned, and parent achievement will be marked as completed. API calls to get achievement lists will never return child achievements. Parent achievements cannot be completed directly (if an achievement with child achievements is called directly, it will return error code 2 - invalid achievement ID)

- What happens if the player rejects taking part in the achievement system

As it is not related with the achievement service, is not in the scope of this specification to define that. Interpreter/engine designers may decide just ignoring the achievements, show them but save locally or whatever they prefer.

Out of curiosity, let’s say that a game makes no provisions for local achievements, instead relying entirely on this server-based system.

What happens if the game is played when there is no internet connection available? Will there be no achievements available at all?

If so, well, it is always the responsibility of the author to check these things, of course, but as a player I would prefer to be able to get those achievements even if my internet isn’t on. If this is easy for authors to overlook, then it might be best to make it not-so-easy to overlook. Like, a game that wishes to use achievements relies on its own locally-saved system, and at some point it outputs a copy of what it’s saving to a server… instead of just outputting directly to the server and maybe making a local copy as an afterthought.

In an Inform 7 extension, for instance, I would imagine all of this could be taken care of automatically - ensuring a standard. A standard which ensures a local copy.

You guys are way in over my head, but as a layman and as a player, this is something I felt I ought to bring up. Do correct me if it’s a non-issue.

Basically, is out of the scope of a online achievements server to define what happens if you are not online, but I understand that as player you would like to have achievements anyway. It’s not as easy as it looks like though:

  • Achievements are game-wide, nor just related to some specific gameplay, so you can get them just once in your life. Having that in mind, if you hace achievements #1, #2 & #4 (from a list of 10 achievements) and then you get offline, saving that status locally, when game detects you may have got achievement #9, but there is no response from server, game just can’t know if you have already got that achievement in some other computer while you were offline in this one, so showing the achievement as completed may be wrong.

  • You can solve that adding a queue of “non-checked” achievements locally (as I think you suggest) so they are checked again when you get back online, but in that case those achievements, if successful, will get the date when they were acknowledged by the server, not when they really happend.

IMHO, if some authoring system/interpreter developer decides to handle offline achievements, that support should be deep tied to the authoring system extension, so nor game author nor player should have to worry about it. But again, I didn’t want to define how that should be treated, cause is very specific for each authoring system/interpreter, and have the cons listed above.

It is a issue, but forcing authoring system/author to implement a complex offline system to meet specifications is raising a barrier I don’t want to raise. IMO the spec shoudl be as simple as possible so having achievement libraries for several authoring systems is as easy as possible.

On the other hand, I think if this service finally succeeds and is used for a few authoring system, eventually all of them will have at least that queue…

Coming at this form a web-devlopment-within-education perspective, a system already exists which I believe fits fairly well with what you’re trying to achieve: Mozilla OpenBadges: http://openbadges.org/ (There’s an implementation and general FAQ here: http://openbadges.org/legal_faq/) From the website:

We would need an ‘issuing authority’ (which could be per-game, or one overall) and there’s already social media integration (if that’s your thing). I believe the APIs are mature at this point but cannot claim to know the details.

Anyway, that’s my 2p.

Plain text passwords? Then HTTPS is a must.

Also, good practice of password handling usually says: Don’t store them raw in the users’ database. Don’t send them in the query string of URLs.

Thanks for sharing this info. On the other hand I had already checked openbadges, and I was checking if it allowed listing all achievers for a game but I stopped checking when I found this:

openbadges.org/legal_faq/#coppa

Openbadges can’t be used for people younger than 13, and though I know most people in IF scene are not in that case, I’m afraid that is not good for a gaming achievement service.

Yes, https is a must, at least for the login method. Also passwords won’t be stored raw in the database, but as some bcrypt/SHA/Md5 encoded hash value (to be decided). About avoiding passwords in the URL, we could use POST, to avoid (at least) passwords being saved in the web server access log. In fact, we can make it so every parameter can be sent either using POST or GET requests, but password (or but login method).

All those are implementation details for the server I haven’t included in the draft, but as the server will be open sourced anyway you all will be able to check and even detect potential risks (if any).