Skip to content

Commit

Permalink
Allow to create custom client & communication between clients (#1254)
Browse files Browse the repository at this point in the history
* allow to create custom client & communication between client

* pass client context

* customClient should not be required

* allow to override Server impl to use

* better docs & typings

* refactor: add new client class for customCommunication

* fix doc

* move custom client to prismarine web client

* restore customPackets
  • Loading branch information
zardoy authored Dec 27, 2023
1 parent 066a2b3 commit 9e99109
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 21 deletions.
24 changes: 13 additions & 11 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ automatically logged in and validated against mojang's auth.
* kickTimeout : default to `10*1000` (10s), kick client that doesn't answer to keepalive after that time
* checkTimeoutInterval : default to `4*1000` (4s), send keepalive packet at that period
* online-mode : default to true
* beforePing : allow customisation of the answer to ping the server does.
* beforePing : allow customisation of the answer to ping the server does.
It takes a function with argument response and client, response is the default json response, and client is client who sent a ping.
It can take as third argument a callback. If the callback is passed, the function should pass its result to the callback, if not it should return.
If the result is `false` instead of a response object then the connection is terminated and no ping is returned to the client.
Expand All @@ -34,7 +34,8 @@ automatically logged in and validated against mojang's auth.
* enforceSecureProfile (optional) : Kick clients that do not have chat signing keys from Mojang (1.19+)
* generatePreview (optional) : Function to generate chat previews. Takes the raw message string and should return the message preview as a string. (1.19-1.19.2)
* socketType (optional) : either `tcp` or `ipc`. Switches from a tcp connection to a ipc socket connection (or named pipes on windows). With the `ipc` option `host` becomes the path off the ipc connection on the local filesystem. Example: `\\.\pipe\minecraft-ipc` (Windows) `/tmp/minecraft-ipc.sock` (unix based systems). See the ipcConnection example for an example.

* Server : You can pass a custom server class to use instead of the default one.

## mc.Server(version,[customPackets])

Create a server instance for `version` of minecraft.
Expand Down Expand Up @@ -112,7 +113,7 @@ Returns a `Client` instance and perform login.
is blank, and `profilesFolder` is specified, we auth with the tokens there instead.
If neither `password` or `profilesFolder` are specified, we connect in offline mode.
* host : default to localhost
* session : An object holding clientToken, accessToken and selectedProfile. Generated after logging in using username + password with mojang auth or after logging in using microsoft auth. `clientToken`, `accessToken` and `selectedProfile: {name: '<username>', id: '<selected profile uuid>'}` can be set inside of `session` when using createClient to login with a client and access Token instead of a password. `session` is also emitted by the `Client` instance with the event 'session' after successful authentication.
* session : An object holding clientToken, accessToken and selectedProfile. Generated after logging in using username + password with mojang auth or after logging in using microsoft auth. `clientToken`, `accessToken` and `selectedProfile: {name: '<username>', id: '<selected profile uuid>'}` can be set inside of `session` when using createClient to login with a client and access Token instead of a password. `session` is also emitted by the `Client` instance with the event 'session' after successful authentication.
* clientToken : generated if a password is given or can be set when when using createClient
* accessToken : generated if a password or microsoft account is given or can be set when using createBot
* selectedProfile : generated if a password or microsoft account is given. Can be set as a object with property `name` and `id` that specifies the selected profile.
Expand All @@ -129,21 +130,22 @@ Returns a `Client` instance and perform login.
* hideErrors : do not display errors, default to false
* skipValidation : do not try to validate given session, defaults to false
* stream : a stream to use as connection
* connect : a function taking the client as parameter and that should client.setSocket(socket)
* connect : a function taking the client as parameter and that should client.setSocket(socket)
and client.emit('connect') when appropriate (see the proxy examples for an example of use)
* agent : a http agent that can be used to set proxy settings for yggdrasil authentication (see proxy-agent on npm)
* agent : a http agent that can be used to set proxy settings for yggdrasil authentication (see proxy-agent on npm)
* fakeHost : (optional) hostname to send to the server in the set_protocol packet
* profilesFolder : optional
* (mojang account) the path to the folder that contains your `launcher_profiles.json`. defaults to your minecraft folder if it exists, otherwise the local directory. set to `false` to disable managing profiles
* (mojang account) the path to the folder that contains your `launcher_profiles.json`. defaults to your minecraft folder if it exists, otherwise the local directory. set to `false` to disable managing profiles
* (microsoft account) the path to store authentication caches, defaults to .minecraft
* onMsaCode(data) : (optional) callback called when signing in with a microsoft account
with device code auth. `data` is an object documented [here](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code#device-authorization-response)
* id : a numeric client id used for referring to multiple clients in a server
* validateChannelProtocol (optional) : whether or not to enable protocol validation for custom protocols using plugin channels. Defaults to true
* disableChatSigning (optional) : Don't try obtaining chat signing keys from Mojang (1.19+)
* realms : An object which should contain one of the following properties: `realmId` or `pickRealm`. When defined will attempt to join a Realm without needing to specify host/port. **The authenticated account must either own the Realm or have been invited to it**
* realmId : The id of the Realm to join.
* pickRealm(realms) : A function which will have an array of the user Realms (joined/owned) passed to it. The function should return a Realm.
* realms : An object which should contain one of the following properties: `realmId` or `pickRealm`. When defined will attempt to join a Realm without needing to specify host/port. **The authenticated account must either own the Realm or have been invited to it**
* realmId : The id of the Realm to join.
* pickRealm(realms) : A function which will have an array of the user Realms (joined/owned) passed to it. The function should return a Realm.
* Client : You can pass a custom client class to use instead of the default one, which would allow you to create completely custom communication. Also note that you can use the `stream` option instead where you can supply custom duplex, but this will still use serialization/deserialization of packets.


## mc.Client(isServer,version,[customPackets])
Expand Down Expand Up @@ -235,7 +237,7 @@ The client's version

### `packet` event

Called with every packet parsed. Takes four paramaters, the JSON data we parsed, the packet metadata (name, state), the buffer (raw data) and the full buffer (includes surplus data and may include the data of following packets on versions below 1.8)
Called with every packet parsed. Takes four paramaters, the JSON data we parsed, the packet metadata (name, state), the buffer (raw data) and the full buffer (includes surplus data and may include the data of following packets on versions below 1.8)

### `raw` event

Expand Down Expand Up @@ -272,7 +274,7 @@ Called when a chat message from another player arrives. The emitted object conta
* type -- the message type - on 1.19, which format string to use to render message ; below, the place where the message is displayed (for example chat or action bar)
* sender -- the UUID of the player sending the message
* senderTeam -- scoreboard team of the player (pre 1.19)
* senderName -- Name of the sender
* senderName -- Name of the sender
* targetName -- Name of the target (for outgoing commands like /tell). Only in 1.19.2+
* verified -- true if message is signed, false if not signed, undefined on versions prior to 1.19

Expand Down
3 changes: 2 additions & 1 deletion src/createClient.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const Client = require('./client')
const DefaultClientImpl = require('./client')
const assert = require('assert')

const encrypt = require('./client/encrypt')
Expand Down Expand Up @@ -31,6 +31,7 @@ function createClient (options) {
options.majorVersion = version.majorVersion
options.protocolVersion = version.version
const hideErrors = options.hideErrors || false
const Client = options.Client || DefaultClientImpl

const client = new Client(false, version.minecraftVersion, options.customPackets, hideErrors)

Expand Down
3 changes: 2 additions & 1 deletion src/createServer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const Server = require('./server')
const DefaultServerImpl = require('./server')
const NodeRSA = require('node-rsa')
const plugins = [
require('./server/handshake'),
Expand All @@ -20,6 +20,7 @@ function createServer (options = {}) {
motd = 'A Minecraft server',
'max-players': maxPlayersOld = 20,
maxPlayers: maxPlayersNew = 20,
Server = DefaultServerImpl,
version,
favicon,
customPackets,
Expand Down
18 changes: 10 additions & 8 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ declare module 'minecraft-protocol' {
authTitle?: string
sessionServer?: string
keepAlive?: boolean
closeTimeout?: number
closeTimeout?: number
noPongTimeout?: number
checkTimeoutInterval?: number
version?: string
Expand All @@ -137,6 +137,8 @@ declare module 'minecraft-protocol' {
realms?: RealmsOptions
// 1.19+
disableChatSigning?: boolean
/** Pass custom client implementation if needed. */
Client?: Client
}

export class Server extends EventEmitter {
Expand All @@ -162,9 +164,9 @@ declare module 'minecraft-protocol' {

export interface ServerClient extends Client {
id: number
// You must call this function when the server receives a message from a player and that message gets
// broadcast to other players in player_chat packets. This function stores these packets so the server
// can then verify a player's lastSeenMessages field in inbound chat packets to ensure chain integrity.
/** You must call this function when the server receives a message from a player and that message gets
broadcast to other players in player_chat packets. This function stores these packets so the server
can then verify a player's lastSeenMessages field in inbound chat packets to ensure chain integrity. */
logSentMessageFromPeer(packet: object): boolean
}

Expand All @@ -188,12 +190,12 @@ declare module 'minecraft-protocol' {
hideErrors?: boolean
agent?: Agent
validateChannelProtocol?: boolean
// 1.19+
// Require connecting clients to have chat signing support enabled
/** (1.19+) Require connecting clients to have chat signing support enabled */
enforceSecureProfile?: boolean
// 1.19.1 & 1.19.2 only: If client should send previews of messages they are typing to the server
/** 1.19.1 & 1.19.2 only: If client should send previews of messages they are typing to the server */
enableChatPreview?: boolean
socketType?: 'tcp' | 'ipc'
Server?: Server
}

export interface SerializerOptions {
Expand All @@ -202,7 +204,7 @@ declare module 'minecraft-protocol' {
state?: States
version: string
}

export interface MicrosoftDeviceAuthorizationResponse {
device_code: string
user_code: string
Expand Down

0 comments on commit 9e99109

Please sign in to comment.