From a5827da5906313ea8843cf5b533c0852eb2da512 Mon Sep 17 00:00:00 2001 From: Tau Date: Mon, 29 Apr 2019 11:58:28 -0400 Subject: [PATCH] wip create working(?) --- src/idz/db/_util.ts | 6 +-- src/idz/db/backgrounds.ts | 4 +- src/idz/db/car.ts | 14 ++--- src/idz/db/chara.ts | 10 ++-- src/idz/db/coursePlays.ts | 6 +-- src/idz/db/index.ts | 88 +++++++++++++++++++++++++++++-- src/idz/db/missions.ts | 6 +-- src/idz/db/profile.ts | 87 ++++++++++++++++++------------ src/idz/db/settings.ts | 6 +-- src/idz/db/story.ts | 6 +-- src/idz/db/tickets.ts | 6 +-- src/idz/db/timeAttack.ts | 6 +-- src/idz/db/titles.ts | 4 +- src/idz/db/unlocks.ts | 6 +-- src/idz/decoder/createProfile.ts | 7 +-- src/idz/handler/createProfile.ts | 3 +- src/idz/handler/saveProfile.ts | 20 ++++--- src/idz/handler/saveTimeAttack.ts | 7 ++- src/idz/index.ts | 7 ++- src/idz/repo.ts | 8 ++- src/idz/request/createProfile.ts | 3 +- 21 files changed, 214 insertions(+), 96 deletions(-) diff --git a/src/idz/db/_util.ts b/src/idz/db/_util.ts index 6be4494..e458142 100644 --- a/src/idz/db/_util.ts +++ b/src/idz/db/_util.ts @@ -1,14 +1,14 @@ import * as sql from "sql-bricks"; -import { Client } from "pg"; +import { ClientBase } from "pg"; import { ExtId } from "../model/base"; import { Profile } from "../model/profile"; import { Id } from "../../db"; export async function _findProfile( - conn: Client, + conn: ClientBase, extId: ExtId -): Promise | undefined> { +): Promise> { const lookupSql = sql .select("r.id") .from("idz.profile r") diff --git a/src/idz/db/backgrounds.ts b/src/idz/db/backgrounds.ts index 9f055d0..2349cdb 100644 --- a/src/idz/db/backgrounds.ts +++ b/src/idz/db/backgrounds.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks"; import { _findProfile } from "./_util"; @@ -9,7 +9,7 @@ import { generateId } from "../../db"; export class SqlBackgroundsRepository implements FlagRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} async loadAll(extId: ExtId): Promise> { const loadSql = sql diff --git a/src/idz/db/car.ts b/src/idz/db/car.ts index 276d7d7..90fe611 100644 --- a/src/idz/db/car.ts +++ b/src/idz/db/car.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks-postgres"; import { _findProfile } from "./_util"; @@ -29,7 +29,7 @@ function _extractRow(row: any): Car { } export class SqlCarRepository implements CarRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} async countCars(extId: ExtId): Promise { const countSql = sql @@ -94,7 +94,7 @@ export class SqlCarRepository implements CarRepository { field_5E: car.field_5E, }) .onConflict("profile_id", "selector") - .doUpdate( + .doUpdate([ "field_00", "field_02", "field_04", @@ -108,8 +108,8 @@ export class SqlCarRepository implements CarRepository { "field_5A", "field_5B", "field_5C", - "field_5E" - ) + "field_5E", + ]) .toParams(); await this._conn.query(saveSql); @@ -136,12 +136,12 @@ export class SqlCarRepository implements CarRepository { } const saveSql = sql - .insert("idz.car", { + .insert("idz.car_selection", { id: profileId, car_id: row.id, }) .onConflict("id") - .doUpdate("car_id") + .doUpdate(["car_id"]) .toParams(); await this._conn.query(saveSql); diff --git a/src/idz/db/chara.ts b/src/idz/db/chara.ts index ab79608..e1e3627 100644 --- a/src/idz/db/chara.ts +++ b/src/idz/db/chara.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks-postgres"; import { _findProfile } from "./_util"; @@ -8,7 +8,7 @@ import { Profile } from "../model/profile"; import { FacetRepository } from "../repo"; export class SqlCharaRepository implements FacetRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} async load(extId: ExtId): Promise { const loadSql = sql @@ -53,15 +53,15 @@ export class SqlCharaRepository implements FacetRepository { background: chara.background, }) .onConflict("id") - .doUpdate( + .doUpdate([ "field_02", "field_04", "field_06", "field_08", "field_0A", "field_0C", - "field_0E" - ) + "field_0E", + ]) .toParams(); await this._conn.query(saveSql); diff --git a/src/idz/db/coursePlays.ts b/src/idz/db/coursePlays.ts index 135ff54..1363ece 100644 --- a/src/idz/db/coursePlays.ts +++ b/src/idz/db/coursePlays.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks-postgres"; import { _findProfile } from "./_util"; @@ -8,7 +8,7 @@ import { CoursePlaysRepository } from "../repo"; import { generateId } from "../../db"; export class SqlCoursePlaysRepository implements CoursePlaysRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} async loadAll(extId: ExtId): Promise> { const loadSql = sql @@ -43,7 +43,7 @@ export class SqlCoursePlaysRepository implements CoursePlaysRepository { count: v, }) .onConflict("profile_id", "course_no") - .doUpdate("count"); + .doUpdate(["count"]); await this._conn.query(saveSql); } diff --git a/src/idz/db/index.ts b/src/idz/db/index.ts index e95578f..1fc27b3 100644 --- a/src/idz/db/index.ts +++ b/src/idz/db/index.ts @@ -1,5 +1,87 @@ -import { Transaction } from "../repo"; +import { PoolClient } from "pg"; -export async function beginDbSession(): Promise { - throw new Error("WIP"); +import { SqlBackgroundsRepository } from "./backgrounds"; +import { SqlCarRepository } from "./car"; +import { SqlCharaRepository } from "./chara"; +import { SqlCoursePlaysRepository } from "./coursePlays"; +import { SqlMissionsRepository } from "./missions"; +import { SqlProfileRepository } from "./profile"; +import { SqlSettingsRepository } from "./settings"; +import { SqlStoryRepository } from "./story"; +import { SqlTicketsRepository } from "./tickets"; +import { SqlTimeAttackRepository } from "./timeAttack"; +import { SqlTitlesRepository } from "./titles"; +import { SqlUnlocksRepository } from "./unlocks"; +import * as Model from "../model"; +import * as Repo from "../repo"; +import { connect } from "../../db"; + +class TransactionImpl implements Repo.Transaction { + constructor(private readonly _conn: PoolClient) {} + + backgrounds(): Repo.FlagRepository { + return new SqlBackgroundsRepository(this._conn); + } + + car(): Repo.CarRepository { + return new SqlCarRepository(this._conn); + } + + chara(): Repo.FacetRepository { + return new SqlCharaRepository(this._conn); + } + + coursePlays(): Repo.CoursePlaysRepository { + return new SqlCoursePlaysRepository(this._conn); + } + + missions(): Repo.FacetRepository { + return new SqlMissionsRepository(this._conn); + } + + profile(): Repo.ProfileRepository { + return new SqlProfileRepository(this._conn); + } + + settings(): Repo.FacetRepository { + return new SqlSettingsRepository(this._conn); + } + + story(): Repo.FacetRepository { + return new SqlStoryRepository(this._conn); + } + + tickets(): Repo.FacetRepository { + return new SqlTicketsRepository(this._conn); + } + + timeAttack(): Repo.TimeAttackRepository { + return new SqlTimeAttackRepository(this._conn); + } + + titles(): Repo.FlagRepository { + return new SqlTitlesRepository(this._conn); + } + + unlocks(): Repo.FacetRepository { + return new SqlUnlocksRepository(this._conn); + } + + async commit(): Promise { + await this._conn.query("commit"); + await this._conn.release(); + } + + async rollback(): Promise { + await this._conn.query("rollback"); + await this._conn.release(); + } +} + +export async function beginDbSession(): Promise { + const conn = await connect(); + + await conn.query("begin"); + + return new TransactionImpl(conn); } diff --git a/src/idz/db/missions.ts b/src/idz/db/missions.ts index 3d628b1..675980f 100644 --- a/src/idz/db/missions.ts +++ b/src/idz/db/missions.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks-postgres"; import { _findProfile } from "./_util"; @@ -9,7 +9,7 @@ import { FacetRepository } from "../repo"; import { generateId, Id } from "../../db"; export class SqlMissionsRepository implements FacetRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} private async _load( extId: ExtId @@ -73,7 +73,7 @@ export class SqlMissionsRepository implements FacetRepository { value: grid[j], }) .onConflict("profile_id", "grid_no", "cell_no") - .doUpdate("value") + .doUpdate(["value"]) .toParams(); await this._conn.query(saveSql); diff --git a/src/idz/db/profile.ts b/src/idz/db/profile.ts index 0babcd8..6be43fa 100644 --- a/src/idz/db/profile.ts +++ b/src/idz/db/profile.ts @@ -1,5 +1,5 @@ import * as sql from "sql-bricks"; -import { Client } from "pg"; +import { ClientBase } from "pg"; import { _findProfile } from "./_util"; import { ExtId } from "../model/base"; @@ -23,30 +23,42 @@ function _extractRow(row: any): Profile { } export class SqlProfileRepository implements ProfileRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} + + private async _tryLoadByAimeId( + aimeId: AimeId + ): Promise { + const lookupSql = sql + .select("p.id") + .from("idz.profile p") + .join("aime.player r", { "p.player_id": "r.id" }) + .where("r.ext_id", aimeId) + .toParams(); + + const { rows } = await this._conn.query(lookupSql); + const row = rows[0]; + + if (row === undefined) { + return undefined; + } else { + return _extractRow(row); + } + } async discoverByAimeId(aimeId: AimeId): Promise { - const profileId = await this._findProfile(aimeId); + const result = await this._tryLoadByAimeId(aimeId); - return profileId !== undefined; + return result !== undefined; } async loadByAimeId(aimeId: AimeId): Promise { - const profileId = await this._findProfile(aimeId); + const result = await this._tryLoadByAimeId(aimeId); - if (profileId === undefined) { - throw new Error("Profile not found"); + if (result === undefined) { + throw new Error("Profile not found for Aime ID"); } - const loadSql = sql - .select() - .from("idz.profile") - .where("id", profileId) - .toParams(); - - const { rows } = await this._conn.query(loadSql); - - return _extractRow(rows[0]); + return result; } async load(extId: ExtId): Promise { @@ -61,7 +73,7 @@ export class SqlProfileRepository implements ProfileRepository { return _extractRow(rows[0]); } - async save(profile: Profile): Promise { + async save(profile: Profile, timestamp: Date): Promise { const saveSql = sql .update("idz.profile", { lv: profile.lv, @@ -69,6 +81,7 @@ export class SqlProfileRepository implements ProfileRepository { fame: profile.fame, dpoint: profile.dpoint, mileage: profile.mileage, + access_time: timestamp, }) .where("ext_id", profile.id) .toParams(); @@ -76,13 +89,32 @@ export class SqlProfileRepository implements ProfileRepository { await this._conn.query(saveSql); } - async create(profile: ProfileSpec): Promise> { + async create( + aimeId: AimeId, + profile: ProfileSpec, + timestamp: Date + ): Promise> { + const findSql = sql + .select("r.id") + .from("aime.player r") + .where("r.ext_id", aimeId) + .toParams(); + + const { rows } = await this._conn.query(findSql); + const row = rows[0]; + + if (row === undefined) { + throw new Error("Aime ID not found"); + } + const id = generateId(); const extId = generateExtId() as ExtId; + const playerId = row.id; const createSql = sql .insert("idz.profile", { id: id, + player_id: playerId, ext_id: extId, name: profile.name, lv: profile.lv, @@ -90,6 +122,8 @@ export class SqlProfileRepository implements ProfileRepository { fame: profile.fame, dpoint: profile.dpoint, mileage: profile.mileage, + register_time: timestamp, + access_time: timestamp, }) .toParams(); @@ -97,21 +131,4 @@ export class SqlProfileRepository implements ProfileRepository { return extId; } - - async _findProfile(aimeId: AimeId): Promise | undefined> { - const lookupSql = sql - .select("r.id") - .from("idz.profile r") - .join("aime.player p", { "r.player_id": "p.id" }) - .where("p.aime_id", aimeId) - .toParams(); - - const { rows } = await this._conn.query(lookupSql); - - if (rows.length > 0) { - return rows[0].id as Id; - } else { - return undefined; - } - } } diff --git a/src/idz/db/settings.ts b/src/idz/db/settings.ts index 3634e57..edc9b60 100644 --- a/src/idz/db/settings.ts +++ b/src/idz/db/settings.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks-postgres"; import { _findProfile } from "./_util"; @@ -8,7 +8,7 @@ import { Profile } from "../model/profile"; import { FacetRepository } from "../repo"; export class SqlSettingsRepository implements FacetRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} async load(extId: ExtId): Promise { const loadSql = sql @@ -41,7 +41,7 @@ export class SqlSettingsRepository implements FacetRepository { gauges: settings.gauges, }) .onConflict("id") - .doUpdate("music", "pack", "paper_cup", "gauges") + .doUpdate(["music", "pack", "paper_cup", "gauges"]) .toParams(); await this._conn.query(saveSql); diff --git a/src/idz/db/story.ts b/src/idz/db/story.ts index fd4096b..99db2c0 100644 --- a/src/idz/db/story.ts +++ b/src/idz/db/story.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks-postgres"; import { _findProfile } from "./_util"; @@ -9,7 +9,7 @@ import { FacetRepository } from "../repo"; import { generateId, Id } from "../../db"; export class SqlStoryRepository implements FacetRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} private async _load(extId: ExtId): Promise<[Story, Id]> { const profileId = await _findProfile(this._conn, extId); @@ -75,7 +75,7 @@ export class SqlStoryRepository implements FacetRepository { y: story.y, }) .onConflict("id") - .doUpdate("x", "y") + .doUpdate(["x", "y"]) .toParams(); await this._conn.query(headSql); diff --git a/src/idz/db/tickets.ts b/src/idz/db/tickets.ts index 937da7f..ebe2621 100644 --- a/src/idz/db/tickets.ts +++ b/src/idz/db/tickets.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks-postgres"; import { _findProfile } from "./_util"; @@ -10,7 +10,7 @@ import { FacetRepository } from "../repo"; // TODO free continue export class SqlTicketsRepository implements FacetRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} async load(extId: ExtId): Promise { const loadSql = sql @@ -48,7 +48,7 @@ export class SqlTicketsRepository implements FacetRepository { valid_from: freeCar.validFrom, }) .onConflict("id") - .doUpdate("valid_from") + .doUpdate(["valid_from"]) .toParams(); await this._conn.query(saveSql); diff --git a/src/idz/db/timeAttack.ts b/src/idz/db/timeAttack.ts index 963260d..f526176 100644 --- a/src/idz/db/timeAttack.ts +++ b/src/idz/db/timeAttack.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks"; import { _findProfile } from "./_util"; @@ -9,7 +9,7 @@ import { TimeAttackRepository } from "../repo"; import { generateId } from "../../db"; export class SqlTimeAttackRepository implements TimeAttackRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} async loadAll(extId: ExtId): Promise { const loadSql = sql @@ -72,7 +72,7 @@ export class SqlTimeAttackRepository implements TimeAttackRepository { .toParams(); await this._conn.query(insertSql); - } else if (row.total_time > score.totalTime) { + } else if (score.totalTime < row.total_time) { const updateSql = sql .update("idz.ta_best", { total_time: score.totalTime, diff --git a/src/idz/db/titles.ts b/src/idz/db/titles.ts index fb1da77..b5a9d6d 100644 --- a/src/idz/db/titles.ts +++ b/src/idz/db/titles.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks"; import { _findProfile } from "./_util"; @@ -8,7 +8,7 @@ import { FlagRepository } from "../repo"; import { generateId } from "../../db"; export class SqlTitlesRepository implements FlagRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} async loadAll(extId: ExtId): Promise> { const loadSql = sql diff --git a/src/idz/db/unlocks.ts b/src/idz/db/unlocks.ts index 9ead680..a220c72 100644 --- a/src/idz/db/unlocks.ts +++ b/src/idz/db/unlocks.ts @@ -1,4 +1,4 @@ -import { Client } from "pg"; +import { ClientBase } from "pg"; import * as sql from "sql-bricks-postgres"; import { _findProfile } from "./_util"; @@ -8,7 +8,7 @@ import { Unlocks } from "../model/unlocks"; import { FacetRepository } from "../repo"; export class SqlUnlocksRepository implements FacetRepository { - constructor(private readonly _conn: Client) {} + constructor(private readonly _conn: ClientBase) {} async load(extId: ExtId): Promise { const loadSql = sql @@ -41,7 +41,7 @@ export class SqlUnlocksRepository implements FacetRepository { last_mileage_reward: unlocks.lastMileageReward, }) .onConflict("id") - .doUpdate("cup", "gauges", "music", "last_mileage_reward") + .doUpdate(["cup", "gauges", "music", "last_mileage_reward"]) .toParams(); await this._conn.query(saveSql); diff --git a/src/idz/decoder/createProfile.ts b/src/idz/decoder/createProfile.ts index a7ae11c..cdf04aa 100644 --- a/src/idz/decoder/createProfile.ts +++ b/src/idz/decoder/createProfile.ts @@ -1,9 +1,10 @@ import iconv = require("iconv-lite"); -import { RequestCode } from "./_defs"; -import { CreateProfileRequest } from "../request/createProfile"; import { car } from "./_car"; import { chara } from "./_chara"; +import { RequestCode } from "./_defs"; +import { CreateProfileRequest } from "../request/createProfile"; +import { AimeId } from "../../model"; createProfile.msgCode = 0x0066 as RequestCode; createProfile.msgLen = 0x00c0; @@ -11,7 +12,7 @@ createProfile.msgLen = 0x00c0; export function createProfile(buf: Buffer): CreateProfileRequest { return { type: "create_profile_req", - aimeId: buf.readInt32LE(0x0004), + aimeId: buf.readInt32LE(0x0004) as AimeId, luid: buf.slice(0x0008, buf.indexOf("\0", 0x0008)).toString("ascii"), name: iconv.decode( buf.slice(0x001e, buf.indexOf("\0", 0x001e)), diff --git a/src/idz/handler/createProfile.ts b/src/idz/handler/createProfile.ts index f1fd202..c8de754 100644 --- a/src/idz/handler/createProfile.ts +++ b/src/idz/handler/createProfile.ts @@ -12,6 +12,7 @@ export async function createProfile( w: Repositories, req: CreateProfileRequest ): Promise { + const now = new Date(); const profile: ProfileSpec = { teamId: 2 as ExtId, // TODO name: req.name, @@ -32,7 +33,7 @@ export async function createProfile( lastMileageReward: 0, }; - const profileId = await w.profile().create(profile); + const profileId = await w.profile().create(req.aimeId, profile, now); await w.chara().save(profileId, req.chara); await w.car().saveCar(profileId, req.car); diff --git a/src/idz/handler/saveProfile.ts b/src/idz/handler/saveProfile.ts index 45a266b..5cb8a4a 100644 --- a/src/idz/handler/saveProfile.ts +++ b/src/idz/handler/saveProfile.ts @@ -6,17 +6,21 @@ export async function saveProfile( w: Repositories, req: SaveProfileRequest ): Promise { + const now = new Date(); const profile = await w.profile().load(req.profileId); const chara = await w.chara().load(req.profileId); - await w.profile().save({ - ...profile, - lv: req.lv, - exp: req.exp, - fame: req.fame, - dpoint: req.dpoint, - mileage: req.mileage, - }); + await w.profile().save( + { + ...profile, + lv: req.lv, + exp: req.exp, + fame: req.fame, + dpoint: req.dpoint, + mileage: req.mileage, + }, + now + ); await w.chara().save(req.profileId, { ...chara, diff --git a/src/idz/handler/saveTimeAttack.ts b/src/idz/handler/saveTimeAttack.ts index 7d32678..f49438c 100644 --- a/src/idz/handler/saveTimeAttack.ts +++ b/src/idz/handler/saveTimeAttack.ts @@ -6,7 +6,12 @@ export async function saveTimeAttack( w: Repositories, req: SaveTimeAttackRequest ): Promise { - await w.timeAttack().save(req.profileId, req.payload); + // Override client time since we might be doing some maintenance window + // avoidance time warping stuff + + await w + .timeAttack() + .save(req.profileId, { ...req.payload, timestamp: new Date() }); return { type: "save_time_attack_res", diff --git a/src/idz/index.ts b/src/idz/index.ts index e4a2ebb..98866f8 100644 --- a/src/idz/index.ts +++ b/src/idz/index.ts @@ -5,17 +5,20 @@ import { dispatch } from "./handler"; import { setup } from "./setup"; export default async function idz(socket: Socket) { - const world = await beginDbSession(); + const txn = await beginDbSession(); const { input, output } = setup(socket); console.log("Idz: Connection opened"); try { for await (const req of input) { - output.write(await dispatch(world, req)); + output.write(await dispatch(txn, req)); } + + txn.commit(); } catch (e) { console.log("Idz: Error:", e); + txn.rollback(); } console.log("Idz: Connection closed\n"); diff --git a/src/idz/repo.ts b/src/idz/repo.ts index 631edb4..9cbad6c 100644 --- a/src/idz/repo.ts +++ b/src/idz/repo.ts @@ -56,9 +56,13 @@ export interface ProfileRepository { load(id: Model.ExtId): Promise; - save(profile: Model.Profile): Promise; + save(profile: Model.Profile, timestamp: Date): Promise; - create(profile: ProfileSpec): Promise>; + create( + aimeId: AimeId, + profile: ProfileSpec, + timestamp: Date + ): Promise>; } export interface TimeAttackRepository { diff --git a/src/idz/request/createProfile.ts b/src/idz/request/createProfile.ts index 735279b..d76415e 100644 --- a/src/idz/request/createProfile.ts +++ b/src/idz/request/createProfile.ts @@ -1,9 +1,10 @@ import { Car } from "../model/car"; import { Chara } from "../model/chara"; +import { AimeId } from "../../model"; export interface CreateProfileRequest { type: "create_profile_req"; - aimeId: number; + aimeId: AimeId; luid: string; name: string; field_0034: number;