mass-request pixels pt.1

This commit is contained in:
HF 2020-12-04 21:11:11 +01:00
parent 2f04d24452
commit e34609c046
5 changed files with 125 additions and 55 deletions

View File

@ -135,7 +135,6 @@ document.addEventListener('DOMContentLoaded', () => {
// on captcha received
// TODO: this really isn't beautiful
window.onCaptcha = async function onCaptcha(token: string) {
const body = JSON.stringify({
token,

View File

@ -28,15 +28,18 @@ import { THREE_CANVAS_HEIGHT, THREE_TILE_SIZE, TILE_SIZE } from './constants';
* @param canvasId
* @param i Chunk coordinates
* @param j
* @param offset Offset of pixel withing chunk
* @param pixels Array of indiviual pixels within the chunk, with:
* [[offset, color], [offset2, color2],...]
* Offset is the offset of the pixel within the chunk
* @return Promise<Object>
*/
export async function drawByOffset(
export async function drawByOffsets(
user: User,
canvasId: number,
color: ColorIndex,
i: number,
j: number,
offset: number,
pixels: Array,
): Promise<Object> {
let wait = 0;
let coolDown = 0;
@ -51,10 +54,13 @@ export async function drawByOffset(
retCode: 1,
};
}
const { size: canvasSize, v: is3d } = canvas;
const { size: canvasSize, v: is3d } = canvas;
try {
const tileSize = (is3d) ? THREE_TILE_SIZE : TILE_SIZE;
/*
* canvas/chunk validation
*/
if (i >= canvasSize / tileSize) {
// x out of bounds
throw new Error(2);
@ -63,19 +69,6 @@ export async function drawByOffset(
// y out of bounds
throw new Error(3);
}
const maxSize = (is3d) ? tileSize * tileSize * THREE_CANVAS_HEIGHT
: tileSize * tileSize;
if (offset >= maxSize) {
// z out of bounds or weird stuff
throw new Error(4);
}
if (color >= canvas.colors.length
|| (color < canvas.cli && !(canvas.v && color === 0))
) {
// color out of bounds
throw new Error(5);
}
if (canvas.req !== -1) {
if (user.id === null) {
// not logged in
@ -89,8 +82,29 @@ export async function drawByOffset(
}
const isAdmin = (user.userlvl === 1);
/*
* TODO benchmark if requesting by pixel or chunk
* is better
*/
const chunk = await RedisCanvas.getChunk(canvasId, i, j);
const setColor = await RedisCanvas.getPixelByOffset(canvasId, i, j, offset);
/*
* pixel validation
*/
const maxSize = (is3d) ? tileSize * tileSize * THREE_CANVAS_HEIGHT
: tileSize * tileSize;
if (offset >= maxSize) {
// z out of bounds or weird stuff
throw new Error(4);
}
if (color >= canvas.colors.length
|| (color < canvas.cli && !(canvas.v && color === 0))
) {
// color out of bounds
throw new Error(5);
}
if (setColor & 0x80
/* 3D Canvas Minecraft Avatars */
// && x >= 96 && x <= 128 && z >= 35 && z <= 100
@ -331,7 +345,6 @@ export async function drawByCoords(
* @param x
* @param y
* @param z (optional for 3d canvas)
* @returns {Promise.<boolean>}
*/
export function drawSafeByCoords(
user: User,
@ -340,7 +353,7 @@ export function drawSafeByCoords(
x: number,
y: number,
z: number = null,
): Promise<Cell> {
): Promise<Object> {
// can just check for one unique occurence,
// we use ip, because id for logged out users is
// always null
@ -364,20 +377,19 @@ export function drawSafeByCoords(
*
* @param user
* @param canvasId
* @param color
* @param i Chunk coordinates
* @param j
* @param offset Offset of pixel withing chunk
* @returns {Promise.<boolean>}
* @param pixels Array of indiviual pixels within the chunk, with:
* [[offset, color], [offset2, color2],...]
* Offset is the offset of the pixel within the chunk
* @return Promise<Object>
*/
export function drawSafeByOffset(
export function drawSafeByOffsets(
user: User,
canvasId: number,
color: ColorIndex,
i: number,
j: number,
offset: number,
): Promise<Cell> {
pixels: Array,
): Promise<Object> {
// can just check for one unique occurence,
// we use ip, because id for logged out users is
// always null
@ -387,7 +399,7 @@ export function drawSafeByOffset(
using(
redlock.disposer(`locks:${userId}`, 5000, logger.error),
async () => {
const ret = await drawByOffset(user, canvasId, color, i, j, offset);
const ret = await drawByOffsets(user, canvasId, i, j, pixels);
resolve(ret);
},
); // <-- unlock is automatically handled by bluebird

View File

@ -1,5 +1,3 @@
/**
*
* @flow
*/
@ -125,11 +123,16 @@ class ProtocolClient extends EventEmitter {
if (~pos) chunks.splice(pos, 1);
}
/*
* Send pixel request
* @param i, j chunk coordinates
* @param pixel Array of [[offset, color],...] pixels within chunk
*/
requestPlacePixel(
i, j, offset,
color,
i: number, j: number,
pixels: Array,
) {
const buffer = PixelUpdate.dehydrate(i, j, offset, color);
const buffer = PixelUpdate.dehydrate(i, j, pixels);
this.sendWhenReady(buffer);
}

View File

@ -1,4 +1,9 @@
/* @flow */
/*
* Packet for sending and receiving pixels per chunk
* Multiple pixels can be sent at once
*
* @flow
*/
import type { ColorIndex } from '../../core/Palette';
@ -6,7 +11,7 @@ import type { ColorIndex } from '../../core/Palette';
type PixelUpdatePacket = {
x: number,
y: number,
color: ColorIndex,
pixels: Array,
};
const OP_CODE = 0xC1;
@ -14,25 +19,50 @@ const OP_CODE = 0xC1;
export default {
OP_CODE,
hydrate(data: DataView): PixelUpdatePacket {
/*
* chunk coordinates
*/
const i = data.getUint8(1);
const j = data.getUint8(2);
const offset = (data.getUint8(3) << 16) | data.getUint16(4);
const color = data.getUint8(6);
/*
* offset and color of every pixel
* 3 bytes offset
* 1 byte color
*/
const pixels = [];
let off = data.byteLength;
while (off >= 3) {
const color = data.getUint8(off -= 1);
const offsetL = data.getUint16(off -= 2);
const offsetH = data.getUint8(off -= 1) << 16;
const pixels.push([offsetH | offsetL, color]);
}
return {
i, j, offset, color,
i, j, pixels,
};
},
dehydrate(i, j, offset, color): Buffer {
const buffer = new ArrayBuffer(1 + 1 + 1 + 1 + 2 + 1);
dehydrate(i, j, pixels): Buffer {
const buffer = new ArrayBuffer(1 + 1 + 1 + pixels.length * 4);
const view = new DataView(buffer);
view.setUint8(0, OP_CODE);
/*
* chunk coordinates
*/
view.setUint8(1, i);
view.setUint8(2, j);
view.setUint8(3, offset >>> 16);
view.setUint16(4, offset & 0x00FFFF);
view.setUint8(6, color);
/*
* offset and color of every pixel
* 3 bytes offset
* 1 byte color
*/
let cnt = 2;
for (let i = 0; i < pixels.length; i += 1) {
const [offset, color] = pixels[i];
view.setUint8(cnt += 1, offset >>> 16);
view.setUint16(cnt += 1, offset & 0x00FFFF);
view.setUint8(cnt += 2, color);
}
return buffer;
},

View File

@ -6,7 +6,7 @@ import type { ColorIndex } from '../../core/Palette';
type PixelUpdatePacket = {
x: number,
y: number,
color: ColorIndex,
pixels: Array,
};
const OP_CODE = 0xC1;
@ -14,23 +14,49 @@ const OP_CODE = 0xC1;
export default {
OP_CODE,
hydrate(data: Buffer): PixelUpdatePacket {
/*
* chunk coordinates
*/
const i = data.readUInt8(1);
const j = data.readUInt8(2);
const offset = (data.readUInt8(3) << 16) | data.readUInt16BE(4);
const color = data.readUInt8(6);
/*
* offset and color of every pixel
* 3 bytes offset
* 1 byte color
*/
const pixels = [];
let off = data.length;
while (off >= 3) {
const color = data.readUInt8(off -= 1);
const offsetL = data.readUInt16BE(off -= 2);
const offsetH = data.readUInt8(off -= 1) << 16;
const pixels.push([offsetH | offsetL, color]);
}
return {
i, j, offset, color,
i, j, pixels,
};
},
dehydrate(i, j, offset, color): Buffer {
const buffer = Buffer.allocUnsafe(1 + 1 + 1 + 1 + 2 + 1);
buffer.writeUInt8(OP_CODE, 0);
dehydrate(i, j, pixels): Buffer {
const buffer = Buffer.allocUnsafe(1 + 1 + 1 + pixels.length * 4);
buffer.writeUInt8(OP_CODE, 0);
/*
* chunk coordinates
*/
buffer.writeUInt8(i, 1);
buffer.writeUInt8(j, 2);
buffer.writeUInt8(offset >>> 16, 3);
buffer.writeUInt16BE(offset & 0x00FFFF, 4);
buffer.writeUInt8(color, 6);
/*
* offset and color of every pixel
* 3 bytes offset
* 1 byte color
*/
let cnt = 2;
for (let i = 0; i < pixels.length; i += 1) {
const [offset, color] = pixels[i];
buffer.writeUInt8(offset >>> 16, cnt += 1);
buffer.writeUInt16BE(offset & 0x00FFFF, cnt += 1);
buffer.writeUInt8(color, cnt += 2);
}
return buffer;
},