Improve handling of the user pixel stack

This commit is contained in:
Marius DAVID 2024-07-13 00:03:06 +00:00 committed by Grant
parent c2fba721f4
commit 5a44446138
8 changed files with 58 additions and 23 deletions

View File

@ -18,6 +18,7 @@ const getTimeLeft = (pixels: { available: number }, config: ClientConfig) => {
const cooldown = CanvasLib.getPixelCooldown(pixels.available + 1, config); const cooldown = CanvasLib.getPixelCooldown(pixels.available + 1, config);
const pixelExpiresAt = const pixelExpiresAt =
Canvas.instance?.lastPlace && Canvas.instance.lastPlace + cooldown * 1000; Canvas.instance?.lastPlace && Canvas.instance.lastPlace + cooldown * 1000;
const pixelCooldown = pixelExpiresAt && (Date.now() - pixelExpiresAt) / 1000; const pixelCooldown = pixelExpiresAt && (Date.now() - pixelExpiresAt) / 1000;
if (!pixelCooldown) return undefined; if (!pixelCooldown) return undefined;
@ -43,7 +44,7 @@ const PlaceCountdown = () => {
return ( return (
<> <>
{timeLeft {timeLeft
? pixels.available + 1 < config.canvas.pixel.maxStack && timeLeft + "s" ? pixels.available < config.canvas.pixel.maxStack && timeLeft + "s"
: ""} : ""}
</> </>
); );

View File

@ -53,11 +53,9 @@ export class Canvas extends EventEmitter<CanvasEvents> {
this.PanZoom.addListener("click", this.handleMouseDown.bind(this)); this.PanZoom.addListener("click", this.handleMouseDown.bind(this));
this.PanZoom.addListener("longPress", this.handleLongPress); this.PanZoom.addListener("longPress", this.handleLongPress);
Network.waitForState("pixelLastPlaced").then(
([time]) => (this.lastPlace = time)
);
Network.on("pixel", this.handlePixel); Network.on("pixel", this.handlePixel);
Network.on("square", this.handleSquare); Network.on("square", this.handleSquare);
Network.on("pixelLastPlaced", this.handlePixelLastPlaced);
} }
destroy() { destroy() {
@ -70,6 +68,7 @@ export class Canvas extends EventEmitter<CanvasEvents> {
Network.off("pixel", this.handlePixel); Network.off("pixel", this.handlePixel);
Network.off("square", this.handleSquare); Network.off("square", this.handleSquare);
Network.off("pixelLastPlaced", this.handlePixelLastPlaced);
} }
/** /**
@ -309,6 +308,10 @@ export class Canvas extends EventEmitter<CanvasEvents> {
getRenderer().usePixel({ x, y, hex: palette?.hex || "null" }); getRenderer().usePixel({ x, y, hex: palette?.hex || "null" });
}; };
handlePixelLastPlaced = (time: number) => {
this.lastPlace = time;
};
Pallete = { Pallete = {
getColor: (colorId: number) => { getColor: (colorId: number) => {
return this.config.pallete.colors.find((c) => c.id === colorId); return this.config.pallete.colors.find((c) => c.id === colorId);
@ -358,7 +361,6 @@ export class Canvas extends EventEmitter<CanvasEvents> {
) )
.then((ack) => { .then((ack) => {
if (ack.success) { if (ack.success) {
this.lastPlace = Date.now();
this.handlePixel(ack.data); this.handlePixel(ack.data);
} else { } else {
console.warn( console.warn(

View File

@ -12,7 +12,7 @@ Table User {
display_name String display_name String
picture_url String picture_url String
profile_url String profile_url String
lastPixelTime DateTime [default: `now()`, not null] lastTimeGainStarted DateTime [default: `now()`, not null]
pixelStack Int [not null, default: 0] pixelStack Int [not null, default: 0]
undoExpires DateTime undoExpires DateTime
isAdmin Boolean [not null, default: false] isAdmin Boolean [not null, default: false]

View File

@ -0,0 +1 @@
ALTER TABLE "User" RENAME COLUMN "lastPixelTime" TO "lastTimeGainStarted";

View File

@ -25,7 +25,7 @@ model User {
picture_url String? picture_url String?
profile_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 pixelStack Int @default(0) // amount of pixels stacked for this user
undoExpires DateTime? // when the undo for the most recent pixel expires at undoExpires DateTime? // when the undo for the most recent pixel expires at

View File

@ -430,11 +430,6 @@ class Canvas {
}, },
}); });
await prisma.user.update({
where: { sub: user.sub },
data: { lastPixelTime: new Date() },
});
// maybe only update specific element? // maybe only update specific element?
// i don't think it needs to be awaited // i don't think it needs to be awaited
await this.updateCanvasRedisAtPos(x, y); await this.updateCanvasRedisAtPos(x, y);

View File

@ -63,7 +63,7 @@ prisma.paletteColor
Logger.error("Failed to get pallete colors", e); Logger.error("Failed to get pallete colors", e);
}); });
const getClientConfig = (): ClientConfig => { export const getClientConfig = (): ClientConfig => {
return { return {
version: commitHash, version: commitHash,
pallete: { pallete: {
@ -122,12 +122,9 @@ export class SocketServer {
continue; continue;
} }
// time in seconds since last pixel placement // time in seconds since last stack gain (including a potential bonus depending on previously remaining time)
// 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
const timeSinceLastPlace = const timeSinceLastPlace =
(Date.now() - user.lastPixelTime.getTime()) / 1000; (Date.now() - user.lastTimeGainStarted.getTime()) / 1000;
const cooldown = CanvasLib.getPixelCooldown( const cooldown = CanvasLib.getPixelCooldown(
user.pixelStack + 1, user.pixelStack + 1,
getClientConfig() getClientConfig()
@ -180,7 +177,7 @@ export class SocketServer {
if (user) { if (user) {
socket.emit("availablePixels", user.pixelStack); socket.emit("availablePixels", user.pixelStack);
socket.emit("pixelLastPlaced", user.lastPixelTime.getTime()); socket.emit("pixelLastPlaced", user.lastTimeGainStarted.getTime());
const ban = user.getBan(); const ban = user.getBan();
socket.emit( socket.emit(

View File

@ -10,7 +10,8 @@ import {
import { Ban, User as UserDB } from "@prisma/client"; import { Ban, User as UserDB } from "@prisma/client";
import { Instance } from "./Instance"; import { Instance } from "./Instance";
import { ConditionalPromise } from "../lib/utils"; import { ConditionalPromise } from "../lib/utils";
import { CanvasLib } from "@sc07-canvas/lib/src/canvas";
import { getClientConfig } from "../lib/SocketServer";
const Logger = getLogger(); const Logger = getLogger();
/** /**
@ -37,7 +38,7 @@ export class User {
static instances: Map<string, User> = new Map(); static instances: Map<string, User> = new Map();
sub: string; sub: string;
lastPixelTime: Date; lastTimeGainStarted: Date;
pixelStack: number; pixelStack: number;
authSession?: AuthSession; authSession?: AuthSession;
undoExpires?: Date; undoExpires?: Date;
@ -54,7 +55,7 @@ export class User {
Logger.debug("User class instansiated for " + data.sub); Logger.debug("User class instansiated for " + data.sub);
this.sub = data.sub; this.sub = data.sub;
this.lastPixelTime = data.lastPixelTime; this.lastTimeGainStarted = data.lastTimeGainStarted;
this.pixelStack = data.pixelStack; this.pixelStack = data.pixelStack;
this.undoExpires = data.undoExpires || undefined; this.undoExpires = data.undoExpires || undefined;
@ -80,7 +81,7 @@ export class User {
if (!userData) throw new UserNotFound(); if (!userData) throw new UserNotFound();
this.lastPixelTime = userData.lastPixelTime; this.lastTimeGainStarted = userData.lastTimeGainStarted;
this.pixelStack = userData.pixelStack; this.pixelStack = userData.pixelStack;
this.undoExpires = userData.undoExpires || undefined; this.undoExpires = userData.undoExpires || undefined;
this.isAdmin = userData.isAdmin; this.isAdmin = userData.isAdmin;
@ -120,15 +121,53 @@ export class User {
} }
async modifyStack(modifyBy: number): Promise<any> { async modifyStack(modifyBy: number): Promise<any> {
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({ const updatedUser = await prisma.user.update({
where: { sub: this.sub }, where: { sub: this.sub },
data: { data: {
pixelStack: { increment: modifyBy }, pixelStack: { increment: modifyBy },
lastTimeGainStarted: new_date,
}, },
}); });
for (const socket of this.sockets) { for (const socket of this.sockets) {
socket.emit("availablePixels", updatedUser.pixelStack); socket.emit("availablePixels", updatedUser.pixelStack);
socket.emit("pixelLastPlaced", updatedUser.lastTimeGainStarted.getTime());
} }
// we just modified the user data, so we should force an update // we just modified the user data, so we should force an update