Neopets system includes
The Neopets system includes are a set of Flash SWFs that are included and used by the Flash games.
❗ This page is going to suck until I better understand Flash development and reverse engineering. ❗
Old versions
The promotional disc[1] has the following old versions of the system includes:
- /system/bios.swf, 6441 bytes, modified 2007-06-04
- /system/np6_include_v7.swf, 49408 bytes, modified 2007-03-06
- /system/np8_include_v7.swf, 54949 bytes, modified 2007-03-06
BIOS
This file is set to SWF version 6 (Flash 6 minimum). Based on my not-good understanding of ActionScript, it appears to do the following things:
On frame 1
- Check how the BIOS is loaded
- Check if
String(this) == "_level0"
; if so, trace "Bios: Error, load this externally!" and stop the movie - Check if
String(this).split(".")[0] == "_level0"
; if not, set a local flag to false, which appears to change some sort of progress meter display slightly and set a translation string path to use non-local paths - Else, set the local flag to true and set a separate offline flag to 1
- Check if
- Set up the loading fade-in effect for the "NEO*BIOS 330-MEGA" chip
- Wait for itself to finish loading
- If the local flag is true, open
system/np6_include_v7.swf
onto the target "_level100" and wait for it to finish loading - Play this movie's parent's parent
On the "bios" sprite
- If the parent is _level0, print "BIOS must be loaded by another file!" and stop the movie
- copyInProps, which seems to copy the properties of the BIOS child of either _level10 (if loaded) (not 100) or _level0 (if _level10 not loaded) onto this object
- addProtoCode, which seems to change Object.toString to be a serialization system like
{key1:value1,key2:{k3:v3,k4:v4}}
, but with a new line for each level, and adjustable indentation for each level - If fully loaded, make the parent invisible, and finish
- If not fully loaded, load "http://images.neopets.com/games/high_scores/include_movie.swf" onto the target _level100 and wait for it to finish loading
Other notes
The circuit background appears to actually be stored in the game resources instead of in the BIOS movie itself.
NP6
This file is set to SWF version 6 (Flash 6). This analysis was done with the v7 version.
Reporting cheaters
This is in DefineSprite (51) -> frame 1 (wait) -> DoAction.
There are two mechanisms, called gameMsg and msg. They take two parameters: index and append. They create a "body" string of the format "game_id - game_filename - game_username", then append "append" if it isn't undefined. This is then POSTed to "http://www.neopets.com/games/dgs/dgs_protocol.phtml?id=index&subject=game_id&body=body" in obfuscated fashion, and the variables from the response loaded to _level0.
gameMsg is similar, except that " - old call -" is appended to "append" before anything happens, and the game then opens http://www.neopets.com/games/cheatmonster.phtml in a new window.
Dictionary support
This is in the same object and frame, but DoAction [7].
It loads "http://www.neopets.com/games/utilities/flash_dictionary/flash_dictionary_en_vVERSION.swf", where VERSION is the value of _level0.game_dict_ver. While live site games using NP8 use dict version 62, I can't find any versions for this URL from v7 anymore. However, the dictionary can check if something is a word, a bad word, dictionary word, and/or Neopian word. Words can also be scrambled and selected randomly.
NeoStatus
DoAction [9]. These URLs are hit:
- NEOSTATUS_URL = http://www.neopets.com/neostatus.phtml
- PROCESS_URL = http://www.neopets.com/process_click.phtml
- tracking URL base: http://www.neopets.com/track_plays.phtml?game_id=id where id is _level0.game_id
- nc_track URL base: http://www.neopets.com/nc_track.phtml
Events information
There is an idea of events, which have a tag name, status code, and offset. When creating an event, a base_multiple is also needed. This sets "multiple" to base_multiple * 2
and "active" to random(offset) + 1 == 1
.
Tag Name | Status Code | Offset |
---|---|---|
Game Started | 900 | 1 |
Multiplayer Game Started 1 | 901 | 1 |
Multiplayer Game Started 2 | 902 | 1 |
Multiplayer Game Started 3 | 903 | 1 |
Multiplayer Game Started 4 | 904 | 1 |
Game Finished | 1000 | 1 |
Sent Score | 1001 | 1 |
Reached Level n (where 1 <= n <= 100) | 7000 + n | 10 |
Sponsor Item Shown | 8010 | 30 |
Sponsor Logo Shown | 8020 | 10 |
Sponsor Item Collected | 8030 | 30 |
Sponsor Banner Shown | 8040 | 30 |
If tracking is enabled (Number(_level0.game_tracking) == 1
), Game Started and Game Finished events hit the tracking URL base with the following parameters added to the query string:
- dowhat=game_starts if Game Started, or game_ends if Game Finished
- multiple= followed by
_level0.game_multiple
- r= followed by
random(999999999)
The variables from this call to the tracking URL are then loaded. When I try URLs like https://www.neopets.com/track_plays.phtml?game_id=987&dowhat=game_ends&multiple=0&r=8309101 using the values from 200m Peanut Dash, nothing is in the response, so I'm not sure what the NP6 loader was trying to find.
Then, for the main NeoStatus URL, this gets hit once for each event:
- item_id= followed by
item_id
, which seems to be -1 if _level0.nsid is undefined and debug not -1, 1 if undefined and 1, or _level0.nsid - multiple= value of multiple for this event, which is that base_multiplier * 2 from earlier
- status= value of statusCode for this event
- If URLSuffix is undefined for the call: nc_value= followed by
_level0.nc_referer
. Note that nc_referer doesn't seem to be set to anything other than a blank string on the live website, though that's with NP8 games and not NP6. - r= value of random(999999999)
There is client-side logic to prevent sending an event if item_id is set to -1, which happens when _level0.nsid is undefined. Sending is also prevented if the "active" flag for the event is not 1. I am going to assume the random() call is exclusive, so events will be sent with probability 1 / offset
. In other words, if offset is 1, then it will always be sent; otherwise, only sometimes.
Process click
Then, for process click, which seems to be unrelated to the main NeoStatus system, these are the query parameters:
- item_id= passed item_id
- random= value of Math.random() * 999999999
If the requested method is POST (the default), open it in the passed windowName window if called via processClickGetURL. Else, if called by processClickLoadVariables, a "type_id" and "nc_value" need to be present too, and the variables from the body are loaded into this object.
Membership stuff
If a signup or login is gotten from the game, hit the nc_track URL base with the following parameters:
- type_id=12
- item_id=3187 if signup, else 3188
- nc_value= if undefined nc_referer, treat it as 0, then concatenate game_id, a hyphen, and nc_referer
- r= value of Math.random(999999)
User Profile
DoAction [11]. You pass an array with any of the following items:
- 1: scores_sent
- 2: high_score
- 3: user_age
- 4: user_gender
- 5: pet1_name
- 6: pet1_color
- 7: pet1_species
- 8: user_full_name
- 9: user_email
- 10: user_country
- 11: user_dob
- 12: pet2_name
- 13: pet3_name
- 14: pet4_name
Slap a semicolon at the end of the numbers of each one you want, concatenate them together, call it typestring, then hit http://www.neopets.com/high_scores/fg_get_info.phtml?game_id=game_id&type=typestring. The response is then read using loadVariables()
.
Note that the pet names appear to be buggy. If you specify 12, 13, or 14, you only get pet1_name and pet2_name, regardless of which number you enter. If you specify two of them, you also get pet3_name. If you specify all three, you also get pet4_name. Yes, you can get the four pet names without actually specifying 5. Also, if you specify enough other numbers, you get access to the following numbers too:
- 15: active_pet_mood=hardcoded_as_happy
- 17: user_full_name (note that the one under number 8 doesn't seem to work anymore sometime after NP6 v7)
- 18: is_admin
- 19: is_sponsor
- 20: neopoints
- 21: lang
Scoring
DoAction [13]. TODO: document this better
http://www.neopets.com/high_scores/process_flash_score.phtml with params:
- cn= value of 300 * _level0.game_id
- gd= value of getTimer() - _level0.game_isLoaded
- asp_cName=val for every pair of cName, val passed to addScoreParameter(cName, val) ahead of time
- r= value of Math.random(999999999)
- gmd_g= value of _level0.game_hash
- mltpl_g= value of _level0.game_multiple
- gmdt_g= value of sSlashed, see below
- sh_g= value of _level0.game_hash
- sk_g= value of _level0.game_sk
- usrnm_g= value of _level0.game_username
- dc_g= value of _level0.game_dailyChallenge
Where sSlashed is the following stuff in query string (minus the question mark) format, encoded in some crazy algorithm defined in np.projects.include.Strings:
- ssnhsh= value of _level0.game_hash
- ssnky= value of _level0.game_sk
- gmd= value of _level0.game_id
- scr= value of _SCORE.show()
- frmrt= value of _level0.average_real_framerate
- chllng= value of _level0.game_challenge
- gmdrtn= value of getTimer() - _level0.game_isLoaded
NP8
This file is set to SWF version 8 (Flash 8).