Negative pixel patch

This commit is contained in:
Ategon Dev 2024-07-15 22:51:23 +00:00 committed by Grant
parent 95b61fa050
commit d61530f91d
4 changed files with 53 additions and 0 deletions

View File

@ -403,6 +403,9 @@ export class Canvas extends EventEmitter<CanvasEvents> {
case "no_user": case "no_user":
toast.error("You are not logged in."); toast.error("You are not logged in.");
break; break;
case "pixel_already_pending":
toast.error("You are already placing a pixel");
break;
case "palette_color_invalid": case "palette_color_invalid":
toast.error("This isn't a color that you can use...?"); toast.error("This isn't a color that you can use...?");
break; break;

View File

@ -20,6 +20,12 @@ export const CanvasLib = new (class {
// oh god last minute change to match activity cooldown // oh god last minute change to match activity cooldown
// 100 = user count // 100 = user count
// band aid over negative nums
if (pixelNumber < 1) {
pixelNumber = 1
}
return (2.5 * Math.sqrt(100 + 11.96) + 6.5) * 1 * pixelNumber; return (2.5 * Math.sqrt(100 + 11.96) + 6.5) * 1 * pixelNumber;
} }
})(); })();

View File

@ -55,6 +55,7 @@ export interface ClientToServerEvents {
| "palette_color_invalid" | "palette_color_invalid"
| "you_already_placed_that" | "you_already_placed_that"
| "banned" | "banned"
| "pixel_already_pending"
> >
) => void ) => void
) => void; ) => void;

View File

@ -85,6 +85,13 @@ type Socket = RawSocket<ClientToServerEvents, ServerToClientEvents>;
export class SocketServer { export class SocketServer {
static instance: SocketServer; static instance: SocketServer;
io: Server<ClientToServerEvents, ServerToClientEvents>; io: Server<ClientToServerEvents, ServerToClientEvents>;
/**
* Prevent users from time attacking pixel placements to place more pixels than stacked
*
* @key user sub (grant@grants.cafe)
* @value timestamp
*/
userPlaceLock = new Map<string, number>();
constructor(server: http.Server) { constructor(server: http.Server) {
SocketServer.instance = this; SocketServer.instance = this;
@ -96,6 +103,29 @@ export class SocketServer {
this.io.engine.use(session); this.io.engine.use(session);
this.io.on("connection", this.handleConnection.bind(this)); this.io.on("connection", this.handleConnection.bind(this));
// clear pixel locks if they have existed for more than a minute
setInterval(() => {
const oneMinuteAgo = new Date();
oneMinuteAgo.setMinutes(oneMinuteAgo.getMinutes() - 1);
const expired = [...this.userPlaceLock.entries()].filter(
([user, time]) => time < oneMinuteAgo.getTime()
);
if (expired.length > 0) {
Logger.warn(
"A pixel lock has existed for too long for " +
expired.length +
" users : " +
expired.map((a) => a[0]).join(",")
);
}
for (const expire of expired) {
this.userPlaceLock.delete(expire[0]);
}
}, 1000 * 30);
// pixel stacking // pixel stacking
// - needs to be exponential (takes longer to aquire more pixels stacked) // - needs to be exponential (takes longer to aquire more pixels stacked)
// - convert to config options instead of hard-coded // - convert to config options instead of hard-coded
@ -254,19 +284,29 @@ export class SocketServer {
// force a user data update // force a user data update
await user.update(true); await user.update(true);
if (this.userPlaceLock.has(user.sub)) {
ack({ success: false, error: "pixel_already_pending" });
return;
}
this.userPlaceLock.set(user.sub, Date.now());
if (bypassCooldown && !user.isModerator) { if (bypassCooldown && !user.isModerator) {
// only moderators can do this // only moderators can do this
ack({ success: false, error: "invalid_pixel" }); ack({ success: false, error: "invalid_pixel" });
this.userPlaceLock.delete(user.sub);
return; return;
} }
if (!bypassCooldown && user.pixelStack < 1) { if (!bypassCooldown && user.pixelStack < 1) {
ack({ success: false, error: "pixel_cooldown" }); ack({ success: false, error: "pixel_cooldown" });
this.userPlaceLock.delete(user.sub);
return; return;
} }
if ((user.getBan()?.expires || 0) > new Date()) { if ((user.getBan()?.expires || 0) > new Date()) {
ack({ success: false, error: "banned" }); ack({ success: false, error: "banned" });
this.userPlaceLock.delete(user.sub);
return; return;
} }
@ -280,6 +320,7 @@ export class SocketServer {
success: false, success: false,
error: "palette_color_invalid", error: "palette_color_invalid",
}); });
this.userPlaceLock.delete(user.sub);
return; return;
} }
@ -291,6 +332,7 @@ export class SocketServer {
pixelAtTheSameLocation.color === paletteColor.hex pixelAtTheSameLocation.color === paletteColor.hex
) { ) {
ack({ success: false, error: "you_already_placed_that" }); ack({ success: false, error: "you_already_placed_that" });
this.userPlaceLock.delete(user.sub);
return; return;
} }
@ -319,6 +361,7 @@ export class SocketServer {
data: newPixel, data: newPixel,
}); });
socket.broadcast.emit("pixel", newPixel); socket.broadcast.emit("pixel", newPixel);
this.userPlaceLock.delete(user.sub);
}); });
socket.on("undo", async (ack) => { socket.on("undo", async (ack) => {