1
0
forked from 0ad/0ad

refactor: move reload functionality into ps/Filesystem to allow reloading non-resource files (e.g. scripts); VFS is now responsible for returning VirtualPath and invalidating the changed file/directory

This was SVN commit r7171.
This commit is contained in:
janwas 2009-11-06 11:50:04 +00:00
parent 9662666403
commit 6ec9128e78
9 changed files with 99 additions and 118 deletions

View File

@ -239,7 +239,7 @@ VfsPath CColladaManager::GetLoadableFilename(const VfsPath& pathnameNoExtension,
// realDaePath_ is "[..]/mods/whatever/art/meshes/whatever.dae"
fs::wpath realDaePath_;
LibError ret = g_VFS->RealPath(dae, realDaePath_);
LibError ret = g_VFS->GetRealPath(dae, realDaePath_);
debug_assert(ret == INFO::OK);
wchar_t realDaeBuf[PATH_MAX];
wcscpy_s(realDaeBuf, ARRAY_SIZE(realDaeBuf), realDaePath_.string().c_str());

View File

@ -18,10 +18,6 @@
#include "precompiled.h"
#include "vfs.h"
#include "lib/posix/posix_time.h" // usleep
#include "lib/res/h_mgr.h" // h_reload
#include "lib/sysdep/dir_watch.h"
#include "lib/allocators/shared_ptr.h"
#include "lib/path_util.h"
#include "lib/file/common/file_stats.h"
@ -154,7 +150,7 @@ public:
return textRepresentation;
}
virtual LibError RealPath(const VfsPath& pathname, fs::wpath& realPathname)
virtual LibError GetRealPath(const VfsPath& pathname, fs::wpath& realPathname)
{
VfsDirectory* directory;
CHECK_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0));
@ -162,25 +158,24 @@ public:
return INFO::OK;
}
virtual LibError VirtualPath(const fs::wpath& realPathname, VfsPath& pathname)
virtual LibError GetVirtualPath(const fs::wpath& realPathname, VfsPath& pathname)
{
const fs::wpath realPath = AddSlash(realPathname.branch_path());
VfsPath path;
RETURN_ERR(FindRealPathR(realPathname, m_rootDirectory, L"", path));
RETURN_ERR(FindRealPathR(realPath, m_rootDirectory, L"", path));
pathname = path / realPathname.leaf();
return INFO::OK;
}
virtual LibError ReloadChangedFiles()
virtual LibError Invalidate(const VfsPath& pathname)
{
std::vector<DirWatchNotification> notifications;
RETURN_ERR(dir_watch_Poll(notifications));
for(size_t i = 0; i < notifications.size(); i++)
{
if(CanIgnore(notifications[i]))
continue;
RETURN_ERR(NotifyChangedR(m_rootDirectory, L"", notifications[i].Pathname()));
}
m_fileCache.Remove(pathname);
VfsDirectory* directory;
RETURN_ERR(vfs_Lookup(pathname, &m_rootDirectory, directory, 0));
const std::wstring name = pathname.leaf();
directory->Invalidate(name);
return INFO::OK;
}
@ -190,27 +185,6 @@ public:
}
private:
// try to skip unnecessary work by ignoring uninteresting notifications.
static bool CanIgnore(const DirWatchNotification& notification)
{
// ignore directories
const fs::wpath& pathname = notification.Pathname();
if(pathname.leaf() == L".")
return true;
// ignore uninteresting file types (e.g. temp files, or the
// hundreds of XMB files that are generated from XML)
const std::wstring extension = fs::extension(pathname);
const wchar_t* extensionsToIgnore[] = { L".xmb", L".tmp" };
for(size_t i = 0; i < ARRAY_SIZE(extensionsToIgnore); i++)
{
if(!wcscasecmp(extension.c_str(), extensionsToIgnore[i]))
return true;
}
return false;
}
LibError FindRealPathR(const fs::wpath& realPath, const VfsDirectory& directory, const VfsPath& curPath, VfsPath& path)
{
PRealDirectory realDirectory = directory.AssociatedDirectory();
@ -225,7 +199,7 @@ private:
{
const std::wstring& subdirectoryName = it->first;
const VfsDirectory& subdirectory = it->second;
LibError ret = FindRealPathR(realPath, subdirectory, AddSlash(path/subdirectoryName), path);
LibError ret = FindRealPathR(realPath, subdirectory, AddSlash(curPath/subdirectoryName), path);
if(ret == INFO::OK)
return INFO::OK;
}
@ -233,34 +207,6 @@ private:
return ERR::PATH_NOT_FOUND; // NOWARN
}
LibError NotifyChangedR(VfsDirectory& directory, const VfsPath& path, const fs::wpath& realPathname)
{
const fs::wpath realPath = AddSlash(realPathname.branch_path());
const std::wstring name = realPathname.leaf();
LibError ret = directory.NotifyChanged(realPath, name);
if(ret == INFO::OK)
{
const VfsPath pathname(path/name);
m_fileCache.Remove(pathname); // invalidate cached data
debug_printf(L"NotifyChangedR: reloading %ls\n", pathname.string().c_str());
RETURN_ERR(h_reload(pathname));
return INFO::OK;
}
VfsDirectory::VfsSubdirectories& subdirectories = directory.Subdirectories();
for(VfsDirectory::VfsSubdirectories::iterator it = subdirectories.begin(); it != subdirectories.end(); ++it)
{
const std::wstring& subdirectoryName = it->first;
VfsDirectory& subdirectory = it->second;
ret = NotifyChangedR(subdirectory, path/subdirectoryName, realPathname);
if(ret != INFO::SKIPPED)
return ret;
}
return INFO::SKIPPED;
}
size_t m_cacheSize;
FileCache m_fileCache;
PITrace m_trace;

View File

@ -125,7 +125,7 @@ struct IVFS
*
* this is useful for passing paths to external libraries.
**/
virtual LibError RealPath(const VfsPath& pathname, fs::wpath& realPathname) = 0;
virtual LibError GetRealPath(const VfsPath& pathname, fs::wpath& realPathname) = 0;
/**
* retrieve the VFS pathname that corresponds to a real file.
@ -136,15 +136,13 @@ struct IVFS
* number of directories; this could be accelerated by only checking
* directories below a mount point with a matching real path.
**/
virtual LibError VirtualPath(const fs::wpath& realPathname, VfsPath& pathname) = 0;
virtual LibError GetVirtualPath(const fs::wpath& realPathname, VfsPath& pathname) = 0;
/**
* poll for directory change notifications and reload all affected files.
* must be called regularly (e.g. once a frame), else notifications
* may be lost.
* note: polling is much simpler than asynchronous notifications.
* indicate that a file has changed; remove its data from the cache and
* arrange for its directory to be updated.
**/
virtual LibError ReloadChangedFiles() = 0;
virtual LibError Invalidate(const VfsPath& pathname) = 0;
/**
* empty the contents of the filesystem.

View File

@ -122,29 +122,6 @@ VfsDirectory* VfsDirectory::GetSubdirectory(const std::wstring& name)
}
void VfsDirectory::Clear()
{
m_files.clear();
m_subdirectories.clear();
m_realDirectory.reset();
m_shouldPopulate = 0;
}
LibError VfsDirectory::NotifyChanged(const fs::wpath& realPath, const std::wstring& name)
{
// we're not associated with the directory that changed
if(!m_realDirectory || m_realDirectory->Path() != realPath)
return INFO::SKIPPED;
// one of our files changed; ensure its new version supersedes the
// old by removing it and marking the directory for re-population.
m_files.erase(name);
m_shouldPopulate = 1;
return INFO::OK;
}
void VfsDirectory::SetAssociatedDirectory(const PRealDirectory& realDirectory)
{
if(!cpu_CAS(&m_shouldPopulate, 0, 1))
@ -159,6 +136,21 @@ bool VfsDirectory::ShouldPopulate()
}
void VfsDirectory::Invalidate(const std::wstring& name)
{
m_files.erase(name);
m_shouldPopulate = 1;
}
void VfsDirectory::Clear()
{
m_files.clear();
m_subdirectories.clear();
m_realDirectory.reset();
m_shouldPopulate = 0;
}
//-----------------------------------------------------------------------------

View File

@ -110,23 +110,6 @@ public:
return m_subdirectories;
}
VfsSubdirectories& Subdirectories()
{
return m_subdirectories;
}
/**
* empty file and subdirectory lists (e.g. when rebuilding VFS).
* CAUTION: this invalidates all previously returned pointers.
**/
void Clear();
/**
* check if this directory is affected by changes to <pathname>.
* @return INFO::OK if this directory was affected, otherwise INFO::SKIPPED
**/
LibError NotifyChanged(const fs::wpath& realPath, const std::wstring& name);
/**
* side effect: the next ShouldPopulate() will return true.
**/
@ -140,10 +123,22 @@ public:
/**
* @return whether this directory should be populated from its
* AssociatedDirectory(). note that calling this is a promise to
* do so if true is returned -- the flag is immediately reset.
* do so if true is returned -- the flag is reset immediately.
**/
bool ShouldPopulate();
/**
* indicate that a file has changed; ensure its new version supersedes
* the old by removing it and marking the directory for re-population.
**/
void Invalidate(const std::wstring& name);
/**
* empty file and subdirectory lists (e.g. when rebuilding VFS).
* CAUTION: this invalidates all previously returned pointers.
**/
void Clear();
private:
VfsFiles m_files;
VfsSubdirectories m_subdirectories;

View File

@ -239,7 +239,7 @@ static void Frame()
{
PROFILE_START("reload changed files");
MICROLOG(L"reload changed files");
g_VFS->ReloadChangedFiles();
ReloadChangedFiles();
PROFILE_END( "reload changed files");
}

View File

@ -20,6 +20,10 @@
#include "ps/CLogger.h"
#include "lib/posix/posix_time.h" // usleep
#include "lib/res/h_mgr.h" // h_reload
#include "lib/sysdep/dir_watch.h"
#define LOG_CATEGORY L"file"
@ -31,6 +35,44 @@ bool FileExists(const VfsPath& pathname)
}
// try to skip unnecessary work by ignoring uninteresting notifications.
static bool CanIgnore(const DirWatchNotification& notification)
{
// ignore directories
const fs::wpath& pathname = notification.Pathname();
if(pathname.leaf() == L".")
return true;
// ignore uninteresting file types (e.g. temp files, or the
// hundreds of XMB files that are generated from XML)
const std::wstring extension = fs::extension(pathname);
const wchar_t* extensionsToIgnore[] = { L".xmb", L".tmp" };
for(size_t i = 0; i < ARRAY_SIZE(extensionsToIgnore); i++)
{
if(!wcscasecmp(extension.c_str(), extensionsToIgnore[i]))
return true;
}
return false;
}
LibError ReloadChangedFiles()
{
std::vector<DirWatchNotification> notifications;
RETURN_ERR(dir_watch_Poll(notifications));
for(size_t i = 0; i < notifications.size(); i++)
{
if(!CanIgnore(notifications[i]))
{
VfsPath pathname;
RETURN_ERR(g_VFS->GetVirtualPath(notifications[i].Pathname(), pathname));
RETURN_ERR(g_VFS->Invalidate(pathname));
RETURN_ERR(h_reload(pathname));
}
}
return INFO::OK;
}
CVFSFile::CVFSFile()
{

View File

@ -32,6 +32,14 @@ extern PIVFS g_VFS;
extern bool FileExists(const VfsPath& pathname);
/**
* poll for directory change notifications and reload all affected files.
* must be called regularly (e.g. once a frame), else notifications
* may be lost.
* note: polling is much simpler than asynchronous notifications.
**/
extern LibError ReloadChangedFiles();
ERROR_GROUP(CVFSFile);
ERROR_TYPE(CVFSFile, LoadFailed);
ERROR_TYPE(CVFSFile, AlreadyLoaded);

View File

@ -75,7 +75,7 @@ void CXeromyces::GetXMBPath(const PIVFS& vfs, const VfsPath& xmlFilename, const
// get real path of XML file (e.g. mods/official/entities/...)
fs::wpath XMBRealPath_;
vfs->RealPath(xmlFilename, XMBRealPath_);
vfs->GetRealPath(xmlFilename, XMBRealPath_);
wchar_t XMBRealPath[PATH_MAX];
wcscpy_s(XMBRealPath, ARRAY_SIZE(XMBRealPath), XMBRealPath_.string().c_str());