From 5a444461381b3e3eeb1fa079e3dcb6d43b468bf5 Mon Sep 17 00:00:00 2001 From: Marius DAVID Date: Sat, 13 Jul 2024 00:03:06 +0000 Subject: [PATCH] Improve handling of the user pixel stack --- .../src/components/Toolbar/CanvasMeta.tsx | 3 +- packages/client/src/lib/canvas.ts | 10 ++-- packages/server/prisma/dbml/schema.dbml | 2 +- .../migration.sql | 1 + packages/server/prisma/schema.prisma | 2 +- packages/server/src/lib/Canvas.ts | 5 -- packages/server/src/lib/SocketServer.ts | 11 ++--- packages/server/src/models/User.ts | 47 +++++++++++++++++-- 8 files changed, 58 insertions(+), 23 deletions(-) create mode 100644 packages/server/prisma/migrations/20240712202519_rename_last_stack_change/migration.sql diff --git a/packages/client/src/components/Toolbar/CanvasMeta.tsx b/packages/client/src/components/Toolbar/CanvasMeta.tsx index 130348b..6d37201 100644 --- a/packages/client/src/components/Toolbar/CanvasMeta.tsx +++ b/packages/client/src/components/Toolbar/CanvasMeta.tsx @@ -18,6 +18,7 @@ const getTimeLeft = (pixels: { available: number }, config: ClientConfig) => { const cooldown = CanvasLib.getPixelCooldown(pixels.available + 1, config); const pixelExpiresAt = Canvas.instance?.lastPlace && Canvas.instance.lastPlace + cooldown * 1000; + const pixelCooldown = pixelExpiresAt && (Date.now() - pixelExpiresAt) / 1000; if (!pixelCooldown) return undefined; @@ -43,7 +44,7 @@ const PlaceCountdown = () => { return ( <> {timeLeft - ? pixels.available + 1 < config.canvas.pixel.maxStack && timeLeft + "s" + ? pixels.available < config.canvas.pixel.maxStack && timeLeft + "s" : ""} ); diff --git a/packages/client/src/lib/canvas.ts b/packages/client/src/lib/canvas.ts index dd8f3ba..4e40448 100644 --- a/packages/client/src/lib/canvas.ts +++ b/packages/client/src/lib/canvas.ts @@ -53,11 +53,9 @@ export class Canvas extends EventEmitter { this.PanZoom.addListener("click", this.handleMouseDown.bind(this)); this.PanZoom.addListener("longPress", this.handleLongPress); - Network.waitForState("pixelLastPlaced").then( - ([time]) => (this.lastPlace = time) - ); Network.on("pixel", this.handlePixel); Network.on("square", this.handleSquare); + Network.on("pixelLastPlaced", this.handlePixelLastPlaced); } destroy() { @@ -70,6 +68,7 @@ export class Canvas extends EventEmitter { Network.off("pixel", this.handlePixel); Network.off("square", this.handleSquare); + Network.off("pixelLastPlaced", this.handlePixelLastPlaced); } /** @@ -309,6 +308,10 @@ export class Canvas extends EventEmitter { getRenderer().usePixel({ x, y, hex: palette?.hex || "null" }); }; + handlePixelLastPlaced = (time: number) => { + this.lastPlace = time; + }; + Pallete = { getColor: (colorId: number) => { return this.config.pallete.colors.find((c) => c.id === colorId); @@ -358,7 +361,6 @@ export class Canvas extends EventEmitter { ) .then((ack) => { if (ack.success) { - this.lastPlace = Date.now(); this.handlePixel(ack.data); } else { console.warn( diff --git a/packages/server/prisma/dbml/schema.dbml b/packages/server/prisma/dbml/schema.dbml index e72eb3d..f080050 100644 --- a/packages/server/prisma/dbml/schema.dbml +++ b/packages/server/prisma/dbml/schema.dbml @@ -12,7 +12,7 @@ Table User { display_name String picture_url String profile_url String - lastPixelTime DateTime [default: `now()`, not null] + lastTimeGainStarted DateTime [default: `now()`, not null] pixelStack Int [not null, default: 0] undoExpires DateTime isAdmin Boolean [not null, default: false] diff --git a/packages/server/prisma/migrations/20240712202519_rename_last_stack_change/migration.sql b/packages/server/prisma/migrations/20240712202519_rename_last_stack_change/migration.sql new file mode 100644 index 0000000..481366a --- /dev/null +++ b/packages/server/prisma/migrations/20240712202519_rename_last_stack_change/migration.sql @@ -0,0 +1 @@ +ALTER TABLE "User" RENAME COLUMN "lastPixelTime" TO "lastTimeGainStarted"; \ No newline at end of file diff --git a/packages/server/prisma/schema.prisma b/packages/server/prisma/schema.prisma index 9886e2a..75a99e8 100644 --- a/packages/server/prisma/schema.prisma +++ b/packages/server/prisma/schema.prisma @@ -25,7 +25,7 @@ model User { picture_url String? profile_url String? - lastPixelTime DateTime @default(now()) // the time the last pixel was placed at + lastTimeGainStarted DateTime @default(now()) // the time base used to determine the amount of stack the user should gain pixelStack Int @default(0) // amount of pixels stacked for this user undoExpires DateTime? // when the undo for the most recent pixel expires at diff --git a/packages/server/src/lib/Canvas.ts b/packages/server/src/lib/Canvas.ts index 34a4d00..30c9e5e 100644 --- a/packages/server/src/lib/Canvas.ts +++ b/packages/server/src/lib/Canvas.ts @@ -430,11 +430,6 @@ class Canvas { }, }); - await prisma.user.update({ - where: { sub: user.sub }, - data: { lastPixelTime: new Date() }, - }); - // maybe only update specific element? // i don't think it needs to be awaited await this.updateCanvasRedisAtPos(x, y); diff --git a/packages/server/src/lib/SocketServer.ts b/packages/server/src/lib/SocketServer.ts index 49a9a55..defa799 100644 --- a/packages/server/src/lib/SocketServer.ts +++ b/packages/server/src/lib/SocketServer.ts @@ -63,7 +63,7 @@ prisma.paletteColor Logger.error("Failed to get pallete colors", e); }); -const getClientConfig = (): ClientConfig => { +export const getClientConfig = (): ClientConfig => { return { version: commitHash, pallete: { @@ -122,12 +122,9 @@ export class SocketServer { continue; } - // time in seconds since last pixel placement - // TODO: this causes a mismatch between placement times - // - going from 0 stack to 6 stack has a steady increase between each - // - going from 3 stack to 6 stack takes longer + // time in seconds since last stack gain (including a potential bonus depending on previously remaining time) const timeSinceLastPlace = - (Date.now() - user.lastPixelTime.getTime()) / 1000; + (Date.now() - user.lastTimeGainStarted.getTime()) / 1000; const cooldown = CanvasLib.getPixelCooldown( user.pixelStack + 1, getClientConfig() @@ -180,7 +177,7 @@ export class SocketServer { if (user) { socket.emit("availablePixels", user.pixelStack); - socket.emit("pixelLastPlaced", user.lastPixelTime.getTime()); + socket.emit("pixelLastPlaced", user.lastTimeGainStarted.getTime()); const ban = user.getBan(); socket.emit( diff --git a/packages/server/src/models/User.ts b/packages/server/src/models/User.ts index 17be607..1a2980c 100644 --- a/packages/server/src/models/User.ts +++ b/packages/server/src/models/User.ts @@ -10,7 +10,8 @@ import { import { Ban, User as UserDB } from "@prisma/client"; import { Instance } from "./Instance"; import { ConditionalPromise } from "../lib/utils"; - +import { CanvasLib } from "@sc07-canvas/lib/src/canvas"; +import { getClientConfig } from "../lib/SocketServer"; const Logger = getLogger(); /** @@ -37,7 +38,7 @@ export class User { static instances: Map = new Map(); sub: string; - lastPixelTime: Date; + lastTimeGainStarted: Date; pixelStack: number; authSession?: AuthSession; undoExpires?: Date; @@ -54,7 +55,7 @@ export class User { Logger.debug("User class instansiated for " + data.sub); this.sub = data.sub; - this.lastPixelTime = data.lastPixelTime; + this.lastTimeGainStarted = data.lastTimeGainStarted; this.pixelStack = data.pixelStack; this.undoExpires = data.undoExpires || undefined; @@ -80,7 +81,7 @@ export class User { if (!userData) throw new UserNotFound(); - this.lastPixelTime = userData.lastPixelTime; + this.lastTimeGainStarted = userData.lastTimeGainStarted; this.pixelStack = userData.pixelStack; this.undoExpires = userData.undoExpires || undefined; this.isAdmin = userData.isAdmin; @@ -120,15 +121,53 @@ export class User { } async modifyStack(modifyBy: number): Promise { + let new_date = new Date(); + if (modifyBy > 0) { + let cooldown_to_add = 0.0; + for (let i = 0; i < modifyBy; i++) { + cooldown_to_add += CanvasLib.getPixelCooldown( + this.pixelStack + i + 1, + getClientConfig() + ); + } + + new_date = new Date( + this.lastTimeGainStarted.valueOf() + cooldown_to_add * 1000 + ); + } else if (modifyBy < 0) { + const cooldown_before_change_s = CanvasLib.getPixelCooldown( + this.pixelStack + 1, + getClientConfig() + ); + const cooldown_after_change_s = CanvasLib.getPixelCooldown( + this.pixelStack + 1 + modifyBy, + getClientConfig() + ); + const would_gain_next_at_timestamp_ms = + this.lastTimeGainStarted.valueOf() + cooldown_before_change_s * 1000; + const time_before_next = + would_gain_next_at_timestamp_ms - Date.now().valueOf(); + // To avoid issue if a negative value is present for some reason + if (time_before_next > 0) { + if (time_before_next < cooldown_after_change_s * 1000) { + new_date = new Date( + Date.now() - cooldown_after_change_s * 1000 + time_before_next + ); + } + } + } + const updatedUser = await prisma.user.update({ where: { sub: this.sub }, data: { pixelStack: { increment: modifyBy }, + lastTimeGainStarted: new_date, }, }); for (const socket of this.sockets) { socket.emit("availablePixels", updatedUser.pixelStack); + socket.emit("pixelLastPlaced", updatedUser.lastTimeGainStarted.getTime()); } // we just modified the user data, so we should force an update