From de0251bc22d554e2ace4d2d3d061dc1054656bcd Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Mon, 13 May 2024 23:45:45 +0200 Subject: [node] Big Contracts lib refactoring --- nodejs/automation/contracting.ts | 12 ++-- nodejs/automation/init.ts | 5 +- nodejs/automation/mining.ts | 4 +- nodejs/database/contracts.ts | 26 ------- nodejs/lib/contracts.ts | 151 ++++++++++++++++++++------------------- nodejs/lib/ships.ts | 6 +- nodejs/lib/types.ts | 23 ------ 7 files changed, 88 insertions(+), 139 deletions(-) delete mode 100644 nodejs/database/contracts.ts diff --git a/nodejs/automation/contracting.ts b/nodejs/automation/contracting.ts index 9ac5d2f..4648b0d 100644 --- a/nodejs/automation/contracting.ts +++ b/nodejs/automation/contracting.ts @@ -1,10 +1,8 @@ import { debugLog } from '../lib/api.ts'; import { Ship } from '../lib/ships.ts'; -import { Contract } from '../lib/types.ts'; import * as mining from './mining.js'; import * as selling from './selling.js'; -import * as dbContracts from '../database/contracts.ts'; -import * as libContracts from '../lib/contracts.ts'; +import { Contract, getContracts } from '../lib/contracts.ts'; import * as libSystems from '../lib/systems.ts'; import * as systems from '../lib/systems.ts'; import { @@ -12,7 +10,7 @@ import { } from '../lib/utils.ts'; export async function run(ship: Ship): Promise { - const contracts = await libContracts.getContracts(); + const contracts = await getContracts(); const active = contracts.filter(function(c) { if (c.fulfilled) return false; const deadline = new Date(c.terms.deadline).getTime(); @@ -29,7 +27,7 @@ export async function run(ship: Ship): Promise { async function runOne(contract: Contract, ship: Ship): Promise { debugLog(contract); - await libContracts.accept(contract); + await contract.accept(); switch(contract.type) { case 'PROCUREMENT': //if (contract.terms.deliver[0].tradeSymbol.match(/_ORE$/)) { @@ -58,7 +56,7 @@ async function runOreProcurement(contract: Contract, ship: Ship): Promise break; case deliveryPoint.symbol: if (goodCargo !== undefined) { // we could be here if a client restart happens right after selling before we navigate away - contract = await libContracts.deliver(contract, ship); + await contract.deliver(ship); if (contract.fulfilled) return; } await ship.navigate(asteroid); @@ -117,7 +115,7 @@ async function runTradeProcurement(contract: Contract, ship: Ship): Promise { switch(json.error?.code) { case 4111: // 4111 means the agent symbol has already been claimed so no server reset happened // TODO await agents.agents(); - await libContracts.getContracts(); return; default: throw json; @@ -40,5 +38,4 @@ export async function init(): Promise { db.reset(); dbTokens.addToken(json.data.token); dbAgents.addAgent(json.data.agent); - dbContracts.setContract(json.data.contract); } diff --git a/nodejs/automation/mining.ts b/nodejs/automation/mining.ts index cdfcb78..e7ba62f 100644 --- a/nodejs/automation/mining.ts +++ b/nodejs/automation/mining.ts @@ -1,8 +1,7 @@ import * as selling from './selling.js'; -import * as dbContracts from '../database/contracts.js'; +import { Contract } from '../lib/contracts.js'; import { Ship } from '../lib/ships.js'; import { - Contract, Waypoint, } from '../lib/types.ts'; import { categorizeCargo } from '../lib/utils.ts'; @@ -11,7 +10,6 @@ export async function mineUntilFullFor(contract: Contract, ship: Ship, asteroid: // TODO find a good asteroid while(true) { await mineUntilFull(ship); - contract = dbContracts.getContract(contract.id); const deliver = contract.terms.deliver[0]; const cargo = categorizeCargo(ship.cargo, deliver.tradeSymbol); const wantedUnits = Object.values(cargo.wanted).reduce((acc, e) => acc += e, 0); diff --git a/nodejs/database/contracts.ts b/nodejs/database/contracts.ts deleted file mode 100644 index 9adb4c8..0000000 --- a/nodejs/database/contracts.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Contract } from '../lib/types.ts'; -import { DbData, db } from './db.ts'; - -const addContractStatement = db.prepare(`INSERT INTO contracts(data) VALUES (json(?));`); -const getContractStatement = db.prepare(`SELECT data FROM contracts WHERE data->>'id' = ?;`); -const getContractsStatement = db.prepare(`SELECT data FROM contracts WHERE data->>'fulfilled' = false;`); -const updateContractStatement = db.prepare(`UPDATE contracts SET data = json(:data) WHERE data->>'id' = :id;`); - -export function getContract(id: string): Contract { - const data = getContractStatement.get(id) as DbData|undefined; - if (!data) throw `invalid id ${id} in getContract database call`; - return JSON.parse(data.data); -} - -export function getContracts(): Array { - const data = getContractsStatement.all() as Array; - return data.map(contractData => JSON.parse(contractData.data)); -} - -export function setContract(data: Contract): void { - const changes = updateContractStatement.run({ - data: JSON.stringify(data), - id: data.id, - }).changes; - if (changes === 0) addContractStatement.run(JSON.stringify(data)); -} diff --git a/nodejs/lib/contracts.ts b/nodejs/lib/contracts.ts index 0582cc7..1b54178 100644 --- a/nodejs/lib/contracts.ts +++ b/nodejs/lib/contracts.ts @@ -1,7 +1,6 @@ import { Agent, Cargo, - Contract, } from './types.ts'; import { APIError, @@ -11,84 +10,92 @@ import { } from './api.ts'; import { Ship } from './ships.ts'; import * as dbAgents from '../database/agents.ts'; -import * as dbContracts from '../database/contracts.ts'; - -export async function accept(contract: Contract): Promise { - contract = dbContracts.getContract(contract.id); - if (contract.accepted) return contract; - const response = await send<{agent: Agent, contract: Contract, type: ''}>({endpoint: `/my/contracts/${contract.id}/accept`, method: 'POST'}); - if (response.error) { - debugLog(response); - throw response; - } - dbAgents.setAgent(response.data.agent); - dbContracts.setContract(response.data.contract); - return response.data.contract; -} export async function getContracts(): Promise> { const response = await sendPaginated({endpoint: '/my/contracts'}); - response.forEach(contract => dbContracts.setContract(contract)); - return response; -} - -export async function getContract(contract: Contract): Promise { - try { - return dbContracts.getContract(contract.id); - } catch {} - const response = await send({endpoint: `/my/contracts/${contract.id}`}); - if (response.error) { - debugLog(response); - throw response; - } - dbContracts.setContract(response.data); - return response.data; + return response.map(contract => new Contract(contract)); } -export async function deliver(contract: Contract, ship: Ship): Promise { - contract = dbContracts.getContract(contract.id); - if (contract.terms.deliver[0].unitsRequired <= contract.terms.deliver[0].unitsFulfilled) { - return await fulfill(contract); +export class Contract { + accepted: boolean; + deadlineToAccept: Date; + expiration: Date; + factionSymbol: string; + fulfilled: boolean; + id: string; + terms: { + deadline: Date; + payment: { + onAccepted: number; + onFulfilled: number; + }, + deliver: Array<{ + tradeSymbol: string; + destinationSymbol: string; + unitsRequired: number; + unitsFulfilled: number; + }>; + }; + type: string; + constructor(contract: Contract) { + this.accepted = contract.accepted; + this.deadlineToAccept = contract.deadlineToAccept; + this.expiration = contract.expiration; + this.factionSymbol = contract.factionSymbol; + this.fulfilled = contract.fulfilled; + this.id = contract.id; + this.terms = contract.terms; + this.type = contract.type; } - const tradeSymbol = contract.terms.deliver[0].tradeSymbol; - let units = 0; - ship.cargo.inventory.forEach(i => {if (i.symbol === tradeSymbol) units = i.units; }); - await ship.dock(); // we need to be docked to deliver - const response = await send<{contract: Contract, cargo: Cargo}>({ endpoint: `/my/contracts/${contract.id}/deliver`, method: 'POST', payload: { - shipSymbol: ship.symbol, - tradeSymbol: tradeSymbol, - units: units, - }}); - if (response.error) { - switch(response.error.code) { - case 4503: // contract has expired - // TODO sell cargo? the next trading loop should take care of it by itself - contract.fulfilled = true; - return contract; - case 4509: // contract delivery terms have been met - return await fulfill(contract); - default: // yet unhandled error - debugLog(response); - throw response; + async accept(): Promise { + if (this.accepted) return; + const response = await send<{agent: Agent, contract: Contract, type: ''}>({endpoint: `/my/contracts/${this.id}/accept`, method: 'POST'}); + if (response.error) { + debugLog(response); + throw response; } + dbAgents.setAgent(response.data.agent); } - dbContracts.setContract(response.data.contract); - ship.cargo = response.data.cargo; - if(response.data.contract.terms.deliver[0].unitsRequired <= response.data.contract.terms.deliver[0].unitsFulfilled) { - return await fulfill(response.data.contract); + async deliver(ship: Ship): Promise { + const unitsRemaining = this.terms.deliver[0].unitsRequired - this.terms.deliver[0].unitsFulfilled; + if (unitsRemaining <= 0) return await this.fulfill(); + const tradeSymbol = this.terms.deliver[0].tradeSymbol; + let units = 0; + ship.cargo.inventory.forEach(i => {if (i.symbol === tradeSymbol) units = i.units; }); + if (units === 0) return; + if (units > unitsRemaining) units = unitsRemaining; + await ship.dock(); // we need to be docked to deliver + const response = await send<{contract: Contract, cargo: Cargo}>({ endpoint: `/my/contracts/${this.id}/deliver`, method: 'POST', payload: { + shipSymbol: ship.symbol, + tradeSymbol: tradeSymbol, + units: units, + }}); + if (response.error) { + switch(response.error.code) { + case 4503: // contract has expired + // TODO sell cargo? the next trading loop should take care of it by itself + this.fulfilled = true; + return; + case 4509: // contract delivery terms have been met + return await this.fulfill(); + default: // yet unhandled error + debugLog(response); + throw response; + } + } + ship.cargo = response.data.cargo; + if(response.data.contract.terms.deliver[0].unitsRequired <= response.data.contract.terms.deliver[0].unitsFulfilled) { + return await this.fulfill(); + } } - return response.data.contract; -} - -export async function fulfill(contract: Contract): Promise { - contract = dbContracts.getContract(contract.id); - if (contract.fulfilled) return contract; - const response = await send<{agent: Agent, contract: Contract}>({ endpoint: `/my/contracts/${contract.id}/fulfill`, method: 'POST'}); - if (response.error) { - debugLog(response); - throw response; + async fulfill(): Promise { + if (this.terms.deliver[0].unitsRequired > this.terms.deliver[0].unitsFulfilled) return; + if (this.fulfilled) return; + const response = await send<{agent: Agent, contract: Contract}>({ endpoint: `/my/contracts/${this.id}/fulfill`, method: 'POST'}); + if (response.error) { + debugLog(response); + throw response; + } + dbAgents.setAgent(response.data.agent); } - dbAgents.setAgent(response.data.agent); - dbContracts.setContract(response.data.contract); - return response.data.contract; -} +}; diff --git a/nodejs/lib/ships.ts b/nodejs/lib/ships.ts index 74419fc..8df3aea 100644 --- a/nodejs/lib/ships.ts +++ b/nodejs/lib/ships.ts @@ -10,11 +10,11 @@ import { ShipIsStillOnCooldownError, ShipRequiresMoreFuelForNavigationError, } from './errors.ts'; +import { Contract } from './contracts.ts'; import * as libSystems from './systems.ts'; import { Agent, Cargo, - Contract, Cooldown, Fuel, Nav, @@ -25,7 +25,6 @@ import { shortestPath, } from './utils.ts'; import * as dbAgents from '../database/agents.ts'; -import * as dbContracts from '../database/contracts.ts'; export async function getShips(): Promise> { const response = await send>({endpoint: `/my/ships`, page: 1}); @@ -154,8 +153,7 @@ export class Ship { throw response; } } - dbContracts.setContract(response.data.contract); - return response.data.contract; + return new Contract(response.data.contract); } async orbit(): Promise { if (this.nav.status === 'IN_ORBIT') return; diff --git a/nodejs/lib/types.ts b/nodejs/lib/types.ts index a8e748c..e4d750f 100644 --- a/nodejs/lib/types.ts +++ b/nodejs/lib/types.ts @@ -30,29 +30,6 @@ export type Chart = { submittedOn: Date; }; -export type Contract = { - id: string; - factionSymbol: string; - type: string; - terms: { - deadline: Date; - payment: { - onAccepted: number; - onFulfilled: number; - }, - deliver: Array<{ - tradeSymbol: string; - destinationSymbol: string; - unitsRequired: number; - unitsFulfilled: number; - }>; - }; - accepted: boolean; - fulfilled: boolean; - expiration: Date; - deadlineToAccept: Date; -}; - export type Cooldown = { shipSymbol: string; totalSeconds: number; -- cgit v1.2.3