import { Server, Socket } from "socket.io"; import { handleEvent } from "../handleEvent"; import { MissionMap, missionStoreModel } from "../../storage/missionMap"; import * as Y from "yjs"; import MissionDocHelper from "../../helpers/missionDocHelper"; 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-sync-awareness", { update: [], // do awareness self source: "server", }); 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: { update: Array; timestamp: number }) => { const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home"); try { MissionMap.updateAwareness(socketRooms[0], new Uint8Array(data.update)); return { type: "package-sync-awareness", answer: { update: data.update, source: 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]); } return { type: "deleted", answer: "leave instance", }; }, socket ) ); };