Commit d61530f9 authored by Ategon Dev's avatar Ategon Dev Committed by Grant
Browse files

Negative pixel patch

parent 95b61fa0
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -403,6 +403,9 @@ export class Canvas extends EventEmitter<CanvasEvents> {
            case "no_user":
              toast.error("You are not logged in.");
              break;
            case "pixel_already_pending":
              toast.error("You are already placing a pixel");
              break;
            case "palette_color_invalid":
              toast.error("This isn't a color that you can use...?");
              break;
+6 −0
Original line number Diff line number Diff line
@@ -20,6 +20,12 @@ export const CanvasLib = new (class {

    // oh god last minute change to match activity cooldown
    // 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;
  }
})();
+1 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ export interface ClientToServerEvents {
        | "palette_color_invalid"
        | "you_already_placed_that"
        | "banned"
        | "pixel_already_pending"
      >
    ) => void
  ) => void;
+43 −0
Original line number Diff line number Diff line
@@ -85,6 +85,13 @@ type Socket = RawSocket<ClientToServerEvents, ServerToClientEvents>;
export class SocketServer {
  static instance: SocketServer;
  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) {
    SocketServer.instance = this;
@@ -96,6 +103,29 @@ export class SocketServer {
    this.io.engine.use(session);
    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
    // - needs to be exponential (takes longer to aquire more pixels stacked)
    // - convert to config options instead of hard-coded
@@ -254,19 +284,29 @@ export class SocketServer {
      // force a user data update
      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) {
        // only moderators can do this
        ack({ success: false, error: "invalid_pixel" });
        this.userPlaceLock.delete(user.sub);
        return;
      }

      if (!bypassCooldown && user.pixelStack < 1) {
        ack({ success: false, error: "pixel_cooldown" });
        this.userPlaceLock.delete(user.sub);
        return;
      }

      if ((user.getBan()?.expires || 0) > new Date()) {
        ack({ success: false, error: "banned" });
        this.userPlaceLock.delete(user.sub);
        return;
      }

@@ -280,6 +320,7 @@ export class SocketServer {
          success: false,
          error: "palette_color_invalid",
        });
        this.userPlaceLock.delete(user.sub);
        return;
      }

@@ -291,6 +332,7 @@ export class SocketServer {
        pixelAtTheSameLocation.color === paletteColor.hex
      ) {
        ack({ success: false, error: "you_already_placed_that" });
        this.userPlaceLock.delete(user.sub);
        return;
      }

@@ -319,6 +361,7 @@ export class SocketServer {
        data: newPixel,
      });
      socket.broadcast.emit("pixel", newPixel);
      this.userPlaceLock.delete(user.sub);
    });

    socket.on("undo", async (ack) => {