mission management by timestamp
This commit is contained in:
parent
1646d0d6ca
commit
8aaeaa7e0f
5 changed files with 136 additions and 34 deletions
21
package-lock.json
generated
21
package-lock.json
generated
|
@ -37,6 +37,7 @@
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"typeorm": "^0.3.20",
|
"typeorm": "^0.3.20",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
|
"y-protocols": "^1.0.6",
|
||||||
"yjs": "^13.6.23"
|
"yjs": "^13.6.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -4831,6 +4832,26 @@
|
||||||
"node": ">=0.4"
|
"node": ">=0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/y-protocols": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"lib0": "^0.2.85"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "GitHub Sponsors ❤",
|
||||||
|
"url": "https://github.com/sponsors/dmonad"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"yjs": "^13.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"typeorm": "^0.3.20",
|
"typeorm": "^0.3.20",
|
||||||
"uuid": "^10.0.0",
|
"uuid": "^10.0.0",
|
||||||
|
"y-protocols": "^1.0.6",
|
||||||
"yjs": "^13.6.23"
|
"yjs": "^13.6.23"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
29
src/helpers/missionDocHelper.ts
Normal file
29
src/helpers/missionDocHelper.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import * as Y from "yjs";
|
||||||
|
import { MissionMap } from "../storage/missionMap";
|
||||||
|
|
||||||
|
export default abstract class MissionDocHelper {
|
||||||
|
public static async populateDoc(missionId: string) {
|
||||||
|
// get Data from database
|
||||||
|
const doc = new Y.Doc();
|
||||||
|
doc.getMap("form");
|
||||||
|
doc.getText("editor");
|
||||||
|
|
||||||
|
// const ymap = ydoc.getMap('myMap');
|
||||||
|
// ymap.set('titel', 'Mein Dokument');
|
||||||
|
// ymap.set('inhalt', 'Hier ist der initiale Inhalt');
|
||||||
|
// ymap.set('erstelltAm', new Date().toISOString());
|
||||||
|
|
||||||
|
// const yarray = ydoc.getArray('meineArray');
|
||||||
|
// yarray.push(['Element 1', 'Element 2', 'Element 3']);
|
||||||
|
|
||||||
|
// const ytext = ydoc.getText('meinText');
|
||||||
|
// ytext.insert(0, 'Hier ist ein initialer Text');
|
||||||
|
|
||||||
|
MissionMap.write(missionId, { missionId, doc, timestamp: 0 }, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async saveDoc(missionId: string) {
|
||||||
|
// store Data to database
|
||||||
|
const mission = MissionMap.read(missionId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ import * as Y from "yjs";
|
||||||
export interface missionStoreModel {
|
export interface missionStoreModel {
|
||||||
missionId: string;
|
missionId: string;
|
||||||
doc: Y.Doc;
|
doc: Y.Doc;
|
||||||
|
timestamp: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -15,6 +16,18 @@ export abstract class MissionMap {
|
||||||
if (!this.exists(identifier) || overwrite) this.store.set(identifier, data);
|
if (!this.exists(identifier) || overwrite) this.store.set(identifier, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static updateState(identifier: string, data: Uint8Array): void {
|
||||||
|
let mission = this.read(identifier);
|
||||||
|
Y.applyUpdate(mission.doc, data);
|
||||||
|
this.write(identifier, mission, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static updateTimestamp(identifier: string, data: number): void {
|
||||||
|
let mission = this.read(identifier);
|
||||||
|
mission.timestamp = data;
|
||||||
|
this.write(identifier, mission, true);
|
||||||
|
}
|
||||||
|
|
||||||
public static read(identifier: string): missionStoreModel {
|
public static read(identifier: string): missionStoreModel {
|
||||||
return this.store.get(identifier);
|
return this.store.get(identifier);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { Server, Socket } from "socket.io";
|
import { Server, Socket } from "socket.io";
|
||||||
import { handleEvent } from "../handleEvent";
|
import { handleEvent } from "../handleEvent";
|
||||||
import { MissionMap } from "../../storage/missionMap";
|
import { MissionMap, missionStoreModel } from "../../storage/missionMap";
|
||||||
import * as Y from "yjs";
|
import * as Y from "yjs";
|
||||||
|
import MissionDocHelper from "../../helpers/missionDocHelper";
|
||||||
|
|
||||||
export default (io: Server, socket: Socket) => {
|
export default (io: Server, socket: Socket) => {
|
||||||
socket.on(
|
socket.on(
|
||||||
"mission:join",
|
"mission:join",
|
||||||
handleEvent(
|
handleEvent(
|
||||||
{ type: "read", section: "operation", module: "mission" },
|
{ type: "read", section: "operation", module: "mission" },
|
||||||
async (data: string, initialized: boolean) => {
|
async (missionId: string, clientLastUpdate: missionStoreModel) => {
|
||||||
socket.rooms.forEach((room) => {
|
socket.rooms.forEach((room) => {
|
||||||
if (room !== socket.id && room != "home") {
|
if (room !== socket.id && room != "home") {
|
||||||
socket.leave(room);
|
socket.leave(room);
|
||||||
|
@ -16,33 +17,35 @@ export default (io: Server, socket: Socket) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const doc = new Y.Doc();
|
socket.join(missionId);
|
||||||
doc.getMap("form");
|
|
||||||
doc.getText("editor");
|
|
||||||
|
|
||||||
// const ymap = ydoc.getMap('myMap');
|
await MissionDocHelper.populateDoc(missionId);
|
||||||
// ymap.set('titel', 'Mein Dokument');
|
const mission = MissionMap.read(missionId);
|
||||||
// ymap.set('inhalt', 'Hier ist der initiale Inhalt');
|
|
||||||
// ymap.set('erstelltAm', new Date().toISOString());
|
|
||||||
|
|
||||||
// const yarray = ydoc.getArray('meineArray');
|
if (clientLastUpdate && clientLastUpdate.timestamp) {
|
||||||
// yarray.push(['Element 1', 'Element 2', 'Element 3']);
|
// Prüfe, ob der Client aktuell ist oder nicht
|
||||||
|
if (mission.timestamp > clientLastUpdate.timestamp) {
|
||||||
// const ytext = ydoc.getText('meinText');
|
// Server hat neuere Daten, sende vollständigen State
|
||||||
// ytext.insert(0, 'Hier ist ein initialer Text');
|
socket.emit("package-sync", {
|
||||||
|
update: Y.encodeStateAsUpdate(mission.doc),
|
||||||
// get mission data
|
timestamp: mission.timestamp,
|
||||||
MissionMap.write(data, {
|
source: "server",
|
||||||
missionId: data,
|
});
|
||||||
doc,
|
} else {
|
||||||
});
|
// Client hat aktuellere oder gleich aktuelle Daten
|
||||||
|
socket.emit("sync-get-missing-updates", {
|
||||||
const mission = MissionMap.read(data);
|
stateVector: Y.encodeStateVector(mission.doc),
|
||||||
|
timestamp: mission.timestamp,
|
||||||
socket.join(data);
|
source: "server",
|
||||||
|
});
|
||||||
if (!initialized) {
|
}
|
||||||
socket.emit("package-mission", Y.encodeStateAsUpdate(mission.doc));
|
} else {
|
||||||
|
// Client hat keine Zeitstempel-Info, sende vollständigen State
|
||||||
|
socket.emit("package-sync", {
|
||||||
|
update: Y.encodeStateAsUpdate(mission.doc),
|
||||||
|
timestamp: mission.timestamp,
|
||||||
|
source: "server",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -58,23 +61,58 @@ export default (io: Server, socket: Socket) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
socket.on(
|
socket.on(
|
||||||
"mission:sync",
|
"mission:sync-client-updates",
|
||||||
handleEvent(
|
handleEvent(
|
||||||
{ type: "read", section: "operation", module: "mission" },
|
{ type: "create", section: "operation", module: "mission" },
|
||||||
|
async (data: { update: Array<number>; timestamp: number }) => {
|
||||||
|
const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home");
|
||||||
|
try {
|
||||||
|
MissionMap.updateState(socketRooms[0], new Uint8Array(data.update));
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "package-sync",
|
||||||
|
answer: {
|
||||||
|
update: data.update,
|
||||||
|
timestamp: data.timestamp,
|
||||||
|
source: "client",
|
||||||
|
},
|
||||||
|
room: socketRooms[0],
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
const mission = MissionMap.read(socketRooms[0]);
|
||||||
|
socket.emit("package-sync", {
|
||||||
|
update: Y.encodeStateAsUpdate(mission.doc),
|
||||||
|
timestamp: mission.timestamp,
|
||||||
|
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) => {
|
async (data: any) => {
|
||||||
const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home");
|
const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home");
|
||||||
try {
|
try {
|
||||||
const mission = MissionMap.read(socketRooms[0]);
|
const mission = MissionMap.read(socketRooms[0]);
|
||||||
Y.applyUpdate(mission.doc, new Uint8Array(data));
|
const missingUpdates = Y.encodeStateAsUpdate(mission.doc, data.stateVector);
|
||||||
|
|
||||||
socket.emit("status-mission:sync", { status: "success" });
|
|
||||||
return {
|
return {
|
||||||
type: "package-sync",
|
type: "package-sync",
|
||||||
answer: data,
|
answer: {
|
||||||
room: socketRooms[0],
|
update: missingUpdates,
|
||||||
|
timestamp: mission.timestamp,
|
||||||
|
source: "server",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return { type: "status-mission:sync", answer: { status: "failed", msg: error.message } };
|
return { type: "status-mission:sync-get-missing-updates", answer: { status: "failed", msg: error.message } };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
socket
|
socket
|
||||||
|
|
Loading…
Add table
Reference in a new issue