beasty-save-system / guides / slots-and-metadata.md
Slots y metadatos
Un slot es un archivo de guardado. Esta página explica cómo se nombran, listan y eliminan los slots, y cómo adjuntar un pequeño bloque de metadatos a cada uno para que puedas armar una pantalla de slots de guardado sin cargar los guardados.
Qué es un slot
Un slot es un nombre, no una ruta. El sistema de guardado lo convierte en un archivo:
{DataPath o Application.persistentDataPath}/{Folder}/{slot}.{Extension}
DataPath, Folder y Extension vienen de BeastySaveSettings. Con los valores por defecto, el
slot slot1 en Windows se convierte en algo como:
C:/Users/<tú>/AppData/LocalLow/<Empresa>/<Juego>/Saves/slot1.save
Nunca escribes esa ruta tú mismo. Pasas "slot1" y el sistema resuelve el resto. Si necesitas la
ruta real (para mostrarla en una pantalla de soporte, o para abrir la carpeta), pídela:
string folder = BeastySave.GetFolderPath(settings);
string file = BeastySave.GetSlotPath("slot1", settings);
Reglas de nombrado
Un nombre de slot debe ser un nombre de archivo a secas, sin ruta. Se rechaza si:
- está vacío o solo contiene espacios,
- contiene
/o\, - contiene
.., - es una ruta con raíz (
C:\saves\x,/saves/x), - contiene un carácter que no es válido en un nombre de archivo,
- es un nombre de dispositivo reservado de Windows:
CON,PRN,AUX,NUL,COM1-COM9,LPT1-LPT9.
Los nombres de dispositivo se rechazan en todas las plataformas, no solo en Windows, así que una carpeta de guardado escrita en macOS o Linux sigue siendo utilizable cuando el mismo jugador se pasa a Windows.
Un nombre rechazado nunca toca el disco. La llamada devuelve un resultado fallido con el error
InvalidArgument y un mensaje que dice por qué:
Invalid slot name '../hack': it contains '..'.
Cualquier otro nombre es válido: slot1, autosave, quicksave, Chapter 2 - the well. Si dejas que el jugador
escriba el nombre del slot, compruébalo antes de usarlo, o muestra el mensaje que te da el resultado. Consulta
Resultados y errores.
Exists, Delete, ListSlots
| Llamada | Qué hace |
|---|---|
BeastySave.Exists(slot, settings) | True cuando el archivo del slot está en disco. |
BeastySave.Delete(slot, settings) | Elimina el slot y su backup. Devuelve true si el archivo del slot estaba ahí. |
BeastySave.ListSlots(settings) | Los nombres de cada slot en la carpeta. |
Dos cosas que debes saber sobre Delete. También elimina el archivo .bak, así que un slot eliminado desaparece para siempre;
no queda nada que restaurar. Y devuelve false cuando no había ningún archivo de slot para empezar, lo cual
no es un error.
ListSlots devuelve solo los nombres de slot, sin la extensión, y excluye los backups .bak y
cualquier archivo .tmp residual. La lista viene en orden ordinal — carácter por carácter, no como la
ordenaría una persona: slot10 queda antes que slot2. Si eso te importa, o bien rellena tus nombres con ceros
(slot01, slot02, slot10) o bien ordena la lista tú mismo una vez que hayas leído los metadatos — que es la
siguiente sección, y la razón por la que existe esta página.
Metadatos: lo que una lista de slots realmente necesita
Para dibujar una pantalla de slots de guardado necesitas el nombre del capítulo, el tiempo de juego, el nivel, la fecha. No necesitas el inventario completo del jugador. Cargar doce guardados completos para imprimir doce líneas de texto es un desperdicio, y si los guardados están cifrados es peor: tendrías que descifrarlos todos.

Así que Save recibe un diccionario opcional de strings. Se escribe en el archivo de guardado junto a los datos, y
después puede leerse por sí solo.
using System;
using System.Collections.Generic;
using Beasty_SaveSystem;
using Beasty_SaveSystemCore;
var meta = new Dictionary<string, string>
{
{ "chapter", "The Long Winter" },
{ "playtime", "01:22:40" },
{ "level", "7" },
{ "savedAt", DateTime.UtcNow.ToString("o") },
};
SaveResult result = BeastySave.Save(playerData, "slot1", settings, meta);
Tanto las claves como los valores son strings. Los números, fechas y cualquier otra cosa entran como texto, y salen como texto; convertirlos de vuelta es tarea tuya.
El mismo parámetro existe en el flujo de guardado de escena:
BeastySaveManager.Instance.SaveAllNow("slot1", meta);
Para leerlo de vuelta, sin cargar el guardado:
LoadResult<Dictionary<string, string>> meta = BeastySave.ReadMeta("slot1", settings);
if (meta.Success)
{
string chapter = meta.Value["chapter"];
}
ReadMeta lee el envelope del archivo y se detiene. No toca el payload de datos, no verifica el
checksum, y no necesita la clave de cifrado.
Los metadatos son texto plano, incluso cuando el guardado está encriptado
Este trade-off es deliberado — es lo que hace barata una lista de slots — y conviene que lo tengas claro.
Los datos de un guardado pueden estar cifrados. Los metadatos nunca lo están. Se quedan en texto plano en el archivo, así que
ReadMeta puede construir tu lista de slots en un juego cifrado sin descifrar nada, y así
la ventana Save Manager puede mostrarte un resumen de un slot para el que no tiene clave.
Dos consecuencias:
Advertencia Los metadatos son datos de visualización. No confíes en ellos para decisiones de gameplay. Son legibles y editables por cualquiera con un editor de texto, y
ReadMetalos devuelve sin comprobar el checksum del archivo. Pon el nombre del capítulo en los metadatos. No pongas ahí el oro del jugador y luego lo leas de vuelta como la verdad. La verdad vive en el payload de datos, que se verifica con checksum al cargar.
Tampoco pongas nada privado en ellos. Si un valor no debería ser visible en un editor de texto, su lugar está en los datos, no en el meta. Consulta Cifrado.
Un ejemplo trabajado: resúmenes de slot para una UI
Este método devuelve un resumen por slot, listo para vincular a una lista de botones. Nunca carga un guardado.
using System;
using System.Collections.Generic;
using Beasty_SaveSystem;
using Beasty_SaveSystemCore;
public sealed class SlotSummary
{
public string Slot;
public string Chapter;
public string Playtime;
public int Level;
public DateTime SavedAt;
public bool Unreadable; // el archivo está ahí, pero su envelope no se pudo leer
}
public static class SaveSlots
{
public static List<SlotSummary> List(BeastySaveSettings settings)
{
var summaries = new List<SlotSummary>();
foreach (string slot in BeastySave.ListSlots(settings))
{
var summary = new SlotSummary { Slot = slot };
LoadResult<Dictionary<string, string>> meta = BeastySave.ReadMeta(slot, settings);
if (!meta.Success)
{
// Envelope corrupto o ilegible. Igualmente lista el slot: el jugador debe poder
// seleccionarlo, ver que está dañado, y restaurar su backup.
summary.Unreadable = true;
summaries.Add(summary);
continue;
}
summary.Chapter = Text(meta.Value, "chapter", "Unknown chapter");
summary.Playtime = Text(meta.Value, "playtime", "--:--");
if (int.TryParse(Text(meta.Value, "level", "0"), out int level))
summary.Level = level;
if (DateTime.TryParse(Text(meta.Value, "savedAt", null), out DateTime savedAt))
summary.SavedAt = savedAt;
summaries.Add(summary);
}
// ListSlots ordena ordinalmente. Lo más reciente primero suele ser lo que un jugador quiere.
summaries.Sort((a, b) => b.SavedAt.CompareTo(a.SavedAt));
return summaries;
}
private static string Text(IDictionary<string, string> meta, string key, string fallback) =>
meta.TryGetValue(key, out string value) && !string.IsNullOrEmpty(value) ? value : fallback;
}
Fíjate qué le pasa a un slot dañado: sigue en la lista, marcado como Unreadable. Eso es a propósito. Un
slot que el jugador no puede ver es un slot que el jugador no puede reparar, y cada guardado tiene un backup esperando.
Copias de seguridad y corrupción muestra el resto de ese flujo.
Ver también
- Copias de seguridad y corrupción — restaurar un slot dañado
- Settings —
Folder,Extension,DataPath - Cifrado — qué cubre el cifrado y qué no
- La ventana Save Manager — la misma lista de slots, en el editor
- API de BeastySave — cada método de la fachada
- El formato del archivo de guardado — dónde se ubica
metaen el archivo