From 584e017cd742bbf48b3a1f3b864260a8b35744e5 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Sat, 1 Mar 2025 12:29:46 +0100 Subject: [PATCH] sync form --- src/helpers/missionDocHelper.ts | 6 ++- src/storage/missionMap.ts | 8 +++ src/types/y-protocols.d.ts | 3 ++ src/websocket/endpoints/missionManagement.ts | 51 +++++++++++++++++--- 4 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 src/types/y-protocols.d.ts diff --git a/src/helpers/missionDocHelper.ts b/src/helpers/missionDocHelper.ts index e7cc31f..851823b 100644 --- a/src/helpers/missionDocHelper.ts +++ b/src/helpers/missionDocHelper.ts @@ -1,4 +1,5 @@ import * as Y from "yjs"; +import * as AwarenessProtocol from "y-protocols/dist/awareness.cjs"; import { MissionMap } from "../storage/missionMap"; export default abstract class MissionDocHelper { @@ -19,7 +20,10 @@ export default abstract class MissionDocHelper { // const ytext = ydoc.getText('meinText'); // ytext.insert(0, 'Hier ist ein initialer Text'); - MissionMap.write(missionId, { missionId, doc, timestamp: 0 }, true); + const awareness = new AwarenessProtocol.Awareness(doc); + awareness.setLocalState(null); + + MissionMap.write(missionId, { missionId, doc, awareness, timestamp: 0 }); } public static async saveDoc(missionId: string) { diff --git a/src/storage/missionMap.ts b/src/storage/missionMap.ts index 7784a96..28c336d 100644 --- a/src/storage/missionMap.ts +++ b/src/storage/missionMap.ts @@ -1,8 +1,10 @@ import * as Y from "yjs"; +import * as AwarenessProtocol from "y-protocols/dist/awareness.cjs"; export interface missionStoreModel { missionId: string; doc: Y.Doc; + awareness: AwarenessProtocol.Awareness; timestamp: number; } @@ -22,6 +24,12 @@ export abstract class MissionMap { this.write(identifier, mission, true); } + public static updateAwareness(identifier: string, data: Uint8Array, socketId: string): void { + let mission = this.read(identifier); + AwarenessProtocol.applyAwarenessUpdate(mission.awareness, data, socketId); + this.write(identifier, mission, true); + } + public static updateTimestamp(identifier: string, data: number): void { let mission = this.read(identifier); mission.timestamp = data; diff --git a/src/types/y-protocols.d.ts b/src/types/y-protocols.d.ts new file mode 100644 index 0000000..a489418 --- /dev/null +++ b/src/types/y-protocols.d.ts @@ -0,0 +1,3 @@ +declare module "y-protocols/dist/awareness.cjs" { + export * from "y-protocols/awareness"; +} diff --git a/src/websocket/endpoints/missionManagement.ts b/src/websocket/endpoints/missionManagement.ts index cde2cfe..030719b 100644 --- a/src/websocket/endpoints/missionManagement.ts +++ b/src/websocket/endpoints/missionManagement.ts @@ -2,6 +2,7 @@ import { Server, Socket } from "socket.io"; import { handleEvent } from "../handleEvent"; import { MissionMap, missionStoreModel } from "../../storage/missionMap"; import * as Y from "yjs"; +import * as AwarenessProtocol from "y-protocols/dist/awareness.cjs"; import MissionDocHelper from "../../helpers/missionDocHelper"; export default (io: Server, socket: Socket) => { @@ -19,20 +20,20 @@ export default (io: Server, socket: Socket) => { try { socket.join(missionId); - await MissionDocHelper.populateDoc(missionId); + if (!MissionMap.exists(missionId)) { + await MissionDocHelper.populateDoc(missionId); + } + const mission = MissionMap.read(missionId); if (clientLastUpdate && clientLastUpdate.timestamp) { - // Prüfe, ob der Client aktuell ist oder nicht if (mission.timestamp > clientLastUpdate.timestamp) { - // Server hat neuere Daten, sende vollständigen State socket.emit("package-sync", { update: Y.encodeStateAsUpdate(mission.doc), timestamp: mission.timestamp, source: "server", }); } else { - // Client hat aktuellere oder gleich aktuelle Daten socket.emit("sync-get-missing-updates", { stateVector: Y.encodeStateVector(mission.doc), timestamp: mission.timestamp, @@ -40,7 +41,6 @@ export default (io: Server, socket: Socket) => { }); } } else { - // Client hat keine Zeitstempel-Info, sende vollständigen State socket.emit("package-sync", { update: Y.encodeStateAsUpdate(mission.doc), timestamp: mission.timestamp, @@ -48,6 +48,14 @@ export default (io: Server, socket: Socket) => { }); } + socket.emit("package-sync-awareness", { + update: AwarenessProtocol.encodeAwarenessUpdate( + mission.awareness, + Array.from(mission.awareness.getStates().keys()) + ), + source: "server", + }); + return { type: "status-mission:join", answer: { status: "success" }, @@ -74,7 +82,7 @@ export default (io: Server, socket: Socket) => { answer: { update: data.update, timestamp: data.timestamp, - source: "client", + source: socket.id, }, room: socketRooms[0], }; @@ -119,6 +127,37 @@ export default (io: Server, socket: 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), socket.id); + + return { + type: "package-sync-awareness", + answer: { + update: data.update, + source: socket.id, + }, + room: socketRooms[0], + }; + } catch (error) { + const mission = MissionMap.read(socketRooms[0]); + socket.emit("package-sync-awareness", { + update: Y.encodeStateAsUpdate(mission.doc), + source: "server", + error: true, + }); + return { type: "status-mission:sync-client-updates", answer: { status: "failed", msg: error.message } }; + } + }, + socket + ) + ); + socket.on( "mission:leave", handleEvent(