wip create working(?)

This commit is contained in:
Tau 2019-04-29 11:58:28 -04:00
parent 968e98fbb4
commit a5827da590
21 changed files with 214 additions and 96 deletions

View File

@ -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<Profile>
): Promise<Id<Profile> | undefined> {
): Promise<Id<Profile>> {
const lookupSql = sql
.select("r.id")
.from("idz.profile r")

View File

@ -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<BackgroundCode> {
constructor(private readonly _conn: Client) {}
constructor(private readonly _conn: ClientBase) {}
async loadAll(extId: ExtId<Profile>): Promise<Set<BackgroundCode>> {
const loadSql = sql

View File

@ -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<Profile>): Promise<number> {
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);

View File

@ -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<Chara> {
constructor(private readonly _conn: Client) {}
constructor(private readonly _conn: ClientBase) {}
async load(extId: ExtId<Profile>): Promise<Chara> {
const loadSql = sql
@ -53,15 +53,15 @@ export class SqlCharaRepository implements FacetRepository<Chara> {
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);

View File

@ -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<Profile>): Promise<Map<CourseNo, number>> {
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);
}

View File

@ -1,5 +1,87 @@
import { Transaction } from "../repo";
import { PoolClient } from "pg";
export async function beginDbSession(): Promise<Transaction> {
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<Model.BackgroundCode> {
return new SqlBackgroundsRepository(this._conn);
}
car(): Repo.CarRepository {
return new SqlCarRepository(this._conn);
}
chara(): Repo.FacetRepository<Model.Chara> {
return new SqlCharaRepository(this._conn);
}
coursePlays(): Repo.CoursePlaysRepository {
return new SqlCoursePlaysRepository(this._conn);
}
missions(): Repo.FacetRepository<Model.MissionState> {
return new SqlMissionsRepository(this._conn);
}
profile(): Repo.ProfileRepository {
return new SqlProfileRepository(this._conn);
}
settings(): Repo.FacetRepository<Model.Settings> {
return new SqlSettingsRepository(this._conn);
}
story(): Repo.FacetRepository<Model.Story> {
return new SqlStoryRepository(this._conn);
}
tickets(): Repo.FacetRepository<Model.Tickets> {
return new SqlTicketsRepository(this._conn);
}
timeAttack(): Repo.TimeAttackRepository {
return new SqlTimeAttackRepository(this._conn);
}
titles(): Repo.FlagRepository<Model.TitleCode> {
return new SqlTitlesRepository(this._conn);
}
unlocks(): Repo.FacetRepository<Model.Unlocks> {
return new SqlUnlocksRepository(this._conn);
}
async commit(): Promise<void> {
await this._conn.query("commit");
await this._conn.release();
}
async rollback(): Promise<void> {
await this._conn.query("rollback");
await this._conn.release();
}
}
export async function beginDbSession(): Promise<Repo.Transaction> {
const conn = await connect();
await conn.query("begin");
return new TransactionImpl(conn);
}

View File

@ -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<MissionState> {
constructor(private readonly _conn: Client) {}
constructor(private readonly _conn: ClientBase) {}
private async _load(
extId: ExtId<Profile>
@ -73,7 +73,7 @@ export class SqlMissionsRepository implements FacetRepository<MissionState> {
value: grid[j],
})
.onConflict("profile_id", "grid_no", "cell_no")
.doUpdate("value")
.doUpdate(["value"])
.toParams();
await this._conn.query(saveSql);

View File

@ -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<Profile | undefined> {
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<boolean> {
const profileId = await this._findProfile(aimeId);
const result = await this._tryLoadByAimeId(aimeId);
return profileId !== undefined;
return result !== undefined;
}
async loadByAimeId(aimeId: AimeId): Promise<Profile> {
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<Profile>): Promise<Profile> {
@ -61,7 +73,7 @@ export class SqlProfileRepository implements ProfileRepository {
return _extractRow(rows[0]);
}
async save(profile: Profile): Promise<void> {
async save(profile: Profile, timestamp: Date): Promise<void> {
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<ExtId<Profile>> {
async create(
aimeId: AimeId,
profile: ProfileSpec,
timestamp: Date
): Promise<ExtId<Profile>> {
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<Profile>;
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<Id<Profile> | 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<Profile>;
} else {
return undefined;
}
}
}

View File

@ -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<Settings> {
constructor(private readonly _conn: Client) {}
constructor(private readonly _conn: ClientBase) {}
async load(extId: ExtId<Profile>): Promise<Settings> {
const loadSql = sql
@ -41,7 +41,7 @@ export class SqlSettingsRepository implements FacetRepository<Settings> {
gauges: settings.gauges,
})
.onConflict("id")
.doUpdate("music", "pack", "paper_cup", "gauges")
.doUpdate(["music", "pack", "paper_cup", "gauges"])
.toParams();
await this._conn.query(saveSql);

View File

@ -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<Story> {
constructor(private readonly _conn: Client) {}
constructor(private readonly _conn: ClientBase) {}
private async _load(extId: ExtId<Profile>): Promise<[Story, Id<Profile>]> {
const profileId = await _findProfile(this._conn, extId);
@ -75,7 +75,7 @@ export class SqlStoryRepository implements FacetRepository<Story> {
y: story.y,
})
.onConflict("id")
.doUpdate("x", "y")
.doUpdate(["x", "y"])
.toParams();
await this._conn.query(headSql);

View File

@ -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<Tickets> {
constructor(private readonly _conn: Client) {}
constructor(private readonly _conn: ClientBase) {}
async load(extId: ExtId<Profile>): Promise<Tickets> {
const loadSql = sql
@ -48,7 +48,7 @@ export class SqlTicketsRepository implements FacetRepository<Tickets> {
valid_from: freeCar.validFrom,
})
.onConflict("id")
.doUpdate("valid_from")
.doUpdate(["valid_from"])
.toParams();
await this._conn.query(saveSql);

View File

@ -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<Profile>): Promise<TimeAttackScore[]> {
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,

View File

@ -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<TitleCode> {
constructor(private readonly _conn: Client) {}
constructor(private readonly _conn: ClientBase) {}
async loadAll(extId: ExtId<Profile>): Promise<Set<TitleCode>> {
const loadSql = sql

View File

@ -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<Unlocks> {
constructor(private readonly _conn: Client) {}
constructor(private readonly _conn: ClientBase) {}
async load(extId: ExtId<Profile>): Promise<Unlocks> {
const loadSql = sql
@ -41,7 +41,7 @@ export class SqlUnlocksRepository implements FacetRepository<Unlocks> {
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);

View File

@ -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)),

View File

@ -12,6 +12,7 @@ export async function createProfile(
w: Repositories,
req: CreateProfileRequest
): Promise<GenericResponse> {
const now = new Date();
const profile: ProfileSpec = {
teamId: 2 as ExtId<Team>, // 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);

View File

@ -6,17 +6,21 @@ export async function saveProfile(
w: Repositories,
req: SaveProfileRequest
): Promise<GenericResponse> {
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,

View File

@ -6,7 +6,12 @@ export async function saveTimeAttack(
w: Repositories,
req: SaveTimeAttackRequest
): Promise<SaveTimeAttackResponse> {
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",

View File

@ -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");

View File

@ -56,9 +56,13 @@ export interface ProfileRepository {
load(id: Model.ExtId<Model.Profile>): Promise<Model.Profile>;
save(profile: Model.Profile): Promise<void>;
save(profile: Model.Profile, timestamp: Date): Promise<void>;
create(profile: ProfileSpec): Promise<Model.ExtId<Model.Profile>>;
create(
aimeId: AimeId,
profile: ProfileSpec,
timestamp: Date
): Promise<Model.ExtId<Model.Profile>>;
}
export interface TimeAttackRepository {

View File

@ -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;