beasty-save-system / reference / api-beastysave.md

API de BeastySave

BeastySave es la fachada estática y el único punto de entrada del sistema de guardado. Cada método de guardado, carga y slots recibe un BeastySaveSettings y devuelve un resultado tipado. Nada en esta página lanza excepciones, excepto los tres métodos de registro, que lanzan excepciones ante un error del desarrollador.

Espacios de nombres

using Beasty_SaveSystem;          // BeastySave, BeastySaveable, BeastySaveManager
using Beasty_SaveSystemCore;      // BeastySaveSettings, SaveResult, LoadResult, BeastySaveError,
                                  // IBeastyConverter, ConverterUtil, BeastySaveLog, BeastySaveLogLevel
using Beasty_SaveSystemCore.Json; // JsonNode — necesario solo para migraciones y convertidores personalizados

Nombres de slot

Todo método que recibe un slot lo valida. Un slot es un nombre de archivo a secas, sin ruta. Se rechaza con InvalidArgument cuando está vacío o solo contiene espacios, contiene / o \, contiene .., es una ruta enraizada, contiene caracteres inválidos en un nombre de archivo, o 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, de modo que una carpeta de guardado escrita en Linux sigue siendo utilizable en Windows.

Nota Con un backend de almacenamiento configurado (BeastySaveSettings.StorageId), cada llamada de esta página también puede fallar con BackendUnavailable (el módulo del backend no compiló) y — con un backend en la nube — AuthRequired o NetworkError. Una llamada síncrona sobre un backend solo asíncrono falla con BackendRequiresAsync en lugar de bloquear. Consulta Backends de almacenamiento.

Guardar

public static SaveResult Save(object data, string slot, BeastySaveSettings settings,
                              IDictionary<string, string> meta = null)

Serializa data, lo envuelve en un sobre (envelope) y lo escribe en el slot de forma atómica. meta es un diccionario de strings opcional almacenado en texto plano junto al payload; consulta Formato del archivo de guardado. Devuelve SaveResult.Ok() o un fallo.

Errores: InvalidArgument (data nulo, settings nulo, nombre de slot inválido), SerializationFailed (el objeto no puede convertirse en JSON: un ciclo de referencias, un float NaN/Infinity, una clave de diccionario no soportada, un ulong mayor que long.MaxValue, una referencia a UnityEngine.Object en una clase C# plana), IoError (la carpeta no puede crearse, el disco está lleno, el archivo está bloqueado).

public static Task<SaveResult> SaveAsync(object data, string slot, BeastySaveSettings settings,
                                         IDictionary<string, string> meta = null)

Mismo contrato, con la escritura del archivo realizada de forma asíncrona. La serialización y el cifrado se siguen ejecutando en el hilo que llama. Mismos códigos de error. Consulta Guardado asíncrono.

Cargar

public static LoadResult<T> Load<T>(string slot, BeastySaveSettings settings)

Lee el slot y mapea el payload en un nuevo T. En caso de éxito, Value contiene el objeto. El type del sobre debe coincidir con typeof(T).FullName.

Errores: InvalidArgument, FileNotFound, IoError, ParseError, Corrupt, VersionTooNew, DecryptFailed, TypeMismatch, MigrationFailed, FieldMapFailed.

public static Task<LoadResult<T>> LoadAsync<T>(string slot, BeastySaveSettings settings)

Mismo contrato, leyendo el archivo de forma asíncrona. Mismos códigos de error.

public static LoadResult LoadInto(object target, string slot, BeastySaveSettings settings)

Carga el slot sobre un objeto que ya existe, en lugar de crear uno. Esta es la única forma de cargar un MonoBehaviour o cualquier otro UnityEngine.Object: nunca se construyen a partir de datos del archivo. El type del sobre debe coincidir con target.GetType().FullName.

Errores: los mismos que Load<T>, más InvalidArgument cuando target es nulo.

public static Task<LoadResult> LoadIntoAsync(object target, string slot, BeastySaveSettings settings)

Mismo contrato, leyendo el archivo de forma asíncrona. Mismos códigos de error.

Nota BeastySaveSettings.Strict decide qué ocurre con un campo incorrecto: en modo estricto falla toda la carga y no aplica nada, en modo tolerante omite el campo y lo reporta en LoadResult.Warnings. Consulta Carga estricta vs. tolerante.

Slots

public static bool Exists(string slot, BeastySaveSettings settings)

Verdadero cuando el archivo del slot está en disco. Falso si settings es nulo o el nombre de slot es inválido. No abre ni valida el archivo.

public static bool Delete(string slot, BeastySaveSettings settings)

Elimina el archivo del slot y su .bak. Devuelve verdadero cuando el archivo del slot en sí fue eliminado. Hace lo que puede: un archivo bloqueado se omite en silencio, y nunca se lanza una excepción.

public static string[] ListSlots(BeastySaveSettings settings)

Nombres de slot en la carpeta de guardado, ordenados en orden ordinal. Las copias de seguridad (.bak) y los archivos temporales a medio escribir (.tmp) quedan excluidos. Devuelve un array vacío cuando la carpeta no existe.

public static LoadResult<Dictionary<string, string>> ReadMeta(string slot, BeastySaveSettings settings)

Lee únicamente el diccionario meta del sobre. No verifica el checksum, no descifra y nunca toca el payload, por lo que funciona en un guardado cifrado sin la clave. Esto es lo que debería llamar una pantalla de selección de slots. Consulta Slots y metadatos.

Errores: InvalidArgument, FileNotFound, IoError, ParseError, Corrupt (la forma del sobre es inválida).

public static SaveResult RestoreBackup(string slot, BeastySaveSettings settings)

Copia <slot>.<ext>.bak sobre el archivo del slot, de forma atómica. El .bak se deja en su lugar, así que restaurar dos veces es seguro. Consulta Copias de seguridad y corrupción.

Errores: InvalidArgument, FileNotFound (no hay copia de seguridad para ese slot), IoError.

Los gemelos asíncronos

Cada método de slot tiene una contraparte asíncrona con los mismos argumentos, el mismo tipo de resultado y los mismos códigos de error:

public static Task<bool>     ExistsAsync(string slot, BeastySaveSettings settings)
public static Task<bool>     DeleteAsync(string slot, BeastySaveSettings settings)
public static Task<string[]> ListSlotsAsync(BeastySaveSettings settings)
public static Task<LoadResult<Dictionary<string, string>>> ReadMetaAsync(string slot, BeastySaveSettings settings)
public static Task<SaveResult> RestoreBackupAsync(string slot, BeastySaveSettings settings)

En un backend solo asíncrono (una base de datos en la nube) son la única forma que funciona — las formas síncronas devuelven BackendRequiresAsync. Consulta Guardado asíncrono.

JSON sin archivos

Cuatro llamadas producen y consumen texto de guardado en lugar de archivos, para quien tiene su propio transporte — un endpoint HTTP propio, una cola de mensajes, la API de guardado en la nube de una plataforma. No interviene ningún backend de almacenamiento.

public static SaveResult<string> SaveToJson(object data, BeastySaveSettings settings,
                                            IDictionary<string, string> meta = null)

El texto de sobre exacto que un guardado escribiría en disco — checksum, versiones, meta, cifrado opcional — sin escribir nada. Value contiene el texto; BytesWritten es su número de bytes UTF-8. Errores: InvalidArgument, SerializationFailed.

public static LoadResult<T> LoadFromJson<T>(string json, BeastySaveSettings settings)

Carga desde un sobre producido por SaveToJson (o leído de vuelta desde tu propio endpoint). El checksum, la validación de tipo y las migraciones corren exactamente como en una carga de archivo, así que los códigos de error son los que puede producir un Load<T>, menos los del sistema de archivos.

public static SaveResult<string> ToJson(object data, BeastySaveSettings settings)
public static LoadResult<T>      FromJson<T>(string json, BeastySaveSettings settings)

La misma idea sin el sobre: ToJson serializa los datos como JSON limpio — sin checksum, sin versiones — y FromJson<T> los mapea de vuelta. No corre ninguna comprobación de integridad ni migraciones; el mapeo estricto/tolerante sigue aplicando. Para endpoints que quieren datos planos. Errores: InvalidArgument, SerializationFailed / ParseError, FieldMapFailed.

Usa la pareja con sobre cuando quieras las garantías del formato de archivo por el cable; usa la pareja limpia cuando el formato lo define el receptor. SaveResult<T> está descrito en Resultados y errores.

Puntos de extensión

public static void RegisterMigration(int fromVersion, int toVersion, Func<JsonNode, JsonNode> migrate)

Registra un paso de la cadena de migración, aplicado al JsonNode crudo en el momento de carga cuando el dataVersion del archivo es más antiguo que BeastySaveSettings.DataVersion. Los pasos se encadenan: de 1 a 2, de 2 a 3, y así sucesivamente. Lanza ArgumentNullException cuando migrate es nulo y ArgumentException cuando toVersion no es mayor que fromVersion — son errores del desarrollador, no un problema del archivo. Consulta Versionado y migraciones.

public static void RegisterConverter(IBeastyConverter converter)

Registra un convertidor en la capa dev, que tiene la prioridad más alta y por lo tanto sobrescribe tanto a los convertidores de módulo como a los core integrados. Gana el registro más reciente. Lanza ArgumentNullException con un convertidor nulo.

public static void RegisterModule(string moduleId, IEnumerable<IBeastyConverter> converters)

Registra un grupo con nombre de convertidores. Idempotente por id: registrar el mismo id de nuevo reemplaza al grupo. El id se escribe en cada entrada de componente de un guardado de escena. Lanza ArgumentException con un id vacío y ArgumentNullException con una secuencia nula. Consulta Convertidores personalizados.

public static bool TryDescribeConverter(Type type, out string source)

Verdadero cuando algún convertidor registrado maneja el tipo. source es "dev", un id de módulo (por ejemplo "physics2d") o "core". Esto es lo que usa el editor para advertir sobre componentes sin convertidor.

Advertencia Entrar en Play Mode reinicia los estáticos. Los convertidores registrados con RegisterConverter, y toda migración, se pierden en cada Play. Regístralos desde un [RuntimeInitializeOnLoadMethod]. Los módulos registrados con RegisterModule sobreviven al reinicio.

Dos puntos de extensión más viven fuera de la fachada: BeastySaveStorageRegistry.Register añade un backend de almacenamiento propio, y BeastySaveUsers decide de quién son estos guardados. Ambos se cubren en Backends personalizados.

Rutas

public static string GetFolderPath(BeastySaveSettings settings)

La carpeta absoluta que contiene los archivos de guardado: {DataPath o Application.persistentDataPath}/{Folder}. La carpeta se crea si no existe.

public static string GetSlotPath(string slot, BeastySaveSettings settings)

La ruta absoluta del archivo de un slot: {folder}/{slot}.{Extension}. No crea nada y no valida el nombre del slot.

Los dos métodos describen solo la disposición de archivos locales: no se separan por usuario (ScopeByUser añade una subcarpeta <userId> que ellos no conocen) y no dicen nada de un backend en la nube, que no tiene ruta de archivo en absoluto.

Logging

public static BeastySaveLogLevel Level { get; set; }   // Off, Normal, Verbose
public static BeastySaveLogLevel DefaultLevel { get; } // Normal en el editor y en builds de desarrollo, Off en release
public static bool EnableLogs { get; set; }            // atajo: false es Off, true es Normal
public static IBeastySaveLogSink Sink;

public static void Verbose(string message);
public static void Info(string message);
public static void Warning(string message);
public static void Error(string message);

BeastySaveLog es la fachada de logging del save system. Level es la fuente de verdad y adopta DefaultLevel de forma perezosa. Cada línea lleva el prefijo [BeastySave]. Verbose guarda silencio a menos que Level sea BeastySaveLogLevel.Verbose; un sink propio recibe las líneas verbose por Info.

El inspector de BeastySaveManager gobierna Level desde su desplegable Logging, y lo vuelve a aplicar en OnEnable y OnValidate. La historia completa está en logging.md.

Ver también