mirror of
https://github.com/zkldi/Tachi.git
synced 2026-10-09 07:09:57 +03:00
Add USC DB Migration script.
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
import db from "external/mongo/db";
|
||||
import fs from "fs";
|
||||
import {
|
||||
USC_DEFAULT_HOLD,
|
||||
USC_DEFAULT_MISS,
|
||||
USC_DEFAULT_NEAR,
|
||||
USC_DEFAULT_PERFECT,
|
||||
USC_DEFAULT_SLAM,
|
||||
} from "lib/constants/usc-ir";
|
||||
import CreateLogCtx from "lib/logger/logger";
|
||||
import { OrphanScore } from "lib/score-import/framework/orphans/orphans";
|
||||
import UpdateScore from "lib/score-mutation/update-score";
|
||||
import { integer, ScoreDocument } from "tachi-common";
|
||||
import { DeleteScore } from "lib/score-mutation/delete-scores";
|
||||
import { UpdateAllPBs } from "utils/calculations/recalc-scores";
|
||||
|
||||
const logger = CreateLogCtx(__filename);
|
||||
|
||||
const oldChartIDtoSHA1 = JSON.parse(fs.readFileSync(`${__dirname}/usc-map.json`, "utf-8"));
|
||||
|
||||
if (require.main === module) {
|
||||
(async () => {
|
||||
const keep = await db.charts.usc.find(
|
||||
{ "data.hashSHA1": { $in: Object.values(oldChartIDtoSHA1) } },
|
||||
{ projection: { chartID: 1, "data.hashSHA1": 1, songID: 1, playtype: 1 } }
|
||||
);
|
||||
|
||||
// @ts-expect-error asdf
|
||||
const keepHashes = keep.map((e) => e.data.hashSHA1);
|
||||
|
||||
const keepChartIDs = Object.entries(oldChartIDtoSHA1)
|
||||
.filter(([, v]) => keepHashes.includes(v))
|
||||
.map(([k]) => k);
|
||||
|
||||
const rmScores = (await db.scores.find({
|
||||
game: "usc",
|
||||
chartID: { $nin: keepChartIDs },
|
||||
})) as ScoreDocument<"usc:Controller" | "usc:Keyboard">[];
|
||||
|
||||
logger.info(`Re-orphaning and unseating ${rmScores.length} scores.`);
|
||||
|
||||
let i = 0;
|
||||
for (const score of rmScores) {
|
||||
i++;
|
||||
if (i % 100 === 0) {
|
||||
logger.info(`${i}/${rmScores.length}...`);
|
||||
}
|
||||
|
||||
const chartSHA1 = oldChartIDtoSHA1[score.chartID];
|
||||
|
||||
if (!chartSHA1) {
|
||||
logger.warn(`Can't find a chartsha1 for ${score.chartID}?`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await ReOrphanScore(score, chartSHA1);
|
||||
await DeleteScore(score, false, false);
|
||||
}
|
||||
|
||||
const keepScores = await db.scores.find({
|
||||
game: "usc",
|
||||
chartID: { $in: keepChartIDs },
|
||||
});
|
||||
logger.info(`Keeping and migrating ${keepScores.length} scores.`);
|
||||
|
||||
const sha1ToChartID = Object.fromEntries(
|
||||
keep.map((e) => [
|
||||
// @ts-expect-error hashsha1 exists i'm just lazy
|
||||
`${e.data.hashSHA1}:${e.playtype}`,
|
||||
{ chartID: e.chartID, songID: e.songID },
|
||||
])
|
||||
) as { [key: string]: { chartID: string; songID: integer } };
|
||||
|
||||
let j = 0;
|
||||
for (const score of keepScores) {
|
||||
j++;
|
||||
if (j % 100 === 0) {
|
||||
logger.info(`${j}/${keepScores.length}`);
|
||||
}
|
||||
|
||||
const sha1 = oldChartIDtoSHA1[score.chartID];
|
||||
const newChartID = sha1ToChartID[`${sha1}:${score.playtype}`].chartID;
|
||||
const newSongID = sha1ToChartID[`${sha1}:${score.playtype}`].songID;
|
||||
|
||||
if (!newChartID) {
|
||||
logger.error(
|
||||
`Couldn't resolve old:${score.chartID} -> sha1:${sha1} (playtype: ${score.playtype}) -> new:${newChartID}?`
|
||||
);
|
||||
|
||||
throw new Error("panic");
|
||||
}
|
||||
|
||||
await UpdateScore(
|
||||
score,
|
||||
Object.assign({}, score, { songID: newSongID, chartID: newChartID }),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
// You'd think the update scripts don't result in pbs that point to
|
||||
// dead charts, but they do.
|
||||
logger.info(`Reloading USC PBs.`);
|
||||
await db["personal-bests"].remove({ game: "usc" });
|
||||
await UpdateAllPBs(undefined, { game: "usc" });
|
||||
|
||||
logger.info(`Done.`);
|
||||
|
||||
process.exit(0);
|
||||
})();
|
||||
}
|
||||
|
||||
function ReOrphanScore(sc: ScoreDocument<"usc:Controller" | "usc:Keyboard">, chartSHA1: string) {
|
||||
let mirror = false;
|
||||
let random = false;
|
||||
|
||||
if (sc.scoreMeta.noteMod === "MIR-RAN") {
|
||||
mirror = true;
|
||||
random = false;
|
||||
} else if (sc.scoreMeta.noteMod === "RANDOM") {
|
||||
random = true;
|
||||
} else if (sc.scoreMeta.noteMod === "MIRROR") {
|
||||
mirror = true;
|
||||
}
|
||||
|
||||
const gaugeType =
|
||||
sc.scoreMeta.gaugeMod === "NORMAL"
|
||||
? 0
|
||||
: sc.scoreMeta.gaugeMod === "HARD"
|
||||
? 1
|
||||
: sc.scoreMeta.gaugeMod === "PERMISSIVE"
|
||||
? 2
|
||||
: 0;
|
||||
|
||||
return OrphanScore(
|
||||
"ir/usc",
|
||||
sc.userID,
|
||||
{
|
||||
combo: sc.scoreData.hitMeta.maxCombo ?? null,
|
||||
crit: sc.scoreData.judgements.critical!,
|
||||
near: sc.scoreData.judgements.near!,
|
||||
error: sc.scoreData.judgements.miss!,
|
||||
early: sc.scoreData.hitMeta.fast ?? null,
|
||||
late: sc.scoreData.hitMeta.slow ?? null,
|
||||
gauge: sc.scoreData.hitMeta.gauge!,
|
||||
options: {
|
||||
autoFlags: 0,
|
||||
gaugeOpt: 0,
|
||||
gaugeType,
|
||||
mirror,
|
||||
random,
|
||||
},
|
||||
score: sc.scoreData.score,
|
||||
timestamp: sc.timeAchieved!,
|
||||
windows: {
|
||||
perfect: USC_DEFAULT_PERFECT,
|
||||
good: USC_DEFAULT_NEAR,
|
||||
hold: USC_DEFAULT_HOLD,
|
||||
miss: USC_DEFAULT_MISS,
|
||||
slam: USC_DEFAULT_SLAM,
|
||||
},
|
||||
},
|
||||
{
|
||||
chartHash: chartSHA1,
|
||||
playtype: sc.playtype,
|
||||
timeReceived: sc.timeAchieved!,
|
||||
},
|
||||
`Lost parent as a result of USC migration.`,
|
||||
"usc",
|
||||
logger
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Vendored
+3
-1
@@ -32,8 +32,10 @@ const staticIndexes: Partial<Record<Databases, Index[]>> = {
|
||||
index({ userID: 1, game: 1, playtype: 1 }),
|
||||
],
|
||||
sessions: [
|
||||
// lol
|
||||
index({ userID: 1, game: 1, playtype: 1, timeStarted: 1, timeEnded: 1 }),
|
||||
// Optimises score modification, since sessions need to be repointed.
|
||||
// also, just generally useful.
|
||||
index({ "scoreInfo.scoreID": 1 }),
|
||||
index({ name: "text" }),
|
||||
],
|
||||
"game-stats": [index({ userID: 1, game: 1, playtype: 1 }, UNIQUE)],
|
||||
|
||||
@@ -43,7 +43,13 @@ export async function OrphanScore<T extends ImportTypes = ImportTypes>(
|
||||
|
||||
logger.debug("Orphaning document", orphan);
|
||||
|
||||
const orphanID = `O${fjsh.hash(orphan, "sha256")}`;
|
||||
let orphanID;
|
||||
try {
|
||||
orphanID = `O${fjsh.hash(orphan, "sha256")}`;
|
||||
} catch (err) {
|
||||
logger.error(`Failed to orphan chart -- `, { err, orphan });
|
||||
throw new Error(`Failed to orphan chart. ${err.message}`);
|
||||
}
|
||||
|
||||
const exists = await db["orphan-scores"].findOne({ orphanID });
|
||||
|
||||
|
||||
@@ -12,7 +12,11 @@ const logger = CreateLogCtx(__filename);
|
||||
* Deletes the provided score. This needs a dedicated helper method due to
|
||||
* needing to unset things like sessions and recalcs.
|
||||
*/
|
||||
export async function DeleteScore(score: ScoreDocument, blacklist = false) {
|
||||
export async function DeleteScore(
|
||||
score: ScoreDocument,
|
||||
blacklist = false,
|
||||
attemptPBReprocess = true
|
||||
) {
|
||||
await db.scores.remove({
|
||||
scoreID: score.scoreID,
|
||||
});
|
||||
@@ -79,7 +83,7 @@ export async function DeleteScore(score: ScoreDocument, blacklist = false) {
|
||||
chartID: score.chartID,
|
||||
});
|
||||
|
||||
if (userHasOtherScores) {
|
||||
if (userHasOtherScores && attemptPBReprocess) {
|
||||
await ProcessPBs(score.userID, new Set([score.chartID]), logger);
|
||||
} else {
|
||||
await db["personal-bests"].remove({
|
||||
|
||||
@@ -14,7 +14,11 @@ import { FormatUserDoc, GetUserWithID } from "utils/user";
|
||||
*
|
||||
* @note You don't need to recalc the scoreID for newScore, it's done for you.
|
||||
*/
|
||||
export default async function UpdateScore(oldScore: ScoreDocument, newScore: ScoreDocument) {
|
||||
export default async function UpdateScore(
|
||||
oldScore: ScoreDocument,
|
||||
newScore: ScoreDocument,
|
||||
updateOldChart = true
|
||||
) {
|
||||
const userID = oldScore.userID;
|
||||
const user = await GetUserWithID(userID);
|
||||
|
||||
@@ -141,12 +145,15 @@ export default async function UpdateScore(oldScore: ScoreDocument, newScore: Sco
|
||||
await UpdateAllPBs([userID], {
|
||||
chartID: newScore.chartID,
|
||||
});
|
||||
await UpdateAllPBs([userID], {
|
||||
chartID: oldScore.chartID,
|
||||
});
|
||||
|
||||
await UpdateChartRanking(newScore.chartID);
|
||||
await UpdateChartRanking(oldScore.chartID);
|
||||
|
||||
if (updateOldChart) {
|
||||
await UpdateAllPBs([userID], {
|
||||
chartID: oldScore.chartID,
|
||||
});
|
||||
await UpdateChartRanking(oldScore.chartID);
|
||||
}
|
||||
|
||||
const imports = await db.imports.find({
|
||||
scoreIDs: oldScoreID,
|
||||
|
||||
@@ -109,5 +109,5 @@ export async function UpdateAllPBs(userIDs?: integer[], filter = {}) {
|
||||
await ProcessPBs(user.id, new Set(scores.map((e) => e.chartID)), logger);
|
||||
}
|
||||
|
||||
logger.info(`Done!`);
|
||||
logger.verbose(`Done!`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user