forked from because-why-not/awrtc_signaling
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathINetwork.ts
326 lines (271 loc) · 11.4 KB
/
INetwork.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/*
Copyright (c) 2019, because-why-not.com Limited
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** Abstract interfaces and serialization to keep different
* versions compatible to each other.
*
* Watch out before changing anything in this file. Content is reused
* between webclient, signaling server and needs to remain compatible to
* the C# implementation.
*/
export enum NetEventType {
Invalid = 0,
UnreliableMessageReceived = 1,
ReliableMessageReceived = 2,
ServerInitialized = 3,//confirmation that the server was started. other people will be able to connect
ServerInitFailed = 4,//server couldn't be started
ServerClosed = 5,//server was closed. no new incoming connections
NewConnection = 6,//new incoming or outgoing connection established
ConnectionFailed = 7,//outgoing connection failed
Disconnected = 8,//a connection was disconnected
FatalError = 100, //not yet used
Warning = 101,//not yet used
Log = 102, //not yet used
/// <summary>
/// This value and higher are reserved for other uses.
/// Should never get to the user and should be filtered out.
/// </summary>
ReservedStart = 200,
/// <summary>
/// Reserved.
/// Used by protocols that forward NetworkEvents
/// </summary>
MetaVersion = 201,
/// <summary>
/// Reserved.
/// Used by protocols that forward NetworkEvents.
/// </summary>
MetaHeartbeat = 202
}
export enum NetEventDataType {
Null = 0,
ByteArray = 1, //leading 32 bit byte length + byte array
UTF16String = 2, //leading 32 bit length (in utf16 chunks) + UTF 16
}
export class NetworkEvent {
private type: NetEventType;
private connectionId: ConnectionId;
private data: any;
constructor(t: NetEventType, conId?: ConnectionId, data?: any) {
this.type = t;
this.connectionId = conId;
this.data = data;
}
public get RawData(): any {
return this.data;
}
public get MessageData(): Uint8Array {
if (typeof this.data != "string")
return this.data;
return null;
}
public get Info(): string {
if (typeof this.data == "string")
return this.data;
return null;
}
public get Type(): NetEventType {
return this.type;
}
public get ConnectionId(): ConnectionId {
return this.connectionId;
}
//for debugging only
public toString(): string {
let output = "NetworkEvent[";
output += "NetEventType: (";
output += NetEventType[this.type];
output += "), id: (";
output += this.connectionId?.id;
output += "), Data: (";
if (typeof this.data == "string") {
output += this.data;
}
output += ")]";
return output;
}
public static toString(evt: NetworkEvent): string {
return JSON.stringify(evt);
}
public static fromByteArray(arrin: Uint8Array): NetworkEvent {
//old node js versions seem to not return proper Uint8Arrays but
//buffers -> make sure it is a Uint8Array
let arr : Uint8Array = new Uint8Array(arrin)
console.debug('fromByteArray arr:', arr)
let type: NetEventType = arr[0]; //byte
let dataType: NetEventDataType = arr[1]; //byte
let conId: ConnectionId = null;
let data: any = null;
if(type == NetEventType.MetaVersion)
data = dataType;
else if (type != NetEventType.MetaHeartbeat)
{
const id: number = new Int16Array(arr.buffer, arr.byteOffset + 2, 1)[0]; //short
conId = new ConnectionId(id);
if (dataType == NetEventDataType.ByteArray) {
let length: number = new Uint32Array(arr.buffer, arr.byteOffset + 4, 1)[0]; //uint
let byteArray = new Uint8Array(arr.buffer, arr.byteOffset + 8, length);
data = byteArray;
}
else if (dataType == NetEventDataType.UTF16String) {
let length: number = new Uint32Array(arr.buffer, arr.byteOffset + 4, 1)[0]; //uint
let uint16Arr = new Uint16Array(arr.buffer, arr.byteOffset + 8, length);
let str: string = "";
for (let i = 0; i < uint16Arr.length; i++) {
str += String.fromCharCode(uint16Arr[i]);
}
data = str;
}
else if (dataType != NetEventDataType.Null)
throw new Error('Message has an invalid data type flag: ' + dataType);
}
let result: NetworkEvent = new NetworkEvent(type, conId, data);
console.debug('fromByteArray result:', result)
return result;
}
public static toByteArray(evt: NetworkEvent): Uint8Array {
console.debug('toByteArray evt:', evt)
let dataType: NetEventDataType;
let length = 4; //4 bytes are always needed
//getting type and length
if (evt.data == null) {
dataType = NetEventDataType.Null;
} else if (typeof evt.data == "string") {
dataType = NetEventDataType.UTF16String;
let str: string = evt.data;
length += 4 + str.length * 2;
} else {
dataType = NetEventDataType.ByteArray;
let byteArray: Uint8Array = evt.data;
length += 4 + byteArray.length;
}
//creating the byte array
let result = new Uint8Array(length);
result[0] = evt.type;
result[1] = dataType;
let conIdField = new Int16Array(result.buffer, result.byteOffset + 2, 1);
conIdField[0] = evt.connectionId?.id;
if (dataType == NetEventDataType.ByteArray) {
let byteArray: Uint8Array = evt.data;
let lengthField = new Uint32Array(result.buffer, result.byteOffset + 4, 1);
lengthField[0] = byteArray.length;
for (let i = 0; i < byteArray.length; i++) {
result[8 + i] = byteArray[i];
}
} else if (dataType == NetEventDataType.UTF16String) {
let str: string = evt.data;
let lengthField = new Uint32Array(result.buffer, result.byteOffset + 4, 1);
lengthField[0] = str.length;
let dataField = new Uint16Array(result.buffer, result.byteOffset + 8, str.length);
for (let i = 0; i < dataField.length; i++) {
dataField[i] = str.charCodeAt(i);
}
}
console.debug('toByteArray result:', result)
return result;
}
}
export class ConnectionId {
public static INVALID: ConnectionId = new ConnectionId(-1);
id: number;
constructor(nid: number) {
this.id = nid;
}
}
/// <summary>
/// Interface to a network that doesn't enforce storing any states.
///
/// Anything more is reusable between multiple different networks.
/// </summary>
export interface INetwork {
/// <summary>
/// This will return the incoming network events. Call this method and handle the incommen events until it returns false.
/// </summary>
/// <param name="evt"></param>
/// <returns>Returns true if the parameter evt contains a new event. False if there are no events to process left.</returns>
Dequeue(): NetworkEvent;
Peek(): NetworkEvent;
/// <summary>
/// Sends buffered data.
/// Might also clear all unused events from the queue!
/// </summary>
Flush(): void;
/// <summary>
/// Sends the content if a byte array to the given connection.
/// </summary>
/// <param name="id">The id of the recipient</param>
/// <param name="data">Byte array containing the data to send</param>
/// <param name="offset">The index in data where the network should start to send</param>
/// <param name="length">Length in bytes you want to send</param>
/// <param name="reliable">True to send a reliable message(tcp style) and false to send unreliable (udp style)</param>
SendData(id: ConnectionId, data: Uint8Array, /*offset: number, length: number,*/ reliable: boolean): boolean
/// <summary>
/// Disconnects the given connection
/// </summary>
/// <param name="id">Id of the connection to disconnect.</param>
Disconnect(id: ConnectionId): void;
/// <summary>
/// Disconnects all connection and shutsdown the server if started.
/// Dequeue will still return the confirmation messages such as Disconnected event for each connection.
///
/// </summary>
Shutdown(): void;
/// <summary>
/// Call this every frame if you intend to read incoming messages using Dequeue. This will make
/// sure all data is read received by the network.
/// </summary>
Update(): void;
Dispose(): void;
}
/// <summary>
/// Shared interface for WebRtcNetwork and UnityNetwork.
///
/// Keep in mind that in the current version the network can only act as a server (StartServer method) or
/// as a client (via Connect method).
/// </summary>
export interface IBasicNetwork extends INetwork {
/// <summary>
/// Starts a new server. After the server is started the Dequeue method will return a
/// ServerInitialized event with the address in the Info field.
///
/// If the server fails to start it will return a ServerInitFailed event. If the
/// server is closed due to an error or the Shutdown method a ServerClosed event
/// will be triggered.
/// </summary>
StartServer(address?: string): void;
StopServer(): void
/// <summary>
/// Connects to a given address or roomname.
///
/// This call will result in one of those 2 events in response:
/// * NewConnection if the connection was established
/// * ConnectionFailed if the connection failed.
///
/// </summary>
/// <param name="address">A string that identifies the target.</param>
/// <returns>Returns the Connection id the established connection will have (only supported by WebRtcNetwork).</returns>
Connect(address: string): ConnectionId;
}
export interface IWebRtcNetwork extends IBasicNetwork {}
//export {NetEventType, NetworkEvent, ConnectionId, INetwork, IBasicNetwork};