add default cooldown for unregistered first-connections

This commit is contained in:
HF 2023-06-29 17:17:14 +02:00
parent 924fb41741
commit d5469f7dc6
4 changed files with 39 additions and 22 deletions

View File

@ -50,8 +50,12 @@ setInterval(() => {
* @param i Chunk coordinates * @param i Chunk coordinates
* @param j * @param j
* @param pixels Array of individual pixels within the chunk, with: * @param pixels Array of individual pixels within the chunk, with:
* [[offset, color], [offset2, color2],...] * [[offset, color], [offset2, color2],...]
* Offset is the offset of the pixel within the chunk * Offset is the offset of the pixel within the chunk
* @param connectedTs Timestamp when connection got established.
* if the connection is younger than the cooldown of the canvas,
* we fill up the cd on first pixel to nerf one-connection
* ip-changing cheaters
* @return Promise<Object> * @return Promise<Object>
*/ */
export default async function drawByOffsets( export default async function drawByOffsets(
@ -60,6 +64,7 @@ export default async function drawByOffsets(
i, i,
j, j,
pixels, pixels,
connectedTs,
) { ) {
let wait = 0; let wait = 0;
let coolDown = 0; let coolDown = 0;
@ -122,12 +127,13 @@ export default async function drawByOffsets(
const bcd = canvas.bcd * factor; const bcd = canvas.bcd * factor;
const pcd = (canvas.pcd) ? canvas.pcd * factor : bcd; const pcd = (canvas.pcd) ? canvas.pcd * factor : bcd;
const userId = user.id;
const pxlOffsets = []; const pxlOffsets = [];
/* /*
* validate pixels * validate pixels
*/ */
let ranked = canvas.ranked && user.id && pcd; let ranked = canvas.ranked && userId && pcd;
for (let u = 0; u < pixels.length; u += 1) { for (let u = 0; u < pixels.length; u += 1) {
const [offset, color] = pixels[u]; const [offset, color] = pixels[u];
pxlOffsets.push(offset); pxlOffsets.push(offset);
@ -135,7 +141,7 @@ export default async function drawByOffsets(
const [x, y, z] = getPixelFromChunkOffset(i, j, offset, canvasSize, is3d); const [x, y, z] = getPixelFromChunkOffset(i, j, offset, canvasSize, is3d);
pixelLogger.info( pixelLogger.info(
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
`${startTime} ${user.ip} ${user.id} ${canvasId} ${x} ${y} ${z} ${color}`, `${startTime} ${user.ip} ${userId} ${canvasId} ${x} ${y} ${z} ${color}`,
); );
const maxSize = (is3d) ? tileSize * tileSize * THREE_CANVAS_HEIGHT const maxSize = (is3d) ? tileSize * tileSize * THREE_CANVAS_HEIGHT
@ -170,11 +176,17 @@ export default async function drawByOffsets(
} }
} }
const { cds } = canvas;
// start with almost filled cd on new connections
let cdIfNull = cds - pcd + 1000 - startTime + connectedTs;
if (cdIfNull < 0 || userId || bcd === 0) {
cdIfNull = 0;
}
let needProxycheck; let needProxycheck;
[retCode, pxlCnt, wait, coolDown, needProxycheck] = await allowPlace( [retCode, pxlCnt, wait, coolDown, needProxycheck] = await allowPlace(
ip, ip,
user.id, userId,
user.country, user.country,
ranked, ranked,
canvasId, canvasId,
@ -182,7 +194,8 @@ export default async function drawByOffsets(
clrIgnore, clrIgnore,
req, req,
bcd, pcd, bcd, pcd,
canvas.cds, cds,
cdIfNull,
pxlOffsets, pxlOffsets,
); );

View File

@ -39,6 +39,7 @@ export default function allowPlace(
bcd, bcd,
pcd, pcd,
cds, cds,
cdIfNull,
pxls, pxls,
) { ) {
const isalKey = `${ALLOWED_PREFIX}:${ip}`; const isalKey = `${ALLOWED_PREFIX}:${ip}`;
@ -67,7 +68,7 @@ export default function allowPlace(
return client.placePxl( return client.placePxl(
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
isalKey, captKey, ipCdKey, idCdKey, chunkKey, rankset, dailyset, DAILY_CRANKED_KEY, PREV_DAY_TOP_KEY, isalKey, captKey, ipCdKey, idCdKey, chunkKey, rankset, dailyset, DAILY_CRANKED_KEY, PREV_DAY_TOP_KEY,
clrIgnore, bcd, pcd, cds, id, cc, req, clrIgnore, bcd, pcd, cds, cdIfNull, id, cc, req,
...pxls, ...pxls,
); );
} }

View File

@ -20,10 +20,10 @@
-- bcd: number baseCooldown (fixed to cdFactor and 0 if admin) -- bcd: number baseCooldown (fixed to cdFactor and 0 if admin)
-- pcd: number set pixel cooldown (fixed to cdFactor and 0 if admin) -- pcd: number set pixel cooldown (fixed to cdFactor and 0 if admin)
-- cds: max cooldown of canvas -- cds: max cooldown of canvas
-- cdIfNull: cooldown to use when no cooldown is stored
-- userId: '0' if not logged in -- userId: '0' if not logged in
-- cc country code -- cc country code
-- req: requirements of canvas -- req: requirements of canvas ('nope', unsigned integer or 'top')
-- 'nope', unsigned integer or 'top'
-- off1, chunk offset of first pixel -- off1, chunk offset of first pixel
-- off2, chunk offset of second pixel -- off2, chunk offset of second pixel
-- ..., infinite pixels possible -- ..., infinite pixels possible
@ -63,23 +63,23 @@ else
end end
end end
-- check if requirements for canvas met -- check if requirements for canvas met
if ARGV[7] ~= "nope" then if ARGV[8] ~= "nope" then
if ARGV[5] == "0" then if ARGV[6] == "0" then
-- not logged in -- not logged in
ret[1] = 6 ret[1] = 6
return ret; return ret;
end end
if ARGV[7] == "top" then if ARGV[8] == "top" then
local pr = redis.call('zrank', KEYS[9], ARGV[5]) local pr = redis.call('zrank', KEYS[9], ARGV[6])
if not pr or pr > 9 then if not pr or pr > 9 then
-- not in yesterdays top 10 -- not in yesterdays top 10
ret[1] = 12; ret[1] = 12;
return ret; return ret;
end end
else else
local req = tonumber(ARGV[7]) local req = tonumber(ARGV[8])
if req > 0 then if req > 0 then
local sc = tonumber(redis.call('zscore', KEYS[6], ARGV[5])) local sc = tonumber(redis.call('zscore', KEYS[6], ARGV[6]))
if not sc or sc < req then if not sc or sc < req then
-- not enough pxls placed -- not enough pxls placed
ret[1] = 7; ret[1] = 7;
@ -91,7 +91,7 @@ end
-- get cooldown of user -- get cooldown of user
local cd = redis.call('pttl', KEYS[3]) local cd = redis.call('pttl', KEYS[3])
if cd < 0 then if cd < 0 then
cd = 0 cd = tonumber(ARGV[5])
end end
if KEYS[4] ~= "nope" then if KEYS[4] ~= "nope" then
local icd = redis.call('pttl', KEYS[4]) local icd = redis.call('pttl', KEYS[4])
@ -106,7 +106,7 @@ local cli = tonumber(ARGV[1])
local bcd = tonumber(ARGV[2]) local bcd = tonumber(ARGV[2])
local pcd = tonumber(ARGV[3]) local pcd = tonumber(ARGV[3])
local cds = tonumber(ARGV[4]) local cds = tonumber(ARGV[4])
for c = 8,#ARGV do for c = 9,#ARGV do
local off = tonumber(ARGV[c]) * 8 local off = tonumber(ARGV[c]) * 8
-- get color of pixel on canvas -- get color of pixel on canvas
local sclr = redis.call('bitfield', KEYS[5], 'get', 'u8', off) local sclr = redis.call('bitfield', KEYS[5], 'get', 'u8', off)
@ -144,10 +144,11 @@ if pxlcnt > 0 then
end end
-- increment pixelcount -- increment pixelcount
if KEYS[7] ~= 'nope' then if KEYS[7] ~= 'nope' then
redis.call('zincrby', KEYS[6], pxlcnt, ARGV[5]) redis.call('zincrby', KEYS[6], pxlcnt, ARGV[6])
redis.call('zincrby', KEYS[7], pxlcnt, ARGV[5]) redis.call('zincrby', KEYS[7], pxlcnt, ARGV[6])
if ARGV[6] ~= 'xx' then -- increase country stats only by registered users
redis.call('zincrby', KEYS[8], pxlcnt, ARGV[6]) if ARGV[7] ~= 'xx' then
redis.call('zincrby', KEYS[8], pxlcnt, ARGV[7])
end end
end end
end end

View File

@ -75,6 +75,7 @@ class SocketServer {
wss.on('connection', (ws, req) => { wss.on('connection', (ws, req) => {
ws.timeLastMsg = Date.now(); ws.timeLastMsg = Date.now();
ws.connectedTs = ws.timeLastMsg;
ws.canvasId = null; ws.canvasId = null;
const { user } = req; const { user } = req;
ws.user = user; ws.user = user;
@ -538,7 +539,7 @@ class SocketServer {
switch (opcode) { switch (opcode) {
case PIXEL_UPDATE_OP: { case PIXEL_UPDATE_OP: {
const { canvasId } = ws; const { canvasId, connectedTs } = ws;
if (canvasId === null) { if (canvasId === null) {
logger.info(`Closing websocket without canvas from ${ip}`); logger.info(`Closing websocket without canvas from ${ip}`);
@ -560,6 +561,7 @@ class SocketServer {
canvasId, canvasId,
i, j, i, j,
pixels, pixels,
connectedTs,
); );
if (retCode > 9 && retCode !== 13) { if (retCode > 9 && retCode !== 13) {