ff-operation-server/src/websocket/endpoints/missionManagement.ts

236 lines
7.4 KiB
TypeScript
Raw Normal View History

2025-02-26 16:26:56 +01:00
import { Server, Socket } from "socket.io";
import { handleEvent } from "../handleEvent";
2025-03-04 12:01:48 +01:00
import { EditorState, MissionMap, missionStoreModel } from "../../storage/missionMap";
2025-02-26 16:26:56 +01:00
import * as Y from "yjs";
2025-02-28 14:02:21 +01:00
import MissionDocHelper from "../../helpers/missionDocHelper";
2025-03-04 12:01:48 +01:00
import randomColor from "randomcolor";
import { SocketMap } from "../../storage/socketMap";
2025-02-26 16:26:56 +01:00
export default (io: Server, socket: Socket) => {
socket.on(
"mission:join",
handleEvent(
{ type: "read", section: "operation", module: "mission" },
2025-02-28 14:02:21 +01:00
async (missionId: string, clientLastUpdate: missionStoreModel) => {
2025-02-26 16:26:56 +01:00
socket.rooms.forEach((room) => {
if (room !== socket.id && room != "home") {
socket.leave(room);
}
});
try {
2025-02-28 14:02:21 +01:00
socket.join(missionId);
2025-02-26 16:26:56 +01:00
2025-03-01 12:29:46 +01:00
if (!MissionMap.exists(missionId)) {
await MissionDocHelper.populateDoc(missionId);
}
2025-02-28 14:02:21 +01:00
if (clientLastUpdate && clientLastUpdate.timestamp) {
2025-03-03 17:05:55 +01:00
if (MissionDocHelper.docTimestamp(missionId) > clientLastUpdate.timestamp) {
2025-02-28 14:02:21 +01:00
socket.emit("package-sync", {
2025-03-03 17:05:55 +01:00
update: MissionDocHelper.docAsUpdate(missionId),
timestamp: MissionDocHelper.docTimestamp(missionId),
2025-02-28 14:02:21 +01:00
source: "server",
});
} else {
socket.emit("sync-get-missing-updates", {
2025-03-03 17:05:55 +01:00
stateVector: MissionDocHelper.docAsStateVector(missionId),
timestamp: MissionDocHelper.docTimestamp(missionId),
2025-02-28 14:02:21 +01:00
source: "server",
});
}
} else {
socket.emit("package-sync", {
2025-03-03 17:05:55 +01:00
update: MissionDocHelper.docAsUpdate(missionId),
timestamp: MissionDocHelper.docTimestamp(missionId),
2025-02-28 14:02:21 +01:00
source: "server",
});
2025-02-27 15:08:51 +01:00
}
2025-03-04 12:01:48 +01:00
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,
2025-03-05 09:52:01 +01:00
color: randomColor({ format: "hex", luminosity: "dark" }),
2025-03-04 12:01:48 +01:00
},
};
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",
2025-03-01 12:29:46 +01:00
});
2025-02-26 16:26:56 +01:00
return {
2025-02-27 15:08:51 +01:00
type: "status-mission:join",
answer: { status: "success" },
2025-02-26 16:26:56 +01:00
};
} catch (error) {
return { type: "status-join:join", answer: { status: "failed", msg: error.message } };
}
},
socket
)
);
socket.on(
2025-02-28 14:02:21 +01:00
"mission:sync-client-updates",
2025-02-26 16:26:56 +01:00
handleEvent(
2025-02-28 14:02:21 +01:00
{ 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 {
2025-03-03 17:05:55 +01:00
MissionDocHelper.saveDoc(socketRooms[0], new Uint8Array(data.update));
2025-02-28 14:02:21 +01:00
return {
type: "package-sync",
answer: {
update: data.update,
timestamp: data.timestamp,
2025-03-01 12:29:46 +01:00
source: socket.id,
2025-02-28 14:02:21 +01:00
},
room: socketRooms[0],
};
} catch (error) {
socket.emit("package-sync", {
2025-03-03 17:05:55 +01:00
update: MissionDocHelper.docAsUpdate(socketRooms[0]),
timestamp: MissionDocHelper.docTimestamp(socketRooms[0]),
2025-02-28 14:02:21 +01:00
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" },
2025-02-26 16:26:56 +01:00
async (data: any) => {
const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home");
try {
const mission = MissionMap.read(socketRooms[0]);
2025-02-28 14:02:21 +01:00
const missingUpdates = Y.encodeStateAsUpdate(mission.doc, data.stateVector);
2025-02-26 16:26:56 +01:00
return {
type: "package-sync",
2025-02-28 14:02:21 +01:00
answer: {
update: missingUpdates,
2025-03-03 17:05:55 +01:00
timestamp: MissionDocHelper.docTimestamp(socketRooms[0]),
2025-02-28 14:02:21 +01:00
source: "server",
},
2025-02-26 16:26:56 +01:00
};
} catch (error) {
2025-02-28 14:02:21 +01:00
return { type: "status-mission:sync-get-missing-updates", answer: { status: "failed", msg: error.message } };
2025-02-26 16:26:56 +01:00
}
},
socket
)
);
2025-03-01 12:29:46 +01:00
socket.on(
"mission:sync-client-awareness",
handleEvent(
{ type: "create", section: "operation", module: "mission" },
2025-03-04 12:01:48 +01:00
async (data: EditorState) => {
2025-03-01 12:29:46 +01:00
const socketRooms = Array.from(socket.rooms).filter((room) => room !== socket.id && room !== "home");
try {
2025-03-04 12:01:48 +01:00
MissionMap.updateAwareness(socketRooms[0], {
action: "update",
store: "state",
update: data,
socketId: socket.id,
});
2025-03-01 12:29:46 +01:00
return {
type: "package-sync-awareness",
answer: {
2025-03-04 12:01:48 +01:00
type: "state",
action: "update",
update: data,
socketId: socket.id,
2025-03-01 12:29:46 +01:00
},
room: socketRooms[0],
};
} catch (error) {
socket.emit("package-sync-awareness", {
2025-03-03 17:05:55 +01:00
update: MissionDocHelper.docAsUpdate(socketRooms[0]),
2025-03-01 12:29:46 +01:00
source: "server",
error: true,
});
return { type: "status-mission:sync-client-updates", answer: { status: "failed", msg: error.message } };
}
},
socket
)
);
2025-02-26 16:26:56 +01:00
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]);
}
2025-03-04 12:01:48 +01:00
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,
});
2025-02-26 16:26:56 +01:00
return {
type: "deleted",
answer: "leave instance",
};
},
socket
)
);
};