Skip to content
Merged
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
7 changes: 7 additions & 0 deletions client/src/services/socket.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class SocketService {
emitJoinLobby(lobbyName: string) {
this.socket?.emit('JOIN_LOBBY', lobbyName);
}

disconnect() {
if (this.socket) {
this.socket.disconnect();
this.socket = null;
}
}
}

export const socketService = new SocketService();
11 changes: 10 additions & 1 deletion client/src/stores/editor.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,14 @@ export const useEditorStore = defineStore('editor', () => {
});
}

const cleanup = () => {
socketService.disconnect();
isConnected.value = false;
currentLobbyName.value = '';
pixelsBuffer.value = [];
isDrawing.value = false;
};

return {
width,
height,
Expand All @@ -247,6 +255,7 @@ export const useEditorStore = defineStore('editor', () => {
endStroke,
clearCanvas,
isConnected,
init
init,
cleanup
};
});
8 changes: 7 additions & 1 deletion client/src/views/PlayView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { getLobbyById } from '../services/api';

// Setup Store
const store = useEditorStore();
const { width, height, pixels, palette, selectedColorIndex, isConnected, pixelUpdateEvent } = storeToRefs(store);
const { width, height, pixels, palette, pixelUpdateEvent } = storeToRefs(store);

const route = useRoute();

Expand All @@ -34,6 +34,12 @@ onMounted(async () => {

store.init(lobbyName);
});

import { onUnmounted } from 'vue';

onUnmounted(() => {
store.cleanup();
});
</script>

<template>
Expand Down
19 changes: 19 additions & 0 deletions server/src/services/canvas.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,23 @@ export class CanvasService {

console.log(`[CanvasService] Saved lobby '${lobbyName}' to DB`);
}

// Unloads a lobby from memory, persisting it first
static async unloadLobby(lobbyName: string) {
if (!canvasStore.isLobbyInMemory(lobbyName)) return;

console.log(`[CanvasService] Unloading idle lobby: ${lobbyName}`);

// If there's a pending save timer, cancel it and save immediately
if (this.saveTimers.has(lobbyName)) {
clearTimeout(this.saveTimers.get(lobbyName));
this.saveTimers.delete(lobbyName);
}

// Force strict save
await this.saveToDB(lobbyName);

// Remove from memory
canvasStore.removeLobby(lobbyName);
}
}
11 changes: 10 additions & 1 deletion server/src/sockets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,20 @@ export const setupSocket = (io: Server) => {


// --- DISCONNECTING ---
socket.on('disconnecting', () => {
socket.on('disconnecting', async () => {
// Notify rooms that user is leaving
for (const room of socket.rooms) {
if (room !== socket.id) {
socket.to(room).emit(CONFIG.EVENTS.SERVER.USER_LEFT, (socket as AuthenticatedSocket).user);

// Check if room is empty (excluding this socket)
const socketsInRoom = await io.in(room).fetchSockets();
const remainingUsers = socketsInRoom.length - 1; // fetchSockets includes the disconnecting socket

if (remainingUsers <= 0) {
// Room is empty, unload from hot storage
await CanvasService.unloadLobby(room);
}
}
}
});
Expand Down
5 changes: 5 additions & 0 deletions server/src/store/canvas.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export class CanvasStore {
public getInMemoryLobbyIds(): string[] {
return Array.from(this.lobbies.keys());
}

public removeLobby(lobbyName: string): boolean {
console.log(`[CanvasStore] Removing lobby from memory: ${lobbyName}`);
return this.lobbies.delete(lobbyName);
}
}

export const canvasStore = new CanvasStore();