Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d8d5845c9 | ||
|
|
143f534ba1 | ||
|
|
4db3d94ee8 | ||
|
|
e195cf77cd | ||
|
|
ac9622f1bd | ||
|
|
e8535b1a80 | ||
|
|
d81b763d9b | ||
|
|
1c5726be4c | ||
|
|
6da5abcefc | ||
|
|
c5e39f104f | ||
|
|
60d2f7cfd3 | ||
|
|
0ac8b44ce0 | ||
|
|
59fec74d08 | ||
|
|
52f142a2b9 | ||
|
|
3a7132d276 | ||
|
|
4f89894fd1 |
Generated
+10
-41
@@ -666,12 +666,6 @@
|
||||
"normalize-path": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"arg": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz",
|
||||
"integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==",
|
||||
"dev": true
|
||||
},
|
||||
"arr-diff": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
|
||||
@@ -1196,9 +1190,9 @@
|
||||
}
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
|
||||
"integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
@@ -1481,12 +1475,6 @@
|
||||
"integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
|
||||
"dev": true
|
||||
},
|
||||
"diff": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
|
||||
"integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"diff-sequences": {
|
||||
"version": "24.9.0",
|
||||
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz",
|
||||
@@ -2626,9 +2614,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.3.tgz",
|
||||
"integrity": "sha512-B0W4A2U1ww3q7VVthTKfh+epHx+q4mCt6iK+zEAzbMBpWQAwxCeKxEGpj/1oQTpzPXDNSOG7hmG14TsISH50yw==",
|
||||
"version": "4.5.3",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
|
||||
"integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"neo-async": "^2.6.0",
|
||||
@@ -5348,19 +5336,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ts-node": {
|
||||
"version": "8.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.4.1.tgz",
|
||||
"integrity": "sha512-5LpRN+mTiCs7lI5EtbXmF/HfMeCjzt7DH9CZwtkr6SywStrNQC723wG+aOWFiLNn7zT3kD/RnFqi3ZUfr4l5Qw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"arg": "^4.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"source-map-support": "^0.5.6",
|
||||
"yn": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
@@ -5401,13 +5376,13 @@
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.1.tgz",
|
||||
"integrity": "sha512-+dSJLJpXBb6oMHP+Yvw8hUgElz4gLTh82XuX68QiJVTXaE5ibl6buzhNkQdYhBlIhozWOC9ge16wyRmjG4TwVQ==",
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.2.tgz",
|
||||
"integrity": "sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"commander": "2.20.0",
|
||||
"commander": "~2.20.3",
|
||||
"source-map": "~0.6.1"
|
||||
}
|
||||
},
|
||||
@@ -5707,12 +5682,6 @@
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
"jest-resolve": "^24.5.0",
|
||||
"prettier": "^1.16.4",
|
||||
"ts-jest": "^24.0.0",
|
||||
"ts-node": "^8.0.3",
|
||||
"typescript": "^3.6.4",
|
||||
"utility-types": "^3.6.1"
|
||||
},
|
||||
|
||||
@@ -82,6 +82,29 @@ create table "cm_user_activity" (
|
||||
)
|
||||
);
|
||||
|
||||
create table "cm_user_course" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"course_id" integer not null,
|
||||
"class_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"score_rank" integer not null,
|
||||
"event_id" integer not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
"is_clear" text not null,
|
||||
constraint "cm_user_course_uq" unique ("profile_id", "course_id")
|
||||
);
|
||||
|
||||
create table "cm_user_data_ex" (
|
||||
"id" integer primary key not null
|
||||
references "cm_user_data"("id")
|
||||
|
||||
@@ -98,6 +98,7 @@ create table "idz_settings" (
|
||||
on delete cascade,
|
||||
"music" integer not null,
|
||||
"pack" integer not null,
|
||||
"aura" integer not null,
|
||||
"paper_cup" integer not null, -- Not a boolean, oddly enough
|
||||
"gauges" integer not null
|
||||
);
|
||||
@@ -192,6 +193,7 @@ create table "idz_unlocks" (
|
||||
"id" integer primary key not null
|
||||
references "idz_profile"("id")
|
||||
on delete cascade,
|
||||
"auras" integer not null,
|
||||
"cup" integer not null,
|
||||
"gauges" integer not null,
|
||||
"music" integer not null,
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
-- This adds the required columns to save player aura settings and unlocks
|
||||
-- Default values are set according to what the game expects
|
||||
|
||||
alter table "idz_settings" add column "aura" integer NOT NULL DEFAULT '0';
|
||||
alter table "idz_unlocks" add column "auras" integer NOT NULL DEFAULT '1';
|
||||
@@ -0,0 +1,16 @@
|
||||
create table "cm_user_duel_list" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"duel_id" integer not null,
|
||||
"progress" integer not null,
|
||||
"point" integer not null,
|
||||
"is_clear" boolean not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
constraint "cm_user_duel_list_uq" unique ("profile_id", "duel_id")
|
||||
);
|
||||
@@ -0,0 +1,22 @@
|
||||
create table "cm_user_course" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"course_id" integer not null,
|
||||
"class_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"score_rank" integer not null,
|
||||
"event_id" integer not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
"is_clear" text not null,
|
||||
constraint "cm_user_course_uq" unique ("profile_id", "course_id")
|
||||
);
|
||||
@@ -0,0 +1,23 @@
|
||||
-- This was not present in new DBs initialized to schema version 11.
|
||||
create table if not exists "cm_user_course" (
|
||||
"id" integer primary key not null,
|
||||
"profile_id" integer not null
|
||||
references "cm_user_data"("id")
|
||||
on delete cascade,
|
||||
"course_id" integer not null,
|
||||
"class_id" integer not null,
|
||||
"play_count" integer not null,
|
||||
"score_max" integer not null,
|
||||
"is_full_combo" text not null,
|
||||
"is_all_justice" text not null,
|
||||
"is_success" text not null,
|
||||
"score_rank" integer not null,
|
||||
"event_id" integer not null,
|
||||
"last_play_date" text not null,
|
||||
"param1" integer not null,
|
||||
"param2" integer not null,
|
||||
"param3" integer not null,
|
||||
"param4" integer not null,
|
||||
"is_clear" text not null,
|
||||
constraint "cm_user_course_uq" unique ("profile_id", "course_id")
|
||||
);
|
||||
@@ -1,17 +1,28 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserCourseRequest } from "../request/getUserCourse";
|
||||
import { GetUserCourseResponse } from "../response/getUserCourse";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { paginationCookie } from "./_util";
|
||||
import { writeUserCourse } from "../proto/userCourse";
|
||||
|
||||
export default async function getUserCourse(
|
||||
rep: Repositories,
|
||||
req: GetUserCourseRequest
|
||||
): Promise<GetUserCourseResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
const maxCount = parseInt(req.maxCount);
|
||||
const nextIndex = parseInt(req.nextIndex);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep
|
||||
.userCourse()
|
||||
.load(profileId, { limit: maxCount, offset: nextIndex });
|
||||
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
nextIndex: "-1",
|
||||
userCourseList: [
|
||||
// This will get saved at some point I think
|
||||
],
|
||||
length: items.length.toString(),
|
||||
nextIndex: paginationCookie(items, { maxCount, nextIndex }),
|
||||
userCourseList: items.map(writeUserCourse)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserDuelRequest } from "../request/getUserDuel";
|
||||
import { GetUserDuelResponse } from "../response/getUserDuel";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserDuelList } from "../proto/userDuelList";
|
||||
|
||||
export default async function getUserDuel(
|
||||
rep: Repositories,
|
||||
req: GetUserDuelRequest
|
||||
): Promise<GetUserDuelResponse> {
|
||||
const aimeId = readAimeId(req.userId);
|
||||
// Get all duel entities if the client specifies to.
|
||||
const duelId = req.isAllDuel ? undefined : parseInt(req.duelId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
const items = await rep.userDuelList().load(profileId, duelId);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
userDuelList: [],
|
||||
length: items.length.toString(),
|
||||
userDuelList: items.map(writeUserDuelList),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
import { Repositories } from "../repo";
|
||||
import { GetUserRecentRatingRequest } from "../request/getUserRecentRating";
|
||||
import { GetUserRecentRatingResponse } from "../response/getUserRecentRating";
|
||||
import { readAimeId } from "../proto/base";
|
||||
import { writeUserRecentRatingFromLog } from "../proto/userRecentRating";
|
||||
|
||||
export default async function getUserRecentRating(
|
||||
rep: Repositories,
|
||||
req: GetUserRecentRatingRequest
|
||||
): Promise<GetUserRecentRatingResponse> {
|
||||
// I don't really know what subset of data to return here.
|
||||
const aimeId = readAimeId(req.userId);
|
||||
|
||||
const profileId = await rep.userData().lookup(aimeId);
|
||||
// Return recent 30 plays to calculate rating
|
||||
const items = await rep.userPlaylog().loadLatest(profileId, 30);
|
||||
|
||||
return {
|
||||
userId: req.userId,
|
||||
length: "0",
|
||||
userRecentRatingList: [],
|
||||
length: items.length.toString(),
|
||||
userRecentRatingList: items.map(writeUserRecentRatingFromLog),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ export default function chunithm(db: DataSource) {
|
||||
|
||||
app.use(quirks);
|
||||
app.use(compression({ threshold: 0 }));
|
||||
app.use(express.json());
|
||||
app.use(express.json({ limit: "50mb" })); // that ought to be enough
|
||||
app.use(trace);
|
||||
|
||||
app.use("/ChuniServlet", wrapper);
|
||||
|
||||
@@ -11,7 +11,9 @@ import { readUserItem } from "../proto/userItem";
|
||||
import { readUserMusicDetail } from "../proto/userMusic";
|
||||
import { readUserActivity } from "../proto/userActivity";
|
||||
import { readUserDataEx } from "../proto/userDataEx";
|
||||
import { readUserDuelList } from "../proto/userDuelList";
|
||||
import { readUserPlaylog } from "../proto/userPlaylog";
|
||||
import { readUserCourse } from "../proto/userCourse";
|
||||
|
||||
// It shouldn't need to be said really, but seeing as this message (A) requires
|
||||
// fairly lengthy processing and (B) is only sent right at the end of a credit,
|
||||
@@ -65,9 +67,17 @@ export default async function upsertUserAll(
|
||||
await rep.userPlaylog().save(profileId, readUserPlaylog(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userCourseList || []) {
|
||||
await rep.userCourse().save(profileId, readUserCourse(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userDataEx || []) {
|
||||
await rep.userDataEx().save(profileId, readUserDataEx(item));
|
||||
}
|
||||
|
||||
for (const item of payload.userDuelList || []) {
|
||||
await rep.userDuelList().save(profileId, readUserDuelList(item));
|
||||
}
|
||||
|
||||
return { returnCode: "1" };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
export interface UserCourseItem {
|
||||
courseId: number;
|
||||
classId: number;
|
||||
playCount: number;
|
||||
scoreMax: number;
|
||||
isFullCombo: boolean;
|
||||
isAllJustice: boolean;
|
||||
isSuccess: boolean;
|
||||
scoreRank: number;
|
||||
eventId: number;
|
||||
lastPlayDate: Date;
|
||||
param1: number;
|
||||
param2: number;
|
||||
param3: number;
|
||||
param4: number;
|
||||
isClear: boolean;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface UserDuelListItem {
|
||||
duelId: number;
|
||||
progress: number;
|
||||
point: number;
|
||||
isClear: boolean;
|
||||
lastPlayDate: Date;
|
||||
param1: number;
|
||||
param2: number;
|
||||
param3: number;
|
||||
param4: number;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { UserCourseItem } from "../model/userCourse";
|
||||
import { Crush, readBoolean, readDate, writeObject } from "./base";
|
||||
|
||||
export type UserCourseJson = Crush<UserCourseItem>;
|
||||
|
||||
export function readUserCourse(json: UserCourseJson): UserCourseItem {
|
||||
return {
|
||||
classId: parseInt(json.classId),
|
||||
courseId: parseInt(json.courseId),
|
||||
eventId: parseInt(json.eventId),
|
||||
isAllJustice: readBoolean(json.isAllJustice),
|
||||
isClear: readBoolean(json.isClear),
|
||||
isFullCombo: readBoolean(json.isFullCombo),
|
||||
isSuccess: readBoolean(json.isSuccess),
|
||||
lastPlayDate: readDate(json.lastPlayDate),
|
||||
param1: parseInt(json.param1),
|
||||
param2: parseInt(json.param2),
|
||||
param3: parseInt(json.param3),
|
||||
param4: parseInt(json.param4),
|
||||
playCount: parseInt(json.playCount),
|
||||
scoreMax: parseInt(json.scoreMax),
|
||||
scoreRank: parseInt(json.scoreRank)
|
||||
}
|
||||
}
|
||||
|
||||
export function writeUserCourse(obj: UserCourseItem): UserCourseJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Crush, readBoolean, readDate, writeObject } from "./base";
|
||||
import { UserDuelListItem } from "../model/userDuelList";
|
||||
|
||||
export type UserDuelListJson = Crush<UserDuelListItem>;
|
||||
|
||||
export function readUserDuelList(
|
||||
json: UserDuelListJson
|
||||
): UserDuelListItem {
|
||||
return {
|
||||
duelId: parseInt(json.duelId),
|
||||
progress: parseInt(json.progress),
|
||||
point: parseInt(json.point),
|
||||
isClear: readBoolean(json.isClear),
|
||||
lastPlayDate: readDate(json.lastPlayDate),
|
||||
param1: parseInt(json.param1),
|
||||
param2: parseInt(json.param2),
|
||||
param3: parseInt(json.param3),
|
||||
param4: parseInt(json.param4),
|
||||
};
|
||||
}
|
||||
|
||||
export function writeUserDuelList(
|
||||
obj: UserDuelListItem
|
||||
): UserDuelListJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UserPlaylogItem } from "./../model/userPlaylog";
|
||||
import { Crush, writeObject } from "./base";
|
||||
import { UserRecentRatingItem } from "../model/userRecentRating";
|
||||
|
||||
@@ -19,3 +20,15 @@ export function writeUserRecentRating(
|
||||
): UserRecentRatingJson {
|
||||
return writeObject(obj);
|
||||
}
|
||||
|
||||
export function writeUserRecentRatingFromLog(
|
||||
obj: UserPlaylogItem
|
||||
): UserRecentRatingJson {
|
||||
return {
|
||||
musicId: obj.musicId.toString(),
|
||||
difficultId: obj.level.toString(),
|
||||
// game version not saved in play log, just return a fixed version now
|
||||
romVersionCode: "1030000",
|
||||
score: obj.score.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { UserCourseRepository } from "./userCourse";
|
||||
|
||||
export { Page } from "./_defs";
|
||||
|
||||
import { UserActivityRepository } from "./userActivity";
|
||||
import { UserCharacterRepository } from "./userCharacter";
|
||||
import { UserDataRepository } from "./userData";
|
||||
import { UserDataExRepository } from "./userDataEx";
|
||||
import { UserDuelListRepository } from "./userDuelList";
|
||||
import { UserGameOptionRepository } from "./userGameOption";
|
||||
import { UserGameOptionExRepository } from "./userGameOptionEx";
|
||||
import { UserItemRepository } from "./userItem";
|
||||
@@ -20,6 +23,8 @@ export interface Repositories {
|
||||
|
||||
userDataEx(): UserDataExRepository;
|
||||
|
||||
userDuelList(): UserDuelListRepository;
|
||||
|
||||
userGameOption(): UserGameOptionRepository;
|
||||
|
||||
userGameOptionEx(): UserGameOptionExRepository;
|
||||
@@ -31,4 +36,6 @@ export interface Repositories {
|
||||
userMusic(): UserMusicRepository;
|
||||
|
||||
userPlaylog(): UserPlaylogRepository;
|
||||
|
||||
userCourse(): UserCourseRepository;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import { UserCourseItem } from "../model/userCourse";
|
||||
import { RepositoryN } from "./_defs";
|
||||
|
||||
export type UserCourseRepository = RepositoryN<UserCourseItem>;
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Page } from "./_defs";
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { UserDuelListItem } from "../model/userDuelList";
|
||||
import { Id } from "../../model";
|
||||
|
||||
export interface UserDuelListRepository {
|
||||
load(
|
||||
profileId: Id<UserDataItem>,
|
||||
duelId?: number,
|
||||
): Promise<UserDuelListItem[]>;
|
||||
|
||||
save(profileId: Id<UserDataItem>, obj: UserDuelListItem): Promise<void>;
|
||||
}
|
||||
@@ -7,4 +7,5 @@ export interface UserPlaylogRepository {
|
||||
// is nice to have.
|
||||
|
||||
save(profileId: Id<UserDataItem>, obj: UserPlaylogItem): Promise<void>;
|
||||
loadLatest(profileId: Id<UserDataItem>, size: number): Promise<UserPlaylogItem[]>;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ import { UserMusicDetailJson } from "../proto/userMusic";
|
||||
import { UserActivityJson } from "../proto/userActivity";
|
||||
import { UserRecentRatingJson } from "../proto/userRecentRating";
|
||||
import { UserPlaylogJson } from "../proto/userPlaylog";
|
||||
import { UserCourseJson } from "../proto/userCourse";
|
||||
import { UserDataExJson } from "../proto/userDataEx";
|
||||
import { UserDuelListJson } from "../proto/userDuelList";
|
||||
|
||||
export interface UpsertUserAllRequest {
|
||||
/** Integer, AiMe ID */
|
||||
@@ -25,7 +27,9 @@ export interface UpsertUserAllRequest {
|
||||
userActivityList?: UserActivityJson[];
|
||||
userRecentRatingList?: UserRecentRatingJson[];
|
||||
userPlaylogList?: UserPlaylogJson[];
|
||||
userCourseList?: UserCourseJson[];
|
||||
userDataEx?: UserDataExJson[];
|
||||
userDuelList?: UserDuelListJson[];
|
||||
|
||||
/** String of binary digits */
|
||||
isNewMapList: string;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { UserCourseJson } from "../proto/userCourse";
|
||||
|
||||
export interface GetUserCourseResponse {
|
||||
/** Integer, AiMe ID */
|
||||
userId: string;
|
||||
@@ -8,6 +10,5 @@ export interface GetUserCourseResponse {
|
||||
/** Integer, pagination cookie */
|
||||
nextIndex: string;
|
||||
|
||||
/** TBD */
|
||||
userCourseList: [];
|
||||
userCourseList: UserCourseJson[];
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { UserDuelListJson } from "../proto/userDuelList";
|
||||
|
||||
export interface GetUserDuelResponse {
|
||||
/** Integer, AiMe ID */
|
||||
userId: string;
|
||||
@@ -5,6 +7,9 @@ export interface GetUserDuelResponse {
|
||||
/** Integer, number of results returned */
|
||||
length: string;
|
||||
|
||||
/** TBD */
|
||||
userDuelList: [];
|
||||
/**
|
||||
* List of duel entities, either all duel entities associated with `userId`
|
||||
* or single requested by `duelId` in request.
|
||||
*/
|
||||
userDuelList: UserDuelListJson[];
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { SqlUserActivityRepository } from "./userActivity";
|
||||
import { SqlUserCharacterRepository } from "./userCharacter";
|
||||
import { SqlUserDataRepository } from "./userData";
|
||||
import { SqlUserDataExRepository } from "./userDataEx";
|
||||
import { SqlUserDuelListRepository } from "./userDuelList";
|
||||
import { SqlUserGameOptionRepository } from "./userGameOption";
|
||||
import { SqlUserGameOptionExRepository } from "./userGameOptionEx";
|
||||
import { SqlUserItemRepository } from "./userItem";
|
||||
@@ -13,6 +14,7 @@ import { UserActivityRepository } from "../repo/userActivity";
|
||||
import { UserCharacterRepository } from "../repo/userCharacter";
|
||||
import { UserDataRepository } from "../repo/userData";
|
||||
import { UserDataExRepository } from "../repo/userDataEx";
|
||||
import { UserDuelListRepository } from "../repo/userDuelList";
|
||||
import { UserGameOptionRepository } from "../repo/userGameOption";
|
||||
import { UserGameOptionExRepository } from "../repo/userGameOptionEx";
|
||||
import { UserItemRepository } from "../repo/userItem";
|
||||
@@ -20,6 +22,8 @@ import { UserMapRepository } from "../repo/userMap";
|
||||
import { UserMusicRepository } from "../repo/userMusic";
|
||||
import { UserPlaylogRepository } from "../repo/userPlaylog";
|
||||
import { Transaction } from "../../sql";
|
||||
import { UserCourseRepository } from "../repo/userCourse";
|
||||
import { SqlUserCourseRepository } from "./userCourse";
|
||||
|
||||
export class SqlRepositories implements Repositories {
|
||||
constructor(private readonly _txn: Transaction) {}
|
||||
@@ -40,6 +44,10 @@ export class SqlRepositories implements Repositories {
|
||||
return new SqlUserDataExRepository(this._txn);
|
||||
}
|
||||
|
||||
userDuelList(): UserDuelListRepository {
|
||||
return new SqlUserDuelListRepository(this._txn);
|
||||
}
|
||||
|
||||
userGameOption(): UserGameOptionRepository {
|
||||
return new SqlUserGameOptionRepository(this._txn);
|
||||
}
|
||||
@@ -63,4 +71,8 @@ export class SqlRepositories implements Repositories {
|
||||
userPlaylog(): UserPlaylogRepository {
|
||||
return new SqlUserPlaylogRepository(this._txn);
|
||||
}
|
||||
|
||||
userCourse(): UserCourseRepository {
|
||||
return new SqlUserCourseRepository(this._txn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import sql from "sql-bricks-postgres";
|
||||
import { createSqlMapper, T } from "../../sql/util";
|
||||
import { UserCourseRepository } from "../repo/userCourse";
|
||||
import { Id } from "../../model";
|
||||
import { Page } from "../repo";
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { UserCourseItem } from "../model/userCourse";
|
||||
import { Transaction } from "../../sql";
|
||||
|
||||
const { readRow, writeRow, colNames } = createSqlMapper({
|
||||
courseId: T.number,
|
||||
classId: T.number,
|
||||
playCount: T.number,
|
||||
scoreMax: T.number,
|
||||
isFullCombo: T.boolean,
|
||||
isAllJustice: T.boolean,
|
||||
isSuccess: T.boolean,
|
||||
scoreRank: T.number,
|
||||
eventId: T.number,
|
||||
lastPlayDate: T.Date,
|
||||
param1: T.number,
|
||||
param2: T.number,
|
||||
param3: T.number,
|
||||
param4: T.number,
|
||||
isClear: T.boolean
|
||||
});
|
||||
|
||||
export class SqlUserCourseRepository implements UserCourseRepository {
|
||||
constructor(private readonly _txn: Transaction) {}
|
||||
|
||||
async load(
|
||||
profileId: Id<UserDataItem>,
|
||||
page?: Page
|
||||
): Promise<UserCourseItem[]> {
|
||||
const stmt = sql
|
||||
.select("*")
|
||||
.from("cm_user_course")
|
||||
.where("profile_id", profileId);
|
||||
|
||||
if (page) {
|
||||
stmt.limit(page.limit).offset(page.offset);
|
||||
}
|
||||
|
||||
const rows = await this._txn.fetchRows(stmt);
|
||||
|
||||
return rows.map(readRow);
|
||||
}
|
||||
|
||||
save(profileId: Id<UserDataItem>, obj: UserCourseItem): Promise<void> {
|
||||
const stmt = sql
|
||||
.insert("cm_user_course", {
|
||||
id: this._txn.generateId(),
|
||||
profile_id: profileId,
|
||||
...writeRow(obj),
|
||||
})
|
||||
.onConflict("profile_id", "course_id")
|
||||
.doUpdate(colNames);
|
||||
|
||||
return this._txn.modify(stmt);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import sql from "sql-bricks-postgres";
|
||||
|
||||
import { UserDataItem } from "../model/userData";
|
||||
import { UserDuelListItem } from "../model/userDuelList";
|
||||
import { Page } from "../repo";
|
||||
import { UserDuelListRepository } from "../repo/userDuelList";
|
||||
import { Id } from "../../model";
|
||||
import { Transaction } from "../../sql";
|
||||
import { T, createSqlMapper } from "../../sql/util";
|
||||
|
||||
const { readRow, writeRow, colNames } = createSqlMapper({
|
||||
duelId: T.number,
|
||||
progress: T.number,
|
||||
point: T.number,
|
||||
isClear: T.boolean,
|
||||
lastPlayDate: T.Date,
|
||||
param1: T.number,
|
||||
param2: T.number,
|
||||
param3: T.number,
|
||||
param4: T.number,
|
||||
});
|
||||
|
||||
export class SqlUserDuelListRepository implements UserDuelListRepository {
|
||||
constructor(private readonly _txn: Transaction) {}
|
||||
|
||||
async load(
|
||||
profileId: Id<UserDataItem>,
|
||||
duelId?: number
|
||||
): Promise<UserDuelListItem[]> {
|
||||
const stmt = sql
|
||||
.select("*")
|
||||
.from("cm_user_duel_list")
|
||||
.where("profile_id", profileId);
|
||||
|
||||
if (duelId !== undefined) {
|
||||
stmt.where("duel_id", duelId);
|
||||
}
|
||||
|
||||
const rows = await this._txn.fetchRows(stmt);
|
||||
|
||||
return rows.map(readRow);
|
||||
}
|
||||
|
||||
save(profileId: Id<UserDataItem>, obj: UserDuelListItem): Promise<void> {
|
||||
const stmt = sql
|
||||
.insert("cm_user_duel_list", {
|
||||
id: this._txn.generateId(),
|
||||
profile_id: profileId,
|
||||
...writeRow(obj),
|
||||
})
|
||||
.onConflict("profile_id", "duel_id")
|
||||
.doUpdate(colNames);
|
||||
|
||||
return this._txn.modify(stmt);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { UserPlaylogRepository } from "../repo/userPlaylog";
|
||||
import { Transaction } from "../../sql";
|
||||
import { T, createSqlMapper } from "../../sql/util";
|
||||
|
||||
const { writeRow } = createSqlMapper({
|
||||
const { readRow, writeRow } = createSqlMapper({
|
||||
orderId: T.number,
|
||||
sortNumber: T.number,
|
||||
placeId: T.number,
|
||||
@@ -72,4 +72,16 @@ export class SqlUserPlaylogRepository implements UserPlaylogRepository {
|
||||
|
||||
return this._txn.modify(stmt);
|
||||
}
|
||||
|
||||
async loadLatest(profileId: Id<UserDataItem>, size: number): Promise<UserPlaylogItem[]> {
|
||||
const stmt = sql
|
||||
.select("music_id", "score", "level","user_play_date")
|
||||
.from("cm_user_playlog")
|
||||
.where("profile_id", profileId)
|
||||
.limit(size).order("user_play_date DESC");
|
||||
|
||||
const rows = await this._txn.fetchRows(stmt);
|
||||
|
||||
return rows.map(readRow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ export function saveProfile2(buf: Buffer): SaveProfileRequest2 {
|
||||
rows: storyRows,
|
||||
},
|
||||
unlocks: {
|
||||
auras: buf.readUInt16LE(0x010c),
|
||||
cup: buf.readUInt8(0x0110),
|
||||
gauges: buf.readUInt16LE(0x0114),
|
||||
music: buf.readUInt16LE(0x0140),
|
||||
@@ -90,6 +91,7 @@ export function saveProfile2(buf: Buffer): SaveProfileRequest2 {
|
||||
settings: {
|
||||
music: buf.readUInt16LE(0x045a),
|
||||
pack: buf.readUInt32LE(0x0034),
|
||||
aura: buf.readUInt8(0x002c),
|
||||
paperCup: buf.readUInt8(0x00f6),
|
||||
gauges: buf.readUInt8(0x00f7),
|
||||
},
|
||||
|
||||
@@ -69,6 +69,7 @@ export function saveProfile3(buf: Buffer): SaveProfileRequest2 {
|
||||
rows: storyRows,
|
||||
},
|
||||
unlocks: {
|
||||
auras: buf.readUInt16LE(0x010c),
|
||||
cup: buf.readUInt8(0x0110),
|
||||
gauges: buf.readUInt16LE(0x0114),
|
||||
music: buf.readUInt16LE(0x0140),
|
||||
@@ -92,6 +93,7 @@ export function saveProfile3(buf: Buffer): SaveProfileRequest2 {
|
||||
settings: {
|
||||
music: buf.readUInt16LE(0x04ee),
|
||||
pack: buf.readUInt32LE(0x0034),
|
||||
aura: buf.readUInt8(0x002c),
|
||||
paperCup: buf.readUInt8(0x00f6),
|
||||
gauges: buf.readUInt8(0x00f7),
|
||||
},
|
||||
|
||||
@@ -16,8 +16,8 @@ export function saveSettings(buf: Buffer): SaveSettingsRequest {
|
||||
pack: buf.readUInt32LE(0x000c),
|
||||
paperCup: buf.readUInt8(0x0011),
|
||||
gauges: buf.readUInt8(0x0012),
|
||||
aura: buf.readUInt8(0x0013),
|
||||
},
|
||||
field_0010: buf.readUInt8(0x0010),
|
||||
field_0013: buf.readUInt8(0x0013),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ export function loadProfile2(res: LoadProfileResponse2) {
|
||||
}
|
||||
|
||||
buf.writeUInt16LE(0x0065, 0x0000);
|
||||
buf.writeUInt16LE(res.unlocks.auras, 0x00b0);
|
||||
buf.writeUInt8(res.unlocks.cup, 0x00b4);
|
||||
buf.writeUInt16LE(res.unlocks.gauges, 0x00b8);
|
||||
buf.writeUInt32LE(res.unlocks.lastMileageReward, 0x01e8);
|
||||
@@ -92,6 +93,7 @@ export function loadProfile2(res: LoadProfileResponse2) {
|
||||
encodeMission(res.missions.solo).copy(buf, 0x06e4);
|
||||
encodeChara(res.chara).copy(buf, 0x070c);
|
||||
encodeBitmap(res.titles, 0xb4).copy(buf, 0x720);
|
||||
buf.writeUInt8(res.settings.aura, 0x07d6);
|
||||
buf.writeUInt8(res.settings.paperCup, 0x07d9);
|
||||
buf.writeUInt8(res.settings.gauges, 0x07da);
|
||||
buf.writeUInt32LE(res.teamId || 0xffffffff, 0x07e0);
|
||||
|
||||
@@ -71,6 +71,7 @@ export function loadProfile3(res: LoadProfileResponse3) {
|
||||
}
|
||||
|
||||
buf.writeUInt16LE(0x012e, 0x0000);
|
||||
buf.writeUInt16LE(res.unlocks.auras, 0x00b0);
|
||||
buf.writeUInt8(res.unlocks.cup, 0x00b4);
|
||||
buf.writeUInt16LE(res.unlocks.gauges, 0x00b8);
|
||||
buf.writeUInt32LE(res.unlocks.lastMileageReward, 0x0218);
|
||||
@@ -92,6 +93,7 @@ export function loadProfile3(res: LoadProfileResponse3) {
|
||||
encodeMission(res.missions.solo).copy(buf, 0x0858);
|
||||
encodeChara(res.chara).copy(buf, 0x0880);
|
||||
encodeBitmap(res.titles, 0xb4).copy(buf, 0x0894);
|
||||
buf.writeUInt8(res.settings.aura, 0x094a);
|
||||
buf.writeUInt8(res.settings.paperCup, 0x094d);
|
||||
buf.writeUInt8(res.settings.gauges, 0x094e);
|
||||
buf.writeUInt32LE(res.teamId || 0xffffffff, 0x0954);
|
||||
|
||||
@@ -50,13 +50,13 @@ export function loadTopTen(res: LoadTopTenResponse): Buffer {
|
||||
buf.writeUInt8(row.field_0F ? 1 : 0, innerOff + 0x000f); // Boolean
|
||||
buf.writeUInt8(row.field_10, innerOff + 0x0010);
|
||||
iconv.encode(row.driverName, "shift_jis").copy(buf, innerOff + 0x0014);
|
||||
iconv.encode(row.teamName, "shift_jis").copy(buf, innerOff + 0x0028);
|
||||
iconv.encode(row.team.name, "shift_jis").copy(buf, innerOff + 0x0028);
|
||||
iconv.encode(row.shopName, "shift_jis").copy(buf, innerOff + 0x0048);
|
||||
buf.writeUInt32LE(row.field_74, innerOff + 0x0074);
|
||||
buf.writeUInt16LE(row.field_78, innerOff + 0x0078);
|
||||
buf.writeUInt32LE(row.team.nameBg, innerOff + 0x0074);
|
||||
buf.writeUInt16LE(row.team.nameFx, innerOff + 0x0078);
|
||||
buf.writeUInt8(row.field_7C, innerOff + 0x007c);
|
||||
buf.writeUInt8(row.field_7D, innerOff + 0x007d);
|
||||
// 66 bytes of fucking nothing what
|
||||
// 66 bytes of empty space at the end. Maybe a string used to live here?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,10 @@ export async function createProfile(
|
||||
};
|
||||
|
||||
const missions: MissionState = { team: [], solo: [] };
|
||||
const settings: Settings = { music: 0, pack: 13640, paperCup: 0, gauges: 5 };
|
||||
const settings: Settings = { music: 0, pack: 13640, aura: 0, paperCup: 0, gauges: 5 };
|
||||
const story: Story = { x: 0, y: 0, rows: [] };
|
||||
const unlocks: Unlocks = {
|
||||
auras: 1,
|
||||
cup: 0,
|
||||
gauges: 1 << 5,
|
||||
music: 0,
|
||||
|
||||
@@ -18,7 +18,7 @@ export async function loadTopTen(
|
||||
}
|
||||
|
||||
const { routeNo, minTimestamp } = selector;
|
||||
const src = await w.timeAttack().loadTopTen(routeNo, minTimestamp);
|
||||
const src = await w.timeAttack().loadTop(routeNo, minTimestamp, 10);
|
||||
|
||||
if (src.length === 0) {
|
||||
continue;
|
||||
@@ -33,10 +33,8 @@ export async function loadTopTen(
|
||||
field_0F: false,
|
||||
field_10: 0,
|
||||
driverName: srcItem.driverName,
|
||||
teamName: process.env.TEAM_NAME || "",
|
||||
shopName: process.env.SHOP_NAME || "",
|
||||
field_74: 0,
|
||||
field_78: 0,
|
||||
team: srcItem.team,
|
||||
field_7C: 0,
|
||||
field_7D: 0,
|
||||
ta: srcItem.ta,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export interface Settings {
|
||||
music: number;
|
||||
pack: number;
|
||||
aura: number;
|
||||
paperCup: number;
|
||||
gauges: number;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export interface Unlocks {
|
||||
auras: number;
|
||||
cup: number;
|
||||
gauges: number;
|
||||
music: number;
|
||||
|
||||
+4
-2
@@ -111,13 +111,15 @@ export interface TeamReservationRepository {
|
||||
// TODO extend and factorize
|
||||
export interface TopTenResult {
|
||||
driverName: string;
|
||||
team: Model.Team;
|
||||
ta: Model.TimeAttackScore;
|
||||
}
|
||||
|
||||
export interface TimeAttackRepository {
|
||||
loadTopTen(
|
||||
loadTop(
|
||||
routeNo: Model.RouteNo,
|
||||
minTimestamp: Date
|
||||
minTimestamp: Date,
|
||||
limit: number
|
||||
): Promise<TopTenResult[]>;
|
||||
|
||||
loadAll(profileId: Id<Model.Profile>): Promise<Model.TimeAttackScore[]>;
|
||||
|
||||
@@ -7,5 +7,4 @@ export interface SaveSettingsRequest {
|
||||
dpoint: number; // ?? why
|
||||
settings: Settings;
|
||||
field_0010: number;
|
||||
field_0013: number;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@ export interface LoadTopTenResponseRow {
|
||||
field_0F: boolean;
|
||||
field_10: number;
|
||||
driverName: string;
|
||||
teamName: string;
|
||||
shopName: string;
|
||||
field_74: number;
|
||||
field_78: number;
|
||||
team: {
|
||||
name: string;
|
||||
nameBg: number;
|
||||
nameFx: number;
|
||||
};
|
||||
field_7C: number;
|
||||
field_7D: number;
|
||||
ta: TimeAttackScore;
|
||||
|
||||
@@ -24,6 +24,7 @@ export class SqlSettingsRepository implements FacetRepository<Settings> {
|
||||
return {
|
||||
music: parseInt(row.music),
|
||||
pack: parseInt(row.pack),
|
||||
aura: parseInt(row.aura),
|
||||
paperCup: parseInt(row.paper_cup),
|
||||
gauges: parseInt(row.gauges),
|
||||
};
|
||||
@@ -35,11 +36,12 @@ export class SqlSettingsRepository implements FacetRepository<Settings> {
|
||||
id: profileId,
|
||||
music: settings.music,
|
||||
pack: settings.pack,
|
||||
aura: settings.aura,
|
||||
paper_cup: settings.paperCup,
|
||||
gauges: settings.gauges,
|
||||
})
|
||||
.onConflict("id")
|
||||
.doUpdate(["music", "pack", "paper_cup", "gauges"]);
|
||||
.doUpdate(["music", "pack", "aura", "paper_cup", "gauges"]);
|
||||
|
||||
await this._txn.modify(saveSql);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ export class SqlTeamMemberRepository implements TeamMemberRepository {
|
||||
.where("id", teamId)
|
||||
.forUpdate();
|
||||
|
||||
await this._txn.modify(lockSql);
|
||||
await this._txn.fetchRow(lockSql);
|
||||
|
||||
// Double-check (with lock held) that there is room to join this team.
|
||||
// If this fails then the error will propagate to the client and it will
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import sql from "sql-bricks-postgres";
|
||||
|
||||
import { RouteNo } from "../model/base";
|
||||
import { ExtId, RouteNo } from "../model/base";
|
||||
import { CarSelector } from "../model/car";
|
||||
import { Team } from "../model/team";
|
||||
import { Profile } from "../model/profile";
|
||||
import { TimeAttackScore } from "../model/timeAttack";
|
||||
import { TimeAttackRepository, TopTenResult } from "../repo";
|
||||
@@ -23,23 +24,51 @@ function _extractRow(row: Row): TimeAttackScore {
|
||||
export class SqlTimeAttackRepository implements TimeAttackRepository {
|
||||
constructor(private readonly _txn: Transaction) {}
|
||||
|
||||
async loadTopTen(
|
||||
async loadTop(
|
||||
routeNo: RouteNo,
|
||||
minTimestamp: Date
|
||||
minTimestamp: Date,
|
||||
limit: number
|
||||
): Promise<TopTenResult[]> {
|
||||
// We're not using an ORM here so this join-heavy SQL is unfortunately
|
||||
// going to be a boilerplated mess.
|
||||
|
||||
const loadSql = sql
|
||||
.select("p.name", "ta.*")
|
||||
.select(
|
||||
// Profile
|
||||
"p.name as profile_name",
|
||||
// Team
|
||||
"t.ext_id as team_ext_id",
|
||||
"t.name as team_name",
|
||||
"t.name_bg as team_name_bg",
|
||||
"t.name_fx as team_name_fx",
|
||||
"t.register_time as team_register_time",
|
||||
// Time Attack
|
||||
"ta.*"
|
||||
)
|
||||
.from("idz_ta_best ta")
|
||||
.join("idz_profile p", { "ta.profile_id": "p.id" })
|
||||
// This is an inner join, so it will exclude anybody who is not currently
|
||||
// a member of a team from the leader boards. Since the only way to play
|
||||
// without a team is to be ejected from one we can consider this an edge
|
||||
// case that we probably don't need to worry about.
|
||||
.join("idz_team_member tm", { "ta.profile_id": "tm.id" })
|
||||
.join("idz_team t", { "tm.team_id": "t.id" })
|
||||
.where("ta.route_no", routeNo)
|
||||
.where(sql.gt("ta.timestamp", minTimestamp))
|
||||
.orderBy(["ta.total_time asc", "ta.timestamp asc"])
|
||||
.limit(10);
|
||||
.limit(limit);
|
||||
|
||||
const rows = await this._txn.fetchRows(loadSql);
|
||||
|
||||
return rows.map(row => ({
|
||||
driverName: row.name,
|
||||
driverName: row.profile_name,
|
||||
team: {
|
||||
extId: parseInt(row.team_ext_id) as ExtId<Team>,
|
||||
name: row.team_name,
|
||||
nameBg: parseInt(row.team_name_bg),
|
||||
nameFx: parseInt(row.team_name_fx),
|
||||
registerTime: new Date(row.team_register_time),
|
||||
},
|
||||
ta: _extractRow(row),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ export class SqlUnlocksRepository implements FacetRepository<Unlocks> {
|
||||
}
|
||||
|
||||
return {
|
||||
auras: parseInt(row.auras),
|
||||
cup: parseInt(row.cup),
|
||||
gauges: parseInt(row.gauges),
|
||||
music: parseInt(row.music),
|
||||
@@ -33,13 +34,14 @@ export class SqlUnlocksRepository implements FacetRepository<Unlocks> {
|
||||
const saveSql = sql
|
||||
.insert("idz_unlocks", {
|
||||
id: profileId,
|
||||
auras: unlocks.auras,
|
||||
cup: unlocks.cup,
|
||||
gauges: unlocks.gauges,
|
||||
music: unlocks.music,
|
||||
last_mileage_reward: unlocks.lastMileageReward,
|
||||
})
|
||||
.onConflict("id")
|
||||
.doUpdate(["cup", "gauges", "music", "last_mileage_reward"]);
|
||||
.doUpdate(["auras", "cup", "gauges", "music", "last_mileage_reward"]);
|
||||
|
||||
await this._txn.modify(saveSql);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
@echo off
|
||||
|
||||
pushd %~dp0
|
||||
|
||||
where /q node
|
||||
if errorlevel 1 (
|
||||
echo NodeJS does not appear to be installed.
|
||||
@@ -40,8 +42,10 @@ if not exist node_modules (
|
||||
echo.
|
||||
)
|
||||
|
||||
rem Project Diva requires TLSv1.0
|
||||
|
||||
set DEBUG=app:*
|
||||
node .\bin\index.js
|
||||
node --tls-min-v1.0 .\bin\index.js
|
||||
|
||||
:end
|
||||
echo.
|
||||
|
||||
Reference in New Issue
Block a user