@hytopia.com/server-protocol 1.4.0 → 1.4.1

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.
package/exports.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from './packets/bidirectional';
1
2
  export * from './packets/inbound';
2
3
  export * from './packets/outbound';
3
4
  export * from './packets/PacketCore';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hytopia.com/server-protocol",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@hytopia.com/server-protocol": "^1.1.3",
15
- "ajv": "^8.17.1"
15
+ "ajv": "^8.17.1",
16
+ "mediasoup": "^3.15.7"
16
17
  },
17
18
  "repository": {
18
19
  "type": "git",
@@ -39,6 +39,11 @@ export enum PacketId {
39
39
  UI_DATAS = 42,
40
40
  SCENE_UIS = 43,
41
41
  LIGHTS = 44,
42
+ PLAYERS = 45,
43
+
44
+ // Standard Bi-Directional Packet Types: 116 - 127 range
45
+ CONNECTION = 116,
46
+ HEARTBEAT = 117,
42
47
 
43
48
  // Debug Inbound Packet Types: 128 - 191 range
44
49
  DEBUG_CONFIG = 128,
@@ -1,12 +1,13 @@
1
+ import * as bidirectionalPackets from './bidirectional';
1
2
  import * as inboundPackets from './inbound';
2
3
  import * as outboundPackets from './outbound';
3
4
  import type { PacketId, AnyPacket, AnyPacketDefinition, IPacket } from './PacketCore';
4
5
 
5
- export { inboundPackets, outboundPackets };
6
+ export { bidirectionalPackets, inboundPackets, outboundPackets };
6
7
 
7
8
  export const registeredPackets = new Map<PacketId, AnyPacketDefinition>();
8
9
 
9
- const allPackets = { ...inboundPackets, ...outboundPackets };
10
+ const allPackets = { ...bidirectionalPackets, ...inboundPackets, ...outboundPackets };
10
11
 
11
12
  for (const packet of Object.values(allPackets)) {
12
13
  if ('id' in packet && 'schema' in packet) {
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketId } from '../PacketCore';
2
+ import type { IPacket } from '../PacketCore';
3
+ import { connectionSchema } from '../../schemas/Connection';
4
+ import type { ConnectionSchema } from '../../schemas/Connection';
5
+
6
+ export type ConnectionPacket = IPacket<typeof PacketId.CONNECTION, ConnectionSchema>;
7
+
8
+ export const connectionPacketDefinition = definePacket(
9
+ PacketId.CONNECTION,
10
+ connectionSchema
11
+ );
@@ -0,0 +1,11 @@
1
+ import { definePacket, PacketId } from '../PacketCore';
2
+ import type { IPacket } from '../PacketCore';
3
+ import { heartbeatSchema } from '../../schemas/Heartbeat';
4
+ import type { HeartbeatSchema } from '../../schemas/Heartbeat';
5
+
6
+ export type HeartbeatPacket = IPacket<typeof PacketId.HEARTBEAT, HeartbeatSchema>;
7
+
8
+ export const heartbeatPacketDefinition = definePacket(
9
+ PacketId.HEARTBEAT,
10
+ heartbeatSchema
11
+ );
@@ -0,0 +1,2 @@
1
+ export * from './Connection';
2
+ export * from './Heartbeat';
@@ -0,0 +1,12 @@
1
+ import { definePacket, PacketId } from '../PacketCore';
2
+ import type { IPacket } from '../PacketCore';
3
+ import { playersSchema } from '../../schemas/Players';
4
+ import type { PlayersSchema } from '../../schemas/Players';
5
+ import type { WorldTick } from '../PacketCore';
6
+
7
+ export type PlayersPacket = IPacket<typeof PacketId.PLAYERS, PlayersSchema> & [WorldTick];
8
+
9
+ export const playersPacketDefinition = definePacket(
10
+ PacketId.PLAYERS,
11
+ playersSchema,
12
+ );
@@ -8,6 +8,7 @@ export * from './Entities';
8
8
  export * from './Lights';
9
9
  export * from './PhysicsDebugRender';
10
10
  export * from './PhysicsDebugRaycasts';
11
+ export * from './Players';
11
12
  export * from './SceneUIs';
12
13
  export * from './SyncResponse';
13
14
  export * from './UI';
package/schemas/Audio.ts CHANGED
@@ -5,6 +5,7 @@ import type { VectorSchema } from './Vector';
5
5
  export type AudioSchema = {
6
6
  i: number; // audio id
7
7
  a?: string; // audio uri
8
+ cd?: number; // cutoff distance
8
9
  d?: number; // duration
9
10
  de?: number; // detune
10
11
  di?: number; // distortion (0 to 1, or 1+)
@@ -26,6 +27,7 @@ export const audioSchema: JSONSchemaType<AudioSchema> = {
26
27
  properties: {
27
28
  i: { type: 'number' },
28
29
  a: { type: 'string', nullable: true },
30
+ cd: { type: 'number', nullable: true },
29
31
  d: { type: 'number', nullable: true },
30
32
  de: { type: 'number', nullable: true },
31
33
  di: { type: 'number', minimum: 0, nullable: true },
@@ -3,8 +3,9 @@ import type { JSONSchemaType } from 'ajv';
3
3
  import type { HexColorSchema } from './HexColor';
4
4
 
5
5
  export type ChatMessageSchema = {
6
- m: string; // message
6
+ m: string; // message
7
7
  c?: HexColorSchema; // color
8
+ p?: string; // player id
8
9
  }
9
10
 
10
11
  export const chatMessageSchema: JSONSchemaType<ChatMessageSchema> = {
@@ -12,6 +13,7 @@ export const chatMessageSchema: JSONSchemaType<ChatMessageSchema> = {
12
13
  properties: {
13
14
  m: { type: 'string' },
14
15
  c: { ...hexColorSchema, nullable: true },
16
+ p: { type: 'string', nullable: true },
15
17
  },
16
18
  required: ['m'],
17
19
  additionalProperties: false,
@@ -0,0 +1,188 @@
1
+ import * as mediasoup from 'mediasoup';
2
+ import { JSONSchemaType } from 'ajv';
3
+
4
+ /**
5
+ * Types
6
+ */
7
+
8
+ type WrtcConnectAck = {
9
+ i: string; // transport id
10
+ }
11
+
12
+ type WrtcDataChannel = {
13
+ i?: string; // data channel id (server -> client channel only)
14
+ pi?: string; // data producer id (server -> client channel only)
15
+ l: string; // label
16
+ s: mediasoup.types.SctpStreamParameters; // sctp stream parameters
17
+ }
18
+
19
+ type WrtcProduceDataAck = {
20
+ i: string; // producer id
21
+ l: string; // label, used for client reference of ack
22
+ }
23
+
24
+ type WrtcTransport = {
25
+ i: string; // transport id
26
+ f: 'cs' | 'sc'; // data flow (cs: client -> server | sc: server -> client)
27
+ d: mediasoup.types.DtlsParameters; // dtls parameters
28
+ ic: mediasoup.types.IceCandidate[]; // ice candidates
29
+ ip: mediasoup.types.IceParameters; // ice parameters
30
+ s: mediasoup.types.SctpParameters; // sctp parameters
31
+ }
32
+
33
+ type WrtcTransportConnect = {
34
+ i: string; // transport id
35
+ d: mediasoup.types.DtlsParameters; // dtls parameters
36
+ }
37
+
38
+ export type ConnectionSchema = {
39
+ i?: string; // connection id | server -> client | on initial WS connection
40
+ c?: WrtcTransportConnect, // wrtc transport connect | client -> server | 2nd wrtc packet
41
+ ca?: WrtcConnectAck, // wrtc connect ack | server -> client | 3rd wrtc packet
42
+ d?: WrtcDataChannel[], // wrtc data channels | server <-> client | 1st wrtc packet s->c, 4th wrtc packet c->s
43
+ pa?: WrtcProduceDataAck, // wrtc produce data ack | server -> client | 5th wrtc packet
44
+ t?: WrtcTransport[], // wrtc transports | server -> client | 1st wrtc packet
45
+ };
46
+
47
+ /**
48
+ * Schemas
49
+ */
50
+
51
+ const wrtcConnectAckSchema: JSONSchemaType<WrtcConnectAck> = {
52
+ type: 'object',
53
+ properties: {
54
+ i: { type: 'string' },
55
+ },
56
+ required: [ 'i' ],
57
+ additionalProperties: false,
58
+ };
59
+
60
+ const wrtcDtlsParametersSchema: JSONSchemaType<mediasoup.types.DtlsParameters> = {
61
+ type: 'object',
62
+ properties: {
63
+ role: { type: 'string', enum: [ 'auto', 'client', 'server' ], nullable: true },
64
+ fingerprints: {
65
+ type: 'array',
66
+ items: {
67
+ type: 'object',
68
+ properties: {
69
+ algorithm: { type: 'string', enum: [ 'sha-1', 'sha-224', 'sha-256', 'sha-384', 'sha-512' ] },
70
+ value: { type: 'string' },
71
+ },
72
+ required: [ 'algorithm', 'value' ],
73
+ }
74
+ },
75
+ },
76
+ required: [ 'fingerprints' ],
77
+ additionalProperties: false,
78
+ };
79
+
80
+ const wrtcIceCandidateSchema: JSONSchemaType<mediasoup.types.IceCandidate> = {
81
+ type: 'object',
82
+ properties: {
83
+ foundation: { type: 'string' },
84
+ priority: { type: 'number' },
85
+ ip: { type: 'string' },
86
+ address: { type: 'string' },
87
+ protocol: { type: 'string', enum: [ 'udp', 'tcp' ] },
88
+ port: { type: 'number' },
89
+ type: { type: 'string', enum: [ 'host' ] },
90
+ tcpType: { type: 'string', enum: [ 'passive' ], nullable: true },
91
+ },
92
+ required: [ 'foundation', 'priority', 'ip', 'address', 'protocol', 'port', 'type' ],
93
+ additionalProperties: false,
94
+ };
95
+
96
+ const wrtcIceParametersSchema: JSONSchemaType<mediasoup.types.IceParameters> = {
97
+ type: 'object',
98
+ properties: {
99
+ usernameFragment: { type: 'string' },
100
+ password: { type: 'string' },
101
+ iceLite: { type: 'boolean', nullable: true },
102
+ },
103
+ required: [ 'usernameFragment', 'password' ],
104
+ additionalProperties: false,
105
+ };
106
+
107
+ const wrtcProduceDataAckSchema: JSONSchemaType<WrtcProduceDataAck> = {
108
+ type: 'object',
109
+ properties: {
110
+ i: { type: 'string' },
111
+ l: { type: 'string' },
112
+ },
113
+ required: [ 'i', 'l' ],
114
+ additionalProperties: false,
115
+ };
116
+
117
+ const wrtcSctpParametersSchema: JSONSchemaType<mediasoup.types.SctpParameters> = {
118
+ type: 'object',
119
+ properties: {
120
+ port: { type: 'number' },
121
+ OS: { type: 'number' },
122
+ MIS: { type: 'number' },
123
+ maxMessageSize: { type: 'number' },
124
+ },
125
+ required: [ 'port', 'OS', 'MIS', 'maxMessageSize' ],
126
+ additionalProperties: true, // May be an SCTPParametersDump
127
+ };
128
+
129
+ const wrtcSctpStreamParametersSchema: JSONSchemaType<mediasoup.types.SctpStreamParameters> = {
130
+ type: 'object',
131
+ properties: {
132
+ streamId: { type: 'number' },
133
+ ordered: { type: 'boolean', nullable: true },
134
+ maxPacketLifeTime: { type: 'number', nullable: true },
135
+ maxRetransmits: { type: 'number', nullable: true },
136
+ },
137
+ required: [ 'streamId' ],
138
+ additionalProperties: false,
139
+ };
140
+
141
+ const wrtcTransportSchema: JSONSchemaType<WrtcTransport> = {
142
+ type: 'object',
143
+ properties: {
144
+ i: { type: 'string' },
145
+ f: { type: 'string', enum: [ 'cs', 'sc' ] },
146
+ ip: wrtcIceParametersSchema,
147
+ ic: { type: 'array', items: wrtcIceCandidateSchema },
148
+ d: wrtcDtlsParametersSchema,
149
+ s: wrtcSctpParametersSchema,
150
+ },
151
+ required: [ 'i', 'f', 'ip', 'ic', 'd', 's' ],
152
+ additionalProperties: false,
153
+ };
154
+
155
+ const wrtcTransportConnectSchema: JSONSchemaType<WrtcTransportConnect> = {
156
+ type: 'object',
157
+ properties: {
158
+ i: { type: 'string' },
159
+ d: wrtcDtlsParametersSchema,
160
+ },
161
+ required: [ 'i', 'd' ],
162
+ additionalProperties: false,
163
+ };
164
+
165
+ const wrtcDataChannelSchema: JSONSchemaType<WrtcDataChannel> = {
166
+ type: 'object',
167
+ properties: {
168
+ i: { type: 'string', nullable: true },
169
+ pi: { type: 'string', nullable: true },
170
+ l: { type: 'string' },
171
+ s: wrtcSctpStreamParametersSchema,
172
+ },
173
+ required: [ 'l', 's' ],
174
+ additionalProperties: false,
175
+ };
176
+
177
+ export const connectionSchema: JSONSchemaType<ConnectionSchema> = {
178
+ type: 'object',
179
+ properties: {
180
+ i: { type: 'string', nullable: true },
181
+ c: { ...wrtcTransportConnectSchema, nullable: true },
182
+ ca: { ...wrtcConnectAckSchema, nullable: true },
183
+ d: { type: 'array', items: { ...wrtcDataChannelSchema }, nullable: true },
184
+ pa: { ...wrtcProduceDataAckSchema, nullable: true },
185
+ t: { type: 'array', items: { ...wrtcTransportSchema }, nullable: true },
186
+ },
187
+ additionalProperties: false,
188
+ };
package/schemas/Entity.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import { rgbColorSchema } from './RgbColor';
2
+ import { modelNodeOverrideSchema } from './ModelNodeOverride';
2
3
  import { quaternionSchema } from './Quaternion';
3
4
  import { vectorSchema } from './Vector';
4
5
  import type { JSONSchemaType } from 'ajv';
6
+ import type { ModelNodeOverrideSchema } from './ModelNodeOverride';
5
7
  import type { QuaternionSchema } from './Quaternion';
6
8
  import type { RgbColorSchema } from './RgbColor';
7
9
  import type { VectorSchema } from './Vector';
@@ -14,11 +16,9 @@ export type EntitySchema = {
14
16
  as?: string[]; // model animations stop
15
17
  bh?: VectorSchema; // block half extents
16
18
  bt?: string; // block texture uri
17
- c?: boolean; // controlled
18
- cf?: string; // control function for client prediction
19
- cs?: object; // control function state for client prediction
20
19
  h?: string[]; // model parts to hide
21
20
  m?: string; // model uri
21
+ mo?: ModelNodeOverrideSchema[]; // model node overrides
22
22
  n?: string; // name
23
23
  o?: number; // opacity
24
24
  p?: VectorSchema; // position
@@ -40,11 +40,9 @@ export const entitySchema: JSONSchemaType<EntitySchema> = {
40
40
  as: { type: 'array', items: { type: 'string' }, nullable: true },
41
41
  bh: { ...vectorSchema, nullable: true },
42
42
  bt: { type: 'string', nullable: true },
43
- c: { type: 'boolean', nullable: true },
44
- cf: { type: 'string', nullable: true },
45
- cs: { type: 'object', nullable: true },
46
43
  h: { type: 'array', items: { type: 'string' }, nullable: true },
47
44
  m: { type: 'string', nullable: true },
45
+ mo: { type: 'array', items: { ...modelNodeOverrideSchema }, nullable: true },
48
46
  n: { type: 'string', nullable: true },
49
47
  o: { type: 'number', nullable: true },
50
48
  p: { ...vectorSchema, nullable: true },
@@ -0,0 +1,8 @@
1
+ import type { JSONSchemaType } from 'ajv';
2
+
3
+ export type HeartbeatSchema = null;
4
+
5
+ export const heartbeatSchema: JSONSchemaType<HeartbeatSchema> = {
6
+ type: 'null',
7
+ nullable: true,
8
+ }
package/schemas/Input.ts CHANGED
@@ -30,6 +30,7 @@ export type InputSchema = {
30
30
  mr?: boolean; // mouse right pressed
31
31
  cp?: number; // camera pitch radians
32
32
  cy?: number; // camera yaw radians
33
+ sq?: number; // sequence number for UDP inputs
33
34
  }
34
35
 
35
36
  export const inputSchema: JSONSchemaType<InputSchema> = {
@@ -64,6 +65,7 @@ export const inputSchema: JSONSchemaType<InputSchema> = {
64
65
  mr: { type: 'boolean', nullable: true },
65
66
  cp: { type: 'number', nullable: true },
66
67
  cy: { type: 'number', nullable: true },
68
+ sq: { type: 'number', nullable: true },
67
69
  },
68
70
  additionalProperties: false,
69
71
  }
@@ -0,0 +1,26 @@
1
+ import { rgbColorSchema } from './RgbColor';
2
+ import type { JSONSchemaType } from 'ajv';
3
+ import type { RgbColorSchema } from './RgbColor';
4
+
5
+ export type ModelNodeOverrideSchema = {
6
+ n: string; // node name match
7
+ h?: boolean; // hidden
8
+ o?: number; // opacity
9
+ r?: boolean; // reset override to defaults
10
+ s?: number; // scale
11
+ t?: RgbColorSchema; // tint color
12
+ }
13
+
14
+ export const modelNodeOverrideSchema: JSONSchemaType<ModelNodeOverrideSchema> = {
15
+ type: 'object',
16
+ properties: {
17
+ n: { type: 'string' },
18
+ h: { type: 'boolean', nullable: true },
19
+ o: { type: 'number', minimum: 0, maximum: 1, nullable: true },
20
+ r: { type: 'boolean', nullable: true },
21
+ s: { type: 'number', nullable: true },
22
+ t: { ...rgbColorSchema, nullable: true },
23
+ },
24
+ required: [ 'n' ],
25
+ additionalProperties: false,
26
+ }
@@ -0,0 +1,20 @@
1
+ import type { JSONSchemaType } from 'ajv';
2
+
3
+ export type PlayerSchema = {
4
+ i: string; // player id
5
+ p?: string; // profile picture url
6
+ rm?: boolean; // removed/left game
7
+ u?: string; // username
8
+ }
9
+
10
+ export const playerSchema: JSONSchemaType<PlayerSchema> = {
11
+ type: 'object',
12
+ properties: {
13
+ i: { type: 'string' },
14
+ p: { type: 'string', nullable: true },
15
+ rm: { type: 'boolean', nullable: true },
16
+ u: { type: 'string', nullable: true },
17
+ },
18
+ required: [ 'i' ],
19
+ additionalProperties: false,
20
+ }
@@ -0,0 +1,10 @@
1
+ import { playerSchema } from './Player';
2
+ import type { JSONSchemaType } from 'ajv';
3
+ import type { PlayerSchema } from './Player';
4
+
5
+ export type PlayersSchema = PlayerSchema[];
6
+
7
+ export const playersSchema: JSONSchemaType<PlayersSchema> = {
8
+ type: 'array',
9
+ items: { ...playerSchema },
10
+ }
package/schemas/World.ts CHANGED
@@ -13,6 +13,7 @@ export type WorldSchema = {
13
13
  dp?: VectorSchema; // directional light position
14
14
  n?: string; // name
15
15
  s?: string; // skyboxUri
16
+ si?: number; // skybox intensity
16
17
  t?: number; // timestep (seconds)
17
18
  };
18
19
 
@@ -27,6 +28,7 @@ export const worldSchema: JSONSchemaType<WorldSchema> = {
27
28
  dp: { ...vectorSchema, nullable: true },
28
29
  n: { type: 'string', nullable: true },
29
30
  s: { type: 'string', nullable: true },
31
+ si: { type: 'number', nullable: true },
30
32
  t: { type: 'number', nullable: true },
31
33
  },
32
34
  required: [ 'i' ],
package/schemas/index.ts CHANGED
@@ -9,16 +9,21 @@ export * from './ChatMessage';
9
9
  export * from './ChatMessages';
10
10
  export * from './Chunk';
11
11
  export * from './Chunks';
12
+ export * from './Connection';
12
13
  export * from './DebugConfig';
13
14
  export * from './Entity';
14
15
  export * from './Entities';
16
+ export * from './Heartbeat';
15
17
  export * from './HexColor';
16
18
  export * from './Input';
17
19
  export * from './Light';
18
20
  export * from './Lights';
21
+ export * from './ModelNodeOverride';
19
22
  export * from './PhysicsDebugRaycast';
20
23
  export * from './PhysicsDebugRaycasts';
21
24
  export * from './PhysicsDebugRender';
25
+ export * from './Player';
26
+ export * from './Players';
22
27
  export * from './Quaternion';
23
28
  export * from './RgbColor';
24
29
  export * from './SceneUI';