Files
Factory47/addons/savekit/README.md
2026-06-04 16:53:41 -05:00

122 lines
5.8 KiB
Markdown

# SaveKit for Godot
A library for saving and loading game state in Godot 4, with pluggable save file formats and a focus on ease of use.
Key features:
- **Easy to get started.** Add nodes to the `saveable` group, then use `SaveManager.save_game()` and `SaveManager.load_game()`.
- **Saves nodes and resources.** Built-in resources, like textures and packed scenes, are saved as references, while data from nodes and custom `SaveKitResource` subclasses is saved in its entirety. This avoids the code injection risks of Godot's `ResourceLoader`, while supporting complex data.
- **JSON and binary serialization built-in**, or implement your own custom save file format by extending `SaveKitSerializer` and `SaveKitDeserializer`.
- **Automatic by default, manual when you need it.** Reflection picks up exported properties for saving/loading automatically, or you can implement custom `save_to_dict` and `load_from_dict` methods for full control.
## Getting started
1. Enable the plugin in **Project > Project Settings > Plugins**. This also installs a `SaveManager` autoload.
2. Add all the nodes you want saved to the `saveable` group.
3. Call one method to save, another to load:
```gdscript
# Save to disk under user://save_games/MyGame/Slot 1.json
SaveManager.save_game(PackedStringArray(["MyGame", "Slot 1"]))
# Load it back later
SaveManager.load_game(PackedStringArray(["MyGame", "Slot 1"]))
```
This will iterate through nodes in the `saveable` group, serialize each node's exported properties, and write the file into `user://save_games/`. Then the reverse is done on load—creating or freeing nodes as needed so the scene tree matches the save file.
There are also other methods offering finer-grained control over the save/load process:
```gdscript
func save_scene_tree_in_memory() -> PackedByteArray
func save_scene_tree_to_disk(absolute_path: String) -> Error
func load_scene_tree_from_memory(data: PackedByteArray) -> bool
func load_scene_tree_from_file(absolute_path: String) -> Error
```
## Saving nodes
By default, SaveKit uses reflection to save all `@export` and `@export_storage` properties whose values differ from their defaults. For a lot of nodes, this is all you need:
```gdscript
extends CharacterBody2D
@export var health: int = 100
@export var player_name: String = ""
@export_storage var checkpoint: Vector2
```
When you need additional control, implement `save_to_dict` and `load_from_dict`:
```gdscript
extends RigidBody2D
func save_to_dict(s: SaveKitSerializer) -> Dictionary:
return {
"transform": s.encode_var(transform),
"linear_velocity": s.encode_var(linear_velocity),
}
func load_from_dict(s: SaveKitDeserializer, data: Dictionary) -> void:
var t: Transform2D = s.decode_var(data["transform"], TYPE_TRANSFORM2D)
PhysicsServer2D.body_set_state(get_rid(), PhysicsServer2D.BODY_STATE_TRANSFORM, t)
linear_velocity = s.decode_var(data["linear_velocity"], TYPE_VECTOR2)
```
You can also mix the two approaches by calling `serializer.default_save_to_dict()` and `deserializer.default_load_from_dict()` from your implementation.
### Node instantiation
If a saved node isn't in the scene tree at load time, SaveKit will instantiate it from the `scene_file_path` it was saved with and parent it where it belongs. Conversely, nodes in the `saveable` group that *aren't* in the save data are freed, so the scene tree always matches the save file after loading.
## Saving resources
For resources that represent persisted data—e.g., inventories, quest state, per-entity stat blocks—extend `SaveKitResource` rather than plain `Resource`:
```gdscript
class_name Inventory
extends SaveKitResource
@export var gold: int = 0
@export var items: Array[Item]
```
Any `SaveKitResource` referenced from a saved node is serialized automatically, and deduplicated. Like nodes, `SaveKitResource` uses reflection over exported properties by default, but you can always implement `save_to_dict` and `load_from_dict` for custom behavior.
Note that plain `Resource` references (textures, scenes, and other things baked into the PCK) are saved as path/UID references. SaveKit will only ever load such resources from within the `res://` filesystem, avoiding the risk of code injection from user-provided resource files.
## Lifecycle hooks
There are a variety of signals and methods to hook into the saving and loading process—`before_save`, `after_save`, `before_load`, `after_load`, etc. See the `SaveManager` API documentation for more details.
## Save file formats
SaveKit includes two built-in file formats:
- **JSON** (`json_serializer.gd`, `json_deserializer.gd`) — human-readable, easy to diff and debug.
- **Binary** (`binary_serializer.gd`, `binary_deserializer.gd`) — compact, obfuscated.
JSON, the default, is recommended in most cases. File size is rarely a concern, and making saves human-readable is more friendly to your players.
You can also implement a custom file format by extending `SaveKitSerializer` and `SaveKitDeserializer` and implementing the abstract methods.
Assign `SaveManager.serializer_script` and `SaveManager.deserializer_script` to switch between formats or use your own:
```gdscript
SaveManager.serializer_script = preload("res://addons/savekit/binary_serializer.gd")
SaveManager.deserializer_script = preload("res://addons/savekit/binary_deserializer.gd")
SaveManager.save_file_extension = ".sav"
```
## Learn more
The [included demo](demo/) has a small interactive scene that is fully saveable, and includes a live view into the JSON file format.
All public classes (`SaveManager`, `SaveKitSerializer`, `SaveKitDeserializer`, `SaveKitResource`) have documentation comments that work with Godot's built-in help. Browse them from the editor for the full API reference.
## License
This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.