mirror of
https://github.com/sbrl/Nibriboard.git
synced 2018-01-10 21:33:49 +00:00
Start filling in all the missing saving/loading logic
This commit is contained in:
parent
af8fe9bbc2
commit
37fd8bff81
5 changed files with 135 additions and 3 deletions
|
@ -53,6 +53,9 @@
|
||||||
<Reference Include="Newtonsoft.Json">
|
<Reference Include="Newtonsoft.Json">
|
||||||
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="SharpCompress">
|
||||||
|
<HintPath>..\packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
|
|
|
@ -4,6 +4,9 @@ using System.Threading.Tasks;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.Serialization.Formatters.Binary;
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using SharpCompress.Writers;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
using SharpCompress.Readers;
|
||||||
|
|
||||||
namespace Nibriboard.RippleSpace
|
namespace Nibriboard.RippleSpace
|
||||||
{
|
{
|
||||||
|
@ -36,6 +39,7 @@ namespace Nibriboard.RippleSpace
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The number of chunks in a square around (0, 0) that should always be
|
/// The number of chunks in a square around (0, 0) that should always be
|
||||||
/// loaded.
|
/// loaded.
|
||||||
|
/// Works like a radius.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int PrimaryChunkAreaSize = 10;
|
public int PrimaryChunkAreaSize = 10;
|
||||||
|
|
||||||
|
@ -83,7 +87,7 @@ namespace Nibriboard.RippleSpace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plane(string inName, int inChunkSize)
|
public Plane(string inName, int inChunkSize, string inStorageDirectoryRoot)
|
||||||
{
|
{
|
||||||
Name = inName;
|
Name = inName;
|
||||||
ChunkSize = inChunkSize;
|
ChunkSize = inChunkSize;
|
||||||
|
@ -93,8 +97,33 @@ namespace Nibriboard.RippleSpace
|
||||||
// Note that the primary chunk area is a radius around (0, 0) - not the diameter
|
// Note that the primary chunk area is a radius around (0, 0) - not the diameter
|
||||||
SoftLoadedChunkLimit = PrimaryChunkAreaSize * PrimaryChunkAreaSize * 4;
|
SoftLoadedChunkLimit = PrimaryChunkAreaSize * PrimaryChunkAreaSize * 4;
|
||||||
|
|
||||||
StorageDirectory = $"./Planes/{Name}";
|
StorageDirectory = inStorageDirectoryRoot + Name;
|
||||||
|
if(File.Exists(StorageDirectory))
|
||||||
|
throw new InvalidOperationException($"Error: The unpacked storage directory {StorageDirectory} already exists!");
|
||||||
|
Directory.CreateDirectory(StorageDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task LoadPrimaryChunks()
|
||||||
|
{
|
||||||
|
List<ChunkReference> primaryChunkRefs = new List<ChunkReference>();
|
||||||
|
|
||||||
|
ChunkReference currentRef = new ChunkReference(this, -PrimaryChunkAreaSize, -PrimaryChunkAreaSize);
|
||||||
|
while(currentRef.Y < PrimaryChunkAreaSize)
|
||||||
|
{
|
||||||
|
primaryChunkRefs.Add(currentRef.Clone() as ChunkReference);
|
||||||
|
|
||||||
|
currentRef.X++;
|
||||||
|
|
||||||
|
if(currentRef.X > PrimaryChunkAreaSize)
|
||||||
|
{
|
||||||
|
currentRef.X = -PrimaryChunkAreaSize;
|
||||||
|
currentRef.Y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await FetchChunks(primaryChunkRefs);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetches a list of chunks by a list of chunk refererences.
|
/// Fetches a list of chunks by a list of chunk refererences.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -102,6 +131,7 @@ namespace Nibriboard.RippleSpace
|
||||||
/// <returns>The chunks attached to the specified chunk references.</returns>
|
/// <returns>The chunks attached to the specified chunk references.</returns>
|
||||||
public async Task<List<Chunk>> FetchChunks(List<ChunkReference> chunkRefs)
|
public async Task<List<Chunk>> FetchChunks(List<ChunkReference> chunkRefs)
|
||||||
{
|
{
|
||||||
|
// todo Paralellise loading with https://www.nuget.org/packages/AsyncEnumerator
|
||||||
List<Chunk> chunks = new List<Chunk>();
|
List<Chunk> chunks = new List<Chunk>();
|
||||||
foreach(ChunkReference chunkRef in chunkRefs)
|
foreach(ChunkReference chunkRef in chunkRefs)
|
||||||
chunks.Add(await FetchChunk(chunkRef));
|
chunks.Add(await FetchChunk(chunkRef));
|
||||||
|
@ -180,6 +210,20 @@ namespace Nibriboard.RippleSpace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Save(Stream destination)
|
||||||
|
{
|
||||||
|
WriterOptions packingOptions = new WriterOptions(CompressionType.GZip);
|
||||||
|
|
||||||
|
IEnumerable<string> chunkFiles = Directory.GetFiles(StorageDirectory);
|
||||||
|
using(IWriter packer = WriterFactory.Open(destination, ArchiveType.Tar, packingOptions))
|
||||||
|
{
|
||||||
|
foreach(string nextChunkFile in chunkFiles)
|
||||||
|
{
|
||||||
|
packer.Write($"{Name}/{Path.GetFileName(nextChunkFile)}", nextChunkFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles chunk updates from the individual loaded chunks on this plane.
|
/// Handles chunk updates from the individual loaded chunks on this plane.
|
||||||
/// Re-emits chunk updates it catches wind of at plane-level.
|
/// Re-emits chunk updates it catches wind of at plane-level.
|
||||||
|
@ -200,5 +244,23 @@ namespace Nibriboard.RippleSpace
|
||||||
// Make the chunk update bubble up to plane-level
|
// Make the chunk update bubble up to plane-level
|
||||||
OnChunkUpdate(sender, eventArgs);
|
OnChunkUpdate(sender, eventArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<Plane> FromFile(string inName, int inChunkSize, string inStorageDirectoryRoot, string sourceFilename)
|
||||||
|
{
|
||||||
|
Plane loadedPlane = new Plane(inName, inChunkSize, inStorageDirectoryRoot);
|
||||||
|
|
||||||
|
// Unpack the plane to the temporary directory
|
||||||
|
using(Stream sourceStream = File.OpenRead(sourceFilename))
|
||||||
|
using(IReader unpacker = ReaderFactory.Open(sourceStream))
|
||||||
|
{
|
||||||
|
unpacker.WriteAllToDirectory(loadedPlane.StorageDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the primary chunks from disk inot the plane
|
||||||
|
await loadedPlane.LoadPrimaryChunks();
|
||||||
|
|
||||||
|
return loadedPlane;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using SharpCompress.Readers;
|
||||||
|
|
||||||
namespace Nibriboard.RippleSpace
|
namespace Nibriboard.RippleSpace
|
||||||
{
|
{
|
||||||
public class RippleSpaceManager
|
public class RippleSpaceManager
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The temporary directory in which we are currently storing our unpacked planes temporarily.
|
||||||
|
/// </summary>
|
||||||
|
public string UnpackedDirectory;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The master list of planes that this PlaneManager is in charge of.
|
/// The master list of planes that this PlaneManager is in charge of.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -26,6 +33,12 @@ namespace Nibriboard.RippleSpace
|
||||||
|
|
||||||
public RippleSpaceManager()
|
public RippleSpaceManager()
|
||||||
{
|
{
|
||||||
|
// Create a temporary directory in which to store our unpacked planes
|
||||||
|
UnpackedDirectory = Path.GetTempFileName();
|
||||||
|
File.Delete(UnpackedDirectory);
|
||||||
|
UnpackedDirectory += "/";
|
||||||
|
Directory.CreateDirectory(UnpackedDirectory);
|
||||||
|
|
||||||
Log.WriteLine("[RippleSpace] New blank ripplespace initialised.");
|
Log.WriteLine("[RippleSpace] New blank ripplespace initialised.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,5 +102,39 @@ namespace Nibriboard.RippleSpace
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<RippleSpaceManager> FromFile(string filename)
|
||||||
|
{
|
||||||
|
if(!File.Exists(filename))
|
||||||
|
throw new FileNotFoundException($"Error: Couldn't find the packed ripplespace at {filename}");
|
||||||
|
|
||||||
|
RippleSpaceManager rippleSpace = new RippleSpaceManager();
|
||||||
|
|
||||||
|
using(Stream packedRippleSpaceStream = File.OpenRead(filename))
|
||||||
|
using(IReader rippleSpaceUnpacker = ReaderFactory.Open(packedRippleSpaceStream))
|
||||||
|
{
|
||||||
|
Log.WriteLine($"[Core] Unpacking ripplespace packed with {rippleSpaceUnpacker.ArchiveType} from {filename}.");
|
||||||
|
rippleSpaceUnpacker.WriteAllToDirectory(UnpackedDirectory);
|
||||||
|
}
|
||||||
|
Log.WriteLine("[Core] done!");
|
||||||
|
|
||||||
|
if(!File.Exists(rippleSpace.UnpackedDirectory + "index.list"))
|
||||||
|
throw new InvalidDataException($"Error: The packed ripplespace at {filename} doesn't appear to contain an index file.");
|
||||||
|
|
||||||
|
Log.WriteLine("[Core] Importing planes");
|
||||||
|
|
||||||
|
StreamReader planes = new StreamReader(rippleSpace.UnpackedDirectory + "index.list");
|
||||||
|
List<Task> planeReaders = new List<Task>();
|
||||||
|
string nextPlane;
|
||||||
|
while((nextPlane = await planes.ReadLineAsync()) != null)
|
||||||
|
{
|
||||||
|
planeReaders.Add(Plane.FromFile(rippleSpace.UnpackedDirectory + nextPlane));
|
||||||
|
}
|
||||||
|
await Task.WhenAll(planeReaders);
|
||||||
|
|
||||||
|
Log.WriteLine("[Core] done!");
|
||||||
|
|
||||||
|
return rippleSpace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,12 @@ namespace Nibriboard
|
||||||
{
|
{
|
||||||
public static class Utilities
|
public static class Utilities
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialises an object from it's binary representation.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type to cast the deseralised object into.</typeparam>
|
||||||
|
/// <param name="sourceStream">The source stream to deseralise an object from.</param>
|
||||||
|
/// <returns>The object deserialised from the given stream.</returns>
|
||||||
public static async Task<T> DeserialiseBinaryObject<T>(Stream sourceStream)
|
public static async Task<T> DeserialiseBinaryObject<T>(Stream sourceStream)
|
||||||
{
|
{
|
||||||
return await Task.Run(() => {
|
return await Task.Run(() => {
|
||||||
|
@ -13,5 +19,18 @@ namespace Nibriboard
|
||||||
return (T)formatter.Deserialize(sourceStream);
|
return (T)formatter.Deserialize(sourceStream);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Serialises an object and outputs a binary representation to the given stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The target to serialise.</param>
|
||||||
|
/// <param name="destinationStream">The destination stream to send the binary representation to.</param>
|
||||||
|
public static async Task SerialiseToBinary(object target, Stream destinationStream)
|
||||||
|
{
|
||||||
|
await Task.Run(() => {
|
||||||
|
BinaryFormatter formatter = new BinaryFormatter();
|
||||||
|
formatter.Serialize(destinationStream, target);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,5 @@
|
||||||
<package id="IotWeb" version="0.8.6" targetFramework="net452" />
|
<package id="IotWeb" version="0.8.6" targetFramework="net452" />
|
||||||
<package id="MimeSharp" version="1.0.0" targetFramework="net45" />
|
<package id="MimeSharp" version="1.0.0" targetFramework="net45" />
|
||||||
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
|
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />
|
||||||
|
<package id="SharpCompress" version="0.17.1" targetFramework="net461" />
|
||||||
</packages>
|
</packages>
|
Loading…
Reference in a new issue