Skip to content

Database Schema

Esta página se mejorará con alguna alternativa más bonita y legible

El schema está diseñado para PostgreSQL. Las relaciones son explícitas mediante claves foráneas y los ENUMs de PostgreSQL se usan para campos de estado — más eficientes que VARCHAR y autodocumentados.


rarities

id             UUID PRIMARY KEY
name           VARCHAR UNIQUE NOT NULL   -- COMMON, UNCOMMON, RARE, LEGENDARY, MYTHIC
multiplier     DECIMAL(4,2) NOT NULL     -- 1.0, 1.3, 1.8, 2.5, 3.5
recycle_value  INTEGER NOT NULL          -- 5, 15, 40, 120, 400

users

Gestionada por Spring Security. El campo password almacena el hash bcrypt.

id          UUID PRIMARY KEY
username    VARCHAR UNIQUE NOT NULL
email       VARCHAR UNIQUE NOT NULL
password    VARCHAR NOT NULL
coins       INTEGER DEFAULT 0
last_reward TIMESTAMP DEFAULT '2000-01-01'
created_at  TIMESTAMP DEFAULT now()

base_cards

Define las cartas del juego. Cada carta tiene stats base y un rango de variación — el generador Rust sortea entre stat_x_base y stat_x_base + stat_x_range al crear cada instancia, lo que hace que dos cartas del mismo tipo sean ligeramente distintas.

id             UUID PRIMARY KEY
name           VARCHAR NOT NULL
stat_hp_min    INTEGER NOT NULL
stat_hp_avg    INTEGER NOT NULL
stat_hp_max    INTEGER NOT NULL
stat_atk_min   INTEGER NOT NULL
stat_atk_avg   INTEGER NOT NULL
stat_atk_max   INTEGER NOT NULL
stat_def_min   INTEGER NOT NULL
stat_def_avg   INTEGER NOT NULL
stat_def_max   INTEGER NOT NULL
stat_spd_min   INTEGER NOT NULL
stat_spd_avg   INTEGER NOT NULL
stat_spd_max   INTEGER NOT NULL
image_url      VARCHAR
theme          VARCHAR           -- "space", "mythology", "summer_2025"...
contributed_by VARCHAR           -- username del contribuidor de la comunidad
created_at     TIMESTAMP DEFAULT now()

pack_types

Define los tipos de sobre disponibles. Un pack de temporada puede limitarse a un pool de cartas específico y desactivarse automáticamente por fecha — sin cambios en el código.

id              UUID PRIMARY KEY
name            VARCHAR UNIQUE NOT NULL      
description     VARCHAR
card_count      INTEGER NOT NULL DEFAULT 5
cost            INTEGER NOT NULL DEFAULT 25  
available_from  TIMESTAMP      -- NULL = siempre disponible
available_until TIMESTAMP      -- NULL = siempre disponible
created_at      TIMESTAMP DEFAULT now()
Pack Cost Probabilidades
BASIC 100 coins Common 60%, Uncommon 30%, Rare 8%, Legendary 1.5%, Mythic 0.5%
GOLD 250 coins Common 35%, Uncommon 35%, Rare 20%, Legendary 8%, Mythic 2%
PREMIUM 600 coins Common 10%, Uncommon 25%, Rare 35%, Legendary 22%, Mythic 8%

pack_type_cards

Relaciona cada pack con las cartas que puede contener. Un pack de temporada solo incluye las cartas de ese tema. Añadir un pack nuevo es solo insertar filas aquí.

pack_type_id  UUID REFERENCES pack_types(id)
base_card_id  UUID REFERENCES base_cards(id)
PRIMARY KEY   (pack_type_id, base_card_id)

El flujo de apertura de sobre: Java consulta esta tabla para obtener el pool de cartas válidas del pack, y se lo pasa a Rust junto con la seed. Rust solo sortea — no decide qué cartas pertenecen a qué pack.


pack_type_rarity_weights

Define las probabilidades de cada rareza para cada tipo de sobre.

pack_type_id  UUID REFERENCES pack_types(id)
rarity_id     UUID REFERENCES rarities(id)
weight        DECIMAL(5,4) NOT NULL    -- 0.6000, 0.3000, etc.
PRIMARY KEY   (pack_type_id, rarity_id)

player_cards

Una sola tabla para todas las cartas de todos los jugadores. El índice en owner_id garantiza que las consultas del tipo "dame las cartas de este jugador" no escaneen la tabla entera.

id           UUID PRIMARY KEY
owner_id     UUID REFERENCES users(id)
base_card_id UUID REFERENCES base_cards(id)
rarity_id    UUID REFERENCES rarities(id)
stat_hp      INTEGER NOT NULL
stat_atk     INTEGER NOT NULL
stat_def     INTEGER NOT NULL
stat_spd     INTEGER NOT NULL
pack_seed    BIGINT            -- seed del sobre del que salió
obtained_at  TIMESTAMP DEFAULT now()

INDEX idx_player_cards_owner (owner_id)

pack_opens

Historial de sobres abiertos. Guardar el seed permite reproducir exactamente cualquier apertura — útil para auditoría de fairness y para debuggear reportes de jugadores.

id           UUID PRIMARY KEY
player_id    UUID REFERENCES users(id)
pack_type_id UUID REFERENCES pack_types(id)
seed         BIGINT NOT NULL
opened_at    TIMESTAMP DEFAULT now()

Trading

ENUMs de estado para las tablas de trading:

CREATE TYPE trade_status AS ENUM ('OPEN', 'COMPLETED', 'CANCELLED');
CREATE TYPE offer_status AS ENUM ('PENDING', 'ACCEPTED', 'REJECTED');

trade_listings — "ofrezco X, quiero Y"

El primero que tenga la wanted_base_card y acepte ejecuta el swap atómico.

id                   UUID PRIMARY KEY
owner_id             UUID REFERENCES users(id)
offered_card_id      UUID REFERENCES player_cards(id)
wanted_base_card_id  UUID REFERENCES base_cards(id)
status               trade_status DEFAULT 'OPEN'
created_at           TIMESTAMP DEFAULT now()

INDEX idx_listings_status (status)
INDEX idx_listings_wanted (wanted_base_card_id)

trade_requests — "quiero X, ¿qué me das?"

El dueño publica qué carta quiere y espera ofertas.

id                   UUID PRIMARY KEY
owner_id             UUID REFERENCES users(id)
wanted_base_card_id  UUID REFERENCES base_cards(id)
status               trade_status DEFAULT 'OPEN'
created_at           TIMESTAMP DEFAULT now()

trade_offers — respuestas a un request

Cuando el dueño acepta una oferta, el swap se ejecuta en una transacción y todas las demás ofertas de ese request pasan a REJECTED en la misma transacción. No se borran — se conservan para auditoría.

id               UUID PRIMARY KEY
request_id       UUID REFERENCES trade_requests(id)
offerer_id       UUID REFERENCES users(id)
offered_card_id  UUID REFERENCES player_cards(id)
status           offer_status DEFAULT 'PENDING'
created_at       TIMESTAMP DEFAULT now()

INDEX idx_offers_request (request_id)