colyseus.js
Version:
Colyseus Multiplayer SDK for JavaScript/TypeScript
232 lines (228 loc) • 10.8 kB
JavaScript
// colyseus.js@0.16.22
;
var tslib = require('tslib');
var Errors = require('./errors/Errors.js');
var Room = require('./Room.js');
var HTTP = require('./HTTP.js');
var Auth = require('./Auth.js');
var discord = require('./3rd_party/discord.js');
var _a;
class MatchMakeError extends Error {
constructor(message, code) {
super(message);
this.code = code;
this.name = "MatchMakeError";
Object.setPrototypeOf(this, MatchMakeError.prototype);
}
}
// - React Native does not provide `window.location`
// - Cocos Creator (Native) does not provide `window.location.hostname`
const DEFAULT_ENDPOINT = (typeof (window) !== "undefined" && typeof ((_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.hostname) !== "undefined")
? `${window.location.protocol.replace("http", "ws")}//${window.location.hostname}${(window.location.port && `:${window.location.port}`)}`
: "ws://127.0.0.1:2567";
class Client {
constructor(settings = DEFAULT_ENDPOINT, options) {
var _a, _b;
if (typeof (settings) === "string") {
//
// endpoint by url
//
const url = (settings.startsWith("/"))
? new URL(settings, DEFAULT_ENDPOINT)
: new URL(settings);
const secure = (url.protocol === "https:" || url.protocol === "wss:");
const port = Number(url.port || (secure ? 443 : 80));
this.settings = {
hostname: url.hostname,
pathname: url.pathname,
port,
secure,
searchParams: url.searchParams.toString() || undefined,
};
}
else {
//
// endpoint by settings
//
if (settings.port === undefined) {
settings.port = (settings.secure) ? 443 : 80;
}
if (settings.pathname === undefined) {
settings.pathname = "";
}
this.settings = settings;
}
// make sure pathname does not end with "/"
if (this.settings.pathname.endsWith("/")) {
this.settings.pathname = this.settings.pathname.slice(0, -1);
}
this.http = new HTTP.HTTP(this, (options === null || options === void 0 ? void 0 : options.headers) || {});
this.auth = new Auth.Auth(this.http);
this.urlBuilder = options === null || options === void 0 ? void 0 : options.urlBuilder;
//
// Discord Embedded SDK requires a custom URL builder
//
if (!this.urlBuilder &&
typeof (window) !== "undefined" &&
((_b = (_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.hostname) === null || _b === void 0 ? void 0 : _b.includes("discordsays.com"))) {
this.urlBuilder = discord.discordURLBuilder;
console.log("Colyseus SDK: Discord Embedded SDK detected. Using custom URL builder.");
}
}
joinOrCreate(roomName_1) {
return tslib.__awaiter(this, arguments, void 0, function* (roomName, options = {}, rootSchema) {
return yield this.createMatchMakeRequest('joinOrCreate', roomName, options, rootSchema);
});
}
create(roomName_1) {
return tslib.__awaiter(this, arguments, void 0, function* (roomName, options = {}, rootSchema) {
return yield this.createMatchMakeRequest('create', roomName, options, rootSchema);
});
}
join(roomName_1) {
return tslib.__awaiter(this, arguments, void 0, function* (roomName, options = {}, rootSchema) {
return yield this.createMatchMakeRequest('join', roomName, options, rootSchema);
});
}
joinById(roomId_1) {
return tslib.__awaiter(this, arguments, void 0, function* (roomId, options = {}, rootSchema) {
return yield this.createMatchMakeRequest('joinById', roomId, options, rootSchema);
});
}
/**
* Re-establish connection with a room this client was previously connected to.
*
* @param reconnectionToken The `room.reconnectionToken` from previously connected room.
* @param rootSchema (optional) Concrete root schema definition
* @returns Promise<Room>
*/
reconnect(reconnectionToken, rootSchema) {
return tslib.__awaiter(this, void 0, void 0, function* () {
if (typeof (reconnectionToken) === "string" && typeof (rootSchema) === "string") {
throw new Error("DEPRECATED: .reconnect() now only accepts 'reconnectionToken' as argument.\nYou can get this token from previously connected `room.reconnectionToken`");
}
const [roomId, token] = reconnectionToken.split(":");
if (!roomId || !token) {
throw new Error("Invalid reconnection token format.\nThe format should be roomId:reconnectionToken");
}
return yield this.createMatchMakeRequest('reconnect', roomId, { reconnectionToken: token }, rootSchema);
});
}
consumeSeatReservation(response, rootSchema, reuseRoomInstance // used in devMode
) {
return tslib.__awaiter(this, void 0, void 0, function* () {
const room = this.createRoom(response.room.name, rootSchema);
room.roomId = response.room.roomId;
room.sessionId = response.sessionId;
const options = { sessionId: room.sessionId };
// forward "reconnection token" in case of reconnection.
if (response.reconnectionToken) {
options.reconnectionToken = response.reconnectionToken;
}
const targetRoom = reuseRoomInstance || room;
room.connect(this.buildEndpoint(response.room, options, response.protocol), response.devMode && (() => tslib.__awaiter(this, void 0, void 0, function* () {
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x1F504)} Re-establishing connection with room id '${room.roomId}'...`); // 🔄
let retryCount = 0;
let retryMaxRetries = 8;
const retryReconnection = () => tslib.__awaiter(this, void 0, void 0, function* () {
retryCount++;
try {
yield this.consumeSeatReservation(response, rootSchema, targetRoom);
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x2705)} Successfully re-established connection with room '${room.roomId}'`); // ✅
}
catch (e) {
if (retryCount < retryMaxRetries) {
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x1F504)} retrying... (${retryCount} out of ${retryMaxRetries})`); // 🔄
setTimeout(retryReconnection, 2000);
}
else {
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x274C)} Failed to reconnect. Is your server running? Please check server logs.`); // ❌
}
}
});
setTimeout(retryReconnection, 2000);
})), targetRoom, response, this.http.headers);
return new Promise((resolve, reject) => {
const onError = (code, message) => reject(new Errors.ServerError(code, message));
targetRoom.onError.once(onError);
targetRoom['onJoin'].once(() => {
targetRoom.onError.remove(onError);
resolve(targetRoom);
});
});
});
}
createMatchMakeRequest(method_1, roomName_1) {
return tslib.__awaiter(this, arguments, void 0, function* (method, roomName, options = {}, rootSchema, reuseRoomInstance) {
const response = (yield this.http.post(`matchmake/${method}/${roomName}`, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(options)
})).data;
// FIXME: HTTP class is already handling this as ServerError.
// @ts-ignore
if (response.error) {
throw new MatchMakeError(response.error, response.code);
}
// forward reconnection token during "reconnect" methods.
if (method === "reconnect") {
response.reconnectionToken = options.reconnectionToken;
}
return yield this.consumeSeatReservation(response, rootSchema, reuseRoomInstance);
});
}
createRoom(roomName, rootSchema) {
return new Room.Room(roomName, rootSchema);
}
buildEndpoint(room, options = {}, protocol = "ws") {
let searchParams = this.settings.searchParams || "";
// forward authentication token
if (this.http.authToken) {
options['_authToken'] = this.http.authToken;
}
// append provided options
for (const name in options) {
if (!options.hasOwnProperty(name)) {
continue;
}
searchParams += (searchParams ? '&' : '') + `${name}=${options[name]}`;
}
if (protocol === "h3") {
protocol = "http";
}
let endpoint = (this.settings.secure)
? `${protocol}s://`
: `${protocol}://`;
if (room.publicAddress) {
endpoint += `${room.publicAddress}`;
}
else {
endpoint += `${this.settings.hostname}${this.getEndpointPort()}${this.settings.pathname}`;
}
const endpointURL = `${endpoint}/${room.processId}/${room.roomId}?${searchParams}`;
return (this.urlBuilder)
? this.urlBuilder(new URL(endpointURL))
: endpointURL;
}
getHttpEndpoint(segments = '') {
const path = segments.startsWith("/") ? segments : `/${segments}`;
let endpointURL = `${(this.settings.secure) ? "https" : "http"}://${this.settings.hostname}${this.getEndpointPort()}${this.settings.pathname}${path}`;
if (this.settings.searchParams) {
endpointURL += `?${this.settings.searchParams}`;
}
return (this.urlBuilder)
? this.urlBuilder(new URL(endpointURL))
: endpointURL;
}
getEndpointPort() {
return (this.settings.port !== 80 && this.settings.port !== 443)
? `:${this.settings.port}`
: "";
}
}
Client.VERSION = "0.16.22";
exports.Client = Client;
exports.MatchMakeError = MatchMakeError;
//# sourceMappingURL=Client.js.map