Skip to content

db: ordered migrations, user_items table, and shared inventory helpers - #32

Open
Seltraeh wants to merge 1 commit into
decompfrontier:devfrom
Seltraeh:split/03-schema-foundation
Open

db: ordered migrations, user_items table, and shared inventory helpers#32
Seltraeh wants to merge 1 commit into
decompfrontier:devfrom
Seltraeh:split/03-schema-foundation

Conversation

@Seltraeh

@Seltraeh Seltraeh commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

db: ordered migrations, user_items table, and shared inventory helpers

Branch: split/03-schema-foundation
Base: dev
Merge position: 03 of 13

Part of the PR #28 split. Each PR branches from dev and contains only its
own changes, so this diff is exactly one subsystem. The set is designed to be
merged in numeric order; merging all 13 reproduces PR #28 byte for byte
(verified against tree 79a4e065).

Later PRs in the series touch Handlers.hpp, GmeControllerHandlers.cpp and
UserInfo.cpp too, so once earlier ones land this branch may need a rebase.
Those conflicts are always additions on both sides — keep both. Maintainer
edits are enabled, so feel free to push the rebase directly to this branch.

Three files, but the highest-risk change in the whole series. Everything downstream depends on these table shapes.

What's included

Migration ordering. Migrations lived in an unordered_map and therefore ran in hash order. They are now a vector, running in declaration order. Because a vector does not dedup, a startup guard aborts on a duplicate migration name, preserving the uniqueness the map gave for free.

02072026_ExtendUserUnitsForUnitOps — 26 additive columns on user_units: per-unit stats, two sphere equipment slots, and the favourite flag. Consolidates three earlier fork migrations onto the upstream table shape.

05072026_CreateUserItemsTable — one row per item stack, UNIQUE(user_id, item_id), with instance_id as the warehouse row id the client references.

Structural tables for the later PRs — town, campaign, scenario and summon-ticket tables land here too, so MigrationManager.cpp is touched once rather than by five branches. All are CREATE TABLE IF NOT EXISTS, structural only, never seeded. Say the word if you'd rather each subsystem carried its own and I'll redistribute.

Common.hpp helpers

  • addUserItem() — upserts a stack, incrementing item_num when the species is already owned.
  • returnEquippedSpheres() — reads sphere slots off units about to be deleted and credits them back to the warehouse. Must be called before the DELETE in any handler that consumes units.

Known debt, flagging deliberately

The 26 user_units columns duplicate upstream's unit_lvl / base_rec / bb_*. Upstream columns remain the source of truth for upstream handlers; these serve handlers not yet on PacketInterface. The intent is to consolidate them away as each handler moves. If you'd rather that port happened before any of this merges, that's a reasonable call and I'd like to know now rather than later.

Verification

Fresh-DB boot runs every migration in declared order; second boot skips recorded ones. Duplicate-name guard tested by temporarily doubling an entry.

Migrations previously lived in an unordered_map and therefore ran in hash
order. They are now a vector so they run in declaration order, with a startup
guard that aborts on a duplicate migration name to preserve the uniqueness the
map gave for free.

Adds 02072026_ExtendUserUnitsForUnitOps: 26 additive columns on user_units
covering per-unit stats, the two sphere equipment slots and the favourite
flag. This consolidates three earlier fork migrations onto the upstream table
shape. Upstream columns (unit_lvl, base_rec, bb_*) remain the source of truth
for upstream handlers; these serve handlers not yet moved to PacketInterface
and are intended to be consolidated away as each one moves.

Adds 05072026_CreateUserItemsTable: one row per item stack, keyed
UNIQUE(user_id, item_id), with instance_id as the warehouse row id the client
references.

Common.hpp gains two helpers. addUserItem() upserts a stack, incrementing
item_num when the species is already owned. returnEquippedSpheres() reads the
sphere slots off units that are about to be deleted and credits them back to
the warehouse; it must be called before the DELETE in any handler that
consumes units, or the spheres are destroyed silently.
// shape. Upstream columns (unit_lvl/base_rec/ext_rec/bb_*) remain the
// source of truth for upstream handlers; these serve the not-yet-ported
// quests handlers and are consolidated away as each moves to
// PacketInterface.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont understand why this exists, a lot of it seems like duplicates of user units.

.insert = true,
}),
field<&::UserUnitInfo::ext_rec>("ext_rec", {
field<&::UserUnitInfo::ext_rec>("ext_heal", {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the rename?

.update = true,
.insert = true,
}),
// Extras the read/display path was missing — mapped to the quests mirror

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i actually dont agree with adding everything here - it introduces bloat

for example, do we really need to add fe_bp? or leader_skill_id?

for each of these fields, we should only add them under 2 conditiions (this applies to every single other field as well)

  1. we understand fully what it does in the client
  2. the client needs it either for game functionailty or it crashes


// UPSERT: bump the stack if it exists, else insert a new one. item_id and
// quantity are server-trusted integers, safe to bind.
auto result = co_await database->execSqlCoro(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont use co_await database->execSqlCoro(, use the existing API

if (!database || identity.userId.empty() || userUnitIdList.empty())
co_return;

const auto rows = co_await database->execSqlCoro(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again dont use co_await database->execSqlCoro(

}

/*!
* Returns any spheres equipped on soon-to-be-consumed units to the owner's

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont understand the mechanism here - does the client delete the sphere if we fuse the unit or sell the unit?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants