public async override Task <ISaveGame> CreateSave(IReadOnlyDirectory saveContents)
        {
            var oldLatestFile = this.ProfileRoot.OpenFile("latest");

            // todo decide on some way to organize these saves.
            var newGuid          = Guid.NewGuid();
            var saveName         = $"{DateTimeOffset.UtcNow.ToString(DateFormat)}-{newGuid}";
            var saveDirectory    = this.ProfileRoot.OpenDirectory(saveName);
            var contentDirectory = saveDirectory.OpenDirectory("content");

            // Copy it twice, once for backup, once for head.
            await foreach (var _ in contentDirectory.CopyFromDirectory(saveContents, true))
            {
            }

            if (oldLatestFile.Created)
            {
                var oldLatestName = oldLatestFile.ReadAllText();
                var oldLatest     = this.ProfileRoot.OpenDirectory(oldLatestName);
                // Delete the old head
                oldLatest.Delete();
            }
            this.ProfileRoot.OpenFile("latest").WriteAllText(saveName, Encoding.UTF8);
            return(this.GetSave(saveDirectory));
        }
Example #2
0
        private async Task CreateBaseSave(IReadOnlyDirectory saveContents)
        {
            var contentDirectory = this.ProfileRoot.OpenDirectory("base/content");

            await foreach (var _ in contentDirectory.CopyFromDirectory(saveContents))
            {
            }
        }
Example #3
0
        public async override Task <ISaveGame> CreateSave(IReadOnlyDirectory saveContents)
        {
            // todo decide on some way to organize these saves.
            var newGuid          = Guid.NewGuid();
            var saveName         = $"{DateTimeOffset.UtcNow.ToString(DateFormat)}-{newGuid}";
            var saveDirectory    = this.ProfileRoot.OpenDirectory(saveName);
            var contentDirectory = saveDirectory.OpenDirectory("content");

            await foreach (var _ in contentDirectory.CopyFromDirectory(saveContents, true))
            {
            }

            this.ProfileRoot.OpenFile("latest").WriteAllText(saveName, Encoding.UTF8);
            return(this.GetSave(saveDirectory) !);
        }
Example #4
0
 public PluginProvision(ILogger logger,
                        IPluginProperties pluginProperties,
                        IPluginConfigurationStore configurationStore,
                        string name,
                        string author,
                        string description,
                        Version version,
                        DirectoryInfo contentDirectory,
                        IDirectory dataDirectory,
                        IReadOnlyDirectory commonResourceDirectory,
                        IReadOnlyDirectory resourceDirectory)
 {
     this.Logger                  = logger;
     this.Properties              = pluginProperties;
     this.Name                    = name;
     this.Author                  = author;
     this.Description             = description;
     this.Version                 = version;
     this.ConfigurationStore      = configurationStore;
     this.ContentDirectory        = contentDirectory;
     this.DataDirectory           = dataDirectory;
     this.ResourceDirectory       = resourceDirectory;
     this.CommonResourceDirectory = commonResourceDirectory;
 }
 public override Task <ISaveGame> CreateSave(IReadOnlyDirectory saveContents)
 {
     return(Task.FromResult <ISaveGame>(new EmptySaveGame(DateTimeOffset.UtcNow, Guid.NewGuid(), this.SaveType)));
 }
Example #6
0
        public async override Task <ISaveGame> CreateSave(IReadOnlyDirectory saveContents)
        {
            if (!this.ProfileRoot.ContainsDirectory("base"))
            {
                await this.CreateBaseSave(saveContents);
            }
            using var rollingHash = new RollingHash(32);

            // setup
            var newGuid          = Guid.NewGuid();
            var saveName         = $"{DateTimeOffset.UtcNow.ToString(DateFormat)}-{newGuid}";
            var saveDirectory    = this.ProfileRoot.OpenDirectory(saveName);
            var contentDirectory = saveDirectory.OpenDirectory("content");

            // diff is for anything that exists in the base directory
            var diffDir = contentDirectory.OpenDirectory("diff");

            // copy is for anything that does not and can not be diffed.
            var copyDir = contentDirectory.OpenDirectory("copy");

            // Traverse base directory in tandem with saveContents
            var baseDir = this.ProfileRoot.OpenDirectory("base/content").AsReadOnly();

            foreach (var f in saveContents.EnumerateFiles())
            {
                if (!baseDir.ContainsFile(f.Name))
                {
                    await copyDir.CopyFromAsync(f);

                    continue;
                }

                using var targetStream = f.OpenReadStream();
                using var baseStream   = baseDir.OpenFile(f.Name).OpenReadStream();

                using var outStream = diffDir.OpenFile(f.Name).OpenStream();
                using var decoder   = new VcEncoder(baseStream, targetStream, outStream,
                                                    rollingHash: rollingHash, blockSize: 32);
                VCDiffResult result = await decoder.EncodeAsync();

                if (result != VCDiffResult.SUCCESS)
                {
                    throw new IOException($"Failed to encode delta for {f.Name}");
                }
            }

            foreach (var d in saveContents.EnumerateDirectories().Where(d => !baseDir.ContainsDirectory(d.Name)))
            {
                // Copy all directories not in the base.
                await foreach (var _ in copyDir.OpenDirectory(d.Name).CopyFromDirectory(d))
                {
                }
            }

            var queuedDirs = (from targetDir in saveContents.EnumerateDirectories()
                              where baseDir.ContainsDirectory(targetDir.Name)
                              select(diffDir, baseDir.OpenDirectory(targetDir.Name), targetDir)).ToList();

            Queue <(IDeletableDirectory parentDir, IReadOnlyDirectory baseDir, IReadOnlyDirectory targetDir)> dirsToProcess =
                new(queuedDirs);

            while (dirsToProcess.Count > 0)
            {
                var(parent, src, diff) = dirsToProcess.Dequeue();
                var dst = parent.OpenDirectory(src.Name);
                foreach (var f in src.EnumerateFiles())
                {
                    if (!diff.ContainsFile(f.Name))
                    {
                        continue;
                    }
                    using var baseStream   = f.OpenReadStream();
                    using var targetStream = diff.OpenFile(f.Name).OpenReadStream();
                    using var outStream    = dst.OpenFile(f.Name).OpenStream();
                    using var decoder      = new VcEncoder(baseStream, targetStream, outStream,
                                                           rollingHash: rollingHash, blockSize: 32);
                    VCDiffResult result = await decoder.EncodeAsync();

                    if (result != VCDiffResult.SUCCESS)
                    {
                        throw new IOException($"Failed to decode delta for {f.Name}");
                    }
                }

                var children = from targetDir in diff.EnumerateDirectories()
                               where src.ContainsDirectory(targetDir.Name)
                               select(dst, src.OpenDirectory(targetDir.Name), targetDir);

                foreach (var childDirectory in children)
                {
                    dirsToProcess.Enqueue(childDirectory);
                }
            }

            this.ProfileRoot.OpenFile("latest").WriteAllText(saveName, Encoding.UTF8);
            return(this.GetSave(saveDirectory) !);
        }
Example #7
0
        /// <summary>
        /// Copies all the files and directories from the source directory recursively into this directory.
        /// </summary>
        /// <param name="this">The destination directory.</param>
        /// <param name="source">The source directory.</param>
        /// <param name="overwrite">Whether or not to overwrite existing files.</param>
        /// <returns>An enumerable of copied files.</returns>
        public static async IAsyncEnumerable <IFile> CopyFromDirectory(this IDirectory @this, IReadOnlyDirectory source, bool overwrite)
        {
            if (@this == source)
            {
                yield break;
            }

            // Do the parent directory
            foreach (var f in source.EnumerateFiles())
            {
                yield return(await @this.CopyFromAsync(f, overwrite));
            }

            var queuedDirs =
                source.EnumerateDirectories()
                .Select(d => (@this, d)).ToList();

            // BFS over all the children.

            Queue <(IDirectory, IReadOnlyDirectory)> dirsToProcess = new (queuedDirs);

            while (dirsToProcess.Count > 0)
            {
                var(parent, src) = dirsToProcess.Dequeue();
                var dst = parent.OpenDirectory(src.Name);
                foreach (var f in src.EnumerateFiles())
                {
                    yield return(await dst.CopyFromAsync(f, overwrite));
                }

                var children = src.EnumerateDirectories()
                               .Select(d => (dst, d)).ToList();

                foreach (var childDirectory in children)
                {
                    dirsToProcess.Enqueue(childDirectory);
                }
            }
        }
Example #8
0
 /// <summary>
 /// Copies all the files and directories from the source directory recursively into this directory.
 /// </summary>
 /// <param name="this">The destination directory.</param>
 /// <param name="source">The source directory.</param>
 /// <returns>An enumerable of copied files.</returns>
 public static IAsyncEnumerable <IFile> CopyFromDirectory(this IDirectory @this, IReadOnlyDirectory source)
 => CopyFromDirectory(@this, source, false);
Example #9
0
 public abstract Task <ISaveGame> CreateSave(IReadOnlyDirectory saveContents);