Skip to content

Commit

Permalink
Define new v1.0 protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
MaddyGuthridge committed Mar 30, 2024
1 parent 5b7a98b commit 540c8b8
Showing 1 changed file with 211 additions and 0 deletions.
211 changes: 211 additions & 0 deletions Protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
# Flapi communication protocol

The Flapi protocol is a simple protocol for communication between the Flapi
server and Flapi clients.

Version 1.0.

## Message format

Messages are all constructed in the following format:

| Section | Meaning |
|-----------------------------------|---------|
| [Sysex header](#sysex-header) | Begin MIDI system-exclusive message and identify the message type. |
| [Message origin](#message-origin) | Shows the origin of the message. |
| [Client ID](#client-id) | Unique identifier for client. |
| [Message type](#message-type) | Type of message being sent. |
| Additional data (optional) | Data is dependent by message type, but often uses [status info](#status-info) and [Python encoded data](#python-encoded-data). |

### Sysex header

The following values are sent at the start of all messages in order to identify
that the message is associated with Flapi. Messages without this header are
ignored.

| Byte | Meaning |
|--------|---------|
| `0xF0` | System exclusive message start |
| `0x7D` | Non-commercial use byte number (see the [MIDI 1.0 core specification](https://midi.org/specifications/midi1-specifications/midi-1-0-core-specifications)) |
| `0x46` | `'F'` |
| `0x6C` | `'l'` |
| `0x61` | `'a'` |
| `0x70` | `'p'` |
| `0x69` | `'i'` |

### Message origin

One of the listed values is used.

| Value | Meaning |
|--------|---------|
| `0x00` | Message originates from client. |
| `0x01` | Message originates from server. |
| `0x02` | Message is internal to server (client should disregard). |

### Client ID

The ID of the client. This should be randomly generated by the client at the
start of a session, and allows multiple clients to connect simultaneously.
Clients should ignore responses targeting a different client ID.

This should be a random byte, with a value between `0x01` and `0x7F`.
The ID `0x00` is reserved for universal messages, which target all clients.

Upon receiving a request with a given client ID, the server will give a
response with the same client ID.

### Message type

The final common byte describes the type of message. The value can be one of
the following values. Other values are reserved for future Flapi versions.

| Value | Meaning |
|--------|---------|
| `0x00` | [Client hello](#client-hello) |
| `0x01` | [Client goodbye](#client-goodbye) |
| `0x02` | [Server goodbye](#server-goodbye) |
| `0x03` | [Version query](#version-query) |
| `0x04` | [Exec](#exec) |
| `0x05` | [Eval](#eval) |
| `0x06` | [Stdout](#stdout) |

The following sections describe each of these message types.

### Status info

Many data formats for message types use a status code within the response,
which indicates the results of the operation. This typically has additional
data

| Code | Meaning | Typical additional data |
|--------|---------|-------------------------|
| `0x00` | Success | Usually [Python encoded data](#python-encoded-data), depending on the message type. |
| `0x01` | An exception was raised during the operation | [Python encoded data](#python-encoded-data) of the exception. The exception is raised within the default client. |
| `0x02` | The server failed to process the request | [Python encoded data](#python-encoded-data) of the error message. Default client raises this message as a `FlapiServerError`. |

## Python encoded data

In order to transfer data between the client and the server, the following
system is used.

* Python objects are encoded using `pickle`
* The resulting string is encoded using base-64

In code, this can be represented as:

```py
import pickle
from base64 import b64encode

data = { "a": 1, "b": 2, "c": 3 }

# Encode
encoded = b64encode(pickle.dumps(data))

# Decode
decoded = pickle.loads(b64decode(encoded))

assert data == decoded
```

### Client hello

This message is sent when a client wants to connect to the Flapi server. For
client hello messages, no additional data is present.

This message type is the only message type to which the server may not respond.
In particular, the server will not respond if that client ID is already in use
by another active client. If a client does not receive a response, it should
generate a new client ID and try again. If the client repeatedly doesn't
receive a response, it is likely that FL Studio isn't running (or there are
tens of thousands of clients connected to the Flapi server, meaning all
available client IDs are taken).

If the connection is accepted, the server will give an empty response targeted
to the newly connected client. This means the server has registered that client
ID session.

### Client goodbye

This message is sent when a client is exiting, and wants to disconnect from the
Flapi server. As additional data, it contains an `int` exit code as
[Python encoded data](#python-encoded-data).

The server will respond with an identical reply. The default Flapi client
(provided by this library) raises a `FlapiClientExit` exception when this
happens, which will (by default) cause the program to exit with the given exit
code.

### Server goodbye

This message should never be sent by clients. Instead, it is sent by the server
when FL Studio is exiting. The default Flapi client raises a `FlapiServerExit`
exception when this happens, which (by default) is not handled.

### Version query

This message is sent by clients to query the version of the Flapi server.

The server will respond with 3 bytes as the message data:
`(major, minor, revision)`.

### Exec

This message is sent by clients, and instructs the server to `exec` code inside
FL Studio. Unlike `eval`, `exec` does not produce a return value, and so should
be used to execute statements, not expressions.

#### Exec request data

[Python encoded data](#python-encoded-data) containing a string with code to
execute.

#### Exec response data

[Status info](#status-info). For success, no data is given.

### Eval

This message is sent by clients, and instructs the server to `eval` code inside
FL Studio. Unlike `exec`, `eval` produces a return value, and so should be used
to evaluate expressions, not statements.

#### Eval request data

[Python encoded data](#python-encoded-data) containing a string with code to
evaluate.

#### Eval response data

[Status info](#status-info). For success, the return value of `eval` is given.

### Stdout

This message is sent from the server to the client to notify it of `stdout`
originating from FL Studio's console. This can be sent at any time, and so
clients should be prepared to handle it at any point when receiving a message.

It contains [Python encoded data](#python-encoded-data) of the string that was
printed to stdout.

## Example

To help clarify, here is an example demonstrating the basic functionality of
the protocol.

Messages are shown as a list of bytes in hexadecimal, with spaces used to
show different sections of the message.

| Origin | Message | Meaning |
|--------|---------------------------------------------------------|---------|
| Client | `F07D466C617069` `00` `01` `00` | Client requests to connect using client ID `01` |
| Server | `F07D466C617069` `01` `01` `00` | Server responds, accepting the connection |
| Client | `F07D466C617069` `00` `01` `03` | Client requests server version information |
| Server | `F07D466C617069` `01` `01` `03` `010000` | Server responds, saying it is using version `1.0.0` |
| Client | `F07D466C617069` `00` `01` `04` `67415...3554C673D3D` | Client requests to execute `import transport` |
| Server | `F07D466C617069` `01` `01` `04` `00` | Server responds, indicating success |
| Client | `F07D466C617069` `00` `01` `05` `67415...7706C43343D` | Client requests to evaluate `transport.start()` |
| Server | `F07D466C617069` `01` `01` `05` `00` `6741524C4343343D` | Server responds, indicating a result of `8` |
| Client | `F07D466C617069` `00` `01` `01` `6741524C4143343D` | Client disconnects with a code of `0` |
| Server | `F07D466C617069` `01` `01` `01` `6741524C4143343D` | Server acknowledges disconnect |

0 comments on commit 540c8b8

Please sign in to comment.