El sistema de guardado proporciona un sistema de almacenamiento de datos clave-valor para persistir el progreso y las preferencias del jugador entre sesiones.
El sistema de Guardado es un simple almacén clave-valor. Le das una clave de tipo cadena (como "xp"), y almacena un valor que seguirá ahí la próxima vez que el jugador se conecte.
Hay dos tipos de datos guardados:
Guardado de jugador: datos personales de un jugador (XP, mejoras, configuraciones, inventario, etc)
Guardado a nivel de juego: compartido por todos en el juego (récords mundiales, configuraciones del servidor, contadores globales, etc)
No uses el sistema de guardado para almacenar compras que los jugadores hicieron con chispas. Usa el APIs de Compra/Producto en su lugar.
Inicio rápido (guardar + cargar una estadística)
El patrón más común es:
Cargar valores guardados en ao_start
Guardar de nuevo cada vez que el valor cambie
Player::class:Player_Base{xp:s64;nivel:s64;ao_start::method(){// Se usan valores por defecto para jugadores totalmente nuevosxp=Save.get_int(this,"xp",0);nivel=Save.get_int(this,"level",1);}}add_xp::proc(player:Player,amount:s64){player.xp+=amount;// ... tu lógica de subida de nivel aquí ...// Guardar inmediatamente cuando cambieSave.set_int(player,"xp",player.xp);Save.set_int(player,"level",player.level);}
Guardado de jugador (datos por jugador)
El guardado de jugador está limitado a un solo jugador. Cada jugador tiene sus propios datos de clave/valor aislados.
Siempre proporciona un valor por defecto razonable al leer. Los jugadores nuevos aún no tendrán claves, y get_* devolverá tu valor por defecto.
Guardando datos “más grandes” (JSON)
Si tienes un pequeño conjunto de campos (como progreso + cosas desbloqueadas), a menudo es mejor guardarlo como un blob JSON único.
Solo se guardan los campos marcados con @ao_serialize .
Save.try_get_json devuelve false si la clave no existe o el JSON guardado ya no coincide con tu estructura. Trata eso como “empezar de cero con valores por defecto”.
Versionado de guardado (migrar datos antiguos de forma segura)
Si alguna vez cambias tu formato de guardado, mantén una clave de versión y migra los guardados antiguos hacia adelante.
Guardado a nivel de juego (compartido por todos)
El guardado a nivel de juego se comparte en todo el juego, no por jugador.
Usa Save.increment_game_int para contadores que varios jugadores podrían actualizar al mismo tiempo (bajas, uniones, rondas jugadas, etc).
Patrones comunes
Booleanos
Guarda booleanos como 0/1:
Nomenclatura de claves
Las claves son solo cadenas, así que elige nombres que no choquen más adelante:
"xp", "level", "selected_skin"
"tycoon.cash", "tycoon.upgrades.mouth_level"
Si tienes varios juegos conectados mediante jerarquía de juegos (hub + minijuegos), consulta Productos/Datos entre Juegos cómo se pueden compartir los datos guardados.
// Preferencias
Save.set_string(player, "selected_skin", "knight");
music_volume := Save.get_f64(player, "music_volume", 0.8);
// Eliminar una clave (útil al migrar/eliminar datos antiguos)
Save.delete_key(player, "old_key_name");
Player_Progress :: class {
version: s64 @ao_serialize;
max_health: s64 @ao_serialize;
unlocked_skins: [..]string @ao_serialize;
}
save_progress :: proc(player: Player, progress: ref Player_Progress) {
Save.set_json(player, "progress", progress);
}
load_progress :: proc(player: Player, out: ref Player_Progress) {
if !Save.try_get_json(player, "progress", out) {
// Falta la clave (o el parseo falló) → rellenar con valores por defecto
out.version = 1;
out.max_health = 100;
out.unlocked_skins = .{};
}
}
Player :: class : Player_Base {
hp: f64;
ao_start :: method() {
save_version := Save.get_int(this, "version", 0);
if save_version < 6 {
// Ejemplo de migración: hp solía ser un entero, ahora es un float
save_version = 6;
old_hp := Save.get_int(this, "hp", 100);
Save.delete_key(this, "hp");
Save.set_f64(this, "hp", old_hp.(f64));
}
Save.set_int(this, "version", save_version);
// Cargar el formato actual
hp = Save.get_f64(this, "hp", 100);
}
}
// Titular del récord global
Save.set_game_string("world_record_holder", player->get_username());
holder := Save.get_game_string("world_record_holder", "nadie aún");
// Contadores globales (incremento atómico, seguro cuando muchos jugadores lo actualizan)
Save.increment_game_int("total_games_played", 1);
total := Save.get_game_int("total_games_played", 0);