WorldTime uses WorldTimeSerializer to persist time state to JSON. The serializer preserves exact microsecond precision for time-of-day values.
Overview
WorldTimeSerializer handles:
WorldTimeStateComponent— Current date, time, time scale, pause stateTimeOfDayComponent— Per-entity time tracking (for day/night lighting)GameCalendar— Calendar structure with months, years, event days
Saving State
Basic Save with WorldTimeEcsRuntime
using MoonBark.CalendarTime.ECS;
using MoonBark.CalendarTime.ECS.Serialization;
// Get runtime from ECS world
var runtime = world.GetPlugin<WorldTimeEcsPlugin>();
// Serialize to JSON string
string saveJson = runtime.TimeProvider.SaveToJson();
// Write to file
var file = FileAccess.Open("user://worldtime_save.json", FileAccess.ModeFlags.Write);
file.StoreString(saveJson);
file.Close();
GD.Print("WorldTime saved");
Save Using EntityStore Directly
using MoonBark.CalendarTime.ECS.Serialization;
var store = world.Store;
string json = WorldTimeSerializer.Serialize(store);
var file = FileAccess.Open("user://worldtime_save.json", FileAccess.ModeFlags.Write);
file.StoreString(json);
file.Close();
Loading State
Basic Load with WorldTimeEcsRuntime
using MoonBark.CalendarTime.ECS;
using MoonBark.CalendarTime.ECS.Serialization;
var runtime = world.GetPlugin<WorldTimeEcsPlugin>();
// Read from file
var file = FileAccess.Open("user://worldtime_save.json", FileAccess.ModeFlags.Read);
string json = file.GetAsText();
file.Close();
// Restore state
runtime.TimeProvider.LoadFromJson(json);
GD.Print("WorldTime loaded");
Load Using EntityStore Directly
using MoonBark.CalendarTime.ECS.Serialization;
var file = FileAccess.Open("user://worldtime_save.json", FileAccess.ModeFlags.Read);
string json = file.GetAsText();
file.Close();
var store = world.Store;
WorldTimeSerializer.Deserialize(store, json);
Save Data Format
{
"FormatVersion": 1,
"WorldState": {
"Id": "main",
"Calendar": { ... },
"TimeScale": { ... },
"CurrentDateTime": { ... },
"ElapsedGameSeconds": 12345.0,
"WorldAge": 42,
"IsPaused": false,
"AccumulatedRealMicroseconds": 86400000000
},
"EntityTimeOfDay": [
{ "EntityId": 1, "TotalHours": 14.5, "DayLengthHours": 24.0, "DayIndex": 5 }
]
}
Pause and Resume
The serializer preserves pause state. When loading a paused game, time remains paused:
// Save while paused
runtime.TimeProvider.SetPaused(true);
string json = runtime.TimeProvider.SaveToJson();
// Load - still paused
runtime.TimeProvider.LoadFromJson(json);
GD.Print(runtime.TimeProvider.IsPaused); // true
Custom Save Integration
Adding WorldTime to Game Save System
public partial class GameSaveManager : Node
{
[Export] private WorldTimeEcsPlugin? _worldTimePlugin;
public void SaveGame(string path)
{
var saveData = new Dictionary<string, Variant>
{
["version"] = 1,
["save_time"] = Time.GetDatetimeStringFromSystem(),
["world_time"] = _worldTimePlugin.TimeProvider.SaveToJson(),
// ... other game data
};
var file = FileAccess.Open(path, FileAccess.ModeFlags.Write);
file.StoreString(JSON.stringify(saveData));
file.Close();
}
public void LoadGame(string path)
{
var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
var json = file.GetAsText();
file.Close();
var saveData = JSON.parse_string(json) as Dictionary<string, Variant>;
// Restore WorldTime
string timeJson = (string)saveData["world_time"];
_worldTimePlugin.TimeProvider.LoadFromJson(timeJson);
// ... load other game data
}
}
Time Precision Guarantee
The serializer preserves exact microseconds for HoursTime:
// Save/load preserves exact microseconds
var original = new HoursTime(14, 30, 500000); // 14:30.5
string json = WorldTimeSerializer.Serialize(store);
WorldTimeSerializer.Deserialize(store, json);
var restored = runtime.TimeProvider.GetTimeOfDay(entityId);
// restored.Microseconds == original.Microseconds — exact match
This means repeated save/load cycles don't accumulate drift.
Related Guides
- Time Precision — Why microseconds matter
- Time Scaling — Time scale configuration
- Day/Night Cycle — Lighting integration