///
namespace gdjs {
const logger = new gdjs.Logger('Multiplayer');
/** @category Multiplayer */
export namespace multiplayerPeerJsHelper {
/**
* The type of the data that is sent across peerjs.
* We use UInt8Array to send compressed data, but we only manipulate objects once received.
*/
type NetworkMessage = {
messageName: string;
data: Uint8Array | string;
};
type PeerJSInitOptions = {
onPeerUnavailable?: () => void;
};
/** @category Multiplayer */
export type CompressionMethod = 'none' | 'cs:gzip' | 'cs:deflate';
/**
* Helper to discard invalid messages when received.
*/
const isValidNetworkMessage = (
message: unknown
): message is NetworkMessage =>
typeof message === 'object' &&
message !== null &&
typeof message['messageName'] === 'string' &&
typeof message['data'] === 'object';
export interface IMessageData {
readonly data: any; // The data sent with the message, an object with unknown content.
readonly sender: String;
getData(): any;
getSender(): string;
}
/**
* The data bound to a message name.
* @category Multiplayer
*/
export class MessageData implements IMessageData {
public readonly data: any;
public readonly sender: string;
constructor(data: object, sender: string) {
this.data = data;
this.sender = sender;
}
public getData(): any {
return this.data;
}
public getSender(): string {
return this.sender;
}
}
export interface IMessagesList {
getName(): string;
getMessages(): IMessageData[];
pushMessage(data: object, sender: string): void;
}
/** @category Multiplayer */
export class MessagesList implements IMessagesList {
private readonly data: IMessageData[] = [];
private readonly messageName: string;
constructor(messageName: string) {
this.messageName = messageName;
}
public getName(): string {
return this.messageName;
}
public getMessages(): IMessageData[] {
return this.data;
}
public pushMessage(data: object, sender: string): void {
this.data.push(new MessageData(data, sender));
}
}
/**
* The peer to peer configuration.
*/
let peerConfig: Peer.PeerJSOption = { debug: 1 };
/**
* The p2p client.
*/
let peer: Peer | null = null;
/**
* All connected p2p clients, keyed by their ID.
*/
const connections = new Map>();
/**
* Contains a map of message triggered by other p2p clients.
* It is keyed by the event name.
*/
const allMessages = new Map();
/**
* True if PeerJS is initialized and ready.
*/
let ready = false;
let _peerIdToConnectToOnceReady: string | null = null;
/**
* List of IDs of peers that just disconnected.
*/
const justDisconnectedPeers: string[] = [];
/**
* List of IDs of peers that just remotely initiated a connection.
*/
const justConnectedPeers: string[] = [];
/**
* The compression method used to compress data sent over the network.
*/
let compressionMethod: CompressionMethod = 'none';
export const setCompressionMethod = (method: CompressionMethod) => {
compressionMethod = method;
};
/**
* Helper function to compress data sent over the network.
*/
async function compressData(data: object): Promise {
if (compressionMethod === 'none') {
// If no compression is used, we just stringify the data,
// PeerJS will compress it to binary data.
const jsonString = JSON.stringify(data);
return jsonString;
}
const compressionStreamFormat =
compressionMethod === 'cs:gzip' ? 'gzip' : 'deflate';
const jsonString = JSON.stringify(data);
const encoder = new TextEncoder();
const array = encoder.encode(jsonString);
// @ts-ignore - We checked that CompressionStream is available in the browser.
const cs = new CompressionStream(compressionStreamFormat);
const writer = cs.writable.getWriter();
writer.write(array);
writer.close();
const compressedStream = cs.readable;
const reader = compressedStream.getReader();
const chunks: any[] = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
const compressedData = new Uint8Array(
chunks.reduce((acc, chunk) => acc.concat(Array.from(chunk)), [])
);
return compressedData;
}
/**
* Helper function to decompress data received over the network.
* It returns the parsed JSON object, if valid, or undefined.
*/
async function decompressData(
receivedData: Uint8Array | string
): Promise