Repository navigation
Almacenamiento y blobs
English · Español
Todo lo que la app guarda en local se reparte en dos sitios: app_data_dir (la biblioteca SQLite library.db, el almacén de imágenes direccionado por contenido blobs/, los recursos descargados, la partida en curso y la identidad para jugar en red) y app_cache_dir (ficheros derivados que se pueden borrar en cualquier momento). Los mazos dependen de app_data_dir; nada depende de app_cache_dir.
Código: src-tauri/src/store/ (db.rs, decks.rs, blobs.rs, etiquetas.rs, colecciones.rs, presets.rs, settings.rs, listas.rs, partidas.rs, migrations/*.sql), src-tauri/src/mesa/guardada.rs (la partida en curso), src-tauri/src/state.rs (Paths, AppState::open), src-tauri/src/render.rs (caché de renderizado) y src/stores/deck-store.ts (el lado de la pantalla).
Decisión: las imágenes de los mazos viven en el almacén de blobs bajo app_data_dir/blobs/, no en la caché. Motivo: los mazos referencian las imágenes por SHA-256, así que las imágenes son datos del usuario; el sistema operativo o el usuario pueden vaciar un directorio de caché cuando quieran, y eso nunca debe romper un mazo. La caché (app_cache_dir/renders/) solo guarda renderizados derivados (miniaturas, vistas previas de diseños) que se recalculan a partir de los blobs cuando hacen falta.
AppState::open(paths) (state.rs) crea los dos directorios, abre library.db, ejecuta store::decks::rellenar_refs una sola vez, construye BlobStore::new(data/blobs) y Recursos::abrir(data), y carga (o crea) la clave de identidad para jugar en red con red::identidad::cargar_o_crear(data). Paths { data, cache } se rellena en src-tauri/src/lib.rs con app_data_dir() / app_cache_dir() de Tauri, pasando por red::perfil::carpeta (con un perfil de debug, perfiles/<nombre>/ dentro de cada una).
Db (store/db.rs) envuelve una única rusqlite::Connection en un Mutex (rusqlite no es Sync; los trabajos largos sueltan el cerrojo entre pasos). Al abrir fija journal_mode=WAL, foreign_keys=ON, synchronous=NORMAL y después ejecuta las migraciones. Db::in_memory() se usa en los tests.
Las migraciones son ficheros SQL con include_str! listados en orden en MIGRACIONES (store/db.rs) y registrados en schema_migrations (version, applied_at) (que se crea con CREATE TABLE IF NOT EXISTS antes del bucle). Cada una se ejecuta en su propia transacción, junto con su fila de schema_migrations. Regla: las migraciones solo se añaden; una publicada nunca se edita.
| # | Fichero | Añade |
|---|---|---|
| 1 | 0001_init.sql |
decks, deck_cards (+ índices deck_cards_orden, deck_cards_front, deck_cards_back), blobs, presets (+ presets_kind), app_settings, scryfall_cards (+ scryfall_nombre, scryfall_impresion). |
| 2 | 0002_blob_refs.sql |
blob_refs (owner_kind, owner_id, blob) + blob_refs_blob: referencias a imágenes que no son ni el anverso ni el reverso de una carta (arte del diseñador, emblemas, imágenes «antes»). |
| 3 | 0003_biblioteca.sql |
Organización de la biblioteca: columnas decks.pinned, decks.notes, decks.opened_at, decks.cover_card_id; tablas tags (+ único tags_name sobre name COLLATE NOCASE) y deck_tags (+ deck_tags_tag). |
| 4 | 0004_idioma_deck.sql |
Columna decks.lang_override (idioma del mazo elegido a mano; NULL significa «calculado a partir de las cartas»). |
| 5 | 0005_listas_juego.sql |
Tabla deck_listas (deck_id, id, nombre, comandantes, cartas, updated_at), clave primaria (deck_id, id), deck_id referencia a decks(id) con ON DELETE CASCADE: las listas para jugar de la mesa de juego (Mesa de juego). |
| 6 | 0006_partidas.sql |
Tablas partidas (una fila por partida terminada: hora de inicio y de fin, formato y modo como valores JSON, jugadores y ganadores como JSON, cómo acabó, una nota, rondas, duración, versión del catálogo y registro, el registro comprimido o NULL) y partida_decks (partida_id, jugador, deck_id, puesto), clave primaria (partida_id, jugador), con partida_id referenciando partidas(id) ON DELETE CASCADE; índices partidas_terminada y partida_decks_deck: el historial de partidas (Mesa de juego). |
Test: abrir_dos_veces_aplica_la_migracion_una_sola_vez (db.rs) también ejecuta PRAGMA integrity_check.
erDiagram
decks ||--o{ deck_cards : "ON DELETE CASCADE"
decks ||--o{ deck_tags : "ON DELETE CASCADE"
tags ||--o{ deck_tags : "ON DELETE CASCADE"
decks ||--o{ deck_listas : "ON DELETE CASCADE"
decks }o--o{ blobs : "default_back_blob"
deck_cards }o--o{ blobs : "front_blob / back_blob"
blob_refs }o--|| blobs : "blob"
decks ||--o{ blob_refs : "owner_kind=deck"
presets ||--o{ blob_refs : "owner_kind=preset"
decks ||--o{ blob_refs : "owner_kind=mesa (tapete de la mesa)"
partidas ||--o{ partida_decks : "ON DELETE CASCADE"
decks {
text id PK
text name
text decklist_text
text settings_json
text default_back_json
text default_back_blob
int schema_version
int created_at
int updated_at
int pinned
text notes
int opened_at
text cover_card_id
text lang_override
}
deck_cards {
text id
text deck_id FK
int position
text front_blob
text back_blob
text card_json
}
blobs {
text hash PK
text ext
text mime
int width
int height
int bytes
text origin
int created_at
}
blob_refs {
text owner_kind
text owner_id
text blob
}
presets {
text id PK
text kind
text name
text json
int updated_at
}
tags {
text id PK
text name
text color
int created_at
}
deck_tags {
text deck_id
text tag_id
}
deck_listas {
text deck_id
text id
text nombre
text comandantes
text cartas
int updated_at
}
app_settings {
text key PK
text value
}
scryfall_cards {
text id PK
text oracle_id
text name
text lang
text set_code
text collector_number
text json
int fetched_at
}
partidas {
text id PK
int empezada
int terminada
text formato
text modo
text jugadores
text ganadores
text como
text nota
int rondas
int duracion_ms
int version_catalogo
blob registro
}
partida_decks {
text partida_id FK
int jugador
text deck_id
int puesto
}
Notas sobre las tablas:
- La clave primaria de
deck_cardses(deck_id, id);front_blob/back_blobson copias desnormalizadas de los hashes de imagen de la carta, para que el recolector de basura y la biblioteca encuentren las imágenes en uso sin interpretarcard_json. ElDeckCardcompleto escard_json. Un id de carta duplicado dentro de un mismo mazo se convierte enInvalidInputconmotivo = duplicate_card_id. -
decks.default_back_jsones elFaceImageserializado;default_back_blobes su hash (por la misma razón). -
presets.kindes uno defrontOffsets,backOffsets,print(store/presets.rs, se guardan por nombre: mismo tipo y nombre sustituye) ocollection(store/colecciones.rs, se guarda por id para que renombrar no cree una segunda colección; una colección que ya no se deserializa, por ejemplo de una versión futura, la omitelist). -
scryfall_cardses una caché de metadatos (TTL de una semana,scryfall/cache.rs; los precios se refrescan a las 24 h). Ten en cuenta que, aunque es una caché, vive dentro delibrary.dbenapp_data_dir, porque las estadísticas y la biblioteca funcionan sin conexión a partir de ella (commands/stats.rs,biblioteca.rs). -
deck_listas(store/listas.rs) guarda las listas para jugar de la mesa de juego:comandantesycartasson arrays JSON de ids de copias del deck (DeckCard.id). Viven aparte del deck, como las etiquetas, porquedecks::savereescribe el deck en cada autoguardado y las pisaría; los ids que ya no están en el deck solo se avisan al comprobar la lista.guardarinserta o sustituye por(deck_id, id)(un id nuevo si viene vacío) con el nombre limpio y cortado a 60 caracteres. -
partidasypartida_decks(store/partidas.rs) son el historial de la mesa de juego.registroes laPartidaGuardadade la partida (el mismo JSON comprimido que la partida guardada,mesa/guardada.rs) oNULLcuando quien la guardó nunca tuvo la partida entera (un invitado en red).partida_decksno tiene clave foránea adecksa propósito: borrar un deck deja sus partidas, con el nombre del deck escrito dentro del JSON de los jugadores. Guardar una partida con un id que ya existe la reemplaza (y a suspartida_decks); jugar nunca tocadecks.updated_at. Cada jugador del JSON lleva también la lista para jugar usada (listaId,lista,cartasLista), escrita desdedeck_listasal guardar la partida; las filas guardadas antes se leen sin ellos. -
tags.colores uno deCOLORES(gray red orange amber green teal sky blue violet pink); un color desconocido se queda en gris; los nombres de etiqueta se recortan, se limitan a 40 caracteres, son únicos sin distinguir mayúsculas ycreatedevuelve la etiqueta existente si el nombre ya existe. - Las fechas son milisegundos desde 1970 (
store::ahora_ms).
decks::save y decks::rename fijan updated_at. Fijar como favorito, las notas (máx. 4000 caracteres), la carta de portada, el idioma a mano, las etiquetas, las listas para jugar y «abierto en» deliberadamente no lo tocan (etiquetas.rs, decks::marcar_abierto), para que organizar la biblioteca no la reordene por fecha de modificación. Test: favorito_notas_y_portada_no_cambian_la_fecha.
| Función | Comportamiento |
|---|---|
create(db, name, default_back) |
Uuid nuevo, PrintSettings por defecto, DECK_SCHEMA_VERSION, y después save. La capa de comandos (commands/library.rs crear_deck) le pasa el reverso genérico. |
open(db, id) |
Lee la fila del mazo y todos los card_json ordenados por position; NotFound si no existe, Db si el JSON ya no se deserializa. deck_open también llama a marcar_abierto; deck_peek no. |
save(db, deck) |
Una transacción. Primero valida que toda imagen referenciada existe en blobs (anversos, reversos, reverso del mazo e imágenes del diseñador de refs_del_deck); si falta alguna, InvalidInput, motivo = blob_missing. Después hace upsert de la fila de decks (conserva created_at y las columnas de la biblioteca), borra y vuelve a insertar todos los deck_cards en orden, y reescribe los blob_refs del mazo. Devuelve la hora de guardado. |
list(db) |
Un DeckSummary por mazo para la biblioteca: número de cartas, portada (la carta elegida si sigue presente y con imagen; si no, la primera carta con imagen), etiquetas. Ordenado por updated_at DESC. |
duplicate(db, id, name) |
Lo abre, asigna uuids nuevos al mazo y a cada carta, lo guarda y después copia notas, portada (reasignada al id nuevo de la carta), idioma a mano y etiquetas (no el favorito). Las imágenes se comparten, no se copian. |
delete(db, id) |
Borra el mazo (la cascada elimina cartas, etiquetas y listas para jugar), sus blob_refs (owner_kind = 'deck'), la referencia de su tapete de la mesa de juego (owner_kind = 'mesa') y el ajuste de ese tapete (mesa.tapete.<id>). Los ficheros de imagen no se eliminan; lo hace después la recolección de basura. |
rellenar_refs(db) |
Relleno único de blob_refs a partir de los mazos existentes, protegido por la clave blob_refs_v1 de app_settings (una tabla vacía no es un marcador válido porque un mazo sin diseños la deja vacía). |
Tests: guardar_y_reabrir_135_cartas_da_lo_mismo, no_se_guarda_una_carta_con_una_imagen_que_no_existe, dos_cartas_con_el_mismo_id_es_un_error_claro, duplicar_renombrar_y_borrar, la_limpieza_respeta_el_arte_de_los_disenos, el_relleno_de_referencias_se_hace_una_vez.
BlobStore (store/blobs.rs) es un almacén de imágenes direccionado por contenido.
- Estructura:
app_data_dir/blobs/<primeros dos caracteres hex>/<sha256>.<ext>(BlobStore::path_of).extesjpg,pngowebp(proxy_core::imaging::probe::Kind). - La identidad es el SHA-256 de los bytes del fichero, en hexadecimal minúscula, 64 caracteres (
es_huellalo valida y se reutiliza para validar rutas que vienen de paquetes y del protocolo de imágenes). Dos mazos con la misma carta comparten un único fichero. - Fila de metadatos en
blobs:ext,mime,width,height,bytes,origin,created_at.
| Función | Comportamiento |
|---|---|
put_bytes(db, bytes, origin) -> FaceImage |
Detecta el formato (ErrorCode::Unsupported si no es una imagen admitida; si está corrupta, ErrorCode::Image con motivo = corrupt), rechaza cualquier lado superior a LADO_MAXIMO = 12000 px (Image, motivo = too_large), calcula el hash, escribe de forma atómica si no existe y hace INSERT OR IGNORE de la fila. Si el blob ya existía, el origin devuelto es con el que entró la primera vez. |
import_files(db, paths) |
Lee ficheros del usuario y les aplica put_bytes como Upload. Copia, nunca enlaza, así que borrar el original no puede romper un mazo. |
generic_back(db) |
Inserta el reverso genérico incluido (assets/reverso-generico.png, 745x1040, origen Generated) y guarda su hash en app_settings bajo generic_back_blob. |
locate(db, hash) |
Ruta y Kind de un blob conocido (busca ext en blobs); None para hashes mal formados. |
gc(db, ocupado) |
Véase más abajo. |
escribir_atomico(dest, bytes) |
Escribe en un temporal .part en la misma carpeta (su nombre es único en cada escritura: id del proceso y un contador, porque la preparación de la mesa escribe en paralelo y dos copias de la misma carta, o dos decks que la comparten, escriben el mismo destino a la vez; con un nombre fijo uno renombraba el temporal del otro y fallaba con «no existe el fichero», que se veía como «no se puede escribir en disco»; test escribir_a_la_vez_el_mismo_destino_no_falla), hace sync_all y renombra; un fallo nunca deja un fichero a medias con el nombre final. Se reutiliza en casi todas las descargas de la app. |
BlobStore::gc borra los blobs que nada usa y que tienen más de 24 horas (GRACIA_MS). Un blob se conserva si se cumple cualquiera de estas condiciones:
-
deck_cards.front_bloboback_blobes igual a su hash. -
decks.default_back_blobes igual a él. - Una fila de
blob_refsapunta a él (arte del diseñador, emblema del diseñador, imágenes «antes», emblemas de colecciones, tapetes de la mesa de juego, la imagen de los dados, el avatar para jugar en red). - Es el reverso genérico (
app_settings.generic_back_blob). - Se creó hace menos de 24 horas.
Por qué el periodo de gracia: una imagen puede pertenecer a una descarga cuyo mazo aún no se ha guardado. Por qué ocupado: gc devuelve ErrorCode::Busy mientras hay un trabajo de resolución o exportación en marcha (AppState::ocupado()), porque el trabajo puede tener hashes que ninguna fila de mazo referencia todavía. ocupado() también es verdadero mientras hay una partida abierta en la mesa (Mesa de juego). El único que la llama es blob_gc (commands/library.rs), lanzado por la acción «Liberar espacio» de la pantalla de la biblioteca (src/features/library/library-screen.tsx); no hay recolección automática. Primero se elimina el fichero (si falta no pasa nada) y después la fila de blobs.
Los propietarios de blob_refs: owner_kind = 'deck' (se reescribe en cada decks::save a partir de CardDesign::blobs()), 'preset' (el emblema de una colección, escrito en colecciones::save y eliminado en colecciones::delete) y 'mesa' (el tapete de la mesa de juego: owner_id es el id del deck o general; lo escribe y lo quita mesa/tapete.rs::guardar, y se borra con su deck en decks::delete; con owner_id = 'dados', la imagen de los dados del jugador, que escribe mesa/dados.rs::guardar) y 'red' (owner_id = 'avatar', el avatar para jugar en red, que escribe y quita red/identidad.rs::guardar). El tapete no puede usar 'deck' porque decks::save reescribe las referencias 'deck' de un deck en cada guardado y lo borraría. Test: la_limpieza_solo_borra_lo_huerfano_y_viejo.
Trampa conocida: cualquier lugar nuevo que guarde un hash de blob debe quedar reflejado en una de las cuatro protecciones anteriores, o la recolección acabará borrando la imagen y el mazo fallará al guardarse o imprimirse (save se niega a guardar una referencia a un blob inexistente, así que normalmente lo notarás en el siguiente guardado).
AppState::carpeta_renders() (render.rs) es el único directorio de caché que gestiona la interfaz de la app. Contenido, todo derivado y regenerable bajo demanda:
Ruta bajo renders/
|
Contenido | Lo escribe |
|---|---|---|
<key>.jpg |
Miniaturas de vista previa procesadas (clave de RenderSpec::key()) |
AppState::miniatura (render.rs) |
t/<hash>.jpg |
Miniaturas de resultados de búsqueda obtenidas de Scryfall u otras fuentes | protocol.rs |
d/<hash>-<width>.png |
Vistas previas de cartas del diseñador |
diseno/vista.rs imagen
|
c/<hash>-<width>.png, c/<hash>.json
|
«Capa sin ilustración» de la carta del diseñador y la información del hueco de la ilustración |
diseno/vista.rs capa
|
b/<blob>-<side>.jpg o .png
|
Ilustración reducida para el arrastre en vivo |
diseno/vista.rs arte_reducido
|
m/<sha256 o s-<id>-<cara>>-<ancho>.jpg |
Imágenes de la mesa de juego reducidas al ancho que pide la pantalla |
mesa/imagenes.rs servir
|
El renderizado del PDF no tiene caché en disco («pesa demasiado», render_para_pdf). Los comandos cache_stats / cache_clear (commands/library.rs) miden y hacen remove_dir_all exactamente de esta carpeta; se exponen en el panel de la lista (src/features/decklist/decklist-panel.tsx) con un texto de confirmación. Test: vaciar_la_cache_no_toca_las_imagenes_de_los_decks.
Se accede mediante store/settings.rs (get, set, upsert) y, desde la pantalla, los comandos genéricos app_setting_get / app_setting_set (src/lib/ipc.ts). Claves vistas en el código:
| Clave | La escribe | Significado |
|---|---|---|
last_deck_id |
src/stores/deck-store.ts |
Mazo que se abre al arrancar. |
generic_back_blob |
BlobStore::generic_back |
Hash del reverso genérico; lo protege de la recolección. |
blob_refs_v1 |
decks::rellenar_refs |
Marca que blob_refs ya se rellenó. |
carpeta_importar, carpeta_exportar
|
commands/carpetas.rs |
Última carpeta usada por los diálogos de fichero de importar / exportar (se guardan por separado). |
mesa.tapete.<id del deck>, mesa.tapete
|
mesa/tapete.rs |
El tapete de la mesa de juego de un deck, o el general: JSON { blob, velo, desenfoque }. La clave del deck se borra en decks::delete. |
mesa.dados |
mesa/dados.rs |
El aspecto de los dados y la moneda del jugador (AspectoDados, JSON). |
red.apodo, red.avatar
|
red/identidad.rs |
El apodo y la huella del blob del avatar para jugar en red. |
Otras preferencias de la interfaz (tema, skin, idioma, tamaños de paneles, vista de la biblioteca) no están aquí; son claves de localStorage con el prefijo proxybuilder. (por ejemplo proxybuilder.lang, proxybuilder.theme, proxybuilder.preferencias, proxybuilder.biblioteca), véanse Ajustes y shell y Temas y skins.
El mazo abierto vive en el store de zustand useDeck (deck, guardado, errorGuardado, version, pasado, futuro). Véase también Stores de estado.
-
Un único punto de entrada. Todo cambio del usuario pasa por
aplicar(cambio): empuja una instantánea (cards,settings,defaultBack; no el nombre ni el texto de la lista) apasado(con topeMAX_HISTORIAL = 100), vacíafuturo, poneguardado = "dirty"y subeversion.setCartasysetAjustesson envoltorios. Ninguna pantalla llama adeck_savepor su cuenta. -
Cambios que no se pueden deshacer.
setSinHistorial(nombre,decklistText) marca como sucio sin historial.refrescarCartas(antes, nuevas)sustituye cartas (por ejemplo miniaturas de diseños regeneradas) sin historial y solo para las cartas que siguen siendo el mismo objeto que enantes, de modo que una edición concurrente del usuario gana. -
Deshacer/rehacer.
deshacer/rehacerintercambian instantáneas entrepasadoyfuturoy marcan como sucio (así que deshacer también se guarda).cargar(deck)reinicia el historial. -
Autoguardado.
iniciarAutoguardado(retardoMs = 800)(se inicia ensrc/features/shell/use-deck-inicial.ts) se suscribe a los cambios deversionmientras está sucio y retarda un temporizador de800 ms. Solo hay un guardado en curso a la vez: si llega otro cambio durante el guardado, encadena otro guardado después con el último mazo. Tras guardar, el estado pasa a"saved"solo siversionno se movió entretanto; si no, sigue en"dirty". Si falla, el estado es"error"(conerrorGuardadofijado); el siguiente cambio vuelve a armar el guardado. -
Volcado inmediato.
guardarAhora()guarda al instante si no está ya guardado; se llama antes de abrir otro mazo, de duplicar o exportar desde la biblioteca y antes de que se instale una actualización de la app. -
Arranque.
abrirDeckInicial(nombre)leelast_deck_id, pruebadeck_openy, si falla, recurre adeck_create; después escribelast_deck_id. -
Lado de Rust.
deck_save->decks::savereescribe el mazo entero en una transacción (véase más arriba), de modo que un guardado es atómico: o queda el mazo nuevo entero o el anterior.
Tests: src/stores/__tests__/deck-store.test.ts (cinco cambios rápidos guardan una sola vez, deshacer x3 restaura y rehacer repite, error y reintento, cambios sin historial), deck-store-abrir.test.ts.
Todo lo que sigue es relativo al app_data_dir o app_cache_dir de la plataforma (los resuelve Tauri; las rutas no están fijadas en el código). «Se puede borrar» significa que no se pierden datos de mazos; la entrada se recrea o se vuelve a descargar cuando hace falta.
| Ruta | Contenido | Lo crea | ¿Se puede borrar? |
|---|---|---|---|
library.db (+ -wal, -shm) |
Biblioteca SQLite: mazos, cartas, etiquetas, presets (incluidas las colecciones), ajustes, scryfall_cards
|
store/db.rs |
No. Borrarla borra todos los mazos. Cierra antes la app; copia los tres ficheros juntos. |
blobs/<xx>/<sha256>.<ext> |
Imágenes de los mazos (direccionadas por contenido) | store/blobs.rs |
No. Los mazos quedarían referenciando imágenes inexistentes. Usa en su lugar «Liberar espacio» de la app. |
modelos/ |
Dos modelos de OCR (text-detection.rten, text-recognition.rten, ~12 MB), descargados la primera vez que se usa el OCR |
ocr.rs |
Sí (se vuelven a descargar cuando se usa el OCR otra vez). |
temas/<id>.json, temas/fuentes/<hash>.woff2
|
Temas de tweakcn instalados (convertidos a CSS) y sus Google Fonts | temas.rs |
Sí, pero esos temas desaparecen hasta que se reinstalen. |
simbolos/ (indice.json + SVG) |
Símbolos de maná y de sets de Scryfall, renovados cuando el índice tiene más de 30 días | diseno/recursos.rs |
Sí (se vuelven a descargar). |
fuentes/<sha256>.<ttf|otf>, fuentes/licencias/
|
Letras importadas por el usuario o del catálogo de letras libres, con sus licencias |
diseno/recursos.rs, diseno/letras_libres.rs
|
Solo si aceptas que los diseños que las usan recurran a otra letra. Las letras importadas por el usuario no se pueden recuperar. |
marcos/<id>/ |
Packs de marcos: original (marcos de Card Conjurer, descargado), packs descargables como Balatro y packs importados por el usuario |
diseno/original.rs, diseno/descargables.rs, diseno/recursos.rs
|
Los descargados: sí (se vuelven a descargar y se comprueba su SHA-256). Los packs importados por el usuario: no hay copia en ningún otro sitio. |
packs-extras/<id>/ |
Contenido extra de los packs descargables (reversos, fichas, cartas hechas), guardado aparte del pack | diseno/descargables.rs |
Sí (se vuelve a descargar). |
partidas/actual.json.gz |
La partida en curso de la mesa (PartidaGuardada): versión del catálogo, configuración, decks preparados, partida inicial, eventos, hora del guardado y el marcador del «al mejor de», como JSON en gzip escrito de forma atómica. Se escribe al abrir la partida, cada 50 eventos (CADA), a los 20 s con eventos sin guardar (CADA_MS) y cuando se fuerza (mesa_guardar, que la pantalla llama al salir de la pestaña o cerrar la ventana; mesa_salir con guardar; mesa_siguiente_partida). No lo escribe un invitado en red (la partida la guarda el anfitrión) ni durante una repetición. Lo borra mesa_salir sin guardar (no al salir de una repetición). Las partidas terminadas van a la tabla partidas
|
mesa/guardada.rs, commands/mesa.rs
|
Sí, pero se pierde la partida en curso. Un fichero de un catálogo más nuevo se rechaza con Unsupported (motivo: version). |
identidad/clave |
La clave de iroh de la instalación (32 bytes, ed25519, permisos 0600, escrita de forma atómica): la identidad del jugador en red y en el servidor de salas. Al importar una identidad, la clave anterior se queda en identidad/clave.anterior
|
red/identidad.rs |
Solo si aceptas ser un jugador nuevo (los playgroups pedirían otra invitación). Expórtala antes (Ajustes > Mesa de juego) para conservarla. |
perfiles/<nombre>/ |
Otra carpeta de datos entera para una instancia de debug (PB_PERFIL; nunca en release) |
red/perfil.rs |
Sí. |
letras-cjk/<código>.ttf, letras-cjk/Noto-CJK-OFL.txt
|
Noto Sans JP/KR/SC/TC para las cartas diseñadas en japonés, coreano y chino (ja, ko, zhs, zht; unos 10–18 MB cada una) y su licencia OFL, bajadas la primera vez que un deck usa uno de esos idiomas de diseño |
diseno/letras_cjk.rs, diseno/recursos.rs (guardar_cjk, leer_cjk) |
Sí (se vuelven a bajar y se comprueba su SHA-256 al elegir otra vez el idioma; mientras, esas cartas enseñan el aviso de que faltan letras). |
| Ruta | Contenido | Lo crea | ¿Se puede borrar? |
|---|---|---|---|
renders/ (+ t/, d/, c/, b/, m/) |
Miniaturas derivadas, vistas previas del diseñador e imágenes reducidas de la mesa de juego |
render.rs, protocol.rs, diseno/vista.rs, mesa/imagenes.rs
|
Sí (botón en el panel de la lista). |
mesa/ (<xx>/<sha256>.jpg, s/<id>-<cara>.jpg, i/<clave>.json) |
Caras de las cartas de la mesa de juego preparadas a 745 px (ANCHO_MESA, JPEG 85), impresiones de Scryfall, tapetes reducidos a 1920 px de ancho (ANCHO_TAPETE, clave del índice t-<blob>; el tapete en sí sigue en blobs/) y el índice de clave de preparación a imagen |
mesa/imagenes.rs, mesa/preparar.rs, mesa/tapete.rs
|
Sí (se vuelven a preparar al empezar o seguir una partida). |
mesa/s/<id de Scryfall>-<cara>.jpg |
Las caras de Scryfall que enseña «Originales en mi idioma» (y las que una partida en red baja de Scryfall), tal como se bajaron |
mesa/imagenes.rs, mesa/originales.rs
|
Sí (se vuelven a bajar al enseñarlas). |
mesa/r/<xx>/<sha256>.jpg |
En una partida en red, los bytes de las imágenes propias de los demás tal como llegaron (los que dan la SHA-256, para pasárselos a otro); la copia saneada que se ve va a mesa/<xx>/
|
red/imagenes.rs |
Sí (se vuelven a pedir al enseñarlas). |
scryfall-card-names.json, scryfall-set-codes.json
|
Catálogo de nombres de cartas y códigos de sets de Scryfall para el reconocimiento local del OCR, renovado cada semana | scryfall/nombres.rs |
Sí (se vuelve a descargar cuando hace falta; requiere red). |
cockatrice-tokens.xml |
Base de datos de fichas de Cockatrice (solo se usan las fichas que Scryfall no tiene), renovada cada semana | fuentes/cockatrice.rs |
Sí. |
mtgjson/nombres.sqlite |
Nombres de cartas sin conexión en otros idiomas (opcional), construidos a partir de MTGJSON | fuentes/mtgjson.rs |
Sí (una acción de Ajustes también lo elimina). |
perfiles/<nombre>/ (+ webview/) |
La caché de una instancia de debug (PB_PERFIL), con su propia carpeta de datos de la vista web para que las instancias no compartan localStorage
|
red/perfil.rs, lib.rs (crear_ventana) |
Sí. |
- Fichero de registro
proxy-builder.logen elapp_log_dirde Tauri (registro.rs): ahí se registra cada petición de red, para poder comprobar que abrir un mazo guardado no descarga nada. - La geometría de la ventana la guarda
tauri-plugin-window-state(registrado enlib.rs, con el indicador de visibilidad excluido). Deducido: escribe en el directorio de configuración de la app; no se lee en este repo. - La gestión del paquete descargado por el actualizador y de su firma se describe en Versiones y actualizaciones automáticas.
-
app_settings:red.apodo(apodo) yred.avatar(la huella de un blob, protegida con una fila deblob_refsde tipored/avatar). El aspecto de los dados esmesa.dados(su imagen,mesa/dados). - El servidor de salas guarda su propio SQLite (jugadores, playgroups, invitaciones) en el servidor; las salas viven en su memoria. La app no guarda nada de ellas: tras un reinicio vuelve a registrar su sala. Ver Juego en línea.
| Situación | Resultado |
|---|---|
Guardar un mazo cuya carta referencia una imagen que no está en blobs
|
InvalidInput (motivo = blob_missing, blob = <hash>); el estado del autoguardado pasa a "error". |
| Dos cartas con el mismo id en un mazo |
InvalidInput (motivo = duplicate_card_id). |
| Bytes de imagen no admitidos o corruptos |
Unsupported / Image (motivo = corrupt). |
| Imagen de más de 12000 px por lado |
Image (motivo = too_large). |
blob_gc mientras corre un trabajo o hay una partida abierta |
Busy. |
| JSON guardado que ya no se deserializa |
Db (con detalle). |
| Mutex de la base de datos envenenado |
Internal. |
Los códigos de error se definen en src-tauri/src/error.rs; véase IPC y manejo de errores.
- Columna persistida nueva: añade
000N_*.sql, añádelo aMIGRACIONESy no edites nunca los ficheros antiguos. Recuerda que el valor por defecto deALTER TABLE ... ADD COLUMNdebe valer para las filas existentes. - Datos nuevos que referencian una imagen: guarda su hash en un lugar protegido (véase la lista de la recolección) y añádelo a
refs_del_deck/CardDesign::blobs(). - Recurso descargado nuevo: elige
app_data_dirsi las funciones dependen de que siga ahí,app_cache_dirsi se puede recrear por completo; escribe conescribir_atomico.
Use and build · Uso y desarrollo
- User-Guide · Guía de usuario
- Development-Setup · Preparación del entorno de desarrollo
- Testing-and-CI · Tests y CI
- Releases-and-Auto-Updates · Versiones y actualizaciones automáticas
- Conventions · Convenciones
Architecture · Arquitectura
- Architecture · Arquitectura
- IPC-and-Error-Handling · IPC y manejo de errores
- Rust-App-Layer · Capa de aplicación Rust
Data · Datos
- Deck-Model · Modelo del mazo
- Storage-and-Blobs · Almacenamiento y blobs
- Deck-Package-Format · Formato del paquete de mazo
- Deck-Library · Biblioteca de mazos
Getting cards · Conseguir cartas
- Decklist-Parsing · Parseo de la lista del mazo
- Scryfall-Integration · Integración con Scryfall
- External-Sources · Fuentes externas
- Card-Identification-and-OCR · Identificación de cartas y OCR
Layout, images, PDF · Maquetación, imagen y PDF
- Layout-Engine · Motor de maquetación
- Imaging-and-PDF · Imagen y PDF
- Export · Exportación
- Live-Preview · Vista previa en vivo
- Statistics · Estadísticas
Card designer · Diseñador de cartas
- Designer-Overview · Visión general del diseñador
- Designer-Engine · Motor del diseñador
- Designer-Composition · Composición del diseñador
- Frame-Packs · Packs de marcos
- Designer-App-Layer · Capa de aplicación del diseñador
- Designer-UI · Interfaz del diseñador
- Card-Editor-Dialog · Diálogo Editar carta
Game table · Mesa de juego
Frontend · Frontend
- Frontend-Architecture · Arquitectura del frontend
- State-Stores · Stores de estado
- Internationalization · Internacionalización
- Themes-and-Skins · Temas y skins
- Settings-and-Shell · Ajustes y shell
- Tutorials · Tutoriales
- Decklist-Panel · Panel de la lista del mazo
- Add-Card-and-Variants · Añadir carta y variantes
- Set-Browser · Explorador de sets
Reference · Referencia