add punishment for dominating country

This commit is contained in:
HF 2023-12-27 15:43:34 +01:00
parent ccdf670291
commit 057f60f049
4 changed files with 144 additions and 20 deletions

View File

@ -11,6 +11,8 @@ import {
storeOnlinUserAmount, storeOnlinUserAmount,
getCountryDailyHistory, getCountryDailyHistory,
getCountryRanks, getCountryRanks,
getHourlyCountryStats,
storeHourlyCountryStats,
getTopDailyHistory, getTopDailyHistory,
storeHourlyPixelsPlaced, storeHourlyPixelsPlaced,
getHourlyPixelStats, getHourlyPixelStats,
@ -20,30 +22,38 @@ import socketEvents from '../socket/socketEvents';
import logger from './logger'; import logger from './logger';
import { MINUTE } from './constants'; import { MINUTE } from './constants';
import { PUNISH_DOMINATOR } from './config';
import { DailyCron, HourlyCron } from '../utils/cron'; import { DailyCron, HourlyCron } from '../utils/cron';
class Ranks { class Ranks {
ranks = {
// ranking today of users by pixels
dailyRanking: [],
// ranking of users by pixels
ranking: [],
// ranking today of countries by pixels
dailyCRanking: [],
// ranking hourly of countries by pixels
cHourlyStats: [],
// yesterdays ranking of users by pixels
prevTop: [],
// online user amount by hour
onlineStats: [],
// ranking of countries by day
cHistStats: [],
// ranking of users by day
histStats: { users: [], stats: [] },
// pixels placed by hour
pHourlyStats: [],
// pixels placed by day
pDailyStats: [],
};
// if a country dominates, adjust its cooldown
#punishedCountry;
#punishmentFactor;
constructor() { constructor() {
this.ranks = {
// ranking today of users by pixels
dailyRanking: [],
// ranking of users by pixels
ranking: [],
// ranking today of countries by pixels
dailyCRanking: [],
// yesterdays ranking of users by pixels
prevTop: [],
// online user amount by hour
onlineStats: [],
// ranking of countries by day
cHistStats: [],
// ranking of users by day
histStats: { users: [], stats: [] },
// pixels placed by hour
pHourlyStats: [],
// pixels placed by day
pDailyStats: [],
};
/* /*
* we go through socketEvents for sharding * we go through socketEvents for sharding
*/ */
@ -70,12 +80,59 @@ class Ranks {
if (!newRanks) { if (!newRanks) {
return; return;
} }
if (newRanks.cHourlyStats) {
this.setPunishments(newRanks.cHourlyStats);
}
this.ranks = { this.ranks = {
...this.ranks, ...this.ranks,
...newRanks, ...newRanks,
}; };
} }
setPunishments(cHourlyStats) {
if (!cHourlyStats.length || !PUNISH_DOMINATOR) {
return;
}
let outnumbered = 0;
const { cc: leadingCountry } = cHourlyStats[0];
let { px: margin } = cHourlyStats[0];
for (let i = 1; i < cHourlyStats.length; i += 1) {
margin -= cHourlyStats[i].px;
if (margin < 0) {
break;
}
outnumbered += 1;
}
/*
* if the leading country places more pixels
* than the fellowing 2+ countries after,
* 20% gets added to the cooldown for every following
* country, cailed at 200%;
*/
if (outnumbered > 2) {
this.#punishedCountry = leadingCountry;
let punishmentFactor = 1 + 0.2 * (outnumbered - 1);
if (punishmentFactor > 2) {
punishmentFactor = 2;
}
this.#punishmentFactor = punishmentFactor;
logger.info(
// eslint-disable-next-line max-len
`Punishment for dominating country ${leadingCountry} of ${punishmentFactor}`,
);
return;
}
this.#punishedCountry = null;
this.#punishmentFactor = 1.0;
}
getCountryCoolDownFactor(country) {
if (this.#punishedCountry === country) {
return this.#punishmentFactor;
}
return 1.0;
}
static async updateRanking() { static async updateRanking() {
// only main shard does it // only main shard does it
if (!socketEvents.amIImportant()) { if (!socketEvents.amIImportant()) {
@ -107,10 +164,12 @@ class Ranks {
const onlineStats = await getOnlineUserStats(); const onlineStats = await getOnlineUserStats();
const cHistStats = await getCountryDailyHistory(); const cHistStats = await getCountryDailyHistory();
const pHourlyStats = await getHourlyPixelStats(); const pHourlyStats = await getHourlyPixelStats();
const cHourlyStats = await getHourlyCountryStats(1, 100);
const ret = { const ret = {
onlineStats, onlineStats,
cHistStats, cHistStats,
pHourlyStats, pHourlyStats,
cHourlyStats,
}; };
if (socketEvents.amIImportant()) { if (socketEvents.amIImportant()) {
// only main shard sends to others // only main shard sends to others
@ -148,6 +207,7 @@ class Ranks {
const amount = socketEvents.onlineCounter.total; const amount = socketEvents.onlineCounter.total;
await storeOnlinUserAmount(amount); await storeOnlinUserAmount(amount);
await storeHourlyPixelsPlaced(); await storeHourlyPixelsPlaced();
await storeHourlyCountryStats(1, 100);
await Ranks.hourlyUpdateRanking(); await Ranks.hourlyUpdateRanking();
} }

View File

@ -29,6 +29,9 @@ export const BACKUP_DIR = process.env.BACKUP_DIR || null;
export const OUTGOING_ADDRESS = process.env.OUTGOING_ADDRESS || null; export const OUTGOING_ADDRESS = process.env.OUTGOING_ADDRESS || null;
// Punish when a country dominates
export const PUNISH_DOMINATOR = !!process.env.PUNISH_DOMINATOR;
// Proxycheck // Proxycheck
export const USE_PROXYCHECK = parseInt(process.env.USE_PROXYCHECK, 10) || false; export const USE_PROXYCHECK = parseInt(process.env.USE_PROXYCHECK, 10) || false;
export const { PROXYCHECK_KEY } = process.env; export const { PROXYCHECK_KEY } = process.env;

View File

@ -8,6 +8,7 @@ import {
import logger, { pixelLogger } from './logger'; import logger, { pixelLogger } from './logger';
import allowPlace from '../data/redis/cooldown'; import allowPlace from '../data/redis/cooldown';
import socketEvents from '../socket/socketEvents'; import socketEvents from '../socket/socketEvents';
import rankings from './Ranks';
import { setPixelByOffset } from './setPixel'; import { setPixelByOffset } from './setPixel';
import isIPAllowed from './isAllowed'; import isIPAllowed from './isAllowed';
import canvases from './canvases'; import canvases from './canvases';
@ -127,6 +128,8 @@ export default async function drawByOffsets(
} }
*/ */
factor *= rankings.getCountryCoolDownFactor(user.country);
factor *= 0.75; factor *= 0.75;
const bcd = canvas.bcd * factor; const bcd = canvas.bcd * factor;

View File

@ -7,6 +7,9 @@ import { getDateKeyOfTs } from '../../core/utils';
export const RANKED_KEY = 'rank'; export const RANKED_KEY = 'rank';
export const DAILY_RANKED_KEY = 'rankd'; export const DAILY_RANKED_KEY = 'rankd';
export const DAILY_CRANKED_KEY = 'crankd'; export const DAILY_CRANKED_KEY = 'crankd';
export const HOURLY_CRANKED_KEY = 'crankh';
export const PREV_DAILY_CRANKED_KEY = 'pcrankd';
export const PREV_DAILY_CRANKED_TS_KEY = 'pcrankdts';
export const PREV_DAY_TOP_KEY = 'prankd'; export const PREV_DAY_TOP_KEY = 'prankd';
const DAY_STATS_RANKS_KEY = 'ds'; const DAY_STATS_RANKS_KEY = 'ds';
const CDAY_STATS_RANKS_KEY = 'cds'; const CDAY_STATS_RANKS_KEY = 'cds';
@ -100,6 +103,61 @@ export async function getCountryRanks(start, amount) {
return ranks; return ranks;
} }
/*
* get previous daily country ranking (one hour before cranks)
*/
export async function getHourlyCountryStats(start, amount) {
start -= 1;
amount -= 1;
let ranks = await client.zRangeWithScores(
HOURLY_CRANKED_KEY, start, start + amount, {
REV: true,
});
ranks = ranks.map((r) => ({
cc: r.value,
px: Number(r.score),
}));
return ranks;
}
export async function storeHourlyCountryStats(start, amount) {
start -= 1;
amount -= 1;
const tsNow = Date.now();
const prevTs = Number(await client.get(PREV_DAILY_CRANKED_TS_KEY));
const prevData = await client.zRangeWithScores(
PREV_DAILY_CRANKED_KEY, start, start + amount, {
REV: true,
});
await client.copy(DAILY_CRANKED_KEY, PREV_DAILY_CRANKED_KEY, {
REAPLACE: true,
});
await client.set(PREV_DAILY_CRANKED_TS_KEY, String(tsNow));
await client.del(HOURLY_CRANKED_KEY);
if (prevTs && prevTs > tsNow - 1000 * 3600 * 1.5) {
const curData = await client.zRangeWithScores(
DAILY_CRANKED_KEY, start, start + amount, {
REV: true,
});
const prevRanks = new Map();
prevData.forEach(({ value, score }) => prevRanks.set(value, score));
const addArr = [];
curData.forEach(({ value: cc, score: curPx }) => {
const prevPx = prevData.get(cc) || 0;
const px = (curPx > prevPx) ? curPx - prevPx : curPx;
addArr.push({
score: cc,
value: px,
});
});
if (addArr.length) {
await client.zAdd(HOURLY_CRANKED_KEY, addArr);
}
}
}
/* /*
* get top 10 from previous day * get top 10 from previous day
*/ */