Move pixelcount into redis

This commit is contained in:
HF 2022-09-13 16:09:38 +02:00
parent dd757b035e
commit db4f006f4a
16 changed files with 390 additions and 272 deletions

View File

@ -394,7 +394,6 @@ export class ChatProvider {
user.ipSub,
country,
);
console.log(allowed, needProxycheck, name, id, country);
if (allowed) {
logger.info(
`${name} / ${user.ip} tried to send chat message but is not allowed`,

111
src/core/Ranks.js Normal file
View File

@ -0,0 +1,111 @@
/*
* timers and cron for account related actions
*/
import Sequelize from 'sequelize';
import RegUser from '../data/sql/RegUser';
import {
getRanks,
resetDailyRanks,
saveDailyTop,
loadDailyTop,
} from '../data/redis/ranks';
import socketEvents from '../socket/socketEvents';
import logger from './logger';
import { MINUTE } from './constants';
import { DailyCron } from '../utils/cron';
class Ranks {
ranks; // Array
constructor() {
this.updateRanking = this.updateRanking.bind(this);
this.resetDailyRanking = this.resetDailyRanking.bind(this);
this.prevTop = [];
this.ranks = {
dailyRanking: [],
ranking: [],
};
}
async initialize() {
this.prevTop = await loadDailyTop();
await this.updateRanking();
setInterval(this.updateRanking, 10 * MINUTE);
DailyCron.hook(this.resetDailyRanking);
}
/*
* take array of {useId: score} and resolve
* user informations
*/
static async populateRanking(rawRanks) {
if (!rawRanks.length) {
return rawRanks;
}
const uids = rawRanks.map((r) => r.id);
const userData = await RegUser.findAll({
attributes: [
'id',
'name',
[
Sequelize.fn(
'DATEDIFF',
Sequelize.literal('CURRENT_TIMESTAMP'),
Sequelize.col('createdAt'),
),
'age',
],
],
where: {
id: uids,
},
raw: true,
});
for (let i = 0; i < userData.length; i += 1) {
const { id, name, age } = userData[i];
const dat = rawRanks.find((r) => r.id === id);
if (dat) {
dat.name = name;
dat.age = age;
}
}
return rawRanks;
}
async updateRanking() {
if (socketEvents.amIImportant()) {
// TODO do this only in main shard
}
// populate dictionaries
const ranking = await Ranks.populateRanking(
await getRanks(
false,
1,
100,
));
const dailyRanking = await Ranks.populateRanking(
await getRanks(
true,
1,
100,
));
this.ranks.ranking = ranking;
this.ranks.dailyRanking = dailyRanking;
}
async resetDailyRanking() {
if (!socketEvents.amIImportant()) {
return;
}
this.prevTop = await saveDailyTop(this.ranks.dailyRanking);
logger.info('Resetting Daily Ranking');
await resetDailyRanks();
await this.updateRanking();
}
}
const rankings = new Ranks();
export default rankings;

View File

@ -12,7 +12,7 @@ import {
setPixelByOffset,
setPixelByCoords,
} from './setPixel';
import rankings from './ranking';
import rankings from './Ranks';
import canvases from './canvases';
import { THREE_CANVAS_HEIGHT, THREE_TILE_SIZE, TILE_SIZE } from './constants';
@ -144,6 +144,7 @@ export async function drawByOffsets(
/*
* validate pixels
*/
let ranked = canvas.ranked && user.id && pcd;
for (let u = 0; u < pixels.length; u += 1) {
const [offset, color] = pixels[u];
pxlOffsets.push(offset);
@ -180,15 +181,18 @@ export async function drawByOffsets(
throw new Error(8);
}
/* dont rank antarctica */
// eslint-disable-next-line eqeqeq
if (canvas.ranked && (canvasId != 0 || y < 14450) && pcd) {
pixels[u].push(true);
if (canvasId == 0 && y > 14450) {
ranked = false;
}
}
[retCode, pxlCnt, wait, coolDown, needProxycheck] = await allowPlace(
ip,
user.id,
user.country,
ranked,
canvasId,
i, j,
clrIgnore,
@ -198,11 +202,12 @@ export async function drawByOffsets(
);
for (let u = 0; u < pxlCnt; u += 1) {
const [offset, color, ranked] = pixels[u];
const [offset, color] = pixels[u];
setPixelByOffset(canvasId, color, i, j, offset);
if (ranked) {
rankedPxlCnt += 1;
}
}
if (ranked) {
rankedPxlCnt = pxlCnt;
}
const duration = Date.now() - startTime;
@ -219,10 +224,6 @@ export async function drawByOffsets(
}
}
if (rankedPxlCnt) {
user.incrementPixelcount(rankedPxlCnt);
}
if (retCode !== 13) {
curReqIPs.delete(ip);
}

View File

@ -1,116 +0,0 @@
/*
* timers and cron for account related actions
*/
import Sequelize from 'sequelize';
import sequelize from '../data/sql/sequelize';
import RegUser from '../data/sql/RegUser';
import { saveDailyTop, loadDailyTop } from '../data/redis/PrevDayTop';
import socketEvents from '../socket/socketEvents';
import logger from './logger';
import { MINUTE } from './constants';
import { DailyCron } from '../utils/cron';
class Ranks {
ranks; // Array
constructor() {
this.updateRanking = this.updateRanking.bind(this);
this.resetDailyRanking = this.resetDailyRanking.bind(this);
this.prevTop = [];
this.ranks = {
dailyRanking: [],
ranking: [],
};
}
async initialize() {
this.prevTop = await loadDailyTop();
await this.updateRanking();
setInterval(this.updateRanking, 5 * MINUTE);
DailyCron.hook(this.resetDailyRanking);
}
async updateRanking() {
if (socketEvents.amIImportant()) {
logger.info('Update pixel rankings in SQL');
// recalculate ranking column
await sequelize.query(
// eslint-disable-next-line max-len
'SET @r=0; UPDATE Users SET ranking= @r:= (@r + 1) ORDER BY totalPixels DESC;',
);
await sequelize.query(
// eslint-disable-next-line max-len
'SET @r=0; UPDATE Users SET dailyRanking= @r:= (@r + 1) ORDER BY dailyTotalPixels DESC;',
);
} else {
logger.info('Get pixel rankings from SQL');
}
// populate dictionaries
const ranking = await RegUser.findAll({
attributes: [
'id',
'name',
'totalPixels',
'ranking',
'dailyRanking',
'dailyTotalPixels',
[
Sequelize.fn(
'DATEDIFF',
Sequelize.literal('CURRENT_TIMESTAMP'),
Sequelize.col('createdAt'),
),
'age',
],
],
limit: 100,
where: {
ranking: { [Sequelize.Op.not]: null },
},
order: ['ranking'],
raw: true,
});
const dailyRanking = await RegUser.findAll({
attributes: [
'id',
'name',
'totalPixels',
'ranking',
'dailyRanking',
'dailyTotalPixels',
[
Sequelize.fn(
'DATEDIFF',
Sequelize.literal('CURRENT_TIMESTAMP'),
Sequelize.col('createdAt'),
),
'age',
],
],
limit: 100,
where: {
dailyRanking: { [Sequelize.Op.not]: null },
},
order: ['dailyRanking'],
raw: true,
});
this.ranks.ranking = ranking;
this.ranks.dailyRanking = dailyRanking;
}
async resetDailyRanking() {
if (!socketEvents.amIImportant()) {
return;
}
this.prevTop = await saveDailyTop(this.ranks.dailyRanking);
logger.info('Resetting Daily Ranking');
await RegUser.update({ dailyTotalPixels: 0 }, { where: {} });
await this.updateRanking();
}
}
const rankings = new Ranks();
export default rankings;

View File

@ -10,8 +10,8 @@ import { QueryTypes, Utils } from 'sequelize';
import sequelize from './sql/sequelize';
import { RegUser, Channel, UserBlock } from './sql';
import { incrementPixelcount } from './sql/RegUser';
import { setCoolDown, getCoolDown } from './redis/cooldown';
import { getUserRanks } from './redis/ranks';
import { getIPv6Subnet } from '../utils/ip';
import { ADMIN_IDS } from '../core/config';
@ -50,7 +50,6 @@ export const regUserQueryInclude = [{
class User {
id; // string
ip; // string
wait; // ?number
regUser; // Object
channels; // Object
blocked; // Array
@ -179,13 +178,6 @@ class User {
return getCoolDown(this.ipSub, this.id, canvasId);
}
incrementPixelcount(amount = 1) {
const { id } = this;
if (id) {
incrementPixelcount(id, amount);
}
}
async getTotalPixels() {
const { id } = this;
if (!id) return 0;
@ -210,6 +202,7 @@ class User {
}
async setCountry(country) {
this.country = country;
if (this.regUser && this.regUser.flag !== country) {
this.regUser.update({
flag: country,
@ -229,7 +222,7 @@ class User {
return true;
}
getUserData() {
async getUserData() {
const {
id,
userlvl,
@ -248,23 +241,25 @@ class User {
name: null,
mailVerified: false,
blockDm: false,
totalPixels: 0,
dailyTotalPixels: 0,
ranking: null,
dailyRanking: null,
mailreg: false,
};
}
const { regUser } = this;
const [
totalPixels,
dailyTotalPixels,
ranking,
dailyRanking,
] = await getUserRanks(id);
return {
...data,
name: regUser.name,
mailVerified: regUser.mailVerified,
blockDm: regUser.blockDm,
totalPixels: regUser.totalPixels,
dailyTotalPixels: regUser.dailyTotalPixels,
ranking: regUser.ranking,
dailyRanking: regUser.dailyRanking,
totalPixels,
dailyTotalPixels,
ranking,
dailyRanking,
mailreg: !!(regUser.password),
};
}

View File

@ -1,30 +0,0 @@
/*
* saving and loading the top 10 of the previous day
*/
import client from './client';
import logger from '../../core/logger';
const PREV_DAILY_TOP_KEY = 'prevtop';
/*
* saves the top 10 into redis
* @param dailyRanking Array of dailyRanking
*/
export async function saveDailyTop(dailyRanking) {
const top10 = dailyRanking.slice(0, 10).map((user) => user.id);
const jsonTop = JSON.stringify(top10);
logger.info(`Saving current daily top 10 into redis: ${jsonTop}`);
await client.set(PREV_DAILY_TOP_KEY, jsonTop);
return top10;
}
/*
* load top10 from redis
* @return Promis<Array> Array of user IDs of the top 10
*/
export async function loadDailyTop() {
const jsonTop = await client.get(PREV_DAILY_TOP_KEY);
logger.info(`Loaded current daily top 10 into redis: ${jsonTop}`);
return (jsonTop) ? JSON.parse(jsonTop) : [];
}

View File

@ -10,11 +10,12 @@ import { REDIS_URL, SHARD_NAME } from '../../core/config';
const scripts = {
placePxl: defineScript({
NUMBER_OF_KEYS: 5,
NUMBER_OF_KEYS: 8,
SCRIPT: fs.readFileSync('./workers/lua/placePixel.lua'),
transformArguments(...args) {
return args.map((a) => ((typeof a === 'string') ? a : a.toString()));
},
transformReply(arr) { return arr.map((r) => Number(r)); },
}),
allowedChat: defineScript({
NUMBER_OF_KEYS: 3,
@ -22,6 +23,26 @@ const scripts = {
transformArguments(...args) {
return args.map((a) => ((typeof a === 'string') ? a : a.toString()));
},
transformReply(arr) { return arr.map((r) => Number(r)); },
}),
getUserRanks: defineScript({
NUMBER_OF_KEYS: 2,
SCRIPT: fs.readFileSync('./workers/lua/getUserRanks.lua'),
transformArguments(...args) {
return args.map((a) => ((typeof a === 'string') ? a : a.toString()));
},
transformReply(arr) { return arr.map((r) => Number(r)); },
}),
zmRankRev: defineScript({
NUMBER_OF_KEYS: 1,
SCRIPT: fs.readFileSync('./workers/lua/zmRankRev.lua'),
transformArguments(key, uids) {
return [
key,
...uids.map((a) => ((typeof a === 'string') ? a : a.toString())),
];
},
transformReply(arr) { return arr.map((r) => Number(r)); },
}),
};

View File

@ -5,15 +5,18 @@
import client from './client';
import { PREFIX as CAPTCHA_PREFIX } from './captcha';
import { PREFIX as ALLOWED_PREFIX } from './isAllowedCache';
import { RANKED_KEY, DAILY_RANKED_KEY, DAILY_CRANKED_KEY } from './ranks';
import { CAPTCHA_TIME } from '../../core/config';
const PREFIX = 'cd';
/*
* checks how many of the given pixels can be set,
* sets user cooldown and returns the number of pixels to set
* sets user cooldown, increments pixelcount
* and returns the number of pixels to set
* @param ip ip of request
* @param id userId
* @param ranked boolean if increasing rank
* @param clrIgnore, bcd, pcd, cds incormations about canvas
* @param i, j chunk coordinates
* @param pxls Array with offsets of pixels
@ -22,6 +25,8 @@ const PREFIX = 'cd';
export default function allowPlace(
ip,
id,
country,
ranked,
canvasId,
i, j,
clrIgnore,
@ -35,9 +40,13 @@ export default function allowPlace(
const ipCdKey = `${PREFIX}:${canvasId}:ip:${ip}`;
const idCdKey = (id) ? `${PREFIX}:${canvasId}:id:${id}` : 'nope';
const chunkKey = `ch:${canvasId}:${i}:${j}`;
const cc = country || 'xx';
const rankset = (ranked) ? RANKED_KEY : 'nope';
const dailyset = (ranked) ? DAILY_RANKED_KEY : 'nope';
return client.placePxl(
isalKey, captKey, ipCdKey, idCdKey, chunkKey,
clrIgnore, bcd, pcd, cds,
// eslint-disable-next-line max-len
isalKey, captKey, ipCdKey, idCdKey, chunkKey, rankset, dailyset, DAILY_CRANKED_KEY,
clrIgnore, bcd, pcd, cds, id, cc,
...pxls,
);
}

View File

@ -0,0 +1,30 @@
-- Get ranks of user
-- Currently only pixelcounts and rankings
-- Keys:
-- rankset: 'rank'
-- dailyset: 'rankd'
-- Args:
-- userId
-- Returns:
-- {
-- 1: totalPixels
-- 2: dailyPixels
-- 3: totalRanking
-- 4: dailyRanking
-- }
local ret = {0, 0, 0, 0}
-- get total pixels
local cnt = redis.call('zscore', KEYS[1], ARGV[1])
if cnt then
ret[1] = cnt
-- get total rank
ret [3] = redis.call('zrevrank', KEYS[1], ARGV[1]) + 1
end
-- get daily pixels
local dcnt = redis.call('zscore', KEYS[2], ARGV[1])
if dcnt then
ret[2] = dcnt
-- get daily rank
ret [4] = redis.call('zrevrank', KEYS[2], ARGV[1]) + 1
end
return ret

View File

@ -1,18 +1,26 @@
-- Checking requirements and calculating cooldown of user wthin
-- redis itself. Does not set pixels directly. Pixels are set in batches
-- Checking requirements for placing pixels, calculating cooldown
-- of user and incrementing pixel counts wthin redis itself.
-- Does not set pixels directly. Pixels are set in batches
-- in RedisCanvas.js
-- This script will get copied into the dist/workers directory from webpack
-- Keys:
-- isAlloweed: 'isal:ip' (proxycheck, blacklist, whitelist)
-- isHuman 'human:ip' (captcha needed when expired)
-- isHuman 'human:ip' captcha needed when expired,
-- 'nope' if no captcha should be checked
-- ipCD: 'cd:canvasId:ip:ip'
-- uCD: 'cd:canvasId:id:userId'
-- 'nope' if not logged in
-- chunk: 'ch:canvasId:i:j'
-- rankset: 'rank' sorted set of pixelcount
-- 'nope' if not increasing ranks
-- dailyset: 'rankd' sorted set of daily pixelcount
-- countryset: sorted set for country stats
-- Args:
-- clrIgnore: integer number of what colors are considered unset
-- bcd: number baseColldown (fixed to cdFactor and 0 if admin)
-- pcd: number set pixel cooldown (fixed to cdFactor and 0 if admin)
-- cds: max cooldown of canvas
-- userId: '0' if not logged in
-- cc country code
-- off1, chonk offset of first pixel
-- off2, chonk offset of second pixel
-- ..., infinie pixels possible
@ -21,7 +29,7 @@
-- 1: pixel return status code (check ui/placePixel.js)
-- 2: amount of successfully set pixels
-- 3: total cooldown of user
-- 4: info about placed pixel cooldown (addition of last pixel)
-- 4: added cooldown of last pixel
-- 5: if we have to update isAllowed( proxycheck)
-- }
local ret = {0, 0, 0, 0, 0}
@ -69,9 +77,8 @@ local cli = tonumber(ARGV[1])
local bcd = tonumber(ARGV[2])
local pcd = tonumber(ARGV[3])
local cds = tonumber(ARGV[4])
for c = 5,#ARGV do
for c = 7,#ARGV do
local off = tonumber(ARGV[c]) * 8
local clr = tonumber(ARGV[c + 1])
-- get color of pixel on canvas
local sclr = redis.call('bitfield', KEYS[5], 'get', 'u8', off)
sclr = sclr[1]
@ -88,19 +95,31 @@ for c = 5,#ARGV do
end
cd = cd + pxlcd
if cd > cds then
-- pixelstack used up
-- report difference as last cd
cd = cd - pxlcd
pxlcd = cds - cd - pxlcd
-- pixelstack used up
ret[1] = 9
break
end
pxlcnt = pxlcnt + 1
end
if pxlcnt > 0 and cd > 0 then
redis.call('set', KEYS[3], '', 'px', cd)
if KEYS[4] ~= "nope" then
redis.call('set', KEYS[4], '', 'px', cd)
if pxlcnt > 0 then
-- set cooldown
if cd > 0 then
redis.call('set', KEYS[3], '', 'px', cd)
if KEYS[4] ~= "nope" then
redis.call('set', KEYS[4], '', 'px', cd)
end
end
-- increment pixelcount
if KEYS[6] ~= 'nope' then
redis.call('zincrby', KEYS[6], pxlcnt, ARGV[5])
redis.call('zincrby', KEYS[7], pxlcnt, ARGV[5])
if ARGV[6] ~= 'xx' then
redis.call('zincrby', KEYS[8], pxlcnt, ARGV[6])
end
end
end

View File

@ -0,0 +1,16 @@
-- Get multiple ranks from sorted set
-- Keys:
-- set: sorted set
-- Args:
-- [member,...] dynamic amount of members to look for
-- return:
-- table with the ranks, 0 for an item with no rank
local ret = {}
for c = 1,#ARGV do
local rank = redis.call('zrevrank', KEYS[1], ARGV[c])
if not rank then
rank = 0
end
ret[c] = rank
end
return ret

138
src/data/redis/ranks.js Normal file
View File

@ -0,0 +1,138 @@
/*
* counter for daily and total pixels and ranking
*/
import client from './client';
import logger from '../../core/logger';
export const RANKED_KEY = 'rank';
export const DAILY_RANKED_KEY = 'rankd';
export const DAILY_CRANKED_KEY = 'crankd';
const PREV_DAY_TOP_KEY = 'prankd';
const DAY_STATS_RANKS_KEY = 'ds';
const CDAY_STATS_RANKS_KEY = 'cds';
const PREV_DAILY_TOP_KEY = 'prevtop';
/*
* get pixelcount and ranking
* @param userId
* @return [ totalPixels, dailyPixels, totalRanking, dailyRanking ]
*/
export async function getUserRanks(userId) {
const ranks = await client.getUserRanks(
RANKED_KEY, DAILY_RANKED_KEY,
userId,
);
return ranks.map((r) => Number(r));
}
/*
* get userIds by ranks
* @param daily integer if using daily or total score
* @param start, amount rank to start and end
* @return Array of objects with {userId, score, dailyScore} in given range
*/
export async function getRanks(daily, start, amount) {
start -= 1;
amount -= 1;
let key;
let valueName;
let rankName;
let oKey;
let oValueName;
let oRankName;
if (daily) {
key = DAILY_RANKED_KEY;
valueName = 'dailyTotalPixels';
rankName = 'dailyRanking';
oKey = RANKED_KEY;
oValueName = 'totalPixels';
oRankName = 'ranking';
} else {
key = RANKED_KEY;
valueName = 'totalPixels';
rankName = 'ranking';
oKey = DAILY_RANKED_KEY;
oValueName = 'dailyTotalPixels';
oRankName = 'dailyRanking';
}
/* returns { value: uid, score: pixelCnt } */
const ranks = await client.zRangeWithScores(key, start, start + amount, {
REV: true,
});
const uids = ranks.map((r) => r.value);
if (!uids.length) {
return uids;
}
const oScores = await client.zmScore(oKey, uids);
/* impolemented with lua, which blocks :( */
const oRanks = await client.zmRankRev(oKey, uids);
const ret = [];
for (let i = 0; i < ranks.length; i += 1) {
const uob = {
id: Number(uids[i]),
[valueName]: ranks[i].score,
[rankName]: i + 1,
[oValueName]: oScores[i],
[oRankName]: oRanks[i] + 1,
};
ret.push(uob);
}
return ret;
}
/*
* reset daily ranks
* @return boolean for success
*/
export async function resetDailyRanks() {
// store top 10
console.log('DAILYREDIS SAVE TOP 10');
await client.zRangeWithScores(PREV_DAY_TOP_KEY, DAILY_RANKED_KEY, 0, 9, {
REV: true,
WITHSCORES: true,
});
// store day
const yesterday = new Date(Date.now() - 1000 * 3600 * 24);
let day = yesterday.getUTCDate();
if (day < 10) day = `0${day}`;
let month = yesterday.getUTCMonth() + 1;
if (month < 10) month = `0${month}`;
const year = yesterday.getUTCFullYear();
const dateKey = `${year}${month}${day}`;
console.log('DAILYREDIS', dateKey);
await client.zUnionStore(
`${DAY_STATS_RANKS_KEY}:${dateKey}`,
DAILY_RANKED_KEY,
);
await client.zUnionStore(
`${CDAY_STATS_RANKS_KEY}:${dateKey}`,
DAILY_CRANKED_KEY,
);
// reset daily counter
console.log('DAILYREDIS RESET');
await client.del(DAILY_RANKED_KEY);
await client.del(DAILY_CRANKED_KEY);
}
/*
* saves the top 10 into redis
* @param dailyRanking Array of dailyRanking
*/
export async function saveDailyTop(dailyRanking) {
const top10 = dailyRanking.slice(0, 10).map((user) => user.id);
const jsonTop = JSON.stringify(top10);
logger.info(`Saving current daily top 10 into redis: ${jsonTop}`);
await client.set(PREV_DAILY_TOP_KEY, jsonTop);
return top10;
}
/*
* load top10 from redis
* @return Promis<Array> Array of user IDs of the top 10
*/
export async function loadDailyTop() {
const jsonTop = await client.get(PREV_DAILY_TOP_KEY);
logger.info(`Loaded current daily top 10 into redis: ${jsonTop}`);
return (jsonTop) ? JSON.parse(jsonTop) : [];
}

View File

@ -7,7 +7,6 @@
import { DataTypes, QueryTypes } from 'sequelize';
import logger from '../../core/logger';
import sequelize from './sequelize';
import { generateHash } from '../../utils/hash';
@ -42,28 +41,6 @@ const RegUser = sequelize.define('User', {
allowNull: true,
},
totalPixels: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
},
dailyTotalPixels: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
defaultValue: 0,
},
ranking: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: true,
},
dailyRanking: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: true,
},
// mail and Minecraft verified
verified: {
type: DataTypes.TINYINT,
@ -204,59 +181,4 @@ export async function getNamesToIds(ids) {
return idToNameMap;
}
/*
* increment user pixelcount in batches sequentially
* Queue directly accesses queuedPxlIncrement in user object
*/
let incrementQueue = {};
let pushLoop = null;
const incrementLoop = async () => {
const idKeys = Object.keys(incrementQueue);
if (!idKeys.length) {
pushLoop = null;
return;
}
let queue = incrementQueue;
incrementQueue = {};
const orderedCnts = {};
idKeys.forEach((id) => {
const cnt = queue[id];
const cntArr = orderedCnts[cnt];
if (cntArr) {
cntArr.push(id);
} else {
orderedCnts[cnt] = [id];
}
});
queue = Object.keys(orderedCnts);
for (let u = 0; u < queue.length; u += 1) {
const by = queue[u];
const id = orderedCnts[by];
try {
// eslint-disable-next-line no-await-in-loop
await RegUser.increment(['totalPixels', 'dailyTotalPixels'], {
by,
where: {
id,
},
silent: true,
raw: true,
});
} catch (err) {
logger.warn(`Error on pixel increment: ${err.message}`);
}
}
pushLoop = setTimeout(incrementLoop, 250);
};
export async function incrementPixelcount(id, by) {
if (incrementQueue[id]) {
incrementQueue[id] += by;
} else {
incrementQueue[id] = by;
}
if (!pushLoop) {
pushLoop = setTimeout(incrementLoop, 0);
}
}
export default RegUser;

View File

@ -2,7 +2,7 @@
* send global ranking
*/
import rankings from '../core/ranking';
import rankings from '../core/Ranks';
export default (req, res) => {
res.json(rankings.ranks);

View File

@ -9,7 +9,7 @@ import http from 'http';
import forceGC from './core/forceGC';
import logger from './core/logger';
import rankings from './core/ranking';
import rankings from './core/Ranks';
import sequelize from './data/sql/sequelize';
import { connect as connectRedis } from './data/redis/client';
import routes from './routes';

View File

@ -51,6 +51,9 @@ export default function ranks(
case 's/REC_ME':
case 's/LOGIN': {
if (!action.totalPixels) {
return state;
}
const {
totalPixels,
dailyTotalPixels,