From 7008c91c6fb74f7106c4d9903849d3f1fc17b2f1 Mon Sep 17 00:00:00 2001 From: Julien Dessaux Date: Fri, 2 Jun 2023 00:00:09 +0200 Subject: Improved ships lib with ships db caching --- automation/automation.js | 2 +- automation/contracting.js | 12 +++---- automation/mining.js | 6 ++-- database/002_ships.sql | 6 ++++ database/config.js | 2 +- database/db.js | 1 + database/ships.js | 82 +++++++++++++++++++++++++++++++++++++++++++++++ database/systems.js | 6 ++-- lib/ships.js | 69 +++++++++++++++++++++++++++++++++++---- main.js | 16 +++------ 10 files changed, 170 insertions(+), 32 deletions(-) create mode 100644 database/002_ships.sql create mode 100644 database/ships.js diff --git a/automation/automation.js b/automation/automation.js index c7fa6f7..3184c2f 100644 --- a/automation/automation.js +++ b/automation/automation.js @@ -21,6 +21,6 @@ export async function register(symbol, faction) { } dbConfig.registerAgent(json.data); exploration.init(); - // TODO ship + dbShips.setShip(json.data.ship); // TODO contract } diff --git a/automation/contracting.js b/automation/contracting.js index b5c7292..4eff9d5 100644 --- a/automation/contracting.js +++ b/automation/contracting.js @@ -5,7 +5,7 @@ import * as ships from '../lib/ships.js'; import * as systems from '../lib/systems.js'; export async function auto(ctx) { - let ship = await ships.ship({ship: ctx.ship}); + let ship = await ships.ship({symbol: ctx.ship}); // Fetch our contracts in the system the ship currently is in let cs = await contracts.contracts(); cs = cs.data.filter(c => c.terms.deliver[0].destinationSymbol.startsWith(ship.data.nav.systemSymbol)); @@ -20,7 +20,7 @@ export async function auto(ctx) { const asteroidFields = await systems.type({symbol: ship.data.nav.systemSymbol, type: 'ASTEROID_FIELD'}); const asteroidField = asteroidFields[0].symbol; while (true) { - ship = await ships.ship({ship: ctx.ship}); // TODO we should not need to fetch this + ship = await ships.ship({symbol: ctx.ship}); // TODO we should not need to fetch this // If we are in transit, we wait until we arrive const delay = new Date(ship.data.nav.route.arrival) - new Date(); if (delay > 0) await api.sleep(delay); @@ -31,16 +31,16 @@ export async function auto(ctx) { case asteroidField: let response = await mining.mineUntilFullOf({good: good, ship: ctx.ship}); //console.log(`${ctx.ship}'s cargo is full with ${response.units} of ${good}!`); - await ships.navigate({ship: ctx.ship, waypoint: deliveryPoint}); + await ships.navigate({symbol: ctx.ship, waypoint: deliveryPoint}); break; case deliveryPoint: await ships.dock({symbol: ctx.ship}); - await ships.refuel({ship: ctx.ship}); + await ships.refuel({symbol: ctx.ship}); console.log(`delivering ${goodCargo.units} of ${good}`); await contracts.deliver({contract: contract.id, ship: ctx.ship, good: good, units: goodCargo.units }); - await ships.navigate({ship: ctx.ship, waypoint: asteroidField}); + await ships.navigate({symbol: ctx.ship, waypoint: asteroidField}); await ships.dock({symbol: ctx.ship}); - await ships.refuel({ship: ctx.ship}); + await ships.refuel({symbol: ctx.ship}); await ships.orbit({symbol: ctx.ship}); break; default: diff --git a/automation/mining.js b/automation/mining.js index a719bff..113425b 100644 --- a/automation/mining.js +++ b/automation/mining.js @@ -6,7 +6,7 @@ import * as ships from '../lib/ships.js'; export async function mineUntilFullOf(ctx) { while(true) { let response = await mineUntilFull({ship: ctx.ship}); - if (response === null) response = await ships.ship({ship: ctx.ship}); // TODO we should not need to fetch this + if (response === null) response = await ships.ship({symbol: ctx.ship}); // TODO we should not need to fetch this let good = response.data.cargo.inventory.filter(i => i.symbol === ctx.good)[0]; const inventory = response.data.cargo.inventory.filter(i => i.symbol !== ctx.good); const antimatter = response.data.cargo.inventory.filter(i => i.symbol === 'ANTIMATTER')[0]; @@ -17,7 +17,7 @@ export async function mineUntilFullOf(ctx) { for (let i=0; i= response.data.cargo.capacity * 0.9) return response; diff --git a/database/002_ships.sql b/database/002_ships.sql new file mode 100644 index 0000000..629f739 --- /dev/null +++ b/database/002_ships.sql @@ -0,0 +1,6 @@ +CREATE TABLE ships ( + id INTEGER PRIMARY KEY, + data JSON NOT NULL, + updated DATE DEFAULT NULL +); +CREATE UNIQUE INDEX ships_data_symbol on ships (json_extract(data, '$.symbol')); diff --git a/database/config.js b/database/config.js index 4a86cb6..7a50f68 100644 --- a/database/config.js +++ b/database/config.js @@ -1,6 +1,6 @@ import db from './db.js'; -const getTokenStatement = db.prepare(`SELECT json_extract(value, '$.token') as token from config where key = 'register_data';`); +const getTokenStatement = db.prepare(`SELECT value->>'token' as token from config where key = 'register_data';`); const registerAgentStatement = db.prepare(`INSERT INTO config(key, value) VALUES ('register_data', json(?));`); export function getToken() { diff --git a/database/db.js b/database/db.js index 6deddcc..a6d5174 100644 --- a/database/db.js +++ b/database/db.js @@ -4,6 +4,7 @@ import Database from 'better-sqlite3'; const allMigrations = [ 'database/000_init.sql', 'database/001_systems.sql', + 'database/002_ships.sql', ]; const db = new Database( diff --git a/database/ships.js b/database/ships.js new file mode 100644 index 0000000..0220be6 --- /dev/null +++ b/database/ships.js @@ -0,0 +1,82 @@ +import db from './db.js'; + +const getShipStatement = db.prepare(`SELECT data FROM ships WHERE data->>'symbol' = ?;`); +const setShipStatement = db.prepare(`INSERT INTO ships(data, updated) VALUES (json(?), ?);`); +const setShipCargoStatement = db.prepare(`UPDATE ships SET data = (SELECT json_set(data, '$.cargo', json(:cargo)) FROM ships WHERE data->>'symbol' = :symbol), updated = :date WHERE data->>'symbol' = :symbol;`); +const setShipFuelStatement = db.prepare(`UPDATE ships SET data = (SELECT json_set(data, '$.fuel', json(:fuel)) FROM ships WHERE data->>'symbol' = :symbol), updated = :date WHERE data->>'symbol' = :symbol;`); +const setShipNavStatement = db.prepare(`UPDATE ships SET data = (SELECT json_set(data, '$.nav', json(:nav)) FROM ships WHERE data->>'symbol' = :symbol), updated = :date WHERE data->>'symbol' = :symbol;`); +const updateShipStatement = db.prepare(`UPDATE ships SET data = json(:data), updated = :date WHERE data->>'symbol' = :symbol;`); + +export function getShip(symbol) { + try { + const data = getShipStatement.get(symbol); + if (data === undefined) { + return null; + } + return JSON.parse(data.data); + } catch (err) { + console.log(err); + return null; + } +} + +export function setShip(data) { + if (getShip(data.symbol) === null) { + try { + return setShipStatement.run(JSON.stringify(data), new Date().toISOString()).lastInsertRowid; + } catch (err) { + console.log(err); + return null; + } + } else { + try { + return updateShipStatement.run({ + data: JSON.stringify(data), + date: new Date().toISOString(), + symbol: data.symbol, + }).changes; + } catch (err) { + console.log(err); + return null; + } + } +} + +export function setShipCargo(symbol, cargo) { + try { + setShipCargoStatement.run({ + cargo: JSON.stringify(cargo), + date: new Date().toISOString(), + symbol: symbol, + }).changes; + } catch (err) { + console.log(err); + return null; + } +} + +export function setShipFuel(symbol, fuel) { + try { + setShipFuelStatement.run({ + date: new Date().toISOString(), + fuel: JSON.stringify(fuel), + symbol: symbol, + }).changes; + } catch (err) { + console.log(err); + return null; + } +} + +export function setShipNav(symbol, nav) { + try { + setShipNavStatement.run({ + date: new Date().toISOString(), + nav: JSON.stringify(nav), + symbol: symbol, + }).changes; + } catch (err) { + console.log(err); + return null; + } +} diff --git a/database/systems.js b/database/systems.js index 337126c..479deb6 100644 --- a/database/systems.js +++ b/database/systems.js @@ -1,9 +1,9 @@ import db from './db.js'; -const getSystemStatement = db.prepare(`SELECT data FROM systems WHERE json_extract(data, '$.symbol') = ?;`); -const getSystemUpdatedStatement = db.prepare(`SELECT updated FROM systems WHERE json_extract(data, '$.symbol') = ?;`); +const getSystemStatement = db.prepare(`SELECT data FROM systems WHERE data->>'symbol' = ?;`); +const getSystemUpdatedStatement = db.prepare(`SELECT updated FROM systems WHERE data->>'symbol' = ?;`); const setSystemStatement = db.prepare(`INSERT INTO systems(data) VALUES (json(?));`); -const setSystemWaypointsStatement = db.prepare(`UPDATE systems SET data = (SELECT json_set(data, '$.waypoints', json(:waypoints)) FROM systems WHERE json_extract(data, '$.symbol') = :symbol), updated = :date WHERE json_extract(data, '$.symbol') = :symbol;`); +const setSystemWaypointsStatement = db.prepare(`UPDATE systems SET data = (SELECT json_set(data, '$.waypoints', json(:waypoints)) FROM systems WHERE data->>'symbol' = :symbol), updated = :date WHERE data->>'symbol' = :symbol;`); export function init() { try { diff --git a/lib/ships.js b/lib/ships.js index 55ee585..23f272d 100644 --- a/lib/ships.js +++ b/lib/ships.js @@ -1,7 +1,12 @@ import * as api from './api.js'; +import * as dbConfig from '../database/config.js'; +import * as dbShips from '../database/ships.js'; export async function extract(ctx) { - const response = await api.send({endpoint: `/my/ships/${ctx.ship}/extract`, method: 'POST'}); + // TODO check if our current waypoint has an asteroid field? + await orbit(ctx); + const ship = dbShips.getShip(ctx.symbol); + const response = await api.send({endpoint: `/my/ships/${ctx.symbol}/extract`, method: 'POST'}); if (response.error !== undefined) { switch(response.error.code) { case 4000: // ship is on cooldown @@ -13,12 +18,17 @@ export async function extract(ctx) { throw response; } } else { + dbShips.setShipCargo(ctx.symbol, response.data.cargo); await api.sleep(response.data.cooldown.remainingSeconds*1000); } return response; } export async function dock(ctx) { + const ship = dbShips.getShip(ctx.symbol); + if (ship.nav.status === 'DOCKED') { + return null; + } const response = await api.send({endpoint: `/my/ships/${ctx.symbol}/dock`, method: 'POST'}); if (response.error !== undefined) { switch(response.error.code) { @@ -29,27 +39,41 @@ export async function dock(ctx) { throw response; } } + dbShips.setShipNav(ctx.symbol, response.data.nav); return response; } export async function jump(ctx) { + // TODO const response = await api.send({endpoint: `/my/ships/${ctx.ship}/jump`, method: 'POST', payload: { systemSymbol: ctx.system }}); await api.sleep(response.data.cooldown.remainingSeconds*1000); return response; } export async function navigate(ctx) { - const response = await api.send({endpoint: `/my/ships/${ctx.ship}/navigate`, method: 'POST', payload: { waypointSymbol: ctx.waypoint }}); + const ship = dbShips.getShip(ctx.symbol); + if (ship.nav.waypointSymbol === ctx.waypoint) { + return null; + } + await orbit(ctx); + const response = await api.send({endpoint: `/my/ships/${ctx.symbol}/navigate`, method: 'POST', payload: { waypointSymbol: ctx.waypoint }}); + dbShips.setShipFuel(ctx.symbol, response.data.fuel); + dbShips.setShipNav(ctx.symbol, response.data.nav); const delay = new Date(response.data.nav.route.arrival) - new Date(); await api.sleep(delay); return response; } export async function negotiate(ctx) { + // TODO return await api.send({endpoint: `/my/ships/${ctx.ship}/negotiate/contract`, method: 'POST'}); } export async function orbit(ctx) { + const ship = dbShips.getShip(ctx.symbol); + if (ship.nav.status === 'IN_ORBIT') { + return null; + } const response = await api.send({endpoint: `/my/ships/${ctx.symbol}/orbit`, method: 'POST'}); if (response.error !== undefined) { switch(response.error.code) { @@ -60,28 +84,59 @@ export async function orbit(ctx) { throw response; } } + dbShips.setShipNav(ctx.symbol, response.data.nav); return response; } export async function purchase(ctx) { - return await api.send({endpoint: '/my/ships', method: 'POST', payload: { + const response = await api.send({endpoint: '/my/ships', method: 'POST', payload: { shipType: ctx.shipType, waypointSymbol: ctx.waypoint, }}); + if (response.error !== undefined) { + throw response; + } + dbShips.setShip(response.data.ship); } export async function refuel(ctx) { - return await api.send({endpoint: `/my/ships/${ctx.ship}/refuel`, method: 'POST'}); + // TODO check if our current waypoint has a marketplace (and sells fuel)? + const ship = dbShips.getShip(ctx.symbol); + if (ship.fuel.current >= ship.fuel.capacity * 0.9) { + return null; + } + await dock(ctx); + const response = await api.send({endpoint: `/my/ships/${ctx.symbol}/refuel`, method: 'POST'}); + if (response.error !== undefined) { + throw response; + } + dbShips.setShipFuel(ctx.symbol, response.data.fuel); + // TODO track credits + return response; } export async function sell(ctx) { - return await api.send({endpoint: `/my/ships/${ctx.ship}/sell`, method: 'POST', payload: { symbol: ctx.good, units: ctx.units }}); + await dock(ctx); + const ship = dbShips.getShip(ctx.symbol); + const response = await api.send({endpoint: `/my/ships/${ctx.symbol}/sell`, method: 'POST', payload: { symbol: ctx.good, units: ctx.units }}); + if (response.error !== undefined) { + throw response; + } + dbShips.setShipCargo(ctx.symbol, response.data.cargo); + // TODO track credits + return response; } export async function ship(ctx) { - return await api.send({endpoint: `/my/ships/${ctx.ship}`}); + const response = await api.send({endpoint: `/my/ships/${ctx.symbol}`}); + if (response.error !== undefined) { + throw response; + } + dbShips.setShip(response.data); + return response; } export async function survey(ctx) { - return await api.send({endpoint: `/my/ships/${ctx.ship}/survey`, method: 'POST'}); + // TODO + return await api.send({endpoint: `/my/ships/${ctx.symbol}/survey`, method: 'POST'}); } diff --git a/main.js b/main.js index 8e80c58..61a1ab8 100755 --- a/main.js +++ b/main.js @@ -54,9 +54,6 @@ default: case 'ships.dock': api.debugLog(await ships.dock({symbol: process.argv[3]})); break; - case 'ships.extract': - api.debugLog(await ships.extract({ship: process.argv[3]})); - break; case 'ships.jump': api.debugLog(await ships.jump({ship: process.argv[3], system: process.argv[4]})); break; @@ -64,14 +61,11 @@ default: // api.send({endpoint: `/systems/${process.argv[3]}/waypoints/${process.argv[4]}/market`}); // break; case 'ships.navigate': - api.debugLog(await ships.navigate({ship: process.argv[3], waypoint: process.argv[4]})); + api.debugLog(await ships.navigate({symbol: process.argv[3], waypoint: process.argv[4]})); break; case 'ships.negotiate': api.debugLog(await ships.negotiate({ship: process.argv[3]})); break; - case 'ships.navigate': - api.debugLog(await ships.navigate({ship: process.argv[3], waypoint: process.argv[4]})); - break; case 'ships.orbit': api.debugLog(await ships.orbit({symbol: process.argv[3]})); break; @@ -79,16 +73,16 @@ default: api.debugLog(await ships.purchase({shipType: process.argv[3], waypoint: process.argv[4]})); break; case 'ships.refuel': - api.debugLog(await ships.refuel({ship: process.argv[3]})); + api.debugLog(await ships.refuel({symbol: process.argv[3]})); break; case 'ships.sell': - api.debugLog(await ships.sell({ship: process.argv[3], good: process.argv[4], units: process.argv[5]})); + api.debugLog(await ships.sell({symbol: process.argv[3], good: process.argv[4], units: process.argv[5]})); break; case 'ships.ship': - api.debugLog(await ships.ship({ship: process.argv[3]})); + api.debugLog(await ships.ship({symbol: process.argv[3]})); break; case 'ships.survey': - api.debugLog(await ships.survey({ship: process.argv[3]})); + api.debugLog(await ships.survey({symbol: process.argv[3]})); break; case 'systems.asteroids': api.debugLog(await systems.type({symbol: process.argv[3], type: 'ASTEROID_FIELD'})); -- cgit v1.2.3