1
0
Fork 0

Extensively refactor core to allow for loading. Also work on a bit of saving too.

Now I can see why they say that you should build this in from the beginning....
This commit is contained in:
Starbeamrainbowlabs 2017-07-18 20:43:34 +01:00
parent 37fd8bff81
commit 34d74281e3
8 changed files with 137 additions and 30 deletions

View File

@ -306,8 +306,9 @@ namespace Nibriboard.Client
Log.WriteLine("[NibriClient#{0}] Changing to plane {1}.", Id, message.NewPlaneName);
// Create a new plane with the specified name if it doesn't exist already
// future we might want to allow the user to specify the chunk size
if(manager.SpaceManager[message.NewPlaneName] == default(Plane))
manager.SpaceManager.CreatePlane(message.NewPlaneName);
manager.SpaceManager.CreatePlane(new PlaneInfo(message.NewPlaneName));
// Remove the event listener from the old plane if there is indeed an old plane to remove it from
if(CurrentPlane != null)

View File

@ -66,7 +66,6 @@
<Compile Include="RippleSpace\ChunkReference.cs" />
<Compile Include="RippleSpace\DrawnLine.cs" />
<Compile Include="RippleSpace\Reference.cs" />
<Compile Include="Utilities.cs" />
<Compile Include="NibriboardServer.cs" />
<Compile Include="Log.cs" />
<Compile Include="Utilities\EmbeddedFiles.cs" />
@ -108,6 +107,9 @@
<Compile Include="Client\Messages\LineCompleteReflectionMessage.cs" />
<Compile Include="Client\Messages\LineStartMessage.cs" />
<Compile Include="Client\Messages\LineStartReflectionMessage.cs" />
<Compile Include="Utilities\CalcPaths.cs" />
<Compile Include="RippleSpace\PlaneInfo.cs" />
<Compile Include="Utilities\BinaryIO.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ClientFiles\index.html" />

View File

@ -6,6 +6,8 @@ using System.Collections;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Nibriboard.Utilities;
namespace Nibriboard.RippleSpace
{
public enum ChunkUpdateType
@ -219,7 +221,7 @@ namespace Nibriboard.RippleSpace
}
public static async Task<Chunk> FromStream(Plane plane, Stream chunkSource)
{
Chunk loadedChunk = await Utilities.DeserialiseBinaryObject<Chunk>(chunkSource);
Chunk loadedChunk = await BinaryIO.DeserialiseBinaryObject<Chunk>(chunkSource);
loadedChunk.plane = plane;
return loadedChunk;

View File

@ -7,6 +7,8 @@ using System.Runtime.Serialization;
using SharpCompress.Writers;
using SharpCompress.Common;
using SharpCompress.Readers;
using Nibriboard.Utilities;
using Newtonsoft.Json;
namespace Nibriboard.RippleSpace
{
@ -79,7 +81,8 @@ namespace Nibriboard.RippleSpace
public int UnloadableChunks {
get {
int result = 0;
foreach(KeyValuePair<ChunkReference, Chunk> chunkEntry in loadedChunkspace) {
foreach(KeyValuePair<ChunkReference, Chunk> chunkEntry in loadedChunkspace)
{
if(chunkEntry.Value.CouldUnload)
result++;
}
@ -87,20 +90,21 @@ namespace Nibriboard.RippleSpace
}
}
public Plane(string inName, int inChunkSize, string inStorageDirectoryRoot)
/// <summary>
/// Initialises a new plane.
/// </summary>
/// <param name="inInfo">The settings to use to initialise the new plane.</param>
/// <param name="inStorageDirectory">The storage directory in which we should store the plane's chunks (may be prepopulated).</param>
public Plane(PlaneInfo inInfo, string inStorageDirectory)
{
Name = inName;
ChunkSize = inChunkSize;
Name = inInfo.Name;
ChunkSize = inInfo.ChunkSize;
StorageDirectory = inStorageDirectory;
// Set the soft loaded chunk limit to double the number of chunks in the
// primary chunks area
// Note that the primary chunk area is a radius around (0, 0) - not the diameter
SoftLoadedChunkLimit = PrimaryChunkAreaSize * PrimaryChunkAreaSize * 4;
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()
@ -189,7 +193,7 @@ namespace Nibriboard.RippleSpace
if(LoadedChunks < SoftLoadedChunkLimit ||
UnloadableChunks < MinUnloadeableChunks)
return;
foreach(KeyValuePair<ChunkReference, Chunk> chunkEntry in loadedChunkspace)
{
if(!chunkEntry.Value.CouldUnload)
@ -245,22 +249,39 @@ namespace Nibriboard.RippleSpace
OnChunkUpdate(sender, eventArgs);
}
public static async Task<Plane> FromFile(string inName, int inChunkSize, string inStorageDirectoryRoot, string sourceFilename)
/// <summary>
/// Loads a plane form a given nplane file.
/// </summary>
/// <param name="planeName">The name of the plane to load.</param>
/// <param name="storageDirectoryRoot">The directory to which the plane should be unpacked.</param>
/// <param name="sourceFilename">The path to the nplane file to load.</param>
/// <param name="deleteSource">Whether the source file should be deleted once the plane has been loaded.</param>
/// <returns>The loaded plane.</returns>
public static async Task<Plane> FromFile(string planeName, string storageDirectoryRoot, string sourceFilename, bool deleteSource)
{
Plane loadedPlane = new Plane(inName, inChunkSize, inStorageDirectoryRoot);
string targetUnpackingPath = CalcPaths.UnpackedPlaneDir(storageDirectoryRoot, planeName);
// Unpack the plane to the temporary directory
using(Stream sourceStream = File.OpenRead(sourceFilename))
using(IReader unpacker = ReaderFactory.Open(sourceStream))
{
unpacker.WriteAllToDirectory(loadedPlane.StorageDirectory);
unpacker.WriteAllToDirectory(targetUnpackingPath);
}
PlaneInfo planeInfo = JsonConvert.DeserializeObject<PlaneInfo>(
File.ReadAllText(CalcPaths.UnpackedPlaneIndex(targetUnpackingPath))
);
planeInfo.Name = planeName;
Plane loadedPlane = new Plane(planeInfo, targetUnpackingPath);
// Load the primary chunks from disk inot the plane
await loadedPlane.LoadPrimaryChunks();
if(deleteSource)
File.Delete(sourceFilename);
return loadedPlane;
}
}
}
}

View File

@ -0,0 +1,23 @@
using System;
namespace Nibriboard.RippleSpace
{
public class PlaneInfo
{
public string Name { get; set; }
public int ChunkSize { get; set; }
public PlaneInfo()
{
}
public PlaneInfo(string inName) : this(inName, 1024)
{
}
public PlaneInfo(string inName, int inChunkSize)
{
Name = inName;
ChunkSize = inChunkSize;
}
}
}

View File

@ -3,7 +3,9 @@ using System.IO;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Linq;
using SharpCompress.Readers;
using Nibriboard.Utilities;
namespace Nibriboard.RippleSpace
{
@ -71,16 +73,19 @@ namespace Nibriboard.RippleSpace
/// <summary>
/// Creates a new plane, adds it to this RippleSpaceManager, and then returns it.
/// </summary>
/// <param name="newPlaneName">The name of the new plane to create.</param>
/// <param name="newPlaneInfo">The settings for the new plane to create.</param>
/// <returns>The newly created plane.</returns>
public Plane CreatePlane(string newPlaneName)
public Plane CreatePlane(PlaneInfo newPlaneInfo)
{
if(this[newPlaneName] != null)
throw new InvalidOperationException($"Error: A plane with the name '{newPlaneName}' already exists in this RippleSpaceManager.");
if(this[newPlaneInfo.Name] != null)
throw new InvalidOperationException($"Error: A plane with the name '{newPlaneInfo.Name}' already exists in this RippleSpaceManager.");
Log.WriteLine("[RippleSpace] Creating plane {0}", newPlaneName);
Log.WriteLine("[RippleSpace] Creating plane {0}", newPlaneInfo.Name);
Plane newPlane = new Plane(newPlaneName, DefaultChunkSize);
Plane newPlane = new Plane(
newPlaneInfo,
CalcPaths.UnpackedPlaneDir(UnpackedDirectory, newPlaneInfo.Name)
);
Planes.Add(newPlane);
return newPlane;
}
@ -124,17 +129,29 @@ namespace Nibriboard.RippleSpace
Log.WriteLine("[Core] Importing planes");
StreamReader planes = new StreamReader(rippleSpace.UnpackedDirectory + "index.list");
List<Task> planeReaders = new List<Task>();
List<Task<Plane>> planeReaders = new List<Task<Plane>>();
string nextPlane;
int planeCount = 0;
while((nextPlane = await planes.ReadLineAsync()) != null)
{
planeReaders.Add(Plane.FromFile(rippleSpace.UnpackedDirectory + nextPlane));
planeReaders.Add(Plane.FromFile(
planeName: nextPlane,
storageDirectoryRoot: rippleSpace.UnpackedDirectory,
sourceFilename: CalcPaths.UnpackedPlaneFile(rippleSpace.UnpackedDirectory, nextPlane),
deleteSource: true
));
planeCount++;
}
await Task.WhenAll(planeReaders);
Log.WriteLine("[Core] done!");
rippleSpace.Planes.AddRange(
planeReaders.Select((Task<Plane> planeReader) => planeReader.Result)
);
Log.WriteLine("[Core] done! {0} planes loaded.", planeCount);
return rippleSpace;
}
}
}
}

View File

@ -2,9 +2,10 @@
using System.Threading.Tasks;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace Nibriboard
namespace Nibriboard.Utilities
{
public static class Utilities
public static class BinaryIO
{
/// <summary>
/// Deserialises an object from it's binary representation.

View File

@ -0,0 +1,40 @@
using System;
namespace Nibriboard.Utilities
{
public static class CalcPaths
{
/// <summary>
/// Returns the directory in which a plane's data should be unpacked to.
/// </summary>
/// <param name="unpackingRoot">The root directory to which everything is going to be unpacked.</param>
/// <param name="planeName">The name of the plane that will be unpacked.</param>
/// <returns>The directory to which a plane should unpack it's data to.</returns>
public static string UnpackedPlaneDir(string unpackingRoot, string planeName)
{
string result = $"{unpackingRoot}/Planes/{planeName}/";
return result;
}
/// <summary>
/// Returns the path to the plane index file given a directory that a plane has been unpacked to.
/// </summary>
/// <param name="unpackingPlaneDir">The directory to which a plane's data has been unpacked.</param>
/// <returns>The path to the plane index file.</returns>
public static string UnpackedPlaneIndex(string unpackingPlaneDir)
{
return $"{unpackingPlaneDir}/plane-index.json";
}
/// <summary>
/// Calculates the path to a packed plane file.
/// </summary>
/// <param name="unpackingDir">The directory to which the nplane files were unpacked.</param>
/// <param name="planeName">The name of the plane to fetch the filepath for.</param>
/// <returns>The path to the packed plane file.</returns>
public static string UnpackedPlaneFile(string unpackingDir, string planeName)
{
return $"{unpackingDir}/{planeName}.nplane";
}
}
}