Skip to content

Commit

Permalink
Option to use JS implementation of RakNet, fix 1.17.10 issue (#110)
Browse files Browse the repository at this point in the history
* Default to JS implementation of RakNet

* update server also, adjust test timeout based on number of versions

* 1.17.10: fix texture pack issue, keep raknet default at C++

* force_build
  • Loading branch information
extremeheat authored Jul 17, 2021
1 parent 987bf43 commit f530677
Show file tree
Hide file tree
Showing 17 changed files with 91 additions and 37 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ jobs:
strategy:
matrix:
node-version: [14.x]

env:
FORCE_BUILD: true
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
Expand Down
4 changes: 4 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 3.5.0
* Add 1.17.10 support [#109](https://github.com/PrismarineJS/bedrock-protocol/pull/109)
* You can switch to the JS implementation of raknet by setting `useNativeRaknet: false` in options.

## 3.4.0
* Initial 1.17 support [#99](https://github.com/PrismarineJS/bedrock-protocol/pull/99)
* update connect version based on ping response & fix typings (u9g) [#101](https://github.com/PrismarineJS/bedrock-protocol/pull/101)
Expand Down
8 changes: 4 additions & 4 deletions data/1.17.10/protocol.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions data/latest/proto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,14 @@ packet_resource_packs_info:
must_accept: bool
# If scripting is enabled.
has_scripts: bool
# ForcingServerPacks is currently an unclear field.
force_server_packs: bool
# A list of behaviour packs that the client needs to download before joining the server.
# All of these behaviour packs will be applied together.
behaviour_packs: BehaviourPackInfos
# A list of resource packs that the client needs to download before joining the server.
# The order of these resource packs is not relevant in this packet. It is however important in the Resource Pack Stack packet.
texture_packs: TexturePackInfos
# ForcingServerPacks is currently an unclear field.
force_server_packs: bool

packet_resource_pack_stack:
!id: 0x07
Expand Down
2 changes: 1 addition & 1 deletion docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Returns a `Client` instance and connects to the server.
| profilesFolder | *optional* | Where to store cached authentication tokens. Defaults to .minecraft, or the node_modules folder if not found. |
| autoInitPlayer | *optional* | default to true, If we should send SetPlayerInitialized to the server after getting play_status spawn. |
| skipPing | *optional* | Whether pinging the server to check its version should be skipped. |

| useNativeRaknet | *optional* | Whether to use the C++ version of RakNet. Set to false to use JS. |

## be.createServer(options) : Server

Expand Down
9 changes: 7 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import EventEmitter from "events"

declare module "bedrock-protocol" {
type Version = '1.17.0' | '1.16.220' | '1.16.210' | '1.16.201'
type Version = '1.17.10' | '1.17.0' | '1.16.220' | '1.16.210' | '1.16.201'

enum title { MinecraftNintendoSwitch, MinecraftJava }

Expand All @@ -15,7 +15,12 @@ declare module "bedrock-protocol" {
port?: number
// For the client, if we should login with Microsoft/Xbox Live.
// For the server, if we should verify client's authentication with Xbox Live.
offline?: boolean
offline?: boolean,

// Whether or not to use C++ version of RakNet
useNativeRaknet?: boolean,
// If using JS implementation of RakNet, should we use workers? (This only affects the client)
useRaknetWorker?: boolean
}

export interface ClientOptions extends Options {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"debug": "^4.3.1",
"jose-node-cjs-runtime": "^3.12.1",
"jsonwebtoken": "^8.5.1",
"jsp-raknet": "^2.1.0",
"jsp-raknet": "^2.1.3",
"minecraft-folder-path": "^1.1.0",
"node-fetch": "^2.6.1",
"prismarine-nbt": "^1.5.0",
Expand Down
6 changes: 4 additions & 2 deletions src/client.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const { ClientStatus, Connection } = require('./connection')
const { createDeserializer, createSerializer } = require('./transforms/serializer')
const { RakClient } = require('./rak')
const { serialize, isDebug } = require('./datatypes/util')
const debug = require('debug')('minecraft-protocol')
const Options = require('./options')
Expand All @@ -21,6 +20,9 @@ class Client extends Connection {
super()
this.options = { ...Options.defaultOptions, ...options }
this.validateOptions()

const { RakClient } = require('./rak')(this.options.useNativeRaknet)

this.serializer = createSerializer(this.options.version)
this.deserializer = createDeserializer(this.options.version)

Expand All @@ -30,7 +32,7 @@ class Client extends Connection {

const host = this.options.host
const port = this.options.port
this.connection = new RakClient({ useWorkers: true, host, port })
this.connection = new RakClient({ useWorkers: this.options.useRaknetWorkers, host, port })

this.startGameData = {}
this.clientRuntimeId = null
Expand Down
2 changes: 1 addition & 1 deletion src/createClient.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { Client } = require('./client')
const { RakClient } = require('./rak')
const { RakClient } = require('./rak')(true)
const assert = require('assert')
const advertisement = require('./server/advertisement')
const { sleep } = require('./datatypes/util')
Expand Down
6 changes: 5 additions & 1 deletion src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ const defaultOptions = {
// If true, do not authenticate with Xbox Live
offline: false,
// Milliseconds to wait before aborting connection attempt
connectTimeout: 9000
connectTimeout: 9000,
// Whether or not to use C++ version of RakNet
useNativeRaknet: true,
// If using JS implementation of RakNet, should we use workers? (This only affects the client)
useRaknetWorkers: true
}

module.exports = { defaultOptions, MIN_VERSION, CURRENT_VERSION, Versions }
59 changes: 42 additions & 17 deletions src/rak.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
const { EventEmitter } = require('events')
const ConnWorker = require('./rakWorker')
const { waitFor } = require('./datatypes/util')
// TODO: better way to switch, via an option
try {
var { Client, Server, PacketPriority, PacketReliability } = require('raknet-native') // eslint-disable-line
} catch (e) {
var { Client, Server, EncapsulatedPacket, Reliability } = require('jsp-raknet') // eslint-disable-line
console.debug('[raknet] native not found, using js', e)

let Client, Server, PacketPriority, EncapsulatedPacket, PacketReliability, Reliability

module.exports = nativeRaknet => {
if (nativeRaknet) {
try {
({ Client, Server, PacketPriority, PacketReliability } = require('raknet-native'))
return { RakServer: RakNativeServer, RakClient: RakNativeClient }
} catch (e) {
({ Client, Server, EncapsulatedPacket, Reliability } = require('jsp-raknet'))
console.debug('[raknet] native not found, using js', e)
console.debug('You can suppress the error above by disabling `useNativeRaknet` in your options')
}
} else {
({ Client, Server, EncapsulatedPacket, Reliability } = require('jsp-raknet'))
}
return { RakServer: RakJsServer, RakClient: RakJsClient }
}

class RakNativeClient extends EventEmitter {
Expand Down Expand Up @@ -118,9 +129,10 @@ class RakJsClient extends EventEmitter {
this.sendReliable = this.workerSendReliable
} else {
this.connect = this.plainConnect
this.close = this.plainClose
this.close = reason => this.raknet.close(reason)
this.sendReliable = this.plainSendReliable
}
this.pongCb = null
}

workerConnect (host = this.options.host, port = this.options.port) {
Expand All @@ -137,6 +149,8 @@ class RakJsClient extends EventEmitter {
this.onEncapsulated(ecapsulated, address.hash)
break
}
case 'pong':
this.pongCb?.(evt.args)
}
})
}
Expand Down Expand Up @@ -165,12 +179,21 @@ class RakJsClient extends EventEmitter {
if (immediate) this.connection.sendQueue()
}

plainClose (reason) {
this.raknet.close(reason)
}

ping () {
// TODO
async ping (timeout = 1000) {
if (this.worker) {
this.worker.postMessage({ type: 'ping' })
return waitFor(res => {
this.pongCb = data => res(data)
}, timeout, () => { throw new Error('Ping timed out') })
} else {
if (!this.raknet) this.raknet = new Client(this.options.host, this.options.port)
return waitFor(res => {
this.raknet.ping(data => {
this.raknet.close()
res(data)
})
}, timeout, () => { throw new Error('Ping timed out') })
}
}
}

Expand Down Expand Up @@ -207,9 +230,11 @@ class RakJsServer extends EventEmitter {
this.raknet.on('closeConnection', this.onCloseConnection)
this.raknet.on('encapsulated', this.onEncapsulated)
}
}

module.exports = {
RakClient: PacketPriority ? RakNativeClient : RakJsClient,
RakServer: PacketPriority ? RakNativeServer : RakJsServer
close () {
// Allow some time for the final packets to come in/out
setTimeout(() => {
this.raknet.close()
}, 40)
}
}
5 changes: 5 additions & 0 deletions src/rakWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ function main () {
}
} else if (evt.type === 'close') {
raknet.close()
process.exit(0)
} else if (evt.type === 'ping') {
raknet.ping((args) => {
parentPort.postMessage({ type: 'pong', args })
})
}
})
}
Expand Down
6 changes: 4 additions & 2 deletions src/server.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const { EventEmitter } = require('events')
const { createDeserializer, createSerializer } = require('./transforms/serializer')
const { Player } = require('./serverPlayer')
const { RakServer } = require('./rak')
const { sleep } = require('./datatypes/util')
const { ServerAdvertisement } = require('./server/advertisement')
const Options = require('./options')
Expand All @@ -12,6 +11,9 @@ class Server extends EventEmitter {
super()
this.options = { ...Options.defaultOptions, ...options }
this.validateOptions()

this.RakServer = require('./rak')(this.options.useNativeRaknet).RakServer

this.serializer = createSerializer(this.options.version)
this.deserializer = createDeserializer(this.options.version)
this.advertisement = new ServerAdvertisement(this.options.motd)
Expand Down Expand Up @@ -66,7 +68,7 @@ class Server extends EventEmitter {
}

async listen (host = this.options.host, port = this.options.port) {
this.raknet = new RakServer({ host, port }, this)
this.raknet = new this.RakServer({ host, port }, this)
try {
await this.raknet.listen()
} catch (e) {
Expand Down
2 changes: 1 addition & 1 deletion test/internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ async function requestChunks (x, z, radius) {
return chunks
}

async function timedTest (version, timeout = 1000 * 120) {
async function timedTest (version, timeout = 1000 * 220) {
await waitFor((res) => {
startTest(version, res)
}, timeout, () => {
Expand Down
3 changes: 2 additions & 1 deletion test/internal.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ const { proxyTest } = require('./proxy')
const { Versions } = require('../src/options')

describe('internal client/server test', function () {
this.timeout(240 * 1000)
const vcount = Object.keys(Versions).length
this.timeout(vcount * 80 * 1000)

for (const version in Versions) {
it('connects ' + version, async () => {
Expand Down
3 changes: 2 additions & 1 deletion test/vanilla.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const { clientTest } = require('./vanilla')
const { Versions } = require('../src/options')

describe('vanilla server test', function () {
this.timeout(220 * 1000)
const vcount = Object.keys(Versions).length
this.timeout(vcount * 80 * 1000)

for (const version in Versions) {
it('client spawns ' + version, async () => {
Expand Down
4 changes: 4 additions & 0 deletions tools/startVanillaServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ async function startServer (version, onStart, options = {}) {
await download(os, version, options.path)
configure(options)
const handle = run(!onStart)
handle.on('error', (...a) => {
console.warn('*** THE MINECRAFT PROCESS CRASHED ***', a)
handle.kill('SIGKILL')
})
if (onStart) {
let stdout = ''
handle.stdout.on('data', data => {
Expand Down

0 comments on commit f530677

Please sign in to comment.