Loading package-lock.json +20 −0 Original line number Diff line number Diff line Loading @@ -6602,6 +6602,12 @@ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", Loading Loading @@ -16492,6 +16498,7 @@ "rate-limit-redis": "^4.2.0", "redis": "^4.6.12", "socket.io": "^4.7.2", "uuid": "^10.0.0", "winston": "^3.11.0" }, "devDependencies": { Loading @@ -16499,6 +16506,7 @@ "@types/cors": "^2.8.17", "@types/express": "^4.17.17", "@types/express-session": "^1.17.7", "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^7.1.0", "@typescript-eslint/parser": "^7.1.0", "dotenv": "^16.3.1", Loading Loading @@ -16795,6 +16803,18 @@ "engines": { "node": ">= 0.8" } }, "packages/server/node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "bin": { "uuid": "dist/bin/uuid" } } } } packages/client/src/index.tsx +3 −1 Original line number Diff line number Diff line Loading @@ -7,7 +7,9 @@ import Bugsnag from "@bugsnag/js"; import BugsnagPluginReact from "@bugsnag/plugin-react"; import BugsnagPerformance from "@bugsnag/browser-performance"; let ErrorBoundary: any = <></>; let ErrorBoundary: any = ({ children }: React.PropsWithChildren) => ( <>{children}</> ); if (import.meta.env.VITE_BUGSNAG_KEY) { Bugsnag.start({ Loading packages/client/src/lib/canvas.ts +34 −10 Original line number Diff line number Diff line Loading @@ -102,10 +102,25 @@ export class Canvas extends EventEmitter<CanvasEvents> { if (Object.keys(this.pixels).length > 0) Network.clearPreviousState("canvas"); Network.waitForState("canvas").then(([pixels]) => { console.log("loadConfig just received new canvas data"); this.handleBatch(pixels); // Network.waitForState("canvas").then(([pixels]) => { // console.log("loadConfig just received new canvas data"); // this.handleBatch(pixels); // }); Network.on("canvas", (start, end, pixels) => { console.log("[Canvas] received canvas section"); this.handleBatch(start, end, pixels); }); const chunks = Network.getCanvasChunks(); console.log(`[Canvas] Received ${chunks.length} chunks to load`); let loaded = 0; for (const chunk of chunks) { console.log(`[Canvas] Loading canvas chunk ${loaded}...`); this.handleBatch(chunk.start, chunk.end, chunk.pixels); loaded++; } } hasConfig() { Loading Loading @@ -267,27 +282,36 @@ export class Canvas extends EventEmitter<CanvasEvents> { getRenderer().usePixels(serializeBuild); }; handleBatch = (pixels: string[]) => { handleBatch = ( start: [x: number, y: number], end: [x: number, y: number], pixels: string[] ) => { if (!this.config.canvas) { throw new Error("handleBatch called with no config"); } let serializeBuild: CanvasPixel[] = []; const width = end[0] - start[0]; const height = end[1] - start[1]; for (let x = 0; x < this.config.canvas.size[0]; x++) { for (let y = 0; y < this.config.canvas.size[1]; y++) { const hex = pixels[this.config.canvas.size[0] * y + x]; for (let x = 0; x < width; x++) { for (let y = 0; y < height; y++) { const hex = pixels[width * y + x]; const palette = this.Pallete.getColorFromHex(hex); const canvasX = x + start[0]; const canvasY = y + start[1]; // we still store a copy of the pixels in this instance for non-rendering functions this.pixels[x + "_" + y] = { this.pixels[canvasX + "_" + canvasY] = { type: "full", color: palette?.id || -1, }; serializeBuild.push({ x, y, x: canvasX, y: canvasY, hex: hex === "transparent" ? "null" : hex, }); } Loading packages/client/src/lib/canvasRenderer.ts +38 −44 Original line number Diff line number Diff line Loading @@ -8,8 +8,6 @@ export type CanvasPixel = { hex: string; }; const bezier = (n: number) => n * n * (3 - 2 * n); const isWorker = () => { return ( // @ts-ignore Loading Loading @@ -41,8 +39,18 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { private blank?: RCanvas; private blank_ctx?: RContext; private pixels: CanvasPixel[] = []; private allPixels: CanvasPixel[] = []; /** * Pixels that need to be drawn next draw call * * Key = x,y (eg 0,0) */ private pixels: Map<string, string> = new Map(); /** * Every pixel * * Key = x,y (eg 0,0) */ private allPixels: Map<string, string> = new Map(); private isWorker = isWorker(); private _stopRender = false; Loading Loading @@ -87,37 +95,15 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { } usePixels(pixels: CanvasPixel[], replace = false) { if (replace) { this.pixels = pixels; this.allPixels = pixels; } else { for (const pixel of pixels) { this.usePixel(pixel); } } } usePixel(pixel: CanvasPixel) { { let existing = this.pixels.find( (p) => p.x === pixel.x && p.y === pixel.y ); if (existing) { this.pixels.splice(this.pixels.indexOf(existing), 1); } } { let existing = this.allPixels.find( (p) => p.x === pixel.x && p.y === pixel.y ); if (existing) { this.allPixels.splice(this.allPixels.indexOf(existing), 1); } } this.pixels.push(pixel); this.allPixels.push(pixel); let key = pixel.x + "," + pixel.y; this.pixels.set(key, pixel.hex); this.allPixels.set(key, pixel.hex); } startRender() { Loading Loading @@ -179,16 +165,19 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { draw() { const start = performance.now(); const pixels = [...this.pixels]; this.pixels = []; const pixels = new Map(this.pixels); this.pixels.clear(); if (pixels.length) { console.log("[CanvasRenderer#draw] drawing " + pixels.length + " pixels"); if (pixels.size) { console.log("[CanvasRenderer#draw] drawing " + pixels.size + " pixels"); } for (const pixel of pixels) { this.ctx.fillStyle = pixel.hex === "null" ? "#fff" : "#" + pixel.hex; this.ctx.fillRect(pixel.x, pixel.y, 1, 1); for (const [x_y, hex] of pixels) { const x = parseInt(x_y.split(",")[0]); const y = parseInt(x_y.split(",")[1]); this.ctx.fillStyle = hex === "null" ? "#fff" : "#" + hex; this.ctx.fillRect(x, y, 1, 1); } const diff = performance.now() - start; Loading Loading @@ -219,9 +208,12 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { this.ctx.fillStyle = "#fff"; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); for (const pixel of this.allPixels) { this.ctx.fillStyle = pixel.hex === "null" ? "#fff" : "#" + pixel.hex; this.ctx.fillRect(pixel.x, pixel.y, 1, 1); for (const [x_y, hex] of this.allPixels) { const x = parseInt(x_y.split(",")[0]); const y = parseInt(x_y.split(",")[1]); this.ctx.fillStyle = hex === "null" ? "#fff" : "#" + hex; this.ctx.fillRect(x, y, 1, 1); } } Loading @@ -234,11 +226,13 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { ctx.clearRect(0, 0, canvas.width, canvas.height); for (const pixel of this.allPixels) { if (pixel.hex !== "null") continue; for (const [x_y, hex] of this.allPixels) { if (hex !== "null") continue; const x = parseInt(x_y.split(",")[0]); const y = parseInt(x_y.split(",")[1]); ctx.fillStyle = "rgba(0,140,0,0.5)"; ctx.fillRect(pixel.x, pixel.y, 1, 1); ctx.fillRect(x, y, 1, 1); } } } Loading packages/client/src/lib/network.ts +23 −3 Original line number Diff line number Diff line Loading @@ -20,7 +20,11 @@ export interface INetworkEvents { user: (user: AuthSession) => void; standing: (standing: IAccountStanding) => void; config: (user: ClientConfig) => void; canvas: (pixels: string[]) => void; canvas: ( start: [x: number, y: number], end: [x: number, y: number], pixels: string[] ) => void; pixels: (data: { available: number }) => void; pixelLastPlaced: (time: number) => void; online: (count: number) => void; Loading Loading @@ -55,6 +59,12 @@ class Network extends EventEmitter<INetworkEvents> { [key in keyof INetworkEvents]?: SentEventValue<key>; } = {}; private canvasChunks: { start: [number, number]; end: [number, number]; pixels: string[]; }[] = []; constructor() { super(); Loading Loading @@ -123,8 +133,14 @@ class Network extends EventEmitter<INetworkEvents> { this.emit("config", config); }); this.socket.on("canvas", (pixels) => { this.acceptState("canvas", pixels); this.socket.on("canvas", (start, end, pixels) => { // this.acceptState("canvas", start, end, pixels); this.emit("canvas", start, end, pixels); this.canvasChunks.push({ start, end, pixels }); }); this.socket.on("clearCanvasChunks", () => { this.canvasChunks = []; }); this.socket.on("availablePixels", (count) => { Loading Loading @@ -191,6 +207,10 @@ class Network extends EventEmitter<INetworkEvents> { delete this.stateEvents[ev]; } getCanvasChunks() { return this.canvasChunks; } /** * Wait for event, either being already sent, or new one * Loading Loading
package-lock.json +20 −0 Original line number Diff line number Diff line Loading @@ -6602,6 +6602,12 @@ "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" }, "node_modules/@types/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", Loading Loading @@ -16492,6 +16498,7 @@ "rate-limit-redis": "^4.2.0", "redis": "^4.6.12", "socket.io": "^4.7.2", "uuid": "^10.0.0", "winston": "^3.11.0" }, "devDependencies": { Loading @@ -16499,6 +16506,7 @@ "@types/cors": "^2.8.17", "@types/express": "^4.17.17", "@types/express-session": "^1.17.7", "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "^7.1.0", "@typescript-eslint/parser": "^7.1.0", "dotenv": "^16.3.1", Loading Loading @@ -16795,6 +16803,18 @@ "engines": { "node": ">= 0.8" } }, "packages/server/node_modules/uuid": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], "bin": { "uuid": "dist/bin/uuid" } } } }
packages/client/src/index.tsx +3 −1 Original line number Diff line number Diff line Loading @@ -7,7 +7,9 @@ import Bugsnag from "@bugsnag/js"; import BugsnagPluginReact from "@bugsnag/plugin-react"; import BugsnagPerformance from "@bugsnag/browser-performance"; let ErrorBoundary: any = <></>; let ErrorBoundary: any = ({ children }: React.PropsWithChildren) => ( <>{children}</> ); if (import.meta.env.VITE_BUGSNAG_KEY) { Bugsnag.start({ Loading
packages/client/src/lib/canvas.ts +34 −10 Original line number Diff line number Diff line Loading @@ -102,10 +102,25 @@ export class Canvas extends EventEmitter<CanvasEvents> { if (Object.keys(this.pixels).length > 0) Network.clearPreviousState("canvas"); Network.waitForState("canvas").then(([pixels]) => { console.log("loadConfig just received new canvas data"); this.handleBatch(pixels); // Network.waitForState("canvas").then(([pixels]) => { // console.log("loadConfig just received new canvas data"); // this.handleBatch(pixels); // }); Network.on("canvas", (start, end, pixels) => { console.log("[Canvas] received canvas section"); this.handleBatch(start, end, pixels); }); const chunks = Network.getCanvasChunks(); console.log(`[Canvas] Received ${chunks.length} chunks to load`); let loaded = 0; for (const chunk of chunks) { console.log(`[Canvas] Loading canvas chunk ${loaded}...`); this.handleBatch(chunk.start, chunk.end, chunk.pixels); loaded++; } } hasConfig() { Loading Loading @@ -267,27 +282,36 @@ export class Canvas extends EventEmitter<CanvasEvents> { getRenderer().usePixels(serializeBuild); }; handleBatch = (pixels: string[]) => { handleBatch = ( start: [x: number, y: number], end: [x: number, y: number], pixels: string[] ) => { if (!this.config.canvas) { throw new Error("handleBatch called with no config"); } let serializeBuild: CanvasPixel[] = []; const width = end[0] - start[0]; const height = end[1] - start[1]; for (let x = 0; x < this.config.canvas.size[0]; x++) { for (let y = 0; y < this.config.canvas.size[1]; y++) { const hex = pixels[this.config.canvas.size[0] * y + x]; for (let x = 0; x < width; x++) { for (let y = 0; y < height; y++) { const hex = pixels[width * y + x]; const palette = this.Pallete.getColorFromHex(hex); const canvasX = x + start[0]; const canvasY = y + start[1]; // we still store a copy of the pixels in this instance for non-rendering functions this.pixels[x + "_" + y] = { this.pixels[canvasX + "_" + canvasY] = { type: "full", color: palette?.id || -1, }; serializeBuild.push({ x, y, x: canvasX, y: canvasY, hex: hex === "transparent" ? "null" : hex, }); } Loading
packages/client/src/lib/canvasRenderer.ts +38 −44 Original line number Diff line number Diff line Loading @@ -8,8 +8,6 @@ export type CanvasPixel = { hex: string; }; const bezier = (n: number) => n * n * (3 - 2 * n); const isWorker = () => { return ( // @ts-ignore Loading Loading @@ -41,8 +39,18 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { private blank?: RCanvas; private blank_ctx?: RContext; private pixels: CanvasPixel[] = []; private allPixels: CanvasPixel[] = []; /** * Pixels that need to be drawn next draw call * * Key = x,y (eg 0,0) */ private pixels: Map<string, string> = new Map(); /** * Every pixel * * Key = x,y (eg 0,0) */ private allPixels: Map<string, string> = new Map(); private isWorker = isWorker(); private _stopRender = false; Loading Loading @@ -87,37 +95,15 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { } usePixels(pixels: CanvasPixel[], replace = false) { if (replace) { this.pixels = pixels; this.allPixels = pixels; } else { for (const pixel of pixels) { this.usePixel(pixel); } } } usePixel(pixel: CanvasPixel) { { let existing = this.pixels.find( (p) => p.x === pixel.x && p.y === pixel.y ); if (existing) { this.pixels.splice(this.pixels.indexOf(existing), 1); } } { let existing = this.allPixels.find( (p) => p.x === pixel.x && p.y === pixel.y ); if (existing) { this.allPixels.splice(this.allPixels.indexOf(existing), 1); } } this.pixels.push(pixel); this.allPixels.push(pixel); let key = pixel.x + "," + pixel.y; this.pixels.set(key, pixel.hex); this.allPixels.set(key, pixel.hex); } startRender() { Loading Loading @@ -179,16 +165,19 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { draw() { const start = performance.now(); const pixels = [...this.pixels]; this.pixels = []; const pixels = new Map(this.pixels); this.pixels.clear(); if (pixels.length) { console.log("[CanvasRenderer#draw] drawing " + pixels.length + " pixels"); if (pixels.size) { console.log("[CanvasRenderer#draw] drawing " + pixels.size + " pixels"); } for (const pixel of pixels) { this.ctx.fillStyle = pixel.hex === "null" ? "#fff" : "#" + pixel.hex; this.ctx.fillRect(pixel.x, pixel.y, 1, 1); for (const [x_y, hex] of pixels) { const x = parseInt(x_y.split(",")[0]); const y = parseInt(x_y.split(",")[1]); this.ctx.fillStyle = hex === "null" ? "#fff" : "#" + hex; this.ctx.fillRect(x, y, 1, 1); } const diff = performance.now() - start; Loading Loading @@ -219,9 +208,12 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { this.ctx.fillStyle = "#fff"; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); for (const pixel of this.allPixels) { this.ctx.fillStyle = pixel.hex === "null" ? "#fff" : "#" + pixel.hex; this.ctx.fillRect(pixel.x, pixel.y, 1, 1); for (const [x_y, hex] of this.allPixels) { const x = parseInt(x_y.split(",")[0]); const y = parseInt(x_y.split(",")[1]); this.ctx.fillStyle = hex === "null" ? "#fff" : "#" + hex; this.ctx.fillRect(x, y, 1, 1); } } Loading @@ -234,11 +226,13 @@ export class CanvasRenderer extends EventEmitter<RendererEvents> { ctx.clearRect(0, 0, canvas.width, canvas.height); for (const pixel of this.allPixels) { if (pixel.hex !== "null") continue; for (const [x_y, hex] of this.allPixels) { if (hex !== "null") continue; const x = parseInt(x_y.split(",")[0]); const y = parseInt(x_y.split(",")[1]); ctx.fillStyle = "rgba(0,140,0,0.5)"; ctx.fillRect(pixel.x, pixel.y, 1, 1); ctx.fillRect(x, y, 1, 1); } } } Loading
packages/client/src/lib/network.ts +23 −3 Original line number Diff line number Diff line Loading @@ -20,7 +20,11 @@ export interface INetworkEvents { user: (user: AuthSession) => void; standing: (standing: IAccountStanding) => void; config: (user: ClientConfig) => void; canvas: (pixels: string[]) => void; canvas: ( start: [x: number, y: number], end: [x: number, y: number], pixels: string[] ) => void; pixels: (data: { available: number }) => void; pixelLastPlaced: (time: number) => void; online: (count: number) => void; Loading Loading @@ -55,6 +59,12 @@ class Network extends EventEmitter<INetworkEvents> { [key in keyof INetworkEvents]?: SentEventValue<key>; } = {}; private canvasChunks: { start: [number, number]; end: [number, number]; pixels: string[]; }[] = []; constructor() { super(); Loading Loading @@ -123,8 +133,14 @@ class Network extends EventEmitter<INetworkEvents> { this.emit("config", config); }); this.socket.on("canvas", (pixels) => { this.acceptState("canvas", pixels); this.socket.on("canvas", (start, end, pixels) => { // this.acceptState("canvas", start, end, pixels); this.emit("canvas", start, end, pixels); this.canvasChunks.push({ start, end, pixels }); }); this.socket.on("clearCanvasChunks", () => { this.canvasChunks = []; }); this.socket.on("availablePixels", (count) => { Loading Loading @@ -191,6 +207,10 @@ class Network extends EventEmitter<INetworkEvents> { delete this.stateEvents[ev]; } getCanvasChunks() { return this.canvasChunks; } /** * Wait for event, either being already sent, or new one * Loading