Skip to content

Commit 9fd1688

Browse files
authored
Merge pull request #59 from drkshrk/feature/character-tab-improvements
Character tab improvements & Bug Fix: sortable/resizable tables, filters, inventory item editing
2 parents 2203e80 + dbc6292 commit 9fd1688

13 files changed

Lines changed: 648 additions & 154 deletions

File tree

console/api/src/duneDb.js

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import {
2929

3030
const MAX_INTEL_POINTS = 2779;
3131
const MAX_TABLE_PREVIEW_ROWS = 10000;
32+
const INVENTORY_EDITABLE_COLUMNS = new Set(["stack_size", "quality_level", "position_index", "current_durability", "max_durability"]);
3233
let craftingRecipeCatalogCache = null;
3334

3435
export class UnsupportedCapabilityError extends Error {
@@ -1504,7 +1505,17 @@ export async function playerProfile(db, id) {
15041505
left join dune.accounts ac on ac.id = a.owner_account_id
15051506
where a.id = $1`, [actorId]);
15061507
if (!result.rows[0]) throw new Error("Player not found");
1507-
return { capabilities: await playerCapabilities(db), player: result.rows[0] };
1508+
const row = result.rows[0];
1509+
const [factions, guilds] = await Promise.all([
1510+
leadershipFactions(db).catch(() => new Map()),
1511+
leadershipGuilds(db).catch(() => new Map())
1512+
]);
1513+
const controllerId = String(row.player_controller_id || "");
1514+
const actorIdKey = String(row.actor_id || "");
1515+
const accountIdKey = String(row.account_id || "");
1516+
row.faction = factions.get(controllerId) || factions.get(actorIdKey) || "Unassigned";
1517+
row.guild = guilds.get(controllerId) || guilds.get(actorIdKey) || guilds.get(accountIdKey) || "Unavailable";
1518+
return { capabilities: await playerCapabilities(db), player: row };
15081519
}
15091520

15101521
export async function playerInventory(db, id) {
@@ -1517,7 +1528,11 @@ export async function playerInventory(db, id) {
15171528
i.position_index,
15181529
i.inventory_id,
15191530
coalesce((i.stats->'FItemStackAndDurabilityStats'->1->>'CurrentDurability'), null) as current_durability,
1520-
coalesce((i.stats->'FItemStackAndDurabilityStats'->1->>'MaxDurability'), null) as max_durability,
1531+
coalesce(
1532+
nullif((i.stats->'FItemStackAndDurabilityStats'->1->>'MaxDurability')::numeric, 0),
1533+
nullif((i.stats->'FItemStackAndDurabilityStats'->1->>'DecayedMaxDurability')::numeric, 0),
1534+
null
1535+
) as max_durability,
15211536
i.stats
15221537
from dune.items i
15231538
join dune.inventories inv on i.inventory_id = inv.id
@@ -2526,6 +2541,40 @@ export async function deleteInventoryItem(db, playerId, itemId) {
25262541
});
25272542
}
25282543

2544+
export async function updateInventoryItem(db, playerId, itemId, values) {
2545+
await requireCapability(await supportsInventoryEdit(db), "Inventory edit requires dune.items and dune.inventories.");
2546+
const safeItemId = intParam(itemId, "item id", 1);
2547+
const nextValues = Object.fromEntries(Object.entries(values || {}).filter(([key]) => INVENTORY_EDITABLE_COLUMNS.has(key)));
2548+
return db.transaction(async (tx) => {
2549+
const player = await resolvePlayerMutationTarget(tx, playerId);
2550+
const owned = await tx.query(`
2551+
select i.id, i.stats
2552+
from dune.items i
2553+
join dune.inventories inv on inv.id = i.inventory_id
2554+
where i.id = $1 and inv.actor_id = $2
2555+
for update`, [safeItemId, player.actorId]);
2556+
if (!owned.rows[0]) throw new Error("Inventory item was not found in the selected player's directly-owned inventory");
2557+
2558+
const hasMax = Object.prototype.hasOwnProperty.call(nextValues, "max_durability") && nextValues.max_durability !== null && nextValues.max_durability !== "";
2559+
const hasCurrent = Object.prototype.hasOwnProperty.call(nextValues, "current_durability") && nextValues.current_durability !== null && nextValues.current_durability !== "";
2560+
if (hasMax || hasCurrent) {
2561+
const stats = owned.rows[0].stats || {};
2562+
const durability = { ...(stats.FItemStackAndDurabilityStats?.[1] || {}) };
2563+
const maxKey = Object.prototype.hasOwnProperty.call(durability, "MaxDurability") ? "MaxDurability" : "DecayedMaxDurability";
2564+
const nextMax = numberParam(hasMax ? nextValues.max_durability : durability[maxKey], "max durability", 0, 100);
2565+
const nextCurrent = numberParam(hasCurrent ? nextValues.current_durability : durability.CurrentDurability, "current durability", 0, nextMax);
2566+
durability.CurrentDurability = nextCurrent;
2567+
durability[maxKey] = nextMax;
2568+
nextValues.stats = { ...stats, FItemStackAndDurabilityStats: [stats.FItemStackAndDurabilityStats?.[0] || [], durability] };
2569+
}
2570+
delete nextValues.current_durability;
2571+
delete nextValues.max_durability;
2572+
2573+
const rowId = JSON.stringify({ pk: { id: safeItemId } });
2574+
return updateTableRow(tx, "dune", "items", rowId, nextValues);
2575+
});
2576+
}
2577+
25292578
export async function giveItemToStorage(db, storageId, { itemName = "", itemId = "", templateId = "", quantity = 1, quality = 0 }) {
25302579
await requireCapability(await supportsStorageGiveItem(db), "Storage give-item requires compatible dune.inventories and dune.items insert columns.");
25312580
const target = intParam(storageId, "storage id", 1);
@@ -2740,6 +2789,7 @@ async function playerCapabilities(db) {
27402789
craftingRecipes: await supportsCraftingRecipes(db),
27412790
researchItems: await supportsResearchItems(db),
27422791
inventoryDelete: await supportsInventoryDelete(db),
2792+
inventoryEdit: await supportsInventoryEdit(db),
27432793
repairGear: await supportsRepairGear(db),
27442794
repairVehicleDecay: await supportsRepairVehicleDecay(db),
27452795
refuelVehicle: await supportsRefuelVehicle(db),
@@ -2938,6 +2988,10 @@ async function supportsInventoryDelete(db) {
29382988
await functionExists(db, "dune.delete_item(bigint)");
29392989
}
29402990

2991+
async function supportsInventoryEdit(db) {
2992+
return await tableExists(db, "items") && await tableExists(db, "inventories");
2993+
}
2994+
29412995
async function supportsStorageGiveItem(db) {
29422996
if (!(await tableExists(db, "items")) || !(await tableExists(db, "inventories"))) return false;
29432997
const inventoryColumns = await columnsFor(db, "inventories");

console/api/src/server.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ async function handleApi(req, res) {
438438
if (path.match(/^\/api\/players\/[^/]+\/repair-vehicle-decay$/) && req.method === "POST") return playerDbMutation(req, res, path, "players.repair-vehicle-decay", "REPAIR VEHICLE DECAY", (playerId, body) => duneDb.repairVehicleDecay(db, playerId, body));
439439
if (path.match(/^\/api\/players\/[^/]+\/refuel-vehicle$/) && req.method === "POST") return playerDbMutation(req, res, path, "players.refuel-vehicle", "REFUEL VEHICLE", (playerId, body) => duneDb.refuelVehicle(db, playerId, body));
440440
if (path.match(/^\/api\/players\/[^/]+\/inventory\/[^/]+$/) && req.method === "DELETE") return inventoryDeleteRoute(req, res, path);
441+
if (path.match(/^\/api\/players\/[^/]+\/inventory\/[^/]+$/) && req.method === "PATCH") return inventoryUpdateRoute(req, res, path);
441442
if (path.match(/^\/api\/players\/[^/]+\/crafting-recipes$/)) return dbPlayerRoute(res, path, duneDb.playerCraftingRecipes);
442443
if (path.match(/^\/api\/players\/[^/]+\/research-items$/)) return dbPlayerRoute(res, path, duneDb.playerResearchItems);
443444
if (path.match(/^\/api\/players\/[^/]+\/journey$/)) return dbPlayerRoute(res, path, (database, playerId) => duneDb.playerJourney(database, playerId, journeyTagsData));
@@ -1560,6 +1561,13 @@ async function inventoryDeleteRoute(req, res, path) {
15601561
return directDbMutation(req, res, "players.inventory-delete", "DELETE ITEM", () => duneDb.deleteInventoryItem(db, playerId, itemId), { playerId, itemId });
15611562
}
15621563

1564+
async function inventoryUpdateRoute(req, res, path) {
1565+
const parts = path.split("/");
1566+
const playerId = decodeURIComponent(parts[3]);
1567+
const itemId = decodeURIComponent(parts[5]);
1568+
return directDbMutation(req, res, "players.inventory-update", "SAVE ITEM", (body) => duneDb.updateInventoryItem(db, playerId, itemId, body.values), { playerId, itemId });
1569+
}
1570+
15631571
async function storageGiveItemRoute(req, res, path) {
15641572
const storageId = decodeURIComponent(path.split("/")[3]);
15651573
return directDbMutation(req, res, "storage.give-item", "GIVE ITEM TO STORAGE", async (body) => {

0 commit comments

Comments
 (0)