using System; 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 { public class RippleSpaceManager { /// /// The temporary directory in which we are currently storing our unpacked planes temporarily. /// public string UnpackedDirectory; /// /// The master list of planes that this PlaneManager is in charge of. /// public List Planes = new List(); /// /// The number of milliseconds between each maintenance run. /// public readonly int MaintenanceInternal = 5000; /// /// The number of milliseconds the last maintenance run took. /// public long LastMaintenanceDuration = 0; public int DefaultChunkSize { get; set; } = 512; 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."); } /// /// Gets the plane with the specified name from this RippleSpace. /// /// The plane name to retrieve. public Plane this[string planeName] { get { return GetById(planeName); } } /// /// Gets the plane with the specified name from this RippleSpace. /// /// The plane name to retrieve. /// The plane wwith the specified name. protected Plane GetById(string targetName) { foreach (Plane plane in Planes) { if (plane.Name == targetName) return plane; } return null; } /// /// Creates a new plane, adds it to this RippleSpaceManager, and then returns it. /// /// The settings for the new plane to create. /// The newly created plane. public Plane CreatePlane(PlaneInfo newPlaneInfo) { 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}", newPlaneInfo.Name); Plane newPlane = new Plane( newPlaneInfo, CalcPaths.UnpackedPlaneDir(UnpackedDirectory, newPlaneInfo.Name) ); Planes.Add(newPlane); return newPlane; } public async Task StartMaintenanceMonkey() { Log.WriteLine("[RippleSpace/Maintenance] Automated maintenance monkey created."); while (true) { Stopwatch maintenanceStopwatch = Stopwatch.StartNew(); foreach (Plane plane in Planes) plane.PerformMaintenance(); LastMaintenanceDuration = maintenanceStopwatch.ElapsedMilliseconds; await Task.Delay(MaintenanceInternal); } } public async Task Save() { throw new NotImplementedException(); } public async Task 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> planeReaders = new List>(); string nextPlane; int planeCount = 0; while((nextPlane = await planes.ReadLineAsync()) != null) { planeReaders.Add(Plane.FromFile( planeName: nextPlane, storageDirectoryRoot: rippleSpace.UnpackedDirectory, sourceFilename: CalcPaths.UnpackedPlaneFile(rippleSpace.UnpackedDirectory, nextPlane), deleteSource: true )); planeCount++; } await Task.WhenAll(planeReaders); rippleSpace.Planes.AddRange( planeReaders.Select((Task planeReader) => planeReader.Result) ); Log.WriteLine("[Core] done! {0} planes loaded.", planeCount); return rippleSpace; } } }