tachyon-protocol 0.1.12 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +62 -38
  2. package/dist/bot/slave/request.json +7 -3
  3. package/dist/bot/slave/response.json +67 -19
  4. package/dist/bot/unslave/request.json +7 -3
  5. package/dist/bot/unslave/response.json +67 -19
  6. package/dist/game/launch/response.json +66 -19
  7. package/dist/index.d.ts +505 -278
  8. package/dist/lobby/close/request.json +6 -3
  9. package/dist/lobby/close/response.json +66 -19
  10. package/dist/lobby/create/request.json +6 -3
  11. package/dist/lobby/create/response.json +118 -27
  12. package/dist/lobby/join/request.json +6 -3
  13. package/dist/lobby/join/response.json +248 -47
  14. package/dist/lobby/joined/response.json +66 -19
  15. package/dist/lobby/leave/request.json +6 -3
  16. package/dist/lobby/leave/response.json +92 -23
  17. package/dist/lobby/left/response.json +66 -19
  18. package/dist/lobby/list/request.json +6 -3
  19. package/dist/lobby/list/response.json +66 -19
  20. package/dist/lobby/receiveMessage/response.json +66 -19
  21. package/dist/lobby/sendMessage/request.json +6 -3
  22. package/dist/lobby/sendMessage/response.json +118 -27
  23. package/dist/lobby/updated/response.json +66 -19
  24. package/dist/matchmaking/cancel/request.json +6 -3
  25. package/dist/matchmaking/cancel/response.json +92 -23
  26. package/dist/matchmaking/found/response.json +66 -19
  27. package/dist/matchmaking/list/request.json +6 -3
  28. package/dist/matchmaking/list/response.json +66 -19
  29. package/dist/matchmaking/lost/response.json +66 -19
  30. package/dist/matchmaking/queue/request.json +6 -3
  31. package/dist/matchmaking/queue/response.json +118 -27
  32. package/dist/matchmaking/queueUpdate/response.json +66 -19
  33. package/dist/matchmaking/ready/request.json +6 -3
  34. package/dist/matchmaking/ready/response.json +92 -23
  35. package/dist/matchmaking/readyUpdate/response.json +66 -19
  36. package/dist/meta.json +1 -23
  37. package/dist/system/disconnect/request.json +6 -3
  38. package/dist/system/disconnected/response.json +66 -19
  39. package/dist/system/version/response.json +67 -20
  40. package/package.json +1 -1
  41. package/dist/account/getToken/request.json +0 -74
  42. package/dist/account/getToken/response.json +0 -91
  43. package/dist/account/login/request.json +0 -32
  44. package/dist/account/login/response.json +0 -304
  45. package/dist/account/recover/request.json +0 -15
  46. package/dist/account/recover/response.json +0 -58
  47. package/dist/account/register/request.json +0 -53
  48. package/dist/account/register/response.json +0 -78
  49. package/dist/account/rename/request.json +0 -33
  50. package/dist/account/rename/response.json +0 -66
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # Tachyon
2
2
 
3
- Tachyon is the name of the protocol designed to replace the old [Spring Lobby Protocol](https://springrts.com/dl/LobbyProtocol/ProtocolDescription.html), primarily for use in Beyond All Reason. It describes the message schema that clients should use to communicate with the master server and vice-versa. The exchange format is essentially JSON sent over a WebSocket connection. [JSONSchema](https://json-schema.org/) is used to define the structure of messages, which can be found in the [dist](dist) directory. Server and client implementations of this protocol are encouraged to use a [JSONSchema validator](https://json-schema.org/implementations.html#validators) to validate and sanitize message requests and responses.
3
+ Tachyon is the name of the protocol designed to replace the old [Spring Lobby Protocol](https://springrts.com/dl/LobbyProtocol/ProtocolDescription.html), primarily intended for use in [Beyond All Reason](https://github.com/beyond-all-reason/Beyond-All-Reason). It describes the message schema that clients should use to communicate with the master server and vice-versa. The exchange format is JSON sent over a WebSocket connection. [JSONSchema](https://json-schema.org/) is used to define the structure of messages, which can be found in the [dist](dist) directory. Server and client implementations of this protocol are encouraged to use a [JSONSchema validator](https://json-schema.org/implementations.html#validators) to validate and sanitize message requests and responses.
4
4
 
5
5
  [Schema Reference](docs/README.md)
6
6
 
7
7
  ## Connecting
8
8
 
9
- When connecting to the WebSocket server, the server should send a [`system/version/response`](docs/system.md/#version) command containing the version of the Tachyon protocol that is being served. Clients should ensure their implementation is using the same version of the protocol, which can be found in this repo's [`package.json`](package.json). Once connected, follow the instructions in the [account](docs/account.md) service to register and login in order to gain authorization to the rest of the protocol.
9
+ To connect to a Tachyon WebSocket server, the client should add a `tachyonVersion` query parameter which specifies which version of Tachyon they're using, e.g. `wss://tachyon-server.com?tachyonVersion=1.2.3`. The server should send a [`system/version/response`](docs/system.md/#version) command containing the version of the Tachyon protocol that is being served and `versionParity` field stating how it differs to the client's version. Ideally, clients should ensure their implementation is using the same version of the protocol, which can be found in this repo's [`package.json`](package.json). However, it is up to the client to decide how to handle version mismatches, and the server should not terminate clients because of a version disparity.
10
+
11
+ TODO: document auth
10
12
 
11
13
  ## Terminology
12
14
 
@@ -17,6 +19,7 @@ When connecting to the WebSocket server, the server should send a [`system/versi
17
19
  | Server | The provider of the protocol and what clients connect to. i.e. the master or middleware server |
18
20
  | Command | A JSON message relating to a specific endpoint |
19
21
  | Command ID | The unique string which represents the message type. E.g. `user/login/request` or `lobby/join/response` |
22
+ | Message ID | A unique identifier for a pair of request/response commands which links them together |
20
23
  | Endpoint | E.g. `register`, `login` or `join` |
21
24
  | Service | A collection of endpoints that are categorically related, E.g. `user` or `lobby` |
22
25
  | UserClient | Specifically a registered user who is online |
@@ -29,6 +32,45 @@ When connecting to the WebSocket server, the server should send a [`system/versi
29
32
  | Engine | A version of the engine (Recoil), e.g. `105.1.1-1544-g058c8ea BAR105` |
30
33
  | AI | User-owned game AI instances, e.g. ChickensAI. Can only exist within battles |
31
34
 
35
+ ## Commands
36
+
37
+ Both request and response messages are referred to as "commands". A request command should always be met with a corresponding response command, however, in some cases, the server can send standalone response commands which the client did not initiate with a request. An exaple is the [`system/version/response`](docs/system.md/#version)
38
+
39
+ ### Shared
40
+
41
+ Every command has the following properties:
42
+
43
+ | Property | Type | Description |
44
+ | ---------- | ------ | ------------------------------------------------------------------------------------- |
45
+ | messsageId | string | A unique identifier for a pair of request/response commands which links them together |
46
+ | commandId | string | The identifier of this command's type, e.g. `lobby/create/request` |
47
+
48
+ ### Requests
49
+
50
+ Every request command contains these additional properties:
51
+
52
+ | Property | type | Description |
53
+ | -------- | ------ | -------------------------------------------------------------------------------------------------------- |
54
+ | data | object | A object containing data specific to the command. This may be omitted if the command does not require it |
55
+
56
+ ### Responses
57
+
58
+ Every response command contains these additional properties:
59
+
60
+ | Property | type | Description |
61
+ | -------- | --------------------- | -------------------------------------------------------------------------------------------------------- |
62
+ | status | "success" \| "failed" | A object containing data specific to the command. This may be omitted if the command does not require it |
63
+ | data | object | Command-specific data object. Only present for "success" responses |
64
+ | reason | string | An error code only present for "failed" responses |
65
+
66
+ All `failed` responses that are initiated by a request can return one of the following `reason`s, even though not explicitly defined in each command's definition file:
67
+
68
+ | Reason | Description |
69
+ | --------------- | ------------------------------------------------------------------------------------------ |
70
+ | unauthorized | When a client sends a request command of which they do not have the `role` required to use |
71
+ | internal_error | When the server fails to handle the request in some way, typically sent in a `catch` block |
72
+ | invalid_command | When the request command doesn't match the schema |
73
+
32
74
  ## Contributing
33
75
 
34
76
  The [dist](dist) and [docs](docs) directories are automatically generated based on the files in [src](src). The source files are written in (TypeScript)[https://www.typescriptlang.org/] for [Node.js](https://nodejs.org/en), using the [TypeBox](https://github.com/sinclairzx81/typebox) library for JSONSchema generation.
@@ -37,61 +79,43 @@ A schema file looks like this:
37
79
 
38
80
  ```ts
39
81
  import { Type } from "@sinclair/typebox";
82
+
40
83
  import { defineEndpoint } from "@/helpers";
41
84
 
42
85
  export default defineEndpoint({
43
- description: "Registers a new account.",
44
- requiresLogin: false, // if omitted, defaults to true
86
+ description: "Create a new lobby - intended for player clients to summon a dedicated host.",
45
87
  request: {
46
- // data property is optional
47
88
  data: Type.Object(
48
89
  {
49
- email: Type.String({ format: "email" }), // JSONSchema supports a number of options for each data type
50
- username: Type.String(),
51
- hashedPassword: Type.String(),
90
+ title: Type.String({ minLength: 2, maxLength: 30 }),
91
+ private: Type.Boolean({ default: true }),
92
+ region: Type.String(),
93
+ maxPlayers: Type.Integer({ minimum: 0, default: 16 }),
52
94
  },
53
95
  {
54
- // examples will be shown in the generated documentation
55
96
  examples: [
56
97
  {
57
- email: "bob@test.com",
58
- username: "bob",
59
- hashedPassword: "1b311ff1a6af12fba8720bd2ce02c960",
98
+ title: "8v8 | All Welcome",
99
+ private: false,
100
+ region: "EU",
101
+ maxPlayers: 16,
60
102
  },
61
103
  ],
62
104
  }
63
105
  ),
64
106
  },
65
- response: [
66
- { status: "success" }, // can also add data here
67
- { status: "failed", reason: "email_taken" },
68
- { status: "failed", reason: "username_taken" },
69
- { status: "failed", reason: "invalid_username" },
70
- ],
107
+ response: [{ status: "success" }, { status: "failed", reason: "no_hosts_available" }, { status: "failed", reason: "invalid_region" }],
71
108
  });
72
109
  ```
73
110
 
74
111
  ### Endpoint Options
75
112
 
76
- | Option | Type | Description |
77
- | ------------- | ------- | ------------------------------------------------------------ |
78
- | request | object | The request schema for this endpoint |
79
- | response | object | The response schema for this endpoint |
80
- | description | string | A description of what this endpoint is for |
81
- | requiresLogin | boolean | Does the endpoint require the client to be logged in |
82
- | requiresRole | string | If specified, only users with this role can use the endpoint |
83
- | order | number | Determines the order endpoint appears in the docs |
113
+ | Option | Type | Description |
114
+ | ------------- | ------- | ---------------------------------------------------- |
115
+ | request | object | The request schema for this endpoint |
116
+ | response | object | The response schema for this endpoint |
117
+ | description | string | A description of what this endpoint is for |
118
+ | requiresLogin | boolean | Does the endpoint require the client to be logged in |
119
+ | order | number | Determines the order endpoint appears in the docs |
84
120
 
85
121
  If `request` is omitted, then this describes an endpoint of which the server can send a `response` for at any time, and the client should be ready to handle it. Similarly, if `response` is omitted, this describes an endpoint of which the client should not expect a response, however, the server may still send one, typically for failed responses.
86
-
87
- ### Responses
88
-
89
- A response command has a `status` property which can either be `success` or `failed`. `success` responses have an optional `data` property, and `failed` responses have a `reason` string, which should be a non-whitespace code representing the reason the command failed. This allows clients to provide their own i18n text.
90
-
91
- All `failed` responses can return one of the following `reason`, even though not explicitly defined in each command definition:
92
-
93
- | Reason | Description |
94
- | --------------- | ------------------------------------------------------------------------------------------ |
95
- | unauthorized | When a client sends a request command of which they do not have the `role` required to use |
96
- | internal_error | When the server fails to handle the request in some way, typically sent in a `catch` block |
97
- | invalid_command | When the request command doesn't match the schema |
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "$id": "bot/slave/request",
3
3
  "requiresLogin": false,
4
- "requiresRole": true,
4
+ "requiresRole": "autohost",
5
5
  "type": "object",
6
6
  "properties": {
7
- "command": {
7
+ "messageId": {
8
+ "type": "string"
9
+ },
10
+ "commandId": {
8
11
  "const": "bot/slave/request",
9
12
  "type": "string"
10
13
  },
@@ -23,7 +26,8 @@
23
26
  }
24
27
  },
25
28
  "required": [
26
- "command",
29
+ "messageId",
30
+ "commandId",
27
31
  "data"
28
32
  ]
29
33
  }
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "$id": "bot/slave/response",
3
3
  "requiresLogin": false,
4
- "requiresRole": true,
4
+ "requiresRole": "autohost",
5
5
  "anyOf": [
6
6
  {
7
7
  "type": "object",
8
8
  "properties": {
9
- "command": {
9
+ "messageId": {
10
+ "type": "string"
11
+ },
12
+ "commandId": {
10
13
  "const": "bot/slave/response",
11
14
  "type": "string"
12
15
  },
@@ -16,14 +19,18 @@
16
19
  }
17
20
  },
18
21
  "required": [
19
- "command",
22
+ "messageId",
23
+ "commandId",
20
24
  "status"
21
25
  ]
22
26
  },
23
27
  {
24
28
  "type": "object",
25
29
  "properties": {
26
- "command": {
30
+ "messageId": {
31
+ "type": "string"
32
+ },
33
+ "commandId": {
27
34
  "const": "bot/slave/response",
28
35
  "type": "string"
29
36
  },
@@ -32,24 +39,65 @@
32
39
  "type": "string"
33
40
  },
34
41
  "reason": {
35
- "anyOf": [
36
- {
37
- "const": "internal_error",
38
- "type": "string"
39
- },
40
- {
41
- "const": "unauthorized",
42
- "type": "string"
43
- },
44
- {
45
- "const": "invalid_command",
46
- "type": "string"
47
- }
48
- ]
42
+ "const": "internal_error",
43
+ "type": "string"
44
+ }
45
+ },
46
+ "required": [
47
+ "messageId",
48
+ "commandId",
49
+ "status",
50
+ "reason"
51
+ ]
52
+ },
53
+ {
54
+ "type": "object",
55
+ "properties": {
56
+ "messageId": {
57
+ "type": "string"
58
+ },
59
+ "commandId": {
60
+ "const": "bot/slave/response",
61
+ "type": "string"
62
+ },
63
+ "status": {
64
+ "const": "failed",
65
+ "type": "string"
66
+ },
67
+ "reason": {
68
+ "const": "unauthorized",
69
+ "type": "string"
70
+ }
71
+ },
72
+ "required": [
73
+ "messageId",
74
+ "commandId",
75
+ "status",
76
+ "reason"
77
+ ]
78
+ },
79
+ {
80
+ "type": "object",
81
+ "properties": {
82
+ "messageId": {
83
+ "type": "string"
84
+ },
85
+ "commandId": {
86
+ "const": "bot/slave/response",
87
+ "type": "string"
88
+ },
89
+ "status": {
90
+ "const": "failed",
91
+ "type": "string"
92
+ },
93
+ "reason": {
94
+ "const": "invalid_command",
95
+ "type": "string"
49
96
  }
50
97
  },
51
98
  "required": [
52
- "command",
99
+ "messageId",
100
+ "commandId",
53
101
  "status",
54
102
  "reason"
55
103
  ]
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "$id": "bot/unslave/request",
3
3
  "requiresLogin": false,
4
- "requiresRole": true,
4
+ "requiresRole": "autohost",
5
5
  "type": "object",
6
6
  "properties": {
7
- "command": {
7
+ "messageId": {
8
+ "type": "string"
9
+ },
10
+ "commandId": {
8
11
  "const": "bot/unslave/request",
9
12
  "type": "string"
10
13
  }
11
14
  },
12
15
  "required": [
13
- "command"
16
+ "messageId",
17
+ "commandId"
14
18
  ]
15
19
  }
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "$id": "bot/unslave/response",
3
3
  "requiresLogin": false,
4
- "requiresRole": true,
4
+ "requiresRole": "autohost",
5
5
  "anyOf": [
6
6
  {
7
7
  "type": "object",
8
8
  "properties": {
9
- "command": {
9
+ "messageId": {
10
+ "type": "string"
11
+ },
12
+ "commandId": {
10
13
  "const": "bot/unslave/response",
11
14
  "type": "string"
12
15
  },
@@ -16,14 +19,18 @@
16
19
  }
17
20
  },
18
21
  "required": [
19
- "command",
22
+ "messageId",
23
+ "commandId",
20
24
  "status"
21
25
  ]
22
26
  },
23
27
  {
24
28
  "type": "object",
25
29
  "properties": {
26
- "command": {
30
+ "messageId": {
31
+ "type": "string"
32
+ },
33
+ "commandId": {
27
34
  "const": "bot/unslave/response",
28
35
  "type": "string"
29
36
  },
@@ -32,24 +39,65 @@
32
39
  "type": "string"
33
40
  },
34
41
  "reason": {
35
- "anyOf": [
36
- {
37
- "const": "internal_error",
38
- "type": "string"
39
- },
40
- {
41
- "const": "unauthorized",
42
- "type": "string"
43
- },
44
- {
45
- "const": "invalid_command",
46
- "type": "string"
47
- }
48
- ]
42
+ "const": "internal_error",
43
+ "type": "string"
44
+ }
45
+ },
46
+ "required": [
47
+ "messageId",
48
+ "commandId",
49
+ "status",
50
+ "reason"
51
+ ]
52
+ },
53
+ {
54
+ "type": "object",
55
+ "properties": {
56
+ "messageId": {
57
+ "type": "string"
58
+ },
59
+ "commandId": {
60
+ "const": "bot/unslave/response",
61
+ "type": "string"
62
+ },
63
+ "status": {
64
+ "const": "failed",
65
+ "type": "string"
66
+ },
67
+ "reason": {
68
+ "const": "unauthorized",
69
+ "type": "string"
70
+ }
71
+ },
72
+ "required": [
73
+ "messageId",
74
+ "commandId",
75
+ "status",
76
+ "reason"
77
+ ]
78
+ },
79
+ {
80
+ "type": "object",
81
+ "properties": {
82
+ "messageId": {
83
+ "type": "string"
84
+ },
85
+ "commandId": {
86
+ "const": "bot/unslave/response",
87
+ "type": "string"
88
+ },
89
+ "status": {
90
+ "const": "failed",
91
+ "type": "string"
92
+ },
93
+ "reason": {
94
+ "const": "invalid_command",
95
+ "type": "string"
49
96
  }
50
97
  },
51
98
  "required": [
52
- "command",
99
+ "messageId",
100
+ "commandId",
53
101
  "status",
54
102
  "reason"
55
103
  ]
@@ -1,12 +1,14 @@
1
1
  {
2
2
  "$id": "game/launch/response",
3
3
  "requiresLogin": false,
4
- "requiresRole": false,
5
4
  "anyOf": [
6
5
  {
7
6
  "type": "object",
8
7
  "properties": {
9
- "command": {
8
+ "messageId": {
9
+ "type": "string"
10
+ },
11
+ "commandId": {
10
12
  "const": "game/launch/response",
11
13
  "type": "string"
12
14
  },
@@ -27,7 +29,8 @@
27
29
  }
28
30
  },
29
31
  "required": [
30
- "command",
32
+ "messageId",
33
+ "commandId",
31
34
  "status",
32
35
  "data"
33
36
  ]
@@ -35,7 +38,10 @@
35
38
  {
36
39
  "type": "object",
37
40
  "properties": {
38
- "command": {
41
+ "messageId": {
42
+ "type": "string"
43
+ },
44
+ "commandId": {
39
45
  "const": "game/launch/response",
40
46
  "type": "string"
41
47
  },
@@ -44,24 +50,65 @@
44
50
  "type": "string"
45
51
  },
46
52
  "reason": {
47
- "anyOf": [
48
- {
49
- "const": "internal_error",
50
- "type": "string"
51
- },
52
- {
53
- "const": "unauthorized",
54
- "type": "string"
55
- },
56
- {
57
- "const": "invalid_command",
58
- "type": "string"
59
- }
60
- ]
53
+ "const": "internal_error",
54
+ "type": "string"
55
+ }
56
+ },
57
+ "required": [
58
+ "messageId",
59
+ "commandId",
60
+ "status",
61
+ "reason"
62
+ ]
63
+ },
64
+ {
65
+ "type": "object",
66
+ "properties": {
67
+ "messageId": {
68
+ "type": "string"
69
+ },
70
+ "commandId": {
71
+ "const": "game/launch/response",
72
+ "type": "string"
73
+ },
74
+ "status": {
75
+ "const": "failed",
76
+ "type": "string"
77
+ },
78
+ "reason": {
79
+ "const": "unauthorized",
80
+ "type": "string"
81
+ }
82
+ },
83
+ "required": [
84
+ "messageId",
85
+ "commandId",
86
+ "status",
87
+ "reason"
88
+ ]
89
+ },
90
+ {
91
+ "type": "object",
92
+ "properties": {
93
+ "messageId": {
94
+ "type": "string"
95
+ },
96
+ "commandId": {
97
+ "const": "game/launch/response",
98
+ "type": "string"
99
+ },
100
+ "status": {
101
+ "const": "failed",
102
+ "type": "string"
103
+ },
104
+ "reason": {
105
+ "const": "invalid_command",
106
+ "type": "string"
61
107
  }
62
108
  },
63
109
  "required": [
64
- "command",
110
+ "messageId",
111
+ "commandId",
65
112
  "status",
66
113
  "reason"
67
114
  ]