agario-client
agario-client copied to clipboard
Node.js agar.io client implementation
agario-client
Node.js client for agar.io with API.
Outdated!
Agar developers made completely new code for game. Now code runs inside virtual machine that I can not reverse-engineer due to lack of knowledge and experience.
Unfortunately its time to say goodbye to agario-client.
Initial idea of agario-client was for me to learn websockets, binary protocols, reverse-engineering, github and have fun overall. I believe that I achieved all that.
Thanks to all, it was great time!
Instructions
- Install Node.js
- Install client using
npm install agario-client(ignorepythonerrors) - Run
node ./node_modules/agario-client/examples/basic.js(for testing purpose) - If it works, you're ready to look at API and code
API
There are two types of object that have API:
- Client - thing that connects to agar.io server and talks to it. If you want to spawn and control your
Ball, you need to talk withClient - Ball - thing that
Clientcreates. Everything in game isBalls(viruses/food/players...). You can't controlBallsobjects, only observe what they do.
Both objects have same methods for events from events.EventEmitter:
.on('eventName', callback)attach listener to event.once('eventName', callback)attach listener to event but execute only once.removeListener('eventName', callback)remove listener from event.removeAllListeners('eventName')remove all listeners from event.emit('eventName', p1, p2...)emit your own event- check more in documentation
Client API
var AgarioClient = require('agario-client');
var client = new AgarioClient(client_name);
client_name is string for client that will be used for logging (if you enable it). It's not your ball name.
Client properties
Properties that you can change:
client.debugdebug level. 0-5. 0 is completely silent. 5 is super verbose. Default: 1client.serveraddress that was used inclient.connect()callclient.keykey that was used inclient.connect()callclient.auth_tokentoken to login. See how to get token in additional info.client.agentagent to use for connection. Check additional info.client.local_addresslocal interface to bind to for network connections (IP address of interface)client.headersobject with headers for WebSocket connection. Default: {'Origin':'http://agar.io'}client.inactive_destroytime in ms for how long ball will live in memory after his last known action (if player exit from game or ball eaten outside our field of view, we will not know it since server sends action only about field that you see. Original codedestroy()Ballwhen hedisappearfrom field of view. You can do that inclient.on('ballDisappear')if you want it for some reason). Default: 5*60*1000 (5 minutes)client.inactive_checktime in ms for time interval that search and destroy inactiveBalls. Default: 10*1000 (10 seconds)client.spawn_attemptshow much we need try spawn before disconnect (made for unstable spawn on official servers). Default: 25client.spawn_intervaltime in ms between spawn attempts. Default: 200
Properties that better not to change or you can break something:
client.ballsobject with allBallsthatclientknows about. AccessBalllikeclient.balls[ball_id]client.my_ballsarray of aliveBall's IDs thatclientowns and can control.client.scorepersonal score since spawnclient.leadersarray of leader'sBallsIDs in FFA mode. (player can have lots ofBalls, but sever sends only one ID of oneBall)client.teams_scoresarray of team's scores in teams modeclient.client_namename that you have set forclient(not nickname)client.tick_counternumber of tick packets received (i call them ticks because they contains information about eating/movement/size/disappear... ofBalls)
Client methods
client.connect(server, key)connect to agar.io server. Check Servers part in this readme to see how to get server IP and key. ProTip: each server have few rooms (if it is not party), so you may need connect few times before you will get in room that you want (but you need new key each time and agar.io can ban your IP for flooding with requests). You can lookclient.once('leaderBoardUpdate')to know if you're connected to correct roomclient.disconnect()disconnect from serverclient.spawn(name)will spawnBallwith nickname.client.on('myNewBall')will be called when server sends ourBallinfo. Will returnfalseif connection is not established.client.spectate()will activate spectating mode. Look atclient.on('spectateFieldUpdate')for FOV updates. Will returnfalseif connection is not established.client.spectateModeToggle()switching spectate mode in spectating mode (Q key in official client). Useclient.moveTo()to move your "camera" around. Look atclient.on('spectateFieldUpdate')for movement updates. Will returnfalseif connection is not established.client.moveTo(x,y)send move command.xandyis numbers where you want to move. Coordinates (size) of map you can get inclient.on('mapSizeLoad'). YourBallswill move to coordinates you specified until you send new coordinates to move. Original source code do this every 100ms (10 times in second) and before split and eject. Will returnfalseif connection is not established.client.split()will split yourBallsin two.Ballwill be ejected in last direction that you sent withclient.moveTo().client.on('myNewBall')will be called when server sends ourBallsinfo. Will returnfalseif connection is not established.client.eject()will eject some mass from yourBalls. Mass will be ejected in last direction that you sent withclient.moveTo(). Ejected mass isBalltoo (but we don't own them). Soclient.on('ballAppear')will be called when server sends ejected massBallsinfo. Will returnfalseif connection is not established.
Client events
In this list on.eventName(param1, param2) means you need to do client.on('eventName', function(param1, param2) { ... })
on.connecting()connecting to serveron.connected()connected to server and ready to spawnon.connectionError(err)connection erroron.disconnect()disconnectedon.message(packet)new packet received from server (checkpacket.js)on.myNewBall(ball_id)my newBallcreated (spawn/split/explode...)on.somebodyAteSomething(eater_id, eaten_id)somebody ate somethingon.scoreUpdate(old_score, new_score)personal score updatedon.leaderBoardUpdate(old_highlights, new_highlights, old_names, new_names)leaders update in FFA mode.namesis array of nicknames of leaders andhighlightsis array of numbers0or1corresponded to names where1means nickname should be highlighted in leader board (for example your nickname)on.teamsScoresUpdate(old_scores, new_scores)array of teams scores update in teams modeon.mapSizeLoad(min_x, min_y, max_x, max_y)map size update (as update 16.02.2016 this called then new virtual size of map received)on.reset()when we delete allBallsand stop timers (connection lost?)on.winner(ball_id)somebody won and server going for restarton.ballAction(ball_id, coordinate_x, coordinate_y, size, is_virus, nick)some action about someBallon.ballAppear(ball_id)Ballappear on "screen" (field of view)on.ballDisappear(ball_id)Balldisappear from "screen" (field of view)on.ballDestroy(ball_id, reason)Balldeleted (check reasons at the bottom of this document)on.mineBallDestroy(ball_id, reason)mine (your)Balldeleted (check reasons at the bottom of this document)on.lostMyBalls()all mineBallsdestroyed/eatenon.merge(destroyed_ball_id)mine twoBallsconnects into oneon.ballMove(ball_id, old_x, old_y, new_x, new_y)Ballmoveon.ballResize(ball_id, old_size, new_size)Ballresizeon.ballRename(ball_id, old_name, new_name)Ballset name/change name/we discover nameon.ballUpdate(ball_id, old_update_time, new_update_time)new data about ball receivedon.spectateFieldUpdate(cord_x, cord_y, zoom_level)coordinates of field of view inclient.spectate()modeon.experienceUpdate(level, current_exp, need_exp)experience information update (if logined with auth token)on.packetError(packet, err, preventCrash)unable to parse packet. It can mean that agar changed protocol or issue #46. By default client will crash. But if you sure this is not protocol change and it don't need new issue then you need to callpreventCrash()before callback execution ends. I highly recommend to disconnectclientif this error happens.on.debugLine(line_x, line_y)the server sometimes sends a line for the client to render from your ball to the point though don't expect to see it.on.gotLogin()server authorized you. Missing in original code, check issue 94on.logoutRequest()server forces client to callwindow.logout()
Ball API
var ball = client.balls[ball_id]; ball_id is number that you can get from events
Ball properties
Properties that you can change:
- None. But you can create properties that don't exists for your needs if you want
Properties that better not to change or you can break something:
ball.idID ofBall(number)ball.namenickname of player that own theBallball.xlast known X coordinate ofBall(ifball.visibleistruethen its current coordinate)ball.ylast known Y coordinate ofBall(ifball.visibleistruethen its current coordinate)ball.sizelast known size ofBall(ifball.visibleistruethen its current size)ball.massmass of ball calculated fromball.sizeball.colorstring with color ofBallball.virusiftruethen ball is a virus (green thing that explode big balls)ball.mineiftruethen we do own thisBallball.clientClientthat knows thisBall(if notball.destroyed)ball.destroyediftruethen thisBallno more exists, forget about itball.visibleiftruethen we see thisBallon our "screen" (field of view)ball.last_updatetimestamp when we last saw thisBallball.update_ticklast tick when we saw thisBall
Ball methods
ball.toString()will returnball.idand(ball.name)if set. So you can logballdirectly- Other methods is for internal use
Ball events
In this list on.eventName(param1, param2) means you need to do ball.on('eventName', function(param1, param2) { ... })
on.destroy(reason)Balldeleted (check reasons at the bottom of this document)on.move(old_x, old_y, new_x, new_y)Ballmoveon.resize(old_size, new_size)Ballresizeon.update(old_time, new_time)new data aboutBallreceivedon.rename(old_name, new_name)Ballchange/set name/we discover nameon.appear()Ballappear on "screen" (field of view)on.disappear()Balldisappear from "screen" (field of view)
Servers
When you do var AgarioClient = require('agario-client'); you can access AgarioClient.servers
Functions need opt as options object and cb as callback function.
Servers options
All functions can accept:
opt.agent to use for connection. Check additional info
opt.local_address local interface to bind to for network connections (IP address of interface)
opt.resolve set to true to resolve IP on client side (since SOCKS4 can't accept domain names)
opt.ip if you resolved m.agar.ip IP by other way (will cancel opt.resolve).
servers.getFFAServer(opt, cb)to request FFA server.
Needsopt.regionservers.getTeamsServer(opt, cb)to request teams server.
Needsopt.regionservers.getExperimentalServer(opt, cb)to request experimental server.
Needsopt.region
Use at your own risk! Support of experimental servers are not guaranteed by agario-client!servers.getPartyServer(opt, cb)to request party server.
Needsopt.party_keyservers.createParty(opt, cb)to request party server.
Needsopt.region
Check region list below in this file.
Servers callbacks
Callback will be called with single object that can contain:
server- server's IP:PORT (addws://before passing to connect())key- server's keyerror- error code (WRONG_HTTP_CODE/WRONG_DATA_FORMAT/REQUEST_ERROR/LOOKUP_FAIL)error_source- error object passed fromreq.on.errorwhen available (for example whenREQUEST_ERRORhappens)res- response object when available (for example whenWRONG_HTTP_CODEhappens)data- response data string when available (for example whenWRONG_DATA_FORMAThappens)
LOOKUP_FAIL can happen only if opt.lookup was set to true and will have only error_source
You can check how examples/basic.js uses this.
Additional information
agario-devtools
If you want record/repeat or watch in real time what your client doing through web browser, you might want to check agario-devtools
Regions list
- BR-Brazil
- CN-China
- EU-London
- JP-Tokyo
- RU-Russia
- SG-Singapore
- TK-Turkey
- US-Atlanta
Ball destroy reasons list
{'reason': 'reset'}whenclientdestroys everything (connection lost?){'reason': 'inactive'}when we didn't sawBallforclient.inactive_destroyms{'reason': 'eaten', 'by': ball_id}whenBallgot eaten{'reason': 'merge'}when ourBallmerges with our otherBall{'reason': 'server-forced'}when server commands to delete all balls
Auth token
To login into your account you need to request token. You can check example in examples/auth_token.js
First create new AgarioClient.Account
var account = new AgarioClient.Account();
Then you need to login through facebook on http://agar.io then go to http://www.facebook.com/ and get cookies c_user,datr,xs.
Here is list of properties of account:
- account.c_user - set to cookie "c_user" from http://www.facebook.com/
- account.datr - set to cookie "datr" from http://www.facebook.com/
- account.xs - set to cookie "xs" from http://www.facebook.com/
- account.agent - agent for connection. Tests shows that you can request token from any IP and then use it on any IP so you don't need SOCKS/Proxy.
- account.debug - set 1 to show warnings, otherwise 0. Default: 1
- account.token_expire - contains timestamp in milliseconds when token will expire. Tokens are valid for 1-2 hours. If
(+new Date)>account.token_expirethen you need to request new token and use it in new connection to agar.
Then you call
account.requestFBToken(function(token, info) {
//If you have `token` then you can set it to `client.auth_token`
// and `client.connect()` to agar server
});
If token is null, then something went wrong. Check info which can contain:
- info.error -
Errorof connection error - info.res - response's http.IncomingMessage object
- info.data - content of page
SOCKS/Proxy support
You can change default agent for AgarioClient and AgarioClient.servers to use for connections. You can use libs to do it. For testing and example i used socks lib. Execute node ./node_modules/agario-client/examples/socks.js to test it and read examples/socks.js file to see how to use SOCKS. For proxy you will need to use some other lib.
Adding properties/events
You can add your own properties/events to clients/balls.
var AgarioClient = require('agario-client');
- Prototype of
Clientis located atAgarioClient.prototype. - Prototype of
Ballis located atAgarioClient.Ball.prototype.
For example:
AgarioClient.Ball.prototype.isMyFriend = function() { ... }; //to call ball.isMyFriend()
AgarioClient.prototype.addFriend = function(ball_id) { ... }; //to call client.addFriend(1234)
Events:
client.on('somebodyAteSomething', function(eater_id, eaten_id) { #eat event
if(client.balls[eaten_id].isMyFriend()) { //if this is my friend
client.emit('friendEaten', eater_id, eaten_id); //emit custom event
}
});
client.on('friendEaten', function(eater_id, friend_id) { //on friend eaten
client.log('My friend got eaten!');
});
Check full example in examples/basic.js
Feedback
If something is broken, please email me or create issue. I will not know that something is broken until somebody will tell me that.
License
MIT