1
0
Fork 0
mirror of https://github.com/sbrl/Nibriboard.git synced 2018-01-10 21:33:49 +00:00

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); 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 // 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)) 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 // Remove the event listener from the old plane if there is indeed an old plane to remove it from
if(CurrentPlane != null) if(CurrentPlane != null)

View file

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

View file

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

View file

@ -7,6 +7,8 @@ using System.Runtime.Serialization;
using SharpCompress.Writers; using SharpCompress.Writers;
using SharpCompress.Common; using SharpCompress.Common;
using SharpCompress.Readers; using SharpCompress.Readers;
using Nibriboard.Utilities;
using Newtonsoft.Json;
namespace Nibriboard.RippleSpace namespace Nibriboard.RippleSpace
{ {
@ -79,7 +81,8 @@ namespace Nibriboard.RippleSpace
public int UnloadableChunks { public int UnloadableChunks {
get { get {
int result = 0; int result = 0;
foreach(KeyValuePair<ChunkReference, Chunk> chunkEntry in loadedChunkspace) { foreach(KeyValuePair<ChunkReference, Chunk> chunkEntry in loadedChunkspace)
{
if(chunkEntry.Value.CouldUnload) if(chunkEntry.Value.CouldUnload)
result++; 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; Name = inInfo.Name;
ChunkSize = inChunkSize; ChunkSize = inInfo.ChunkSize;
StorageDirectory = inStorageDirectory;
// Set the soft loaded chunk limit to double the number of chunks in the // Set the soft loaded chunk limit to double the number of chunks in the
// primary chunks area // primary chunks area
// 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 = inStorageDirectoryRoot + Name;
if(File.Exists(StorageDirectory))
throw new InvalidOperationException($"Error: The unpacked storage directory {StorageDirectory} already exists!");
Directory.CreateDirectory(StorageDirectory);
} }
private async Task LoadPrimaryChunks() private async Task LoadPrimaryChunks()
@ -189,7 +193,7 @@ namespace Nibriboard.RippleSpace
if(LoadedChunks < SoftLoadedChunkLimit || if(LoadedChunks < SoftLoadedChunkLimit ||
UnloadableChunks < MinUnloadeableChunks) UnloadableChunks < MinUnloadeableChunks)
return; return;
foreach(KeyValuePair<ChunkReference, Chunk> chunkEntry in loadedChunkspace) foreach(KeyValuePair<ChunkReference, Chunk> chunkEntry in loadedChunkspace)
{ {
if(!chunkEntry.Value.CouldUnload) if(!chunkEntry.Value.CouldUnload)
@ -245,22 +249,39 @@ namespace Nibriboard.RippleSpace
OnChunkUpdate(sender, eventArgs); 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 // Unpack the plane to the temporary directory
using(Stream sourceStream = File.OpenRead(sourceFilename)) using(Stream sourceStream = File.OpenRead(sourceFilename))
using(IReader unpacker = ReaderFactory.Open(sourceStream)) 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 // Load the primary chunks from disk inot the plane
await loadedPlane.LoadPrimaryChunks(); await loadedPlane.LoadPrimaryChunks();
if(deleteSource)
File.Delete(sourceFilename);
return loadedPlane; 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.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using SharpCompress.Readers; using SharpCompress.Readers;
using Nibriboard.Utilities;
namespace Nibriboard.RippleSpace namespace Nibriboard.RippleSpace
{ {
@ -71,16 +73,19 @@ namespace Nibriboard.RippleSpace
/// <summary> /// <summary>
/// Creates a new plane, adds it to this RippleSpaceManager, and then returns it. /// Creates a new plane, adds it to this RippleSpaceManager, and then returns it.
/// </summary> /// </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> /// <returns>The newly created plane.</returns>
public Plane CreatePlane(string newPlaneName) public Plane CreatePlane(PlaneInfo newPlaneInfo)
{ {
if(this[newPlaneName] != null) if(this[newPlaneInfo.Name] != null)
throw new InvalidOperationException($"Error: A plane with the name '{newPlaneName}' already exists in this RippleSpaceManager."); 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); Planes.Add(newPlane);
return newPlane; return newPlane;
} }
@ -124,17 +129,29 @@ namespace Nibriboard.RippleSpace
Log.WriteLine("[Core] Importing planes"); Log.WriteLine("[Core] Importing planes");
StreamReader planes = new StreamReader(rippleSpace.UnpackedDirectory + "index.list"); StreamReader planes = new StreamReader(rippleSpace.UnpackedDirectory + "index.list");
List<Task> planeReaders = new List<Task>(); List<Task<Plane>> planeReaders = new List<Task<Plane>>();
string nextPlane; string nextPlane;
int planeCount = 0;
while((nextPlane = await planes.ReadLineAsync()) != null) 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); 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; return rippleSpace;
} }
}
}
} }

View file

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