6 Commits
Author SHA1 Message Date
Tau 0d8d5845c9 schema: Fix M0011 for newly-created databases 2019-12-09 23:06:20 -05:00
Tau 143f534ba1 npm audit 2019-12-09 22:55:25 -05:00
Tau 4db3d94ee8 idz: Un-hardcode result limit in repo layer
Minor design nit but I should probably take my own code review
advice.
2019-11-30 14:13:49 -05:00
Tau e195cf77cd idz: Wire up TA topTen team results 2019-11-30 14:05:36 -05:00
Tau ac9622f1bd idz: Extend Time Attack topTen result set 2019-11-30 14:05:14 -05:00
Tau e8535b1a80 idz: Label additional team info in topTen response 2019-11-30 13:35:37 -05:00
8 changed files with 102 additions and 25 deletions
+6 -6
View File
@@ -2614,9 +2614,9 @@
"dev": true
},
"handlebars": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz",
"integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==",
"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",
@@ -5376,9 +5376,9 @@
"dev": true
},
"uglify-js": {
"version": "3.6.8",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.8.tgz",
"integrity": "sha512-XhHJ3S3ZyMwP8kY1Gkugqx3CJh2C3O0y8NPiSxtm1tyD/pktLAkFZsFGpuNfTZddKDQ/bbDBLAd2YyA1pbi8HQ==",
"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": {
+23
View File
@@ -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")
@@ -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")
);
+4 -4
View File
@@ -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?
}
}
+2 -4
View File
@@ -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,
+4 -2
View File
@@ -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[]>;
+5 -3
View File
@@ -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;
+35 -6
View File
@@ -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),
}));
}