Files
RobustToolbox/Robust.Shared/ContentPack/DirLoader.cs
Tyler Young 0d52def877 Have RobustSerializer use a shared string dictionary (#1117)
* implements shared string dictionary and handshake from net-code-2

* fix unit test

switch to szr sawmill

* try to silence some warnings around ZipEntry

* rebase and use system zip instead of icsharplib

fix rebase artifacts

* Update Robust.Shared/Interfaces/GameObjects/IComponentFactory.cs

* Update Robust.Shared/Serialization/RobustSerializer.MappedStringSerializer.cs

* Update Robust.Shared/Serialization/RobustSerializer.MappedStringSerializer.cs

* Apply suggestions from code review

* Apply suggestions from code review

* Update Robust.Shared/Serialization/RobustSerializer.cs

* since no longer gathering from paths, make string splitting more robust

* make string gathering ignore strings under 4 chars long

make string gathering yet more robust

* add limit to size of mapped strings

* add more string data to feed into shared string dictionary from YAML files

add JSON importer but don't parse RSI metadata yet

fix typo that breaks nulls in MappedStringSerializer

minor refactoring

make string splitting more robust

add WriteUnsignedInt / ReadUnsignedInt for validating WriteCompressedUnsignedInt / ReadCompressedUnsignedInt aren't bogus

* comment out some log statements

* minor refactor, reorder logging

add null check due to smart typing NRT checks

* Add doc comments, readability improvements to MappedStringSerializer

The protocol, handshake, and internal logic are now more documented.

The main area that could still be improved is the documentation of how
the cache system works, but the code is readable enough for now that it
isn't immediately necessary.

* add documentation, organization

* update some more doc comments

* add flows to doc comment for NetworkInitialize

* more documentation and organization

* more docs

* instead of retrieving INetManager by IoC, assign when NetworkInitialize is invoked

* "document" the regex

* Update Robust.Shared/Network/NetManager.cs

* add missing check for LockMappedStrings

* Update Robust.Shared/Serialization/RobustSerializer.MappedStringSerializer.cs

Co-authored-by: ComicIronic <comicironic@gmail.com>

* change to warning instead of throw for unlocked string mapping

Co-authored-by: ComicIronic <comicironic@gmail.com>
2020-06-12 04:09:55 +02:00

164 lines
5.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using Robust.Shared.Interfaces.Log;
using Robust.Shared.Utility;
namespace Robust.Shared.ContentPack
{
internal partial class ResourceManager
{
/// <summary>
/// Holds info about a directory that is mounted in the VFS.
/// </summary>
class DirLoader : IContentRoot
{
private readonly DirectoryInfo _directory;
private readonly ISawmill _sawmill;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="directory">Directory to mount.</param>
/// <param name="sawmill"></param>
public DirLoader(DirectoryInfo directory, ISawmill sawmill)
{
_directory = directory;
_sawmill = sawmill;
}
/// <inheritdoc />
public void Mount()
{
// Looks good to me
// Nothing to check here since the ResourceManager handles checking permissions.
}
/// <inheritdoc />
public bool TryGetFile(ResourcePath relPath, [NotNullWhen(true)] out Stream? stream)
{
var path = GetPath(relPath);
if (!File.Exists(path))
{
stream = null;
return false;
}
CheckPathCasing(relPath);
stream = File.OpenRead(path);
return true;
}
internal string GetPath(ResourcePath relPath)
{
return Path.GetFullPath(Path.Combine(_directory.FullName, relPath.ToRelativeSystemPath()));
}
/// <inheritdoc />
public IEnumerable<ResourcePath> FindFiles(ResourcePath path)
{
var fullPath = GetPath(path);
if (!Directory.Exists(fullPath))
{
yield break;
}
var paths = PathHelpers.GetFiles(fullPath);
// GetFiles returns full paths, we want them relative to root
foreach (var filePath in paths)
{
var relPath = filePath.Substring(_directory.FullName.Length);
yield return ResourcePath.FromRelativeSystemPath(relPath);
}
}
[Conditional("DEBUG")]
private void CheckPathCasing(ResourcePath path)
{
// Run this inside the thread pool due to overhead.
Task.Run(() =>
{
var prevPath = GetPath(ResourcePath.Root);
var diskPath = ResourcePath.Root;
var mismatch = false;
foreach (var segment in path.EnumerateSegments())
{
var prevDir = new DirectoryInfo(prevPath);
var found = false;
foreach (var info in prevDir.GetFileSystemInfos())
{
if (!string.Equals(info.Name, segment, StringComparison.InvariantCultureIgnoreCase))
{
// Not the dir info for this path segment, ignore it.
continue;
}
if (!string.Equals(info.Name, segment, StringComparison.InvariantCulture))
{
// Segments match insensitively but not the other way around. Bad.
mismatch = true;
}
diskPath /= info.Name;
prevPath = Path.Combine(prevPath, info.Name);
found = true;
break;
}
if (!found)
{
// File doesn't exist. Let somebody else throw the exception.
return;
}
}
if (mismatch)
{
_sawmill.Warning("Path '{0}' has mismatching case from file on disk ('{1}'). " +
"This can cause loading failures on certain file system configurations " +
"and should be corrected.", path, diskPath);
}
});
}
public IEnumerable<string> GetRelativeFilePaths()
{
return GetRelativeFilePaths(_directory);
}
private IEnumerable<string> GetRelativeFilePaths(DirectoryInfo dir)
{
foreach (var file in dir.EnumerateFiles())
{
if ((file.Attributes & FileAttributes.Hidden) != 0 || file.Name[0] == '.')
{
continue;
}
var filePath = file.FullName;
var relPath = filePath.Substring(_directory.FullName.Length);
yield return ResourcePath.FromRelativeSystemPath(relPath).ToRootedPath().ToString();
}
foreach (var subDir in dir.EnumerateDirectories())
{
if (((subDir.Attributes & FileAttributes.Hidden) != 0) || subDir.Name[0] == '.')
{
continue;
}
foreach (var relPath in GetRelativeFilePaths(subDir))
{
yield return relPath;
}
}
}
}
}
}