title | template |
---|---|
JSONAPI |
index.jade |
JSONAPI is a plugin for Bukkit that allows you to access data and other information about your server and your players through a simple, yet secure, HTTP API. This allows you to make awesome websites, iPhone apps, and a way for your players to purchase goods online and automatically receive them in game.
However, this plugin won't do all of that by itself. It is simply an API that allows you to assemble the features in a way that makes sense for your needs.
Download JSONAPI on the releases page
You can find the source code on GitHub at alecgorge/jsonapi. I accept pull requests!
There are 146 different API methods in 8 different categories covering a wide range of features:
- Read the standard API documenation
- Get a live stream of chat, connections and disconnections and/or the console.
- ...and many more standard features (ban, unban, inventory management, etc.)
There are many guides along with an expansive technical reference available on the wiki.
Every API method is documented and viewable online.
You can read a careful documentation of the the request and response format on the wiki if you are thinking about writing a new SDK or plan on rolling your own solution.
SDKs allow for easy usage of all of JSONAPI's capabilities currently there are 4 SDKs:
- PHP: JSONAPI.php (docs)
- .NET: JSONAPI
- JavaScript: jsonapi.js
- Python: MinecraftApi.py
Using the API for JSONAPI, you can easily add new methods or stream sources. Check out the section "For plugin developers" at the bottom of the wiki.
Once you setup Adminium on your server, you can use this test console to easily test all of the available API methods and view the JSON response.
- Integrate your website and your Minecraft server
- Control your server with your iPad/iPhone/iPod Touch through Adminium
- Setup scripts that perform actions on your server, all through an easy to use API!
The second major iteration of the API is very different from the original. One of the biggest features of the v2 API is support for granular permissions but there are many other improvements such as the HTTP and WebSocket API over port 25565
.
Many changes had to be made to properly support permissions. It was important to maintain the ability to call multiple API methods in one HTTP request, but it was also important to prevent the whole request from failing if only one API method call failed due to permissions.
This is best and simplest option if you don't wish to make use of JSONAPI's streaming data.
It is now recommended to make API requests over the Minecraft join port (25565
by default). Even though this sounds unusual, over 16 months of research and experimentation has gone into making HTTP API requests work seamlessly over the Minecraft join port without affecting gameplay.
Of course you can still use port 20059
as you always have, but sometimes using port 25565
will be easier. It is one less port to forward!
To consume the HTTP API, simply send a JSON Request payload in the GET
query parameter json
or in POST
body as raw JSON. POST
-ing the data is recommend.
Data is returned as a JSON Response payload and is always an array—even when you only called one API method.
curl -v "http://localhost:25565/api/2/call?json=%5B%7B%22name%22%3A%22server.version%22%2C%22key%22%3A%221f33a4a68d95a26782fdb41686f4f01d3ffaff14b9eec5c2ddf367840d1adcfc%22%2C%22username%22%3A%22admin%22%2C%22arguments%22%3A%5B%5D%2C%22tag%22%3A%22sampleTag%22%7D%5D"
GET /api/2/call?json=%5B%7B%22name%22%3A%22server.version%22%2C%22key%22%3A%221f33a4a68d95a26782fdb41686f4f01d3ffaff14b9eec5c2ddf367840d1adcfc%22%2C%22username%22%3A%22admin%22%2C%22arguments%22%3A%5B%5D%2C%22tag%22%3A%22sampleTag%22%7D%5D HTTP/1.1
User-Agent: curl/7.21.4 (x86_64-apple-darwin12.2.0) libcurl/7.21.4 OpenSSL/0.9.8y zlib/1.2.5 libidn/1.20
Host: localhost:25565
Accept: */*
HTTP/1.1 200 OK
Content-Type: application/json
Date: Sat, 12 Oct 2013 07:15:22 GMT
Access-Control-Allow-Origin: *
Content-Length: 140
[{"result":"success","is_success":true,"source":"server.version","tag":"sampleTag","success":"git-Bukkit-1.6.2-R1.0-b2879jnks (MC: 1.6.2)"}]
curl -X "POST" "http://localhost:25565/api/2/call" -H "Content-Type: application/json" -d "[{\"name\":\"server.version\",\"key\":\"1f33a4a68d95a26782fdb41686f4f01d3ffaff14b9eec5c2ddf367840d1adcfc\",\"username\":\"admin\",\"arguments\":[],\"tag\":\"sampleTag\"}]"
POST /api/2/call HTTP/1.1
User-Agent: curl/7.21.4 (x86_64-apple-darwin12.2.0) libcurl/7.21.4 OpenSSL/0.9.8y zlib/1.2.5 libidn/1.20
Host: localhost:25565
Accept: */*
Content-Type: application/json
Content-Length: 152
[{"name":"server.version","key":"1f33a4a68d95a26782fdb41686f4f01d3ffaff14b9eec5c2ddf367840d1adcfc","username":"admin","arguments":[],"tag":"sampleTag"}]
HTTP/1.1 200 OK
Content-Type: application/json
Date: Sat, 12 Oct 2013 07:15:22 GMT
Access-Control-Allow-Origin: *
Content-Length: 140
[{"result":"success","is_success":true,"source":"server.version","tag":"sampleTag","success":"git-Bukkit-1.6.2-R1.0-b2879jnks (MC: 1.6.2)"}]
JSONAPI supports making API calls via JavaScript thanks to the JavaScript SDK. If you are familiar with making AJAX requests in JavaScript, you will know that you can't typically make AJAX requests to other domains. This could pose a problem if your website is running on a different domain than your Minecraft server. Fortunately JSONAPI supports 2 workarounds for this issue: Cross-origin resource sharing and JSONP.
Making use of CORS is really easy if you are working browsers that support CORS. If you need to support IE 8 and IE 9 you can use jQuery and the XDomainRequest jQuery Plugin.
Because of JSONAPI's CORS support, you can simply make AJAX calls to your Minecraft the same way you would make an AJAX call to your own script.
Using JSONP is very simple. Simply generate a GET
HTTP request as shown above and then add a query parameter called callback
. The value of callback
is the name of hte function to wrap the response in.
curl -v "http://localhost:25565/api/2/call?callback=myCallbackFunc&json=%5B%7B%22name%22%3A%22server.version%22%2C%22key%22%3A%221f33a4a68d95a26782fdb41686f4f01d3ffaff14b9eec5c2ddf367840d1adcfc%22%2C%22username%22%3A%22admin%22%2C%22arguments%22%3A%5B%5D%2C%22tag%22%3A%22sampleTag%22%7D%5D"
GET /api/2/call?callback=myCallbackFunc&json=%5B%7B%22name%22%3A%22server.version%22%2C%22key%22%3A%221f33a4a68d95a26782fdb41686f4f01d3ffaff14b9eec5c2ddf367840d1adcfc%22%2C%22username%22%3A%22admin%22%2C%22arguments%22%3A%5B%5D%2C%22tag%22%3A%22sampleTag%22%7D%5D HTTP/1.1
User-Agent: curl/7.21.4 (x86_64-apple-darwin12.2.0) libcurl/7.21.4 OpenSSL/0.9.8y zlib/1.2.5 libidn/1.20
Host: localhost:25565
Accept: */*
HTTP/1.1 200 OK
Content-Type: application/json
Date: Sat, 12 Oct 2013 07:27:41 GMT
Access-Control-Allow-Origin: *
Content-Length: 157
myCallbackFunc([{"result":"success","is_success":true,"source":"server.version","tag":"sampleTag","success":"git-Bukkit-1.6.2-R1.0-b2879jnks (MC: 1.6.2)"}]);
Recommended for advanced users only!
95% of people will be perfectly fine using the HTTP API, the Post-hook API and the WebSocket API.
The Socket API is a raw TCP socket that you can use to communicate with JSONAPI. It listens on port 20060
by default (1 higher than the port set in config.yml
).
The Socket API is very similiar to the WebSocket API below. The only difference is that instead of sending text frames back and forth, the Socket API is line based. Lines are terminated with \r\n
.
This is a useful option when you want to consume JSONAPI's streaming data, but you are using a language such as PHP, where you typically can't or don't want to run a process for an extended period of time.
The Stream Pusher API will send a POST request to a URL specify with a payload of JSONAPI stream data every 30 seconds or 500 stream messages, whichever comes first.
TODO Document this more and expose the config file in a more useful yay
The WebSocket API is actually quite similiar to the Socket API. The connection path for the WebSocket API looks like this:
ws://localhost:25565/api/2/websocket
or this:
ws://localhost:20061
Obviously, replace localhost
with your server's IP address or hostname and replace 25565
with the join port. Replace 20061
with the port in config.yml
plus 2 (20059 + 2 == 20061
is the default).
The WebSocket API is frame based. Each text frame sent to JSONAPI should look a HTTP GET
URL:
/api/2/call?json=%5B%7B%22name%22%3A%22server.version%22%2C%22key%22%3A%221f33a4a68d95a26782fdb41686f4f01d3ffaff14b9eec5c2ddf367840d1adcfc%22%2C%22username%22%3A%22admin%22%2C%22arguments%22%3A%5B%5D%2C%22tag%22%3A%22sampleTag%22%7D%5D
This format can be simplified to /api/2/call?json=
suffixed by a RFC 3986
URL encoded a JSON Request payload formatted to be on a single line ending with a \r\n
.
This odd format is necessary to keep things similiar to the HTTP API and to also allow for streams:
/api/2/subscribe?json=%5B%7B%22name%22%3A%22performance%22%2C%22key%22%3A%22059198890c8a68512ca1bef01f57aef81c14fec438caaf9b7cd54a662b638fa7%22%2C%22username%22%3A%22admin%22%2C%22tag%22%3A%22performance%22%2C%22show_previous%22%3Atrue%7D%5D
A JSON Response payload will be sent in a single frame. Note that for streams, they are not sent in an array but as one response object per line and could appear in any frame.
The API request structure is very simple and is used in all the different way to access JSONAPI. However, you will not need to even know how this works if you use one Let's start by looking at a simple example of a single API method call formatted for easier reading. Extraneous whitespace is fine to remove, as with any JSON:
[
{
"name": "server.version",
"key": "1f33a4a68d95a26782fdb41686f4f01d3ffaff14b9eec5c2ddf367840d1adcfc",
"username": "admin",
"arguments": [],
"tag": "sampleTag"
}
]
The first thing to notice about the request structure is that at the root of each request is a JSON array. This array should contain request objects such as the one in this example. Each request object has up to 5 key-value pairs:
name
: This is the name of the JSONAPI API method that you want to call. This comes from the list of JSONAPI API methods.key
: This is asha256
hash of the API method, JSONAPI username and JSONAPI password. More on this below.username
: This is the JSONAPI username of the user that is making this request. It must be the same as theusername
used when generating thekey
.arguments
: This must be an array of all the arguments required the API method. In this example there are no arguments to this API method.tag
: This key-value pair is optional. If you set a tag in the request, the corresponding response will also have the same tag in it. This is useful for streaming situations where you may need to callbacks to certain requests complete.
The response to a request like this will look similar to this:
[
{
"result": "success",
"is_success": true,
"source": "server.version",
"tag": "sampleTag",
"success": "git-Bukkit-1.6.2-R1.0-b2879jnks (MC: 1.6.2)"
}
]
You can read more about the response format below.
In this example, we call multiple API methods and even pass arguments to some API methods:
[
{
"name": "players.name",
"key": "111af5fa82ed1b0c6b23564d491d153d8440f5cae85a1375b8dba6dc307ab8c5",
"arguments": ["alecgorge"],
"username": "admin"
},
{
"name": "players.name.inventory.slots.slot.set",
"key": "f292bb79a3ac1d0efa534d7f06ce63a52ebf91f4258694c285b7882a184544ba",
"username": "admin",
"arguments": ["alecgorge", 0, 35, 6, 0, 2],
"tag": "test"
},
{
"name": "players.name",
"key": "111af5fa82ed1b0c6b23564d491d153d8440f5cae85a1375b8dba6dc307ab8c5",
"arguments": ["alecgorge"],
"username": "admin"
}
]
In this example, we call 3 API methods: players.name
, players.name.inventory.slots.slot.set
and players.name
again. The first and last API methods give us information about a player including his/her inventory. The second API method sets the 0
th slot of alecgorge
's inventory to 2 pink wools (wool's ID is 35
and 6
is the data value for pink wool).
Here is a sample response, with irrelvant information remove:
[
{
"result": "success",
"is_success": true,
"source": "players.name",
"success": {
... player info ...
"inventory": {
... item in hand info ...
"inventory": [
null,
... other inventory slots ...
],
... player info ...
}
},
{
"result": "success",
"is_success": true,
"source": "players.name.inventory.slots.slot.set",
"tag": "test",
"success": true
},
{
"result": "success",
"is_success": true,
"source": "players.name",
"success": {
... player info ...
"inventory": {
... item in hand info ...
"inventory": [
{
"enchantments": {},
"amount": 2,
"durability": 6,
"type": 35,
"dataValue": 6
},
... other inventory slots ...
],
... player info ...
}
}
]
The important things to note here is that alecgorge
didn't have anything in his 0
th slot initially so it showed as null, but after players.name.inventory.slots.slot.set
was called, the slot was filled.
API methods are called in order when they are in an array, but no promises are made about API method calls in separate requests.
You can read more about the response format below.
You can calulate the key
necessary for JSONAPI API requests by taking the sha256
hash of string formed by concatenating the JSONAPI username, API method name or stream name and JSONAPI password. The output should be hex encoded and be entirely in lowercase.
Here is a sample function to calculate a JSONAPI key in PHP
:
function generate_jsonapi_key($username, $password, $api_method_or_stream_name) {
return hash('sha256', $username . $api_method_or_stream_name . $password);
}
This example can easily be carried over to any language.
Subscribing to streams is very similar to calling API methods. For the name
key you provide the name of stream
that you subscribe to.
The stream subscription object also has one extra key: show_previous
. When show_previous
is set to true
in the request payload, the server will automatically send up to the latest 150 messages in the stream. When show_previous
is set to false
, only messages that occur in the stream after the subscription request is processed will be sent.
Otherwise everything is the same:
The reponse structure matches one-to-one with the request structure—that is for every request object there is exactly one response object.
Each response object will be in the position in the response array as the request object was in the request array.
There are a minimum of 4 key-value pairs in each response object and a maximum of 5:
result
: This is a string that will either be "success" or "error". You can the value of this key as the key to find the response data regardless of whether or not the request succeeded.is_success
: This is a boolean that will either betrue
orfalse
to represent whether or not the API method call succeeded.source
: This is the API method name in the request object.tag
: This key-value pair is optional. If the request object had atag
key, the response object will have the sametag
.success
: This key-value pair only appears if the request was successful. It contains API method specific data. It could be an array, futher objects, a number, a boolean or a string.error
: This key-value pair only appears if the request failed. It has two sub-keys:message
: This is a English description of the error that occured.code
: This is an integer that describes the type of failure that occured. There are 10 error codes:1
: Page not found2
: Invalid JSON submitted in request3
: Server offline4
: API Error5
: InvocationTargetException6
: Other caught exception7
: Method does not exist8
: The API key is wrong. This indicates that either the username and password combination was not found on the server or that key does not match the provided username and API method name provided in the request payload9
: Not allowed, but correct API key. This indicates that although the key is valid, the requesting user is not allowed to access the request API method.10
: Missing username from payload
Here is a sample failed request:
[
{
"result": "error",
"is_success": false,
"error": {
"message": "Invalid username, password or salt.",
"code": 8
},
"source": "players.name"
}
]
Note that in an array you can have failures and successes:
[
{
"result": "success",
"is_success": true,
"source": "server.version",
"tag": "1",
"success": "git-Bukkit-1.6.2-R1.0-b2879jnks (MC: 1.6.2)"
},
{
"result": "error",
"is_success": false,
"error": {
"message": "Invalid username, password or salt.",
"code": 8
},
"source": "server.version",
"tag": "2"
}
]
Streams provide slightly different responses. Stream responses are not wrapped in an array and one is sent per line.
Here is an example of a few lines from the performance
stream:
{"result":"success","source":"performance","tag":"performance","success":{"memoryUsage":110.50666046142578,"expectedTicks":600,"time":1381567969,"clockRate":19.999333355554814,"diskMax":238552.5078125,"error":0.0,"players":0,"expectedTime":30000,"expectedClockRate":20.0,"memoryMax":227.5625,"elapsedTicks":600,"diskUsage":233016.75,"elapsedTime":30001}}
{"result":"success","source":"performance","tag":"performance","success":{"memoryUsage":112.44316101074219,"expectedTicks":600,"time":1381567974,"clockRate":19.999333355554814,"diskMax":238552.5078125,"error":0.0,"players":0,"expectedTime":30000,"expectedClockRate":20.0,"memoryMax":227.5625,"elapsedTicks":600,"diskUsage":233016.75,"elapsedTime":30001}}
{"result":"success","source":"performance","tag":"performance","success":{"memoryUsage":104.84185028076172,"expectedTicks":600,"time":1381567979,"clockRate":19.999333355554814,"diskMax":238552.5078125,"error":0.0,"players":0,"expectedTime":30000,"expectedClockRate":20.0,"memoryMax":227.5625,"elapsedTicks":600,"diskUsage":233016.7578125,"elapsedTime":30001}}
{"result":"success","source":"performance","tag":"performance","success":{"memoryUsage":106.78044128417969,"expectedTicks":600,"time":1381567984,"clockRate":19.999333355554814,"diskMax":238552.5078125,"error":0.0,"players":0,"expectedTime":30000,"expectedClockRate":20.0,"memoryMax":227.5625,"elapsedTicks":600,"diskUsage":233017.203125,"elapsedTime":30001}}
{"result":"success","source":"performance","tag":"performance","success":{"memoryUsage":112.02610778808594,"expectedTicks":600,"time":1381567989,"clockRate":19.999333355554814,"diskMax":238552.5078125,"error":0.0,"players":0,"expectedTime":30000,"expectedClockRate":20.0,"memoryMax":227.5625,"elapsedTicks":600,"diskUsage":233017.20703125,"elapsedTime":30001}}
In this document the user
is the username accessing JSONAPI. The groups the user
belongs to determine what features the user
can access.
All permission nodes are tied to a specific set of JSONAPI methods and streams. Unless a group belongs to the ALLOW_ALL
group, it is assumed the user only has access to the specific API methods whitelisted by these permission nodes.
You can modify what groups users belong to in users.yml
and you can modify what permission nodes groups have in groups.yml
. You cannot modify what methods and streams are allowed by each permission node. However, if you open up an issue on GitHub it will likely be implemented.
Please note that these permissions only work v2
of the API (/api/2/call
and /api/2/subscribe
) so it is important to set use-new-api
to true
in config.yml
if you want the permission to be properly enforced.
If you do not set use-new-api
to true
, anyone with a valid JSONAPI username and password will be able to access any API method using the original, deprecated API.
players.name
This allows the user to view all basic information about a player: health, food, banned?, opped?, etc. Although this API call allows you to view a player's inventory. This permission node alone is not enough to view the inventory in Adminium.
dynmap.host
dynmap.port
This allows the user to access API methods to automatically determine the dynmap url if dynmap is installed.
In Adminium this is used to determine whether or not the "Map" menu item should be shown.
server.performance.disk.free
server.performance.disk.size
server.performance.disk.used
server.performance.memory.used
server.performance.memory.total
server.performance.tick_health
performance
This allows the user to view graphs and information about the server's performance: disk usage, RAM usage and TPS.
players.banned.names
This allows the user to view a list of the names of banned players.
players.name.inventory.slots.slot.clear
players.name.inventory.slots.slot.set
players.name.inventory.slots.slot.enchant
This allows the user to give, modify, enchant and take a player's items.
players.name
players.online
players.online.count
players.online.names
This allows the user to view the information of online players.
This allows the user the most recent (up to 150) SEVERE
messages on the server.
files.read
files.read_binary
files.append
files.create
files.create_folder
files.write
files.write_binary
files.move
This gives the user full file system access. They can read, create, edit and delete any file on the Minecraft server.
This allows a user to view his/her push notifications settings for Adminium but not change them.
remotetoolkit.rescheduleServerRestart
remotetoolkit.restartServer
remotetoolkit.startServer
remotetoolkit.stopServer
Turn the server off and on using Remote Toolkit. Remote Toolkit and JSONAPI_RTK needs to be properly setup.
players.whitelisted.names
This allows the user to view a list of the names of whitelisted players.
This allows the user the most recent (up to 150) /calladmin <reason>
usages by players on the server.
This allows a user to recieve push notifications.
players.name.op
players.name.deop
players.name.send_message
players.name.kick
players.name.ban
players.name.pardon
players.name.whitelist
players.name.unwhitelist
players.name.teleport_to.to_name
players.name.teleport_world
This allows a user to take actions on a player. With this permission the user can op, deop, send a PM, kick ban, pardon, whitelist, unwhitelist and teleport a player.
groups.all
This allows the user to see a list of all the name of all the players, organized by group.
players.offline
players.offline.name
players.offline.names
This allows the user to see offline players' names and their corresponding information.
players.name.set_level
players.name.set_food_level
players.name.set_health
players.name.set_game_mode
This allows the user to manipulate a player's level, food, health and game mode.
This allows the user to manipulate his/her push notification settings for Adminium.
chat.with_name
This allows the user to speak in chat.
This allows the user to use something other than their JSONAPI username when chatting.
players.online.names
players.online.count
server.settings.max_players
This allows the user to view the names and number of players current on the server.
worlds.all
worlds.names
worlds.world
This allows the user to view a list of worlds on the server and basic information about each world: seed, time of day, time, difficulty, etc.
worlds.world.set_time
worlds.world.set_difficulty
This allows the user to change the time and difficulty of a world on the server.
plugins.name.enable
plugins.name.disable
This allows the user to enable or disable plugins by name.
This allows the user to install a plugin from a URL.
This allows the user to view a player's inventory, including details about each item. This doesn't allow the user to change the inventory or any item.
Same as inventory but for enderchests. This isn't currently used.
This allows the user to subscribe to the chat stream and view the server's chat in realtime.
This allows the user to remove players from the whitelist.
This allows the user to see the permission nodes and groups a user belongs to. Information provided by Vault and the Bukkit permission API.
This allows the user to see a player's bank balance. Information provided by Vault.
This allows the user to withdraw or deposit into a player's bank account.
This allows the user to add and remove permission nodes and groups from a player.
This allows the user to view all the JSONAPI users and groups on the server. This includes passwords and allowed streams, groups and permission nodes.
This allows the user to add and remove permissions, methods and streams from groups; add and remove groups from a user and set a user's password.
This allows the user to modify a permission group using Vault.
This allows the user to view a plugin and its information.
This allows the user to view the real time console.
This allows the user to pardon banned players.
This allows the user to do the equivlent of /reload
YourKit is kindly supporting JSONAPI open source project with its full-featured Java Profiler. YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. Take a look at YourKit's leading software products: YourKit Java Profiler and YourKit .NET Profiler.