- Bootstrap: Doorstop entry point, loads Harmony/Newtonsoft from the MSCO folder, hooks PlayMakerArrayListProxy.Awake and starts MSCO in the main menu; skips the news screen - No MSCLoader reference anymore; key bindings in PlayerPrefs, bind console command - Harmony 1.2 and Newtonsoft.Json in lib/ (MIT notices); build copies to <game>\MSCO - README/CHANGELOG updated Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
137 lines
5.5 KiB
C#
137 lines
5.5 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
|
|
namespace MSCO
|
|
{
|
|
/// <summary>
|
|
/// Liest den Spielstand des Hosts und ersetzt beim Client den eigenen Spielstand
|
|
/// vorübergehend. Der Spielstand des Clients wird vorher gesichert und beim
|
|
/// nächsten Hauptmenü automatisch wiederhergestellt.
|
|
/// </summary>
|
|
public static class SaveSync
|
|
{
|
|
// Nur diese Dateien werden übertragen (options.txt etc. bleiben beim Spieler)
|
|
public static readonly string[] Files =
|
|
{
|
|
"defaultES2File.txt", "items.txt", "meshsave.txt", "graveyard.txt", "notepad.txt"
|
|
};
|
|
|
|
static string SaveDir { get { return Application.persistentDataPath; } }
|
|
static string BackupDir { get { return Path.Combine(SaveDir, "MSCO_Backup"); } }
|
|
static string Marker { get { return Path.Combine(BackupDir, "backup_active"); } }
|
|
|
|
public static bool HasBackup { get { return File.Exists(Marker); } }
|
|
|
|
static string ArchiveDir { get { return Path.Combine(SaveDir, "MSCO_Saves"); } }
|
|
const int KeepArchives = 10;
|
|
|
|
/// <summary>Gibt es einen eigenen Spielstand zum Weiterspielen?</summary>
|
|
public static bool HasLocalSave { get { return File.Exists(Path.Combine(SaveDir, "defaultES2File.txt")); } }
|
|
|
|
/// <summary>Wann der eigene Spielstand zuletzt gespeichert wurde (null = keiner).</summary>
|
|
public static System.DateTime? LocalSaveTime
|
|
{
|
|
get
|
|
{
|
|
string file = Path.Combine(SaveDir, "defaultES2File.txt");
|
|
return File.Exists(file) ? File.GetLastWriteTime(file) : (System.DateTime?)null;
|
|
}
|
|
}
|
|
|
|
/// <summary>Ordner mit den Spielständen, die vor einem neuen Spiel gesichert wurden.</summary>
|
|
public static string ArchiveFolder { get { return ArchiveDir; } }
|
|
|
|
/// <summary>
|
|
/// Neues Spiel: den bisherigen Spielstand nach MSCO_Saves/Datum kopieren (nichts geht verloren) und die
|
|
/// Spielstand-Dateien leeren, damit das Spiel eine frische Welt anlegt. Gibt den Sicherungsordner zurück (oder null).
|
|
/// </summary>
|
|
public static string ArchiveForNewGame()
|
|
{
|
|
// War noch der Spielstand eines anderen Hosts aktiv, erst den eigenen zurückholen
|
|
if (HasBackup) RestoreBackup();
|
|
string target = null;
|
|
if (HasLocalSave)
|
|
{
|
|
target = Path.Combine(ArchiveDir, System.DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
|
|
Directory.CreateDirectory(target);
|
|
foreach (string name in Files)
|
|
{
|
|
string path = Path.Combine(SaveDir, name);
|
|
if (File.Exists(path)) File.Copy(path, Path.Combine(target, name), true);
|
|
}
|
|
MscoLog.Info("[MSCO] Your previous save game was backed up to " + target);
|
|
// Nur die letzten Sicherungen behalten
|
|
string[] all = Directory.GetDirectories(ArchiveDir);
|
|
System.Array.Sort(all);
|
|
for (int i = 0; i < all.Length - KeepArchives; i++)
|
|
try { Directory.Delete(all[i], true); } catch { }
|
|
}
|
|
DeleteSaveFiles();
|
|
return target;
|
|
}
|
|
|
|
public static List<KeyValuePair<string, byte[]>> ReadLocalSave()
|
|
{
|
|
var result = new List<KeyValuePair<string, byte[]>>();
|
|
foreach (string name in Files)
|
|
{
|
|
string path = Path.Combine(SaveDir, name);
|
|
if (File.Exists(path))
|
|
result.Add(new KeyValuePair<string, byte[]>(name, File.ReadAllBytes(path)));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// <summary>Eigenen Spielstand sichern (nur einmal) und die Spielstand-Dateien leeren.</summary>
|
|
public static void BeginReceive()
|
|
{
|
|
if (!HasBackup)
|
|
{
|
|
Directory.CreateDirectory(BackupDir);
|
|
foreach (string name in Files)
|
|
{
|
|
string path = Path.Combine(SaveDir, name);
|
|
if (File.Exists(path)) File.Copy(path, Path.Combine(BackupDir, name), true);
|
|
}
|
|
File.WriteAllText(Marker, "MSCO backup of the client's own save. Restored automatically.");
|
|
MscoLog.Info("[MSCO] Own save backed up to " + BackupDir);
|
|
}
|
|
DeleteSaveFiles();
|
|
}
|
|
|
|
public static void WriteFile(string name, byte[] data)
|
|
{
|
|
// Nur bekannte Dateinamen erlauben, damit der Host nichts anderes überschreiben kann
|
|
if (System.Array.IndexOf(Files, name) < 0)
|
|
{
|
|
MscoLog.Warning("[MSCO] Ignored unexpected save file: " + name);
|
|
return;
|
|
}
|
|
File.WriteAllBytes(Path.Combine(SaveDir, name), data);
|
|
}
|
|
|
|
public static void RestoreBackup()
|
|
{
|
|
if (!HasBackup) return;
|
|
DeleteSaveFiles();
|
|
foreach (string name in Files)
|
|
{
|
|
string backup = Path.Combine(BackupDir, name);
|
|
if (File.Exists(backup)) File.Copy(backup, Path.Combine(SaveDir, name), true);
|
|
}
|
|
Directory.Delete(BackupDir, true);
|
|
MscoLog.Info("[MSCO] Own save restored.");
|
|
}
|
|
|
|
static void DeleteSaveFiles()
|
|
{
|
|
foreach (string name in Files)
|
|
{
|
|
string path = Path.Combine(SaveDir, name);
|
|
if (File.Exists(path)) File.Delete(path);
|
|
}
|
|
}
|
|
}
|
|
}
|