import { Server, Socket } from "socket.io"; import { handleEvent } from "../handleEvent"; import { EditorState, MissionMap, missionStoreModel } from "../../storage/missionMap"; import * as Y from "yjs"; import MissionDocHelper from "../../helpers/missionDocHelper"; import randomColor from "randomcolor"; import { SocketMap } from "../../storage/socketMap"; export default (io: Server, socket: Socket) => { socket.on( "mission:join", handleEvent( { type: "read", section: "operation", module: "mission" }, async (missionId: string, clientLastUpdate: missionStoreModel) => { socket.rooms.forEach((room) => { if (room !== socket.id && room != "home") { socket.leave(room); } }); try { socket.join(missionId); if (!MissionMap.exists(missionId)) { await MissionDocHelper.populateDoc(missionId); } if (clientLastUpdate && clientLastUpdate.timestamp) { if (MissionDocHelper.docTimestamp(missionId) > clientLastUpdate.timestamp) { socket.emit("package-sync", { update: MissionDocHelper.docAsUpdate(missionId), timestamp: MissionDocHelper.docTimestamp(missionId), source: "server", }); } else { socket.emit("sync-get-missing-updates", { stateVector: MissionDocHelper.docAsStateVector(missionId), timestamp: MissionDocHelper.docTimestamp(missionId), source: "server", }); } } else { socket.emit("package-sync", { update: MissionDocHelper.docAsUpdate(missionId), timestamp: MissionDocHelper.docTimestamp(missionId), source: "server", }); } socket.emit("package-init-awareness", { update: MissionDocHelper.getAwarenessEditors(missionId), type: "editors", }); socket.emit("package-init-awareness", { update: MissionDocHelper.getAwarenessState(missionId), type: "state", }); let newEditor = { socketId: socket.id, data: { username: SocketMap.read(socket.id).username, firstname: SocketMap.read(socket.id).firstname, lastname: SocketMap.read(socket.id).lastname, color: randomColor({ format: "hex" }), }, }; MissionMap.updateAwareness(missionId, { action: "update", socketId: newEditor.socketId, store: "editors", update: newEditor.data, }); socket.to(missionId).emit("package-sync-awareness", { update: newEditor.data, socketId: socket.id, action: "update", type: "editors", }); return { type: "status-mission:join", answer: { status: "success" }, }; } catch (error) { return { type: "status-join:join", answer: { status: "failed", msg: error.message } }; } }, socket ) ); socket.on( "mission:sync-client-updates", handleEvent( { type: "create", section: "operation", module: "mission" }, async (data: { update: Array; timestamp: number }) => { const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home"); try { MissionDocHelper.saveDoc(socketRooms[0], new Uint8Array(data.update)); return { type: "package-sync", answer: { update: data.update, timestamp: data.timestamp, source: socket.id, }, room: socketRooms[0], }; } catch (error) { socket.emit("package-sync", { update: MissionDocHelper.docAsUpdate(socketRooms[0]), timestamp: MissionDocHelper.docTimestamp(socketRooms[0]), source: "server", error: true, }); return { type: "status-mission:sync-client-updates", answer: { status: "failed", msg: error.message } }; } }, socket ) ); socket.on( "mission:sync-get-missing-updates", handleEvent( { type: "create", section: "operation", module: "mission" }, async (data: any) => { const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home"); try { const mission = MissionMap.read(socketRooms[0]); const missingUpdates = Y.encodeStateAsUpdate(mission.doc, data.stateVector); return { type: "package-sync", answer: { update: missingUpdates, timestamp: MissionDocHelper.docTimestamp(socketRooms[0]), source: "server", }, }; } catch (error) { return { type: "status-mission:sync-get-missing-updates", answer: { status: "failed", msg: error.message } }; } }, socket ) ); socket.on( "mission:sync-client-awareness", handleEvent( { type: "create", section: "operation", module: "mission" }, async (data: EditorState) => { const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home"); try { MissionMap.updateAwareness(socketRooms[0], { action: "update", store: "state", update: data, socketId: socket.id, }); return { type: "package-sync-awareness", answer: { type: "state", action: "update", update: data, socketId: socket.id, }, room: socketRooms[0], }; } catch (error) { socket.emit("package-sync-awareness", { update: MissionDocHelper.docAsUpdate(socketRooms[0]), source: "server", error: true, }); return { type: "status-mission:sync-client-updates", answer: { status: "failed", msg: error.message } }; } }, socket ) ); socket.on( "mission:leave", handleEvent( { type: "read", section: "operation", module: "mission" }, async (data: any) => { const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home"); socket.leave(socketRooms[0]); const clients = io.sockets.adapter.rooms.get(socketRooms[0]); if (!clients || clients.size == 0) { MissionMap.delete(socketRooms[0]); } MissionMap.updateAwareness(socketRooms[0], { action: "remove", store: "editors", update: undefined, socketId: socket.id, }); io.to(socketRooms[0]).emit("package-sync-awareness", { type: "editors", update: undefined, action: "remove", socketId: socket.id, }); MissionMap.updateAwareness(socketRooms[0], { action: "remove", store: "state", update: undefined, socketId: socket.id, }); io.to(socketRooms[0]).emit("package-sync-awareness", { type: "state", update: undefined, action: "remove", socketId: socket.id, }); return { type: "deleted", answer: "leave instance", }; }, socket ) ); };