Added back Remote Cursors

This commit is contained in:
ExilProductions
2026-01-16 17:06:43 +01:00
parent fa0762643e
commit 8eed0a0a92
10 changed files with 307 additions and 34 deletions

View File

@@ -1,5 +1,5 @@
import { Server as SocketIOServer, Socket } from 'socket.io';
import { upsertPixels, getAllPixels, db } from './database';
import { upsertPixelsWithConflictResolution, getAllPixels, db, validatePixel } from './database';
import { Cursor, Pixel } from '../shared/types';
import passport from 'passport';
@@ -18,7 +18,19 @@ interface UserSocket extends Socket {
user?: any;
}
// Helper function to get user data from db
interface PaintRequest {
pixels: Pixel[];
timestamp: number;
}
interface PaintAck {
success: boolean;
saved: number;
conflicts: number;
pixels?: Pixel[];
message?: string;
}
function getUserById(userId: number) {
try {
const stmt = db.prepare('SELECT * FROM users WHERE id = ?');
@@ -30,7 +42,7 @@ function getUserById(userId: number) {
}
export function setupSocketIO(io: SocketIOServer): void {
const connectedUsers = new Map<string, { x: number; y: number; color: string; username: string; userId: number }>();
const connectedUsers = new Map<string, { x: number; y: number; color: string; username: string; userId: string }>();
io.on('connection', (socket: Socket) => {
const userSocket = socket as UserSocket;
@@ -75,18 +87,52 @@ export function setupSocketIO(io: SocketIOServer): void {
}
});
socket.on('paint-pixels', async (pixels: Pixel[]) => {
socket.on('paint-pixels', async (request: PaintRequest, callback: (ack: PaintAck) => void) => {
try {
if (!userSocket.user) {
socket.emit('error', 'Authentication required to paint pixels');
const ack: PaintAck = { success: false, saved: 0, conflicts: 0, message: 'Authentication required' };
callback(ack);
return;
}
upsertPixels(pixels);
socket.broadcast.emit('canvas-update', { pixels });
const { pixels, timestamp } = request;
if (!Array.isArray(pixels) || pixels.length === 0) {
const ack: PaintAck = { success: false, saved: 0, conflicts: 0, message: 'Invalid pixels data' };
callback(ack);
return;
}
for (const pixel of pixels) {
const validationError = validatePixel(pixel);
if (validationError) {
const ack: PaintAck = { success: false, saved: 0, conflicts: 0, message: validationError };
callback(ack);
return;
}
}
const userIdNum = typeof userData.id === 'number' ? userData.id : parseInt(userData.id, 10);
const result = upsertPixelsWithConflictResolution(
pixels.map(p => ({ ...p, userId: userIdNum })),
timestamp || 0
);
if (result.saved > 0) {
socket.broadcast.emit('canvas-update', { pixels });
}
const ack: PaintAck = {
success: true,
saved: result.saved,
conflicts: result.conflicts,
pixels: result.conflicts > 0 ? pixels : undefined
};
callback(ack);
} catch (error) {
console.error('Error saving pixels:', error);
socket.emit('error', 'Failed to save pixels');
const ack: PaintAck = { success: false, saved: 0, conflicts: 0, message: 'Failed to save pixels' };
callback(ack);
}
});