Editor/AutoSave.cs
using System;
using System.IO;
using System.Threading.Tasks;
namespace Editor;
/// <summary>
/// Automatic backup save system that creates periodic backups of the current scene
/// </summary>
public static class AutoSave
{
private static RealTimeSince _timeSinceLastSave = 0;
private static bool _initialized = false;
[EditorEvent.Frame]
public static void OnFrame()
{
// Skip if disabled or playing
if ( !MappingToolSettings.AutoSaveEnabled || Game.IsPlaying )
return;
// Initialize timer on first frame
if ( !_initialized )
{
_timeSinceLastSave = 0;
_initialized = true;
return;
}
// Check if it's time to save
float intervalMinutes = MappingToolSettings.AutoSaveIntervalMinutes;
if ( _timeSinceLastSave < intervalMinutes * 60f )
return;
// Reset timer and save
_timeSinceLastSave = 0;
PerformAutoSave();
}
[EditorEvent.Hotload]
public static void OnHotload()
{
_initialized = false;
}
/// <summary>
/// Gets the autosave folder path for the current scene
/// </summary>
public static string GetAutoSaveFolderPath()
{
var session = SceneEditorSession.Active;
if ( session?.Scene?.Source?.ResourcePath == null )
return null;
var originalPath = session.Scene.Source.ResourcePath;
var sceneName = Path.GetFileNameWithoutExtension( originalPath );
var sceneDirectory = Path.GetDirectoryName( originalPath );
var autoSaveFolder = Path.Combine( sceneDirectory, "autosave" );
return FileSystem.ProjectTemporary.GetFullPath( autoSaveFolder );
}
public static void PerformAutoSave()
{
try
{
var session = SceneEditorSession.Active;
if ( session?.Scene == null )
{
Log.Info( "AutoSave: No active scene to save" );
return;
}
var scene = session.Scene;
// Get the original scene path
var originalPath = scene.Source?.ResourcePath;
if ( string.IsNullOrEmpty( originalPath ) )
{
Log.Info( "AutoSave: Scene has no source path (unsaved scene)" );
return;
}
// Get the scene name without extension
var sceneName = Path.GetFileNameWithoutExtension( originalPath );
var sceneDirectory = Path.GetDirectoryName( originalPath );
// Create autosave folder path
var autoSaveFolder = Path.Combine( sceneDirectory, "autosave" );
// Ensure the autosave directory exists
var fullAutoSavePath = FileSystem.ProjectTemporary.GetFullPath( autoSaveFolder );
if ( !Directory.Exists( fullAutoSavePath ) )
{
Directory.CreateDirectory( fullAutoSavePath );
Log.Info( $"AutoSave: Created autosave folder at {autoSaveFolder}" );
}
// Generate timestamp
var timestamp = DateTime.Now.ToString( "yyyy-MM-dd_HH-mm-ss" );
var autoSaveFileName = $"{sceneName}_{timestamp}.scene";
var autoSavePath = Path.Combine( autoSaveFolder, autoSaveFileName );
// Serialize the scene
var json = scene.Serialize();
// Write to file
FileSystem.ProjectTemporary.WriteAllText( autoSavePath, json.ToString() );
CleanupOldBackups( fullAutoSavePath, sceneName );
Log.Info( $"AutoSave: Saved backup to {fullAutoSavePath}" );
// Show toast notification if enabled
if ( MappingToolSettings.AutoSaveShowNotification )
{
_ = Popup( sceneName, timestamp );
}
}
catch ( Exception ex )
{
Log.Warning( $"AutoSave: Failed to save - {ex.Message}" );
}
}
static async Task Popup( string title, string subtitle )
{
using var progress = Application.Editor.ProgressSection();
progress.Title = $"Auto-saved: {title}";
progress.Subtitle = $"Backup: {subtitle}";
await Task.Delay( 2000 );
}
private static void CleanupOldBackups( string fullAutoSavePath, string sceneName )
{
try
{
int maxBackups = MappingToolSettings.AutoSaveMaxBackups;
if ( maxBackups <= 0 ) return; // No limit
if ( !Directory.Exists( fullAutoSavePath ) ) return;
// Get all backup files for this scene, sorted by creation time (oldest first)
var backupFiles = Directory.GetFiles( fullAutoSavePath, $"{sceneName}_*.scene" )
.Select( f => new FileInfo( f ) )
.OrderBy( f => f.CreationTime )
.ToList();
// Delete oldest files if we exceed the limit
while ( backupFiles.Count > maxBackups )
{
var oldest = backupFiles[0];
oldest.Delete();
backupFiles.RemoveAt( 0 );
Log.Info( $"AutoSave: Deleted old backup {oldest.Name}" );
}
}
catch ( Exception ex )
{
Log.Warning( $"AutoSave: Failed to cleanup old backups - {ex.Message}" );
}
}
/// <summary>
/// Force an immediate autosave
/// </summary>
[Menu( "Editor", "Mapping Tools/Force Auto-Save", Icon = "save" )]
public static void ForceAutoSave()
{
PerformAutoSave();
}
}