Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 41 additions & 24 deletions src/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,24 @@ export class Client {
* @param rootSchema (optional) Concrete root schema definition
* @returns Promise<Room>
*/
public async reconnect<T>(reconnectionToken: string, rootSchema?: SchemaConstructor<T>) {
public async reconnect<T>(reconnectionToken: string, rootSchema?: SchemaConstructor<T>) : Promise<Room<T>>;
/**
* Re-establish connection with a room this client was previously connected to.
*
* @param room The previously connected room.
* @returns Promise<Room> - The room that was passed in.
*/
public async reconnect<T>(room: Room<T>) : Promise<Room<T>>;
public async reconnect<T>(value: string | Room<T>, rootSchema?: SchemaConstructor<T>) {
const { reconnectionToken, room } = typeof (value) === "object" ? { reconnectionToken: value.reconnectionToken, room: value } : { reconnectionToken: value, room: undefined };
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 await this.createMatchMakeRequest<T>('reconnect', roomId, { reconnectionToken: token }, rootSchema);
return await this.createMatchMakeRequest<T>('reconnect', roomId, { reconnectionToken: token }, rootSchema, room);
}

public async getAvailableRooms<Metadata = any>(roomName: string = ""): Promise<RoomAvailable<Metadata>[]> {
Expand All @@ -142,9 +151,11 @@ export class Client {
public async consumeSeatReservation<T>(
response: any,
rootSchema?: SchemaConstructor<T>,
reuseRoomInstance?: Room // used in devMode
reuseRoomInstance?: Room, // used in devMode and when reconnecting in place
simpleConnect: boolean = false
): Promise<Room<T>> {
const room = this.createRoom<T>(response.room.name, rootSchema);

const room = reuseRoomInstance ?? this.createRoom<T>(response.room.name, rootSchema);
room.roomId = response.room.roomId;
room.sessionId = response.sessionId;

Expand All @@ -156,32 +167,38 @@ export class Client {
}

const targetRoom = reuseRoomInstance || room;
room.connect(this.buildEndpoint(response.room, options), response.devMode && (async () => {
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x1F504)} Re-establishing connection with room id '${room.roomId}'...`); // 🔄

let retryCount = 0;
let retryMaxRetries = 8;
// true if reconnecting in place
if (simpleConnect) {
room.connection.connect(this.buildEndpoint(response.room, options));
} else {
room.connect(this.buildEndpoint(response.room, options), response.devMode && (async () => {
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x1F504)} Re-establishing connection with room id '${room.roomId}'...`); // 🔄

const retryReconnection = async () => {
retryCount++;
let retryCount = 0;
let retryMaxRetries = 8;

try {
await this.consumeSeatReservation(response, rootSchema, targetRoom);
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x2705)} Successfully re-established connection with room '${room.roomId}'`); // ✅
const retryReconnection = async () => {
retryCount++;

} catch (e) {
if (retryCount < retryMaxRetries) {
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x1F504)} retrying... (${retryCount} out of ${retryMaxRetries})`); // 🔄
setTimeout(retryReconnection, 2000);
try {
await this.consumeSeatReservation(response, rootSchema, targetRoom);
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x2705)} Successfully re-established connection with room '${room.roomId}'`); // ✅

} else {
console.info(`[Colyseus devMode]: ${String.fromCodePoint(0x274C)} Failed to reconnect. Is your server running? Please check server logs.`); // ❌
} 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);
setTimeout(retryReconnection, 2000);
}), targetRoom);
}

return new Promise((resolve, reject) => {
const onError = (code, message) => reject(new ServerError(code, message));
Expand Down Expand Up @@ -221,7 +238,7 @@ export class Client {
response.reconnectionToken = options.reconnectionToken;
}

return await this.consumeSeatReservation<T>(response, rootSchema, reuseRoomInstance);
return await this.consumeSeatReservation<T>(response, rootSchema, reuseRoomInstance, !!reuseRoomInstance);
}

protected createRoom<T>(roomName: string, rootSchema?: SchemaConstructor<T>) {
Expand Down
7 changes: 7 additions & 0 deletions src/Room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export class Room<State= any> {
// Public signals
public onStateChange = createSignal<(state: State) => void>();
public onError = createSignal<(code: number, message?: string) => void>();
public onClose = createSignal<(code: number) => void>();
public onLeave = createSignal<(code: number) => void>();
protected onJoin = createSignal();

Expand Down Expand Up @@ -77,6 +78,12 @@ export class Room<State= any> {
room.onError.invoke(e.code, e.reason);
return;
}
// allow the user to throw an error in onClose to cancel leaving
try {
room.onClose.invoke(e.code);
} catch (error) {
return;
}
if (e.code === CloseCode.DEVMODE_RESTART && devModeCloseCallback) {
devModeCloseCallback();
} else {
Expand Down