Files
ichni_Creator_Studio/Assets/Scripts/Manager/ProjectManager.cs
TRAfoer 1280e32612 大活:Tracker和Interferometer
等待改进
Signed-off-by: TRAfoer <lhf190@outlook.com>
2025-11-02 03:08:40 +08:00

593 lines
22 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using Ichni.Editor;
using Ichni.RhythmGame;
using Ichni.RhythmGame.Beatmap;
using UnityEngine;
namespace Ichni
{
public class ProjectManager
{
public static readonly ES3Settings SaveSettings = new ES3Settings
{
compressionType = ES3.CompressionType.None,
encryptionType = ES3.EncryptionType.None,
format = ES3.Format.JSON,
};
public static readonly ES3Settings ExportSettings = new ES3Settings
{
compressionType = ES3.CompressionType.Gzip,
encryptionType = ES3.EncryptionType.AES,
encryptionPassword = "Soullies515",
format = ES3.Format.JSON,
};
public SaveManager saveManager;
public LoadManager loadManager;
public ExportManager exportManager;
public BeatmapClipManager beatmapClipManager;
public BeatmapMergeManager beatmapMergeManager;
public NotePrefabManager notePrefabManager;
public AutoSaveManager autoSaveManager;
public ProjectManager()
{
saveManager = new SaveManager();
loadManager = new LoadManager();
exportManager = new ExportManager();
beatmapClipManager = new BeatmapClipManager();
beatmapMergeManager = new BeatmapMergeManager();
notePrefabManager = new NotePrefabManager();
autoSaveManager = new AutoSaveManager();
}
public void GenerateEmptyProject(ProjectInformation_BM projectInfo_BM, SongInformation_BM songInfo_BM)
{
projectInfo_BM.ExecuteBM();
songInfo_BM.ExecuteBM();
EditorManager.instance.beatmapContainer = new BeatmapContainer();
EditorManager.instance.commandScripts = new CommandScripts(new List<string>());
}
}
public class ExportManager
{
public void Export()
{
string exportPath = Application.streamingAssetsPath + "/Export/" +
EditorManager.instance.projectInformation.projectName;
string projectInfoPath = exportPath + "/ProjectInfo.bytes";
string songInfoPath = exportPath + "/SongInfo.bytes";
string beatmapPath = exportPath + "/Beatmap.bytes";
string commandScriptsPath = exportPath + "/CommandScripts.bytes";
LogWindow.Log("Start Exporting...");
ExportProjectInfo(projectInfoPath);
ExportSongInfo(songInfoPath);
ExportBeatMap(beatmapPath);
ExportCommandScripts(commandScriptsPath);
LogWindow.Log("Export Complete", Color.green);
}
private void ExportProjectInfo(string exportPath)
{
EditorManager.instance.projectInformation.SaveBM();
ES3.Save("ProjectInformation", EditorManager.instance.projectInformation.matchedBM as ProjectInformation_BM,
exportPath, ProjectManager.ExportSettings);
}
private void ExportSongInfo(string exportPath)
{
EditorManager.instance.songInformation.SaveBM();
ES3.Save("SongInformation", EditorManager.instance.songInformation.matchedBM as SongInformation_BM,
exportPath, ProjectManager.ExportSettings);
}
private void ExportBeatMap(string exportPath)
{
EditorManager.instance.beatmapContainer.SaveExportBM();
ES3.Save("Beatmap", EditorManager.instance.beatmapContainer.MatchingExportElement as BeatmapContainer_BM,
exportPath, ProjectManager.ExportSettings);
}
private void ExportCommandScripts(string exportPath)
{
EditorManager.instance.commandScripts.SaveBM();
ES3.Save("CommandScripts", EditorManager.instance.commandScripts.matchedBM as CommandScripts_BM,
exportPath, ProjectManager.ExportSettings);
}
}
public class SaveManager
{
public void Save()
{
LogWindow.Log("Start Saving...");
_ = SaveAllCoroutine();
}
public async Task SaveAllCoroutine()
{
await Task.Run(() =>
{
SaveProjectInfo();
SaveSongInfo();
SaveBeatMap();
SaveCommandScripts();
});
LogWindow.Log("Save Complete", Color.green);
}
private void SaveProjectInfo()
{
EditorManager.instance.projectInformation.SaveBM();
ES3.Save("ProjectInformation", EditorManager.instance.projectInformation.matchedBM as ProjectInformation_BM,
EditorManager.instance.projectInformation.peojectInfoPath, ProjectManager.SaveSettings);
}
private void SaveSongInfo()
{
EditorManager.instance.songInformation.SaveBM();
ES3.Save("SongInformation", EditorManager.instance.songInformation.matchedBM as SongInformation_BM,
EditorManager.instance.projectInformation.songInfoPath, ProjectManager.SaveSettings);
}
private void SaveBeatMap()
{
EditorManager.instance.beatmapContainer.SaveBM();
ES3.Save("Beatmap", EditorManager.instance.beatmapContainer.matchedBM as BeatmapContainer_BM,
EditorManager.instance.projectInformation.beatmapPath, ProjectManager.SaveSettings);
}
private void SaveCommandScripts()
{
EditorManager.instance.commandScripts.SaveBM();
ES3.Save("CommandScripts", EditorManager.instance.commandScripts.matchedBM as CommandScripts_BM,
EditorManager.instance.projectInformation.CommandScriptsPath, ProjectManager.SaveSettings);
}
}
public class LoadManager
{
public void Load(string projectName)
{
LoadProjectInfo(projectName);
LoadSongInfo();
LoadCommandScripts();
LoadBeatMap();
LogWindow.Log("Load Complete", Color.green);
}
private void LoadProjectInfo(string projectName)
{
string projectInfoPath = Application.streamingAssetsPath + "/Projects/" + projectName + "/ProjectInfo.json";
ES3.Load<ProjectInformation_BM>("ProjectInformation", projectInfoPath, ProjectManager.SaveSettings).ExecuteBM();
}
private void LoadSongInfo()
{
ES3.Load<SongInformation_BM>("SongInformation", EditorManager.instance.projectInformation.songInfoPath,
ProjectManager.SaveSettings).ExecuteBM();
}
private void LoadBeatMap()
{
ES3.Load<BeatmapContainer_BM>("Beatmap", EditorManager.instance.projectInformation.beatmapPath,
ProjectManager.SaveSettings).ExecuteBM();
}
private void LoadCommandScripts()
{
ES3.Load<CommandScripts_BM>("CommandScripts", EditorManager.instance.projectInformation.CommandScriptsPath,
ProjectManager.SaveSettings).ExecuteBM();
}
public void LoadExport(string projectName)
{
LoadProjectInfoExport(projectName);
LoadSongInfoExport(projectName);
LoadCommandScriptsExport(projectName);
LoadBeatMapExport(projectName);
LogWindow.Log("Load Export Complete,", new Color(0.5f, 0f, 1f));
LogWindow.Log("Please Save Your Project As You Can", new Color(0.5f, 0f, 1f));
}
private void LoadProjectInfoExport(string projectName)
{
string projectInfoPath = Application.streamingAssetsPath + "/Export/" + projectName + "/ProjectInfo.bytes";
ES3.Load<ProjectInformation_BM>("ProjectInformation", projectInfoPath, ProjectManager.ExportSettings).ExecuteBM();
}
private void LoadSongInfoExport(string projectName)
{
string songInfoPath = Application.streamingAssetsPath + "/Export/" + projectName + "/SongInfo.bytes";
ES3.Load<SongInformation_BM>("SongInformation", songInfoPath, ProjectManager.ExportSettings).ExecuteBM();
}
private void LoadBeatMapExport(string projectName)
{
string beatMapPath = Application.streamingAssetsPath + "/Export/" + projectName + "/Beatmap.bytes";
ES3.Load<BeatmapContainer_BM>("Beatmap", beatMapPath, ProjectManager.ExportSettings).ExecuteBM();
}
private void LoadCommandScriptsExport(string projectName)
{
string commandScriptsPath = Application.streamingAssetsPath + "/Export/" + projectName + "/CommandScripts.bytes";
ES3.Load<CommandScripts_BM>("CommandScripts", commandScriptsPath, ProjectManager.ExportSettings).ExecuteBM();
}
}
public class BeatmapClipManager
{
public void SaveClip(string clipName)
{
LogWindow.Log("Start Saving Clip...");
if (EditorManager.instance.operationManager.currentSelectedElements.Count != 1) // TODO: 后续适应多选
{
LogWindow.Log("Please select only one Game Element to save the beatmap clip.", Color.red);
return;
}
GameElement selectedElement = EditorManager.instance.operationManager.currentSelectedElements[0];
if (selectedElement is null)
{
LogWindow.Log("Please select a Game Element to save the beatmap clip.", Color.red);
return;
}
_SaveClip(selectedElement, clipName);
LogWindow.Log("Save Clip Complete", Color.green);
}
public void LoadClip(string clipName)
{
LogWindow.Log("Start Loading Clip...");
if (!ES3.FileExists(Application.streamingAssetsPath + "/Clips/" + clipName + ".json"))
{
LogWindow.Log("Clip not found", Color.red);
return;
}
if (EditorManager.instance.operationManager.currentSelectedElements.Count != 1) // TODO: 后续适应多选
{
LogWindow.Log("Please select only one Game Element to load the beatmap clip.", Color.red);
return;
}
GameElement selectedElement = EditorManager.instance.operationManager.currentSelectedElements[0];
if (selectedElement is null)
{
LogWindow.Log("Please select a Game Element to load the beatmap clip.", Color.red);
return;
}
Debug.Log(selectedElement.elementName + " " + selectedElement.elementGuid);
_LoadClip(selectedElement, clipName);
LogWindow.Log("Load Clip Complete", Color.green);
}
private void _SaveClip(GameElement element, string clipName)
{
List<BaseElement_BM> clip = new List<BaseElement_BM>();
element.GetAllGameElementsFromThis().ForEach(e =>
{
e.SaveBM();
clip.Add(e.matchedBM);
e.submoduleList.ForEach(s =>
{
s.SaveBM();
if (s.matchedBM != null)
clip.Add(s.matchedBM);
});
});
string filePath = Application.streamingAssetsPath + "/Clips/" + clipName + ".json";
ES3.Save("Clip", clip, filePath, ProjectManager.SaveSettings);
}
private void _LoadClip(GameElement target, string clipName)
{
try
{
string filePath = Application.streamingAssetsPath + "/Clips/" + clipName + ".json";
List<BaseElement_BM> clip = ES3.Load<List<BaseElement_BM>>("Clip", filePath, ProjectManager.SaveSettings);
if (clip == null || clip.Count == 0)
{
Debug.LogError("Clip is empty or null");
return;
}
// 验证第一个元素
if (!(clip[0] is GameElement_BM first))
{
Debug.LogError("First element is not a GameElement_BM");
return;
}
// 处理目标对象 - 添加清理机制
target.SaveBM();
bool targetAdded = GameElement_BM.identifier.TryAdd(target.elementGuid, target.matchedBM as GameElement_BM);
if (!targetAdded)
{
Debug.LogWarning("Target element already exists in identifier dictionary");
}
// 处理第一个元素及其附加元素
ProcessGameElement(first, clip, target.elementGuid);
// 处理后续元素
for (var index = 1; index < clip.Count; index++)
{
if (clip[index] is GameElement_BM gameElement)
{
ProcessGameElement(gameElement, clip, null);
}
else
{
// 非GameElement_BM直接执行
clip[index].ExecuteBM();
}
}
}
catch (Exception ex)
{
Debug.LogError($"Failed to load clip: {ex.Message}");
}
}
private void ProcessGameElement(GameElement_BM gameElement, List<BaseElement_BM> clip, Guid? attachToGuid)
{
List<BaseElement_BM> attachedElements = GameElement_BM.GetAllAttachedBaseElements(gameElement, clip);
// 生成新GUID并验证
gameElement.elementGuid = Guid.NewGuid();
bool elementAdded = GameElement_BM.identifier.TryAdd(gameElement.elementGuid, gameElement);
if (!elementAdded)
{
Debug.LogError($"Failed to add element with GUID: {gameElement.elementGuid}");
return;
}
// 更新附加元素
attachedElements.ForEach(e =>
{
e.attachedElementGuid = gameElement.elementGuid;
});
// 如果需要附加到其他元素
if (attachToGuid.HasValue)
{
gameElement.attachedElementGuid = attachToGuid.Value;
}
gameElement.ExecuteBM();
}
}
public class BeatmapMergeManager
{
public void MergeBeatmap(string mergeName)
{
string mergePath = Application.streamingAssetsPath + "/Merges/" + mergeName + ".json";
BeatmapContainer_BM merge = ES3.Load<BeatmapContainer_BM>("Beatmap", mergePath, ProjectManager.SaveSettings);
merge.elementList.ForEach(element =>
{
if (element == null)
{
Debug.LogError("Null element detected in elementList. Skipping execution.");
return;
}
if (BeatmapContainer_BM.LowPriorityGameElementTypes.Contains(element.GetType()))
{
return;
}
if (element is GameElement_BM gameElement)
{
GameElement_BM.identifier.Add(gameElement.elementGuid, gameElement);
}
if (element is GameCamera_BM || GameElement_BM.GetElementBM(element.attachedElementGuid) is GameCamera_BM)
{
return;
}
element.ExecuteBM();
});
merge.elementList.ForEach(element =>
{
if (element == null)
{
Debug.LogError("Null element detected in elementList during low-priority execution. Skipping execution.");
return;
}
if (BeatmapContainer_BM.LowPriorityGameElementTypes.Contains(element.GetType()))
{
element.ExecuteBM();
}
});
EditorManager.instance.beatmapContainer.ExecuteLowPriorityActions();
}
}
public class NotePrefabManager
{
private string notePrefabPath => Application.streamingAssetsPath + "/NotePrefabs";
private string GetNotePrefabPath(string notePrefabName) => notePrefabPath + "/" + notePrefabName + ".json";
public void SaveNotePrefab(NoteBase note, string noteName)
{
List<BaseElement_BM> clip = new List<BaseElement_BM>();
note.GetAllGameElementsFromThis().ForEach(e =>
{
e.SaveBM();
clip.Add(e.matchedBM);
e.submoduleList.ForEach(s =>
{
s.SaveBM();
if (s.matchedBM != null)
{
clip.Add(s.matchedBM);
}
});
});
ES3.Save("Note", clip, GetNotePrefabPath(noteName), ProjectManager.SaveSettings);
}
public void LoadNotePrefab(NoteBase target, string noteName)
{
if (target.noteVisual != null)
{
return;
}
List<BaseElement_BM> clip = ES3.Load<List<BaseElement_BM>>("Note", GetNotePrefabPath(noteName), ProjectManager.SaveSettings);
if (clip == null || clip.Count == 0)
{
LogWindow.Log("Note prefab not found", Color.red);
return;
}
target.SaveBM();
GameElement_BM.identifier.TryAdd(target.elementGuid, target.matchedBM as GameElement_BM);
(target.matchedBM as GameElement_BM).matchedElement = target;
GameElement_BM first = clip[0] as GameElement_BM;
List<BaseElement_BM> firstAttaches = GameElement_BM.GetAllAttachedBaseElements(first, clip);
first.elementGuid = target.elementGuid;
GameElement_BM.identifier.TryAdd(first.elementGuid, first);
firstAttaches.ForEach(e => { e.attachedElementGuid = first.elementGuid; });
for (var index = 1; index < clip.Count; index++)
{
var element = clip[index];
if (element is GameElement_BM gameElement)
{
List<BaseElement_BM> attachedElements = GameElement_BM.GetAllAttachedBaseElements(gameElement, clip);
gameElement.elementGuid = Guid.NewGuid();
GameElement_BM.identifier.TryAdd(gameElement.elementGuid, gameElement);
attachedElements.ForEach(e => { e.attachedElementGuid = gameElement.elementGuid; });
}
}
for (var index = 1; index < clip.Count; index++)
{
clip[index].ExecuteBM();
}
target.SetDefaultSubmodules();
}
}
public class AutoSaveManager
{
private string autoSavePath => Application.streamingAssetsPath + "/AutoSave/" +
EditorManager.instance.projectInformation.projectName;
private string GetAutoSavePath(string autoSaveName) => autoSavePath + "/" + autoSaveName + ".json";
private float autoSaveInterval => EditorManager.instance.editorSettings.autoSaveInterval;
private int maximumAutoSaveCount => EditorManager.instance.editorSettings.maximumAutoSaveCount;
public float autoSaveTimer;
public AutoSaveManager()
{
autoSaveTimer = 0;
}
public void UpdateAutoSave()
{
autoSaveTimer += Time.deltaTime;
if (autoSaveTimer >= autoSaveInterval)
{
AutoSave();
autoSaveTimer = 0;
}
}
private void AutoSave()
{
List<string> saveFiles = GetSortedSaveFiles();
if (saveFiles.Count > 0)
{
// 删除最旧的存档(如果超过数量)
if (saveFiles.Count >= maximumAutoSaveCount)
{
string oldestSave = saveFiles[saveFiles.Count - 1];
File.Delete(oldestSave);
saveFiles.RemoveAt(saveFiles.Count - 1);
LogWindow.Log("AutoSaving... , the oldest file deleted");
}
else
{
LogWindow.Log("AutoSaving...");
}
// 依次重命名存档
for (int i = saveFiles.Count - 1; i >= 0; i--)
{
string oldPath = saveFiles[i];
string newPath = GetAutoSavePath($"AutoSave_{i + 1}");
File.Move(oldPath, newPath);
}
}
// 保存最新存档
string newestSavePath = GetAutoSavePath("AutoSave_0");
SaveBeatMap(newestSavePath);
}
private void SaveBeatMap(string autoSavePath)
{
SaveBeatMapCoroutine(autoSavePath);
}
async void SaveBeatMapCoroutine(string autoSavePath)
{
await Task.Run(() =>
{
EditorManager.instance.beatmapContainer.SaveBM();
ES3.Save("Beatmap", EditorManager.instance.beatmapContainer.matchedBM as BeatmapContainer_BM, autoSavePath, ProjectManager.SaveSettings);
});
LogWindow.Log("Auto-saved", Color.green);
}
private List<string> GetSortedSaveFiles()
{
if (!ES3.DirectoryExists(autoSavePath))
{
Directory.CreateDirectory(autoSavePath);
}
List<string> saveFiles = new List<string>(Directory.GetFiles(autoSavePath, "AutoSave_*.json"));
saveFiles.Sort(string.Compare);
return saveFiles;
}
}
}