Protocol Buffers over WebSockets

This communication protocol allows web browsers to communicate with kRPC over a websockets connection, for example from Javascript in a web browser.

Note

If a client library is available for your language, you do not need to implement this protocol.

Connecting to the RPC Server

A client invokes remote procedures by communicating with the RPC server. To establish a connection, open a websockets connection to the server on its RPC port (which defaults to 50000). The connection URI can also contain a name parameter with the name of the client to display in the on the in-game UI. For example: ws://localhost:50000/?name=Jeb

Connecting to the Stream Server

Clients can receive Streams from the stream server. To establish a connection, a client must first connect to the RPC server (as above) then do the following:

  1. Get the identifier of the client by calling the KRPC.GetClientID remote procedure.

  2. Open a websockets connection to the server on its stream port (which defaults to 50001), with an id query parameter set to the client identifier encoded in base64. For example: ws://localhost:50001/?id=Eeh8Vbj2DkWTcJZTkYlEhQ==

Connecting to the stream server is optional. If the client doesn’t require stream functionality, there is no need to connect.

Sending and Receiving Messages

Communication with the server is performed via Protocol Buffer messages, encoded according to the protobuf binary format.

To send a message to the server:

  1. Encode the message using the Protocol Buffers format.

  2. Send a binary websockets message to the server, with payload containing the encoded message data.

To receive a message from the server, do the reverse:

  1. Receive a binary websockets message from the server.

  2. Decode the messages payload using the Protocol Buffers format.

Invoking Remote Procedures

See Messaging Protocol.

Examples

The following code connects to the RPC server at address 127.0.0.1 and port 50000 using the name “Jeb”. Next, it connects to the stream server on port 50001. It then invokes the KRPC.GetStatus RPC, receives and decodes the result and prints out the server version number from the response.

var websocket = require('ws');
var protobufjs = require('protobufjs')
var ByteBuffer = require('bytebuffer')
var proto = protobufjs.loadProtoFile('krpc.proto').build();

console.log('Connecting to RPC server')
let rpcConn = new websocket('ws://127.0.0.1:50000')
rpcConn.binaryType = 'arraybuffer'

rpcConn.onopen = (e) => {
  console.log('Successfully connected')
  let call = new proto.krpc.schema.ProcedureCall('KRPC', 'GetStatus');
  let request = new proto.krpc.schema.Request([call]);
  rpcConn.send(request.toArrayBuffer());
  rpcConn.onmessage = (e) => {
    let response = proto.krpc.schema.Response.decode(e.data)
    let status = proto.krpc.schema.Status.decode(response.results[0].value)
    console.log(status);
    process.exit(0);
  };
};

The following example demonstrates how to set up and receive data from the server over a stream:

'use strict'

var websocket = require('ws');
var protobufjs = require('protobufjs')
var proto = protobufjs.loadProtoFile('krpc.proto').build();

console.log('Connecting to RPC server')
let rpcConn = new websocket('ws://127.0.0.1:50000')
rpcConn.binaryType = 'arraybuffer'

rpcConn.onopen = (evnt) => {
  console.log('Successfully connected')
  console.log('Calling KRPC.GetClientID')
  let call = new proto.krpc.schema.ProcedureCall('KRPC', 'GetClientID');
  let request = new proto.krpc.schema.Request([call]);
  rpcConn.send(request.toArrayBuffer());
};

rpcConn.onmessage = (evnt) => {
  let response = proto.krpc.schema.Response.decode(evnt.data);
  response.results[0].value.readVarint32(); // skip size
  let client_identifier = response.results[0].value.toBase64();
  console.log('Client identifier =', client_identifier);

  console.log('Connecting to Stream server');
  let streamConn = new websocket('ws://127.0.0.1:50001?id=' + client_identifier);
  streamConn.binaryType = 'arraybuffer';

  streamConn.onopen = (evnt) => {
    console.log('Successfully connected');

    let call_to_stream = new proto.krpc.schema.ProcedureCall('KRPC', 'GetStatus');
    let arg = new proto.krpc.schema.Argument(0, call_to_stream.toArrayBuffer());
    let call = new proto.krpc.schema.ProcedureCall('KRPC', 'AddStream', [arg]);
    let request = new proto.krpc.schema.Request([call]);
    rpcConn.send(request.toArrayBuffer());
    rpcConn.onmessage = (evnt) => {
      let response = proto.krpc.schema.Response.decode(evnt.data);
      let stream = proto.krpc.schema.Stream.decode(response.results[0].value);
      console.log("added stream id =", stream.id.toString());
    };
  };

  streamConn.onmessage = (evnt) => {
    let value = new proto.krpc.schema.StreamUpdate.decode(evnt.data);
    let status = proto.krpc.schema.Status.decode(value.results[0].result.value)
    console.log(status);
  };
};