using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace Saradomin.Utilities { public static class SingleplayerManagement { private static readonly string[] DirsToBackup = { "game/data/players", "game/data/serverstore", "game/worldprops" }; private static readonly string[] FilesToBackup = { "game/data/eco/grandexchange.db" }; public static void MakeBackup(Action log) { string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); string newBackupDir = Path.Combine( CrossPlatform.GetSingleplayerBackupsHome(), timestamp ); foreach (string dir in DirsToBackup) { string sourcePath = Path.Combine(CrossPlatform.GetSingleplayerHome(), dir); if (!Directory.Exists(sourcePath)) { log($" Skipping backup of {dir} because it doesn't exist (Full path attempted: {sourcePath})"); continue; } string destPath = Path.Combine(newBackupDir, dir); CopyDirectory(sourcePath, destPath); } foreach (string file in FilesToBackup) { string sourceFilePath = Path.Combine(CrossPlatform.GetSingleplayerHome(), file); if (!File.Exists(sourceFilePath)) { log($" Skipping backup of {file} because it doesn't exist (Full path attempted: {sourceFilePath})"); continue; } string destFilePath = Path.Combine(newBackupDir, file); string directoryForFile = Path.GetDirectoryName(destFilePath); if (!Directory.Exists(directoryForFile)) Directory.CreateDirectory(directoryForFile); File.Copy(sourceFilePath, destFilePath, true); } } private static void CopyDirectory(string sourceDir, string destinationDir) { if (!Directory.Exists(destinationDir)) Directory.CreateDirectory(destinationDir); foreach (string filePath in Directory.GetFiles(sourceDir)) { string fileName = Path.GetFileName(filePath); string destFilePath = Path.Combine(destinationDir, fileName); File.Copy(filePath, destFilePath, true); } foreach (string subDirPath in Directory.GetDirectories(sourceDir)) { string subDirName = Path.GetFileName(subDirPath); string destSubDirPath = Path.Combine(destinationDir, subDirName); // Recurse! CopyDirectory(subDirPath, destSubDirPath); } } public static string FindMostRecentBackupDirectory(IEnumerable directories) { return directories.Select(Path.GetFileName).MaxBy(name => name); } public static void ApplyLatestBackup(Action log) { var backupHome = CrossPlatform.GetSingleplayerBackupsHome(); var singleplayerHome = CrossPlatform.GetSingleplayerHome(); // Get all backup directories string[] backupDirectories = Directory.Exists(backupHome) ? Directory.GetDirectories(backupHome) : Array.Empty(); string mostRecentBackupDirName = FindMostRecentBackupDirectory(backupDirectories); if (mostRecentBackupDirName == null) { log(" No backups found."); return; } string mostRecentBackupFullPath = Path.Combine(backupHome, mostRecentBackupDirName); // Get the list of files in the most recent backup var filesInBackup = Directory.GetFiles( mostRecentBackupFullPath, "*", SearchOption.AllDirectories ); foreach (var file in filesInBackup) { // Calculate the file's destination path in the singleplayer home directory var relativePath = file.Substring(mostRecentBackupFullPath.Length) .TrimStart(Path.DirectorySeparatorChar); var destFilePath = Path.Combine(singleplayerHome, relativePath); // Ensure the destination directory exists var destDirectory = Path.GetDirectoryName(destFilePath); if (!Directory.Exists(destDirectory)) { Directory.CreateDirectory(destDirectory); } // Copy the file to the destination, overwriting any existing file File.Copy(file, destFilePath, true); } log($" Backup from {mostRecentBackupDirName} has been successfully applied."); } private static Dictionary _confCache; private static string ConfPath => Path.Combine(CrossPlatform.GetSingleplayerHome(), "game", "worldprops", "default.conf"); public static Dictionary GrabConfCache() { return File.ReadLines(ConfPath) .Where(line => line.Contains('=') && !line.TrimStart().StartsWith("#")) .Select(l => l.Split(new[] { '#', '=' }, 3)) .ToDictionary(parts => parts[0].Trim(), parts => parts[1].Trim()); } public static T ParseConf(string key, T defaultValue = default) { if (!File.Exists(ConfPath)) return defaultValue; _confCache ??= GrabConfCache(); if (!_confCache.TryGetValue(key, out var value)) return defaultValue; return (T)Convert.ChangeType(value, typeof(T)); } public static void WriteConf(string key, object value) { _confCache[key] = value.ToString().ToLowerInvariant(); // Update cache var lines = File.ReadAllLines(ConfPath).Select(l => l.StartsWith(key + " =") ? $"{key} = {_confCache[key]}" + (l.Contains("#") ? " " + l.Substring(l.IndexOf('#')) : "") : l); File.WriteAllLines(ConfPath, lines); } } }