diff --git a/.gitignore b/.gitignore index 3ff87f1..edc4eae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ node_modules/ package-lock.json -.vscode \ No newline at end of file +.vscode + +*-cache.json +scripts/ \ No newline at end of file diff --git a/HISTORY.md b/HISTORY.md index a2f2d69..02eb728 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,5 @@ ## History -### 1.0.0 +### 0.0.1 * initial implementation diff --git a/README.md b/README.md index 57910f9..b6c73b4 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,147 @@ -# prismarine-template -[![NPM version](https://img.shields.io/npm/v/prismarine-template.svg)](http://npmjs.com/package/prismarine-template) -[![Build Status](https://github.com/PrismarineJS/prismarine-template/workflows/CI/badge.svg)](https://github.com/PrismarineJS/prismarine-template/actions?query=workflow%3A%22CI%22) +# prismarine-realms +[![NPM version](https://img.shields.io/npm/v/prismarine-realms.svg)](http://npmjs.com/package/prismarine-realms) +[![Build Status](https://github.com/PrismarineJS/prismarine-realms/workflows/CI/badge.svg)](https://github.com/PrismarineJS/prismarine-realms/actions?query=workflow%3A%22CI%22) [![Discord](https://img.shields.io/badge/chat-on%20discord-brightgreen.svg)](https://discord.gg/GsEFRM8) -[![Try it on gitpod](https://img.shields.io/badge/try-on%20gitpod-brightgreen.svg)](https://gitpod.io/#https://github.com/PrismarineJS/prismarine-template) +[![Try it on gitpod](https://img.shields.io/badge/try-on%20gitpod-brightgreen.svg)](https://gitpod.io/#https://github.com/PrismarineJS/prismarine-realms) -A template repository to make it easy to create new prismarine repo +Node.JS Wrapper around both the Java & Bedrock Realms API. Minecraft Realms is a subscription based service provided by Mojang where users can create/manage their own private server. The intention of this package is to provide easy access to the internal API used to manage Realms such as opening/closing a Realm, managing players, getting host information and much more. + +## Installation +```shell +npm install prismarine-realms +``` ## Usage +### RealmAPI + +#### .from(authflow: Authflow, platform: 'bedrock' | 'java') + +Takes an **Authflow** instance from [prismarine-auth](https://github.com/PrismarineJS/prismarine-auth), you can see the documentation for this [here.](https://github.com/PrismarineJS/prismarine-auth#authflow) + +## API + +### Definitions + +| Param | Type | Description | +| --------------- | -------------------- | --------------------------------------------------------------------- | +| realmId | `string` | The ID of the Realm | +| realmInviteCode | `string` | The invite code for the Realm (Only on Bedrock) | +| username | `string` | The username of player | +| uuid | `string` | The unique ID of the player, w/o hyphens | +| xuid | `string` | The Xbox User ID of the targeted player | + +--- + ```js -const template = require('prismarine-template') +const { Authflow } = require('prismarine-auth') +const { RealmAPI } = require('prismarine-realms') -template.helloWorld() +const authflow = new Authflow() + +const api = RealmAPI.from(authflow, 'bedrock') ``` -## API +#### getRealms() + +Gets a list of Realms the authenticating account has joined or owns + +```js +await api.getRealms() +``` -### helloWorld() -Prints hello world +#### getRealm(realmId: string) + +Gets detailed information about a Realm if owned + +```js +await api.getRealm('1234567') +``` + +#### getAddress() + +Gets the address for the Realm + +```js +const realm = await api.getRealm('1234567') + +await realm.getAddress() +``` + +#### invitePlayer(uuid: string, name: string) + +Invites a player to the Realm + +```js +const realm = await api.getRealm('1234567') + +await realm.invitePlayer('a8005260a332457097a50bdbe48a9a21', 'Steve') +``` + +#### open() + +Opens a Realm. Allows players to join + +```js +const realm = await api.getRealm('1234567') + +await realm.open() +``` + +#### close() + +Closes a Realm. Removes all current players and restricts joining + +```js +const realm = await api.getRealm('1234567') + +await realm.close() +``` + +--- + +### Using prismarine-realms with Mineflayer, Node Minecraft Protocol and Bedrock Protocol + +Prismarine-auth is used in Mineflayer to allow quick connection to owned/joined Realms by providing a `pickRealm` function which should return a single Realm instance. It will then handle getting the host information and connect your bot to that Realm. The process of getting the Realms connection is handled in the protocol libraries meaning the same options can be passed there as well. + +#### Mineflayer + +```js +const mineflayer = require('mineflayer') + +const bot = mineflayer.createBot({ + username: 'example', + auth: 'microsoft', + realms: { + pickRealm: (realms) => realms.find(realm => realm.name === 'My Realm') + } +}) +``` + +#### Node Minecraft Protocol + +```js +const protocol = require('minecraft-protocol'); + +const client = protocol.createClient({ + username: 'example', + auth: 'microsoft', + realms: { + pickRealm: (realms) => realms.find(realm => realm.name === 'My Realm') + } +}); +``` + +#### Bedrock Protocol + +```js +const protocol = require('bedrock-protocol'); + +const client = protocol.createClient({ + username: 'example', + realms: { + pickRealm: (realms) => realms.find(realm => realm.name === 'My Realm') + } +}); +``` \ No newline at end of file diff --git a/example.js b/example.js deleted file mode 100644 index ceadd46..0000000 --- a/example.js +++ /dev/null @@ -1,3 +0,0 @@ -const template = require('prismarine-template') - -template.helloWorld() diff --git a/examples/realms.js b/examples/realms.js new file mode 100644 index 0000000..58cade4 --- /dev/null +++ b/examples/realms.js @@ -0,0 +1,15 @@ +const { Authflow } = require('prismarine-auth') +const { RealmAPI } = require('prismarine-realms') + +const [, , platform] = process.argv + +if (!platform) { + console.log('Usage: node realms.js ') + process.exit(1) +} + +const authflow = new Authflow() + +const api = RealmAPI.from(authflow, platform) + +api.getRealms().then(console.log) diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..8fbf9c2 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,79 @@ +/// +declare module 'prismarine-realms' { + export class RealmAPI { + /** + * Creates a new RealmAPI instance, which handles and authenticates calls to the Realms API + * @param authflow An Authflow instance from [prismarine-auth](https://github.com/PrismarineJS/prismarine-auth). + * @param platform Which platforms API to access + */ + constructor(authflow: Authflow, platform: 'bedrock' | 'java') + + static from(authflow: Authflow, platform: 'bedrock' | 'java'): BedrockRealmAPI | JavaRealmAPI + + getRealms(): Promise + + getRealm(realmId?: string): Promise + + } + + export class BedrockRealmAPI extends RealmAPI { + getRealmAddress(realmId: string): Promise
+ invitePlayer(realmId: string, uuid: string): Promise + changeRealmState(realmId: string, state: 'open' | 'close'): Pomise + } + + export class JavaRealmAPI extends RealmAPI { + getRealmAddress(realmId: string): Promise
+ invitePlayer(realmId: string, uuid: string, name: string): Promise + changeRealmState(realmId: string, state: 'open' | 'close'): Pomise + } + + export interface Realm { + getAddress(): Promise
+ invitePlayer(uuid: string, name: string): Promise + open(): Promise + close(): Promise + id: number + remoteSubscriptionId: string + owner: null + ownerUUID: string + name: string + motd: string + defaultPermission: string + state: string + daysLeft: number + expired: boolean + expiredTrial: boolean + gracePeriod: boolean + worldType: string + players: RealmPlayer[] + maxPlayers: number + minigameName: string + minigameId: number + minigameImage: string + activeSlot: number + slots: Slot[] + member: boolean + clubId: number + subscriptionRefreshStatus: null + } + + export interface RealmPlayer { + uuid: string, + name: string, + operator: boolean, + accepted: boolean, + online: boolean, + permission: string + } + + export interface Slot { + options: string + slotId: number + } + + export interface Address { + address: string + } + +} diff --git a/index.js b/index.js index 4bd4c75..e2260ea 100644 --- a/index.js +++ b/index.js @@ -4,6 +4,6 @@ if (typeof process !== 'undefined' && parseInt(process.versions.node.split('.')[ process.exit(1) } -module.exports.helloWorld = function () { - console.log('Hello world !') +module.exports = { + RealmAPI: require('./src/index') } diff --git a/package.json b/package.json index 95d3a27..ac5dbcd 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,39 @@ { - "name": "prismarine-template", - "version": "1.0.0", - "description": "A template repository to make it easy to create new prismarine repo", + "name": "prismarine-realms", + "version": "0.0.1", + "description": "Node.JS Wrapper around both the Java & Bedrock Realms API", "main": "index.js", "scripts": { - "test": "mocha --reporter spec --exit", + "test": "mocha --recursive --reporter spec --exit", "pretest": "npm run lint", "lint": "standard", "fix": "standard --fix" }, "repository": { "type": "git", - "url": "git+https://github.com/PrismarineJS/prismarine-template.git" + "url": "git+https://github.com/PrismarineJS/prismarine-realms.git" }, "keywords": [ "prismarine", - "template" + "realms" ], "author": "Romain Beaumont", "license": "MIT", "bugs": { - "url": "https://github.com/PrismarineJS/prismarine-template/issues" + "url": "https://github.com/PrismarineJS/prismarine-realms/issues" }, - "homepage": "https://github.com/PrismarineJS/prismarine-template#readme", + "homepage": "https://github.com/PrismarineJS/prismarine-realms#readme", "devDependencies": { - "prismarine-template": "file:.", - "standard": "^16.0.1", - "mocha": "^9.1.3" + "chai": "^4.3.6", + "chai-as-promised": "^7.1.1", + "chai-exclude": "^2.1.0", + "mocha": "^9.1.3", + "nock": "^13.2.4", + "prismarine-auth": "^1.5.0", + "prismarine-realms": "file:.", + "standard": "^16.0.1" + }, + "dependencies": { + "node-fetch": "^2.6.1" } } diff --git a/src/bedrock/api.js b/src/bedrock/api.js new file mode 100644 index 0000000..de63230 --- /dev/null +++ b/src/bedrock/api.js @@ -0,0 +1,24 @@ +const RealmAPI = require('../index') +const Realm = require('../structures/Realm') + +module.exports = class BedrockRealmAPI extends RealmAPI { + async getRealmAddress (realmId) { + const data = await this.rest.get(`/worlds/${realmId}/join`) + return { address: data.address } + } + + async invitePlayer (realmId, uuid) { + const data = await this.rest.put(`/invites/${realmId}/invite/update`, { + body: { + invites: { + [uuid]: 'ADD' + } + } + }) + return new Realm(this, data) + } + + async changeRealmState (realmId, state) { + return await this.rest.put(`/worlds/${realmId}/${state}`) + } +} diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..fe0485f --- /dev/null +++ b/src/constants.js @@ -0,0 +1,7 @@ +module.exports = { + BedrockHost: 'https://pocket.realms.minecraft.net', + JavaHost: 'https://pc.realms.minecraft.net', + BedrockUserAgent: 'MCPE/UWP', + JavaUserAgent: 'MinecraftLauncher/2.2.10675', + BedrockRealmsRelyingParty: 'https://pocket.realms.minecraft.net/' +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..67b38c2 --- /dev/null +++ b/src/index.js @@ -0,0 +1,29 @@ +const Rest = require('./rest') +const Realm = require('./structures/Realm') + +const PlatformTypes = ['java', 'bedrock'] + +class RealmAPI { + constructor (Authflow, platform, options = {}) { + if (!Authflow) throw new Error('Need to proive an Authflow instance to use the Realm API https://github.com/PrismarineJS/prismarine-auth') + if (!PlatformTypes.includes(platform?.toLowerCase())) throw new Error(`Platform provided is not valid. Must be ${PlatformTypes.join(' | ')}`) + + this.rest = new Rest(Authflow, platform, options) + } + + static from (authflow, platform, options) { + return (platform === 'bedrock') ? new (require('./bedrock/api'))(authflow, platform, options) : new (require('./java/api'))(authflow, platform, options) + } + + async getRealm (realmId) { + const data = await this.rest.get(`/worlds/${realmId}`) + return new Realm(this, data) + } + + async getRealms () { + const data = await this.rest.get('/worlds') + return data.servers.map(realm => new Realm(this, realm)) + } +} + +module.exports = RealmAPI diff --git a/src/java/api.js b/src/java/api.js new file mode 100644 index 0000000..3b4bd12 --- /dev/null +++ b/src/java/api.js @@ -0,0 +1,23 @@ +const RealmAPI = require('../index') +const Realm = require('../structures/Realm') + +module.exports = class JavaRealmAPI extends RealmAPI { + async getRealmAddress (realmId) { + const data = await this.rest.get(`/worlds/v1/${realmId}/join/pc`) + return { address: data.address } + } + + async invitePlayer (realmId, uuid, name) { + const data = await this.rest.post(`/invites/${realmId}`, { + body: { + uuid, + name + } + }) + return new Realm(this, data) + } + + async changeRealmState (realmId, state) { + return await this.rest.put(`/worlds/${realmId}/${state}`) + } +} diff --git a/src/rest.js b/src/rest.js new file mode 100644 index 0000000..b951b8a --- /dev/null +++ b/src/rest.js @@ -0,0 +1,82 @@ +const debug = require('debug')('prismarine-realms') +const fetch = require('node-fetch') + +const { + BedrockRealmsRelyingParty, + BedrockUserAgent, + BedrockHost, + JavaHost, + JavaUserAgent +} = require('./constants') + +const { formatBedrockAuth, formatJavaAuth } = require('./util') + +module.exports = class Rest { + constructor (authflow, platform, options) { + this.options = options + this.platform = platform + this.host = (platform === 'bedrock') ? BedrockHost : JavaHost + this.userAgent = (platform === 'bedrock') ? BedrockUserAgent : JavaUserAgent + this.getAuth = (platform === 'bedrock') ? () => authflow.getXboxToken(BedrockRealmsRelyingParty).then(formatBedrockAuth) : () => authflow.getMinecraftJavaToken({ fetchProfile: true }).then(formatJavaAuth) + } + + get (route, options) { + return this.prepareRequest({ ...options, route, method: 'get' }) + } + + post (route, options) { + return this.prepareRequest({ ...options, route, method: 'post' }) + } + + put (route, options) { + return this.prepareRequest({ ...options, route, method: 'put' }) + } + + delete (route, options) { + return this.prepareRequest({ ...options, route, method: 'delete' }) + } + + async prepareRequest (request) { + const url = `${this.host}${request.route}` + + const headers = { + 'Client-Version': '0.0.0', + 'User-Agent': this.userAgent + } + + if (!this.options.skipAuth) { + const [key, value] = await this.getAuth() + headers[key] = value + } + + let body + + if (request.body !== null) { + body = JSON.stringify(request.body) + headers['Content-Type'] = 'application/json' + } + + const fetchOptions = { + method: request.method, + body, + headers: { ...headers, ...request.headers } + } + + return this.execRequest(url, fetchOptions) + } + + async execRequest (url, options) { + const response = await fetch(url, options) + + if (response.ok) { + if (response.headers.get('Content-Type')?.startsWith('application/json')) { + return response.json() + } + return response.arrayBuffer() + } else { + debug('Request fail', response) + const body = await response.text() + throw new Error(`${response.status} ${response.statusText} ${body}`) + } + } +} diff --git a/src/structures/Realm.js b/src/structures/Realm.js new file mode 100644 index 0000000..6a622ed --- /dev/null +++ b/src/structures/Realm.js @@ -0,0 +1,46 @@ +module.exports = class Realm { + constructor (api, data) { + this.api = api + Object.assign(this, { + id: data.id, + remoteSubscriptionId: data.remoteSubscriptionId, + owner: data.owner, + ownerUUID: data.ownerUUID, + name: data.name, + motd: data.motd, + defaultPermission: data.defaultPermission, + state: data.state, + daysLeft: data.daysLeft, + expired: data.expired, + expiredTrial: data.expiredTrial, + gracePeriod: data.gracePeriod, + worldType: data.worldType, + players: data.players, + maxPlayers: data.maxPlayers, + minigameName: data.minigameName, + minigameId: data.minigameId, + minigameImage: data.minigameImage, + activeSlot: data.activeSlot, + slots: data.slots, + member: data.member, + clubId: data.clubId, + subscriptionRefreshStatus: data.subscriptionRefreshStatus + }) + } + + async getAddress () { + return this.api.getRealmAddress(this.id) + } + + async invitePlayer (uuid, name) { + return this.api.invitePlayer(this.id, uuid, name) + } + + async open () { + return this.api.changeRealmState(this.id, 'open') + } + + async close () { + return this.api.changeRealmState(this.id, 'close') + } +} diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..e497635 --- /dev/null +++ b/src/util.js @@ -0,0 +1,13 @@ +function formatJavaAuth (res) { + if (!res.profile?.id || !res.profile?.name) throw new Error('Failed to obtain profile data, does the authenticating account own minecraft?') + return ['cookie', `sid=token:${res.token}:${res.profile.id}; user=${res.profile.name}; version=0.0.0`] +} + +function formatBedrockAuth (res) { + return ['authorization', `XBL3.0 x=${res.userHash};${res.XSTSToken}`] +} + +module.exports = { + formatJavaAuth, + formatBedrockAuth +} diff --git a/test/RealmAPI.js b/test/RealmAPI.js new file mode 100644 index 0000000..b30d50a --- /dev/null +++ b/test/RealmAPI.js @@ -0,0 +1,21 @@ +/* eslint-env mocha */ +const { expect, use } = require('chai') +const chaiAsPromised = require('chai-as-promised') + +use(chaiAsPromised) + +const PlatformTypes = ['java', 'bedrock'] + +const { RealmAPI } = require('prismarine-realms') +const { Authflow } = require('prismarine-auth') + +const authflow = new Authflow() + +describe('RealmAPI', () => { + it('should error if an Authflow instance isn\'t provided', () => { + expect(() => RealmAPI.from(null, 'bedrock')).to.throw('Need to proive an Authflow instance to use the Realm API https://github.com/PrismarineJS/prismarine-auth') + }) + it('should error if a valid platform isn\'t provided', () => { + expect(() => RealmAPI.from(authflow, 'foo')).to.throw(`Platform provided is not valid. Must be ${PlatformTypes.join(' | ')}`) + }) +}) diff --git a/test/basic.test.js b/test/basic.test.js deleted file mode 100644 index d461258..0000000 --- a/test/basic.test.js +++ /dev/null @@ -1,7 +0,0 @@ -/* eslint-env mocha */ - -describe('basic', () => { - it('test', () => { - - }) -}) diff --git a/test/bedrockAPI.js b/test/bedrockAPI.js new file mode 100644 index 0000000..33b7ac8 --- /dev/null +++ b/test/bedrockAPI.js @@ -0,0 +1,76 @@ +/* eslint-env mocha */ +const nock = require('nock') + +const { expect, use } = require('chai') +const chaiAsPromised = require('chai-as-promised') +const chaiExclude = require('chai-exclude') + +use(chaiExclude) +use(chaiAsPromised) + +const { World, Join } = require('./common/responses.json') + +const { RealmAPI } = require('prismarine-realms') +const { Authflow } = require('prismarine-auth') + +const config = { + realmId: '1112223', + xuid: '1111222233334444' +} + +const authflow = new Authflow() + +const api = RealmAPI.from(authflow, 'bedrock', { skipAuth: true }) + +nock('https://pocket.realms.minecraft.net') + .get('/worlds') + .reply(200, { servers: [World] }) + .get(`/worlds/${config.realmId}`) + .times(5) + .reply(200, World) + .get(`/worlds/${config.realmId}/join`) + .reply(200, Join) + .put(`/invites/${config.realmId}/invite/update`) + .reply(200, (_, body) => ({ ...World, players: [{ uuid: Object.keys(body.invites)[0] }] })) + .put(`/worlds/${config.realmId}/open`) + .reply(200, true) + .put(`/worlds/${config.realmId}/close`) + .reply(200, true) + +describe('Bedrock', () => { + describe('getRealms', () => { + it('should return an array of Realm objects', async () => { + expect(await api.getRealms()).excluding('api').to.deep.equal([World]) + }) + }) + describe('getRealm', () => { + it('should return a Realm object', async () => { + const realm = await api.getRealm(config.realmId) + expect(realm).excluding('api').to.deep.equal(World) + }) + }) + describe('Realm getAddress', () => { + it('should return an object containing the Realms address', async () => { + const realm = await api.getRealm(config.realmId) + expect(await realm.getAddress()).to.deep.equal({ address: '0.0.0.0:19132' }) + }) + }) + describe('Realm InvitePayer', () => { + it('should contain the player uuid and realmId included in the request', async () => { + const realm = await api.getRealm(config.realmId) + expect(await realm.invitePlayer(config.xuid)).excluding('api').to.deep.equal({ ...World, players: [{ uuid: config.xuid }] }) + }) + }) + describe('Realm Open', () => { + it('should return true indicating Realm is now open', async () => { + const realm = await api.getRealm(config.realmId) + expect(await realm.open()).to.deep.equal(true) + }) + }) + describe('Realm Close', () => { + it('should return true indicating Realm is now closed', async () => { + const realm = await api.getRealm(config.realmId) + expect(await realm.close()).to.deep.equal(true) + }) + }) +}) diff --git a/test/common/responses.json b/test/common/responses.json new file mode 100644 index 0000000..778bb50 --- /dev/null +++ b/test/common/responses.json @@ -0,0 +1,31 @@ +{ + "World": { + "id": 1112223, + "remoteSubscriptionId": "02ea6dec4d446c5b123dde157920453", + "owner": "", + "ownerUUID": "1111222233334444", + "name": "Test Realm", + "motd": "Test description", + "defaultPermission": "VISITOR", + "state": "OPEN", + "daysLeft": -826, + "expired": true, + "expiredTrial": true, + "gracePeriod": false, + "worldType": "NORMAL", + "players": null, + "maxPlayers": 10, + "minigameName": null, + "minigameId": null, + "minigameImage": null, + "activeSlot": 1, + "slots": null, + "member": false, + "clubId": 111222333444555, + "subscriptionRefreshStatus": null + }, + "Join": { + "address": "0.0.0.0:19132", + "pendingUpdate": false + } +} \ No newline at end of file diff --git a/test/javaAPI.js b/test/javaAPI.js new file mode 100644 index 0000000..d30cc5e --- /dev/null +++ b/test/javaAPI.js @@ -0,0 +1,77 @@ +/* eslint-env mocha */ +const nock = require('nock') + +const { expect, use } = require('chai') +const chaiAsPromised = require('chai-as-promised') +const chaiExclude = require('chai-exclude') + +use(chaiExclude) +use(chaiAsPromised) + +const { World, Join } = require('./common/responses.json') + +const { RealmAPI } = require('prismarine-realms') +const { Authflow } = require('prismarine-auth') + +const config = { + realmId: '1112223', + uuid: '1111222233334444', + name: 'Steve' +} + +const authflow = new Authflow() + +const api = RealmAPI.from(authflow, 'java', { skipAuth: true }) + +nock('https://pc.realms.minecraft.net') + .get('/worlds') + .reply(200, { servers: [World] }) + .get(`/worlds/${config.realmId}`) + .times(5) + .reply(200, World) + .get(`/worlds/v1/${config.realmId}/join/pc`) + .reply(200, Join) + .post(`/invites/${config.realmId}`) + .reply(200, (_, body) => ({ ...World, players: [{ uuid: body.uuid, name: body.name }] })) + .put(`/worlds/${config.realmId}/open`) + .reply(200, true) + .put(`/worlds/${config.realmId}/close`) + .reply(200, true) + +describe('Java', () => { + describe('getRealms', () => { + it('should return an array of Realm objects', async () => { + expect(await api.getRealms()).excluding('api').to.deep.equal([World]) + }) + }) + describe('getRealm', () => { + it('should return a Realm object', async () => { + const realm = await api.getRealm(config.realmId) + expect(realm).excluding('api').to.deep.equal(World) + }) + }) + describe('Realm getAddress', () => { + it('should return an object containing the Realms address', async () => { + const realm = await api.getRealm(config.realmId) + expect(await realm.getAddress()).to.deep.equal({ address: '0.0.0.0:19132' }) + }) + }) + describe('Realm InvitePayer', () => { + it('should contain the player uuid, name and realmId included in the request', async () => { + const realm = await api.getRealm(config.realmId) + expect(await realm.invitePlayer(config.uuid, config.name)).excluding('api').to.deep.equal({ ...World, players: [{ uuid: config.uuid, name: config.name }] }) + }) + }) + describe('Realm Open', () => { + it('should return true indicating Realm is now open', async () => { + const realm = await api.getRealm(config.realmId) + expect(await realm.open()).to.deep.equal(true) + }) + }) + describe('Realm Close', () => { + it('should return true indicating Realm is now closed', async () => { + const realm = await api.getRealm(config.realmId) + expect(await realm.close()).to.deep.equal(true) + }) + }) +})