Implement load and save UI

This commit is contained in:
Stone_Red
2023-07-30 17:28:36 +02:00
parent 6d0ca85b7f
commit 8b8e4c48e3
22 changed files with 364 additions and 166 deletions
@@ -0,0 +1,9 @@
<Project StylesheetPath="C:\Users\David\My Drive\Programming\StoneRed.LogicSimulator\StoneRed.LogicSimulator\Content\stylesheet\default_ui_skin.xmms">
<Project.ExportOptions />
<Window Title="Load world" Left="266" Top="198" MinWidth="400">
<VerticalStackPanel>
<ListBox HorizontalAlignment="Stretch" Id="saves" />
<TextButton Text="Load" Enabled="False" Margin="0, 10, 0, 0" Padding="5, 5, 8, 5" Id="load" />
</VerticalStackPanel>
</Window>
</Project>
@@ -0,0 +1,12 @@
<Project StylesheetPath="C:\Users\David\My Drive\Programming\StoneRed.LogicSimulator\StoneRed.LogicSimulator\Content\stylesheet\default_ui_skin.xmms">
<Project.ExportOptions />
<Window Title="Load world" Left="266" Top="201" MinWidth="400">
<VerticalStackPanel>
<ListBox HorizontalAlignment="Stretch" Id="saves" />
<HorizontalStackPanel Spacing="10">
<TextButton Text="Save" Enabled="False" Margin="0, 10, 0, 0" Padding="5, 5, 8, 5" Id="save" />
<TextButton Text="New Save" Margin="0, 10, 0, 0" Padding="5, 5, 8, 5" Id="newSave" />
</HorizontalStackPanel>
</VerticalStackPanel>
</Window>
</Project>
+81
View File
@@ -0,0 +1,81 @@
using Myra.Utility;
using System;
using System.IO;
namespace StoneRed.LogicSimulator.Misc;
internal static class Paths
{
public static string GetAppDataPath()
{
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string srlsAppDataPath = Path.Combine(appDataPath, "StoneRed", "LogicSimulator");
if (!Directory.Exists(srlsAppDataPath))
{
_ = Directory.CreateDirectory(srlsAppDataPath);
}
return srlsAppDataPath;
}
public static string GetAppDataPath(string fileName)
{
return Path.Combine(GetAppDataPath(), fileName);
}
public static string GetAppDataPath(params string[] paths)
{
return Path.Combine(GetAppDataPath(), Path.Combine(paths));
}
public static string GetContentPath()
{
return Path.Combine(PathUtils.ExecutingAssemblyDirectory, "Content");
}
public static string GetContentPath(string fileName)
{
return Path.Combine(GetContentPath(), fileName);
}
public static string GetContentPath(params string[] paths)
{
return Path.Combine(GetContentPath(), Path.Combine(paths));
}
public static string GetSettingsPath()
{
return Path.Combine(GetAppDataPath(), "settings.json");
}
public static string GetWorldSavesPath()
{
string savesPath = Path.Combine(GetAppDataPath(), "Saves");
if (!Directory.Exists(savesPath))
{
_ = Directory.CreateDirectory(savesPath);
}
return savesPath;
}
public static string GetWorldSaveDirectoryPath(string saveName)
{
string savePath = Path.Combine(GetWorldSavesPath(), saveName);
if (!Directory.Exists(savePath))
{
_ = Directory.CreateDirectory(savePath);
}
return savePath;
}
public static string GetWorldSaveFilePath(string saveName)
{
return Path.Combine(GetWorldSaveDirectoryPath(saveName), saveName + ".srls");
}
}
@@ -1,7 +1,8 @@
using System.IO;
using System.Text.Json;
using StoneRed.LogicSimulator.Utilities;
namespace StoneRed.LogicSimulator.Utilities;
namespace StoneRed.LogicSimulator.Misc;
internal class Settings
{
+5 -12
View File
@@ -8,8 +8,8 @@ using MonoGame.Extended.Screens;
using Myra;
using Myra.Assets;
using Myra.Graphics2D.UI;
using Myra.Utility;
using StoneRed.LogicSimulator.Misc;
using StoneRed.LogicSimulator.UserInterface.Screens;
using StoneRed.LogicSimulator.UserInterface.Windows;
using StoneRed.LogicSimulator.Utilities;
@@ -36,18 +36,11 @@ internal class Srls : Game
public Settings Settings { get; set; }
public string ContentPath { get; }
public string SettingsPath { get; }
public Srls()
{
Content.RootDirectory = "Content";
ContentPath = Path.Combine(PathUtils.ExecutingAssemblyDirectory, "Content");
SettingsPath = Path.Combine(PathUtils.ExecutingAssemblyDirectory, "settings.json");
Settings = Settings.Load(SettingsPath) ?? new Settings()
Settings = Settings.Load(Paths.GetSettingsPath()) ?? new Settings()
{
Resolution = new Resolution(GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width, GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height),
Scale = 1
@@ -101,7 +94,7 @@ internal class Srls : Game
public TextButton ShowContextMenu(string title, Point position, MenuItem[] menuItems, bool showButton = false)
{
string data = File.ReadAllText(Path.Combine(ContentPath, "ContextMenu.xmmp"));
string data = File.ReadAllText(Paths.GetContentPath("ContextMenu.xmmp"));
VerticalStackPanel contextMenu = (VerticalStackPanel)Project.LoadFromXml(data, AssetManager).Root;
contextMenu.FindChildById<Label>("title").Text = title;
@@ -164,9 +157,9 @@ internal class Srls : Game
Desktop = new Desktop();
FontSystem = new FontSystem();
FontSystem.AddFont(File.ReadAllBytes(Path.Combine(ContentPath, "ManoloMono.ttf")));
FontSystem.AddFont(File.ReadAllBytes(Paths.GetContentPath("ManoloMono.ttf")));
FileAssetResolver assetResolver = new FileAssetResolver(ContentPath);
FileAssetResolver assetResolver = new FileAssetResolver(Paths.GetContentPath());
AssetManager = new AssetManager(assetResolver);
LogicGatesManager = new LogicGatesManager();
@@ -41,6 +41,12 @@
<None Update="Content\LoadingScreen.xmmp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Content\SaveWorldWindow.xmmp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Content\LoadWorldWindow.xmmp">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Content\ManoloMono.ttf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@@ -4,12 +4,9 @@ using Microsoft.Xna.Framework;
using Myra.Graphics2D.UI;
using StoneRed.LogicSimulator.Simulation.LogicGates;
using StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces;
using StoneRed.LogicSimulator.WorldSaveSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -17,14 +14,14 @@ namespace StoneRed.LogicSimulator.UserInterface.Screens;
internal class LoadingScreen : SrlsScreen<VerticalStackPanel>
{
private readonly string filePath;
private readonly string saveName;
private Label label = null!;
private HorizontalProgressBar horizontalProgressBar = null!;
protected override string XmmpPath => "LoadingScreen.xmmp";
public LoadingScreen(string filePath)
public LoadingScreen(string saveName)
{
this.filePath = filePath;
this.saveName = saveName;
}
protected override void Initialize()
@@ -43,8 +40,13 @@ internal class LoadingScreen : SrlsScreen<VerticalStackPanel>
_ = Task.Run(async () =>
{
Result<IEnumerable<LogicGate>> result = await worldLoader.LoadWorld(filePath, progress);
if (result.IsFailed)
Result<WorldData> result = await worldLoader.LoadWorld(saveName, progress);
if (result.IsSuccess)
{
srls.LoadScreen(new WorldScreen(result.Value));
}
else
{
Dialog.CreateMessageBox("Error", string.Join(',', result.Errors.Select(e => e.Message))).Show(srls.Desktop);
}
@@ -53,89 +55,6 @@ internal class LoadingScreen : SrlsScreen<VerticalStackPanel>
protected override void Update(GameTime gameTime)
{
return;
label.Text = $"Loading... {Math.Round(100 / horizontalProgressBar.Maximum * horizontalProgressBar.Value, 0)}%";
if (horizontalProgressBar.Value >= horizontalProgressBar.Maximum)
{
Clock button = new Clock()
{
Id = 0,
WorldData = new LogicGateWorldData()
{
Name = "Clock",
Position = new Vector2(0, 0)
}
};
Switch @switch = new Switch()
{
Id = 1,
WorldData = new LogicGateWorldData()
{
Name = "Switch",
Position = new Vector2(0, 200)
}
};
NotGate testLogicGate1 = new NotGate()
{
Id = 2,
WorldData = new LogicGateWorldData()
{
Name = "Not Gate",
Position = new Vector2(200, 200)
}
};
Pin testLogicGate2 = new Pin()
{
Id = 3,
WorldData = new LogicGateWorldData()
{
Name = "Pin",
Position = new Vector2(400, 400)
}
};
Pin testLogicGate3 = new Pin()
{
Id = 4,
WorldData = new LogicGateWorldData()
{
Name = "Pin",
Position = new Vector2(500, 500)
}
};
Lamp lamp = new Lamp()
{
Id = 5,
WorldData = new LogicGateWorldData()
{
Name = "Lamp",
Position = new Vector2(600, 600)
}
};
button.Connect(testLogicGate1, 0, 0);
@switch.Connect(testLogicGate1, 0, 0);
testLogicGate1.Connect(testLogicGate2, 0, 0);
testLogicGate2.Connect(testLogicGate3, 0, 0);
testLogicGate3.Connect(lamp, 0, 0);
List<LogicGate> logicGates = new List<LogicGate>
{
testLogicGate3,
button,
testLogicGate1,
testLogicGate2,
lamp,
@switch
};
srls.LoadScreen(new WorldScreen(logicGates));
}
}
protected override void Draw(GameTime gameTime)
@@ -4,6 +4,8 @@ using MonoGame.Extended.Screens;
using Myra.Graphics2D.UI;
using StoneRed.LogicSimulator.Misc;
using System;
using System.IO;
@@ -20,7 +22,7 @@ internal abstract class SrlsScreen
{
this.srls = srls;
string data = File.ReadAllText(Path.Combine(srls.ContentPath, XmmpPath));
string data = File.ReadAllText(Paths.GetContentPath(XmmpPath));
srls.Desktop.Root = Project.LoadFromXml(data, srls.AssetManager).Root;
GameScreenWrapper gameScreenWrapper = new GameScreenWrapper(srls);
@@ -1,12 +1,9 @@
using Microsoft.Xna.Framework;
using Myra.Graphics2D.UI;
using Myra.Graphics2D.UI.File;
using StoneRed.LogicSimulator.UserInterface.Windows;
using System.Threading.Tasks;
namespace StoneRed.LogicSimulator.UserInterface.Screens;
internal class StartScreen : SrlsScreen<VerticalStackPanel>
@@ -32,21 +29,6 @@ internal class StartScreen : SrlsScreen<VerticalStackPanel>
private void MenuLoad_Selected(object? sender, System.EventArgs e)
{
FileDialog fileDialog = new FileDialog(FileDialogMode.OpenFile);
fileDialog.Show(srls.Desktop);
fileDialog.Scale = new Vector2(srls.Scale / 3);
fileDialog.Closed += async (s, a) =>
{
if (!fileDialog.Result)
{
return;
}
// Very cheap workaround to make sure the file dialog is closed before loading the screen
await Task.Delay(100);
srls.LoadScreen(new LoadingScreen(fileDialog.FilePath));
};
srls.ShowWindow<LoadWorldWindow>();
}
}
@@ -13,9 +13,9 @@ using StoneRed.LogicSimulator.Simulation;
using StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces;
using StoneRed.LogicSimulator.UserInterface.Windows;
using StoneRed.LogicSimulator.Utilities;
using StoneRed.LogicSimulator.WorldSaveSystem;
using System;
using System.Collections.Generic;
using System.Linq;
using IColorable = StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces.IColorable;
@@ -26,6 +26,7 @@ internal class WorldScreen : SrlsScreen<Grid>
{
private readonly LogicGateSimulator simulator;
private readonly Vector2 logicGateSize = new Vector2(100, 100);
private readonly WorldData worldData;
private OrthographicCamera camera = null!;
private RichTextLayout richTextLayout = null!;
@@ -45,14 +46,16 @@ internal class WorldScreen : SrlsScreen<Grid>
public override bool ScalingEnabled => false;
protected override string XmmpPath => "WorldScreen.xmmp";
public WorldScreen(IEnumerable<LogicGate> logicGates)
public WorldScreen(WorldData worldData)
{
simulator = new LogicGateSimulator(logicGates);
simulator = new LogicGateSimulator(worldData.LogicGates);
this.worldData = worldData;
}
public WorldScreen()
{
simulator = new LogicGateSimulator(Enumerable.Empty<LogicGate>());
worldData = new WorldData(Enumerable.Empty<LogicGate>(), 1, string.Empty);
}
protected override void Initialize()
@@ -230,7 +233,8 @@ internal class WorldScreen : SrlsScreen<Grid>
if (keyboardState.IsKeyDown(Keys.Q))
{
srls.ShowWindow<QuickMenu>();
worldData.LogicGates = simulator.GetLogicGates();
srls.ShowWindow(new QuickMenu(worldData));
}
previousMouseState = mouseState;
@@ -0,0 +1,43 @@
using Myra.Graphics2D.UI;
using StoneRed.LogicSimulator.Misc;
using StoneRed.LogicSimulator.UserInterface.Screens;
using System;
using System.IO;
namespace StoneRed.LogicSimulator.UserInterface.Windows;
internal class LoadWorldWindow : SrlsWindow
{
private ListBox savesListBox = null!;
private TextButton loadButton = null!;
protected override string XmmpPath => "LoadWorldWindow.xmmp";
public override void Initialize()
{
savesListBox = window.FindChildById<ListBox>("saves");
loadButton = window.FindChildById<TextButton>("load");
loadButton.Click += LoadButton_Clicked;
foreach (string directories in Directory.GetDirectories(Paths.GetWorldSavesPath()))
{
string saveName = Path.GetFileName(directories) ?? string.Empty;
savesListBox.Items.Add(new(saveName));
}
savesListBox.SelectedIndexChanged += SavesListBox_SelectedIndexChanged;
}
private void SavesListBox_SelectedIndexChanged(object? sender, EventArgs e)
{
loadButton.Enabled = savesListBox.SelectedItem is not null;
}
private void LoadButton_Clicked(object? sender, EventArgs e)
{
srls.LoadScreen(new LoadingScreen(savesListBox.SelectedItem.Text));
}
}
@@ -1,21 +1,29 @@
using Myra.Graphics2D.UI;
using StoneRed.LogicSimulator.UserInterface.Screens;
using StoneRed.LogicSimulator.WorldSaveSystem;
namespace StoneRed.LogicSimulator.UserInterface.Windows;
internal class QuickMenu : SrlsWindow
{
private readonly WorldData worldData;
protected override string XmmpPath => "QuickMenu.xmmp";
public QuickMenu(WorldData worldData)
{
this.worldData = worldData;
}
public override void Initialize()
{
VerticalMenu menu = window.FindChildById<VerticalMenu>("menu");
menu.FindMenuItemById("menuMainMenu").Selected += (s, a) => srls.LoadScreen<StartScreen>();
menu.FindMenuItemById("menuSettings").Selected += (s, a) => srls.ShowWindow<SettingsWindow>();
menu.FindMenuItemById("menuSave").Selected += (s, a) => srls.ShowWindow<SettingsWindow>();
menu.FindMenuItemById("menuLoad").Selected += (s, a) => srls.ShowWindow<SettingsWindow>();
menu.FindMenuItemById("menuSave").Selected += (s, a) => srls.ShowWindow(new SaveWorldWindow(worldData));
menu.FindMenuItemById("menuLoad").Selected += (s, a) => srls.ShowWindow<LoadWorldWindow>();
menu.FindMenuItemById("menuQuit").Selected += QuickMenu_Selected;
}
@@ -0,0 +1,78 @@
using FluentResults;
using Myra.Graphics2D.UI;
using StoneRed.LogicSimulator.Misc;
using StoneRed.LogicSimulator.WorldSaveSystem;
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace StoneRed.LogicSimulator.UserInterface.Windows;
internal class SaveWorldWindow : SrlsWindow
{
private readonly WorldData worldData;
private ListBox savesListBox = null!;
private TextButton saveButton = null!;
protected override string XmmpPath => "SaveWorldWindow.xmmp";
public SaveWorldWindow(WorldData worldData)
{
this.worldData = worldData;
}
public override void Initialize()
{
savesListBox = window.FindChildById<ListBox>("saves");
saveButton = window.FindChildById<TextButton>("save");
TextButton newSaveButton = window.FindChildById<TextButton>("newSave");
saveButton.Click += SaveButton_Clicked;
newSaveButton.Click += NewSaveButton_Click;
foreach (string directories in Directory.GetDirectories(Paths.GetWorldSavesPath()))
{
string saveName = Path.GetFileName(directories) ?? string.Empty;
savesListBox.Items.Add(new(saveName));
}
savesListBox.SelectedIndexChanged += SavesListBox_SelectedIndexChanged;
}
private void SavesListBox_SelectedIndexChanged(object? sender, EventArgs e)
{
saveButton.Enabled = savesListBox.SelectedItem is not null;
}
private async void NewSaveButton_Click(object? sender, EventArgs e)
{
worldData.SaveName = "New Save";
await Save();
}
private async void SaveButton_Clicked(object? sender, EventArgs e)
{
await Save();
}
private async Task Save()
{
WorldSaver worldSaver = new WorldSaver(srls);
Result result = await worldSaver.SaveWorld(worldData, new Progress<WorldSaveLoadProgress>());
if (result.IsSuccess)
{
_ = Dialog.CreateMessageBox("Success", "Saved world!");
}
else
{
_ = Dialog.CreateMessageBox("Error", string.Join(',', result.Errors.Select(e => e.Message)));
}
Close();
}
}
@@ -2,6 +2,7 @@
using Myra.Graphics2D.UI;
using StoneRed.LogicSimulator.Misc;
using StoneRed.LogicSimulator.Utilities;
using System.Collections.Generic;
@@ -53,7 +54,7 @@ internal class SettingsWindow : SrlsWindow
srls.Settings.Resolution = newResolution;
srls.Settings.Scale = scaleSpinButton.Value.GetValueOrDefault(1);
srls.Settings.Save(srls.SettingsPath);
srls.Settings.Save(Paths.GetSettingsPath());
window.Close();
@@ -2,6 +2,8 @@
using Myra.Graphics2D.UI;
using StoneRed.LogicSimulator.Misc;
using System;
using System.IO;
@@ -23,7 +25,7 @@ internal abstract class SrlsWindow
{
this.srls = srls;
string data = File.ReadAllText(Path.Combine(srls.ContentPath, XmmpPath));
string data = File.ReadAllText(Paths.GetContentPath(XmmpPath));
window = (Window)Project.LoadFromXml(data, srls.AssetManager).Root;
window.Closed += (_, _) => Closed?.Invoke(this, EventArgs.Empty);
}
@@ -0,0 +1,21 @@
using StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces;
using System.Collections.Generic;
namespace StoneRed.LogicSimulator.WorldSaveSystem;
internal class WorldData
{
public IEnumerable<LogicGate> LogicGates { get; set; }
public int SaveVersion { get; set; }
public string SaveName { get; set; }
public WorldData(IEnumerable<LogicGate> logicGates, int saveVersion, string saveName)
{
LogicGates = logicGates;
SaveVersion = saveVersion;
SaveName = saveName;
}
}
@@ -1,10 +1,9 @@
using FluentResults;
using StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces;
using StoneRed.LogicSimulator.Misc;
using StoneRed.LogicSimulator.WorldSaveSystem.WorldReaders;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
@@ -19,9 +18,9 @@ internal class WorldLoader
this.srls = srls;
}
public Task<Result<IEnumerable<LogicGate>>> LoadWorld(string filePath, IProgress<WorldSaveLoadProgress> progress)
public Task<Result<WorldData>> LoadWorld(string saveName, IProgress<WorldSaveLoadProgress> progress)
{
BinaryReader binaryReader = new BinaryReader(File.OpenRead(filePath));
BinaryReader binaryReader = new BinaryReader(File.OpenRead(Paths.GetWorldSaveFilePath(saveName)));
ushort fileVersion = binaryReader.ReadUInt16();
binaryReader.Close();
@@ -33,9 +32,9 @@ internal class WorldLoader
if (worldReader is null)
{
return Task.FromResult(Result.Fail<IEnumerable<LogicGate>>("Invalid file version!"));
return Task.FromResult(Result.Fail<WorldData>("Invalid file version!"));
}
return worldReader.ReadWorld(filePath, progress);
return worldReader.ReadWorld(saveName, progress);
}
}
@@ -1,14 +1,11 @@
using FluentResults;
using StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace StoneRed.LogicSimulator.WorldSaveSystem.WorldReaders;
internal interface IWorldReader
{
Task<Result<IEnumerable<LogicGate>>> ReadWorld(string filePath, IProgress<WorldSaveLoadProgress> progress);
Task<Result<WorldData>> ReadWorld(string saveName, IProgress<WorldSaveLoadProgress> progress);
}
@@ -1,5 +1,6 @@
using FluentResults;
using StoneRed.LogicSimulator.Misc;
using StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces;
using System;
@@ -19,17 +20,17 @@ internal class WorldReaderV1 : IWorldReader
this.srls = srls;
}
public async Task<Result<IEnumerable<LogicGate>>> ReadWorld(string filePath, IProgress<WorldSaveLoadProgress> progress)
public async Task<Result<WorldData>> ReadWorld(string saveName, IProgress<WorldSaveLoadProgress> progress)
{
if (!File.Exists(filePath))
if (!File.Exists(Paths.GetWorldSaveFilePath(saveName)))
{
return Result.Fail($"File \"{filePath}\" does not exist");
return Result.Fail($"Save \"{saveName}\" does not exist");
}
return await Task.Run(() => InternalReadWorld(filePath, progress));
return await Task.Run(() => InternalReadWorld(saveName, progress));
}
public Result<IEnumerable<LogicGate>> InternalReadWorld(string filePath, IProgress<WorldSaveLoadProgress> progress)
public Result<WorldData> InternalReadWorld(string saveName, IProgress<WorldSaveLoadProgress> progress)
{
BinaryReader? reader = null;
Dictionary<LogicGate, List<(ulong GateRefId, int inputIndex, int outputIndex)>> connections = new();
@@ -38,19 +39,23 @@ internal class WorldReaderV1 : IWorldReader
{
progress.Report(new(0, "Opening file"));
reader = new BinaryReader(File.OpenRead(filePath));
_ = reader.ReadUInt16(); // File version
reader = new BinaryReader(File.OpenRead(Paths.GetWorldSaveFilePath(saveName)));
int saveVersion = reader.ReadUInt16();
int numberOfLogicGates = reader.ReadInt32();
for (int gateNumber = 0; gateNumber < numberOfLogicGates; gateNumber++)
{
progress.Report(new((int)((double)gateNumber / numberOfLogicGates * 50d), $"Loading logic gates ({gateNumber / numberOfLogicGates})"));
progress.Report(new((int)((double)gateNumber / numberOfLogicGates * 50d), $"Loading logic gates ({gateNumber}/{numberOfLogicGates})"));
ulong id = reader.ReadUInt64();
string typeName = reader.ReadString();
int inputCount = reader.ReadInt32();
int outputCount = reader.ReadInt32();
string name = reader.ReadString();
string description = reader.ReadString();
float positionX = reader.ReadSingle();
float positionY = reader.ReadSingle();
int numberOfConnections = reader.ReadInt32();
if (srls.LogicGatesManager.TryCreateLogicGate(typeName, out LogicGate? logicGate))
@@ -58,6 +63,9 @@ internal class WorldReaderV1 : IWorldReader
logicGate.Id = id;
logicGate.InputCount = inputCount;
logicGate.OutputCount = outputCount;
logicGate.WorldData.Name = name;
logicGate.WorldData.Description = description;
logicGate.WorldData.Position = new(positionX, positionY);
}
else
{
@@ -80,7 +88,7 @@ internal class WorldReaderV1 : IWorldReader
foreach (KeyValuePair<LogicGate, List<(ulong gateRefId, int inputIndex, int outputIndex)>> connectionPair in connections)
{
progress.Report(new((int)((double)connections.Count / connectionCount * 50d), $"Connecting logic gates ({connections.Count / connectionCount})"));
progress.Report(new((int)((double)connectionCount / connections.Count * 50d), $"Connecting logic gates ({connectionCount}/{connections.Count})"));
LogicGate logicGate = connectionPair.Key;
@@ -98,6 +106,8 @@ internal class WorldReaderV1 : IWorldReader
connectionCount++;
}
return new WorldData(connections.Keys, saveVersion, saveName);
}
catch (IOException ex)
{
@@ -107,7 +117,5 @@ internal class WorldReaderV1 : IWorldReader
{
reader?.Dispose();
}
return connections.Keys;
}
}
@@ -1,5 +1,34 @@
namespace StoneRed.LogicSimulator.WorldSaveSystem;
using FluentResults;
using StoneRed.LogicSimulator.WorldSaveSystem.WorldWriters;
using System;
using System.Threading.Tasks;
namespace StoneRed.LogicSimulator.WorldSaveSystem;
internal class WorldSaver
{
private readonly Srls srls;
public WorldSaver(Srls srls)
{
this.srls = srls;
}
public Task<Result> SaveWorld(WorldData worldData, IProgress<WorldSaveLoadProgress> progress)
{
IWorldWriter? worldWriter = worldData.SaveVersion switch
{
1 => new WorldWriterV1(srls),
_ => null
};
if (worldWriter is null)
{
return Task.FromResult(Result.Fail("Invalid file version!"));
}
return worldWriter.WriteWorld(worldData, progress);
}
}
@@ -1,14 +1,11 @@
using FluentResults;
using StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace StoneRed.LogicSimulator.WorldSaveSystem.WorldWriters;
internal interface IWorldWriter
{
Task<Result> WriteWorld(string filePath, IEnumerable<LogicGate> logicGates, IProgress<WorldSaveLoadProgress> progress);
Task<Result> WriteWorld(WorldData worldData, IProgress<WorldSaveLoadProgress> progress);
}
@@ -1,10 +1,10 @@
using FluentResults;
using StoneRed.LogicSimulator.Misc;
using StoneRed.LogicSimulator.Simulation.LogicGates.Attributes;
using StoneRed.LogicSimulator.Simulation.LogicGates.Interfaces;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -20,27 +20,29 @@ internal class WorldWriterV1 : IWorldWriter
this.srls = srls;
}
public Task<Result> WriteWorld(string filePath, IEnumerable<LogicGate> logicGates, IProgress<WorldSaveLoadProgress> progress)
public Task<Result> WriteWorld(WorldData worldData, IProgress<WorldSaveLoadProgress> progress)
{
return Task.Run(() => InternalWriteWorld(filePath, logicGates, progress));
return Task.Run(() => InternalWriteWorld(worldData, progress));
}
private Result InternalWriteWorld(string filePath, IEnumerable<LogicGate> logicGates, IProgress<WorldSaveLoadProgress> progress)
private Result InternalWriteWorld(WorldData worldData, IProgress<WorldSaveLoadProgress> progress)
{
BinaryWriter? writer = null;
try
{
writer = new BinaryWriter(File.OpenWrite(filePath));
string filePath = Paths.GetWorldSaveFilePath(worldData.SaveName);
writer = new BinaryWriter(File.Open(filePath, FileMode.Create));
progress.Report(new(0, "Counting logic gates"));
int numberOfLogicGates = logicGates.Count();
int numberOfLogicGates = worldData.LogicGates.Count();
writer.Write(1); // Write file version
writer.Write((short)1); // Write file version
writer.Write(numberOfLogicGates);
foreach (LogicGate logicGate in logicGates)
foreach (LogicGate logicGate in worldData.LogicGates)
{
writer.Write(logicGate.Id);
@@ -52,6 +54,10 @@ internal class WorldWriterV1 : IWorldWriter
writer.Write(typeName);
writer.Write(logicGate.InputCount);
writer.Write(logicGate.OutputCount);
writer.Write(logicGate.WorldData.Name);
writer.Write(logicGate.WorldData.Description);
writer.Write(logicGate.WorldData.Position.X);
writer.Write(logicGate.WorldData.Position.Y);
writer.Write(logicGate.LogicGateConnections.Count);
foreach (LogicGateConnection connection in logicGate.LogicGateConnections)