[ AmberSkyNet VR ]

Из трёх переменных в разделе [Path] ini-файла DataPath, PluginsPath, LogPath на сегодняшний момент в движке не используется только одна - DataPath. А это значит - пора делать класс виртуальной файловой системы.

IFileSystem

Для начала - составим интерфейс:


class IFileSystem {
public:
// добавить путь к файлам
virtual bool AddPath(const std::string& Path, bool Recursive)=0;
// получить файл
virtual IFile *GetFile(const std::string& FileName)=0;
// добавляем источник файлов
// если FileSource=NULL - загружаем через систему плагинов
// иначе просто запоминаем с именем FileSourceName
virtual bool AddFileSource (const std::string& FileSourceName,IFileSource *FileSource)=0;
};

Функция AddPath добавляет путь к папке, в которой могут находиться файлы, получаемые из файловой системы при выполнении функции GetFile.
Функция AddFileSource позволяет добавить в файловую систему свои свои источники файлов. Например - менеджер сети может добавить источник чтения файла по какому-либо сетевому протоколу.

IFileSource

Интерфейс IFileSource предназначен для реализации различных источников данных - файлы, хранящиеся напрямую в файловой системе, в архивах, в сети. Для каждого типа источника можно будет написать свой интерфейс и работа с ним будет одинакова в пределах видимости виртуальной файловой системы, а значит, и движка.

class IFileSource {
public:
virtual IFile *LoadFile(const std::string& FileName)=0;
virtual void AddPath(const std::string& PathName, bool Recursive)=0;
};

IFile

При выполнении команды GetFile менеджера файловой системы мы получаем указатель на экземпляр класса, скрывающегося за интерфейсом IFile. Из-за наличия сетевого режима файл может быть в нескольких состояниях, например- не только на диске или в памяти, но и в процессе загрузки с сервера, причём на сервере его может не оказаться, но движок не может ждать пока сервер перешлёт все требуемые файлы на клиента, а должен отрисовывать то, что уже есть в наличии в локальной директории кэша. Отсюда возможные состояния файла несколько специфичны:

enum asn_File_State{
ASN_FILE_NONE, //состояния нет (файл закрыт)
ASN_FILE_NOT_FOUND, //файл не найден (например, на сервере)
ASN_FILE_DISK_OPEN, //файл открыт на диске
ASN_FILE_DISK_IN_MEM, //файл загружен в память и закрыт на диске
ASN_FILE_WAIT, //файл ожидает загрузки
ASN_FILE_LOADING, //файл в процессе загрузки
};
А сам интерфейс класса файл выглядит следующим образом:
class IFile{
public:
//имя файла
virtual std::string GetName();
//состояние файла
virtual asn_File_State GetState()=0;
// открыть файл
virtual asn_File_State FileOpen()=0;
// закрыть файл
virtual void FileClose()=0;
// загрузить файл в память, на выходе - указатель на область
virtual void *LoadInMem()=0;
// освободить память
virtual void FreeMem()=0;
// передвинуть на позицию
virtual void Seek(unsigned int Position, int From)=0;
// получить размер файла в байтах;
virtual unsigned int GetSizeInBytes()=0;
// прочитать часть файла в буфер
virtual void ReadBuf(void *buf, unsigned int size)=0;
};
Мы может узнать состояние файла при помощи функции GetState(), а изменять состояния файла выполнением функций FileOpen,FileClose,LoadInMem.
Вообще мне не очень нравится, конечно, что функционал как бы оказался размазаным по классам IFile и IFileSystem, однако я стремился сделать файлы более автономными и внешне не зависящими от файловой системы - иначе бы с появлением в IFileSystem функций наподобие CloseFile пришлось бы делать поиск по всем открытым файлам для нахождения нужного файла, который бы следовало закрыть.

CFileSystem

Реализация CFileSystem при создании читает переменную окружения [Paths]DataPath и запоминает её у себя под именем SandBox :) Все данные, которые доступны для чтения файловой системой располагаются не выше указанной папки. Далее в переменных окружения находится переменная [FileManager]DataReader, в которой перечислены через запятую все источники файлов (т.е. менеджеры загрузки файлов из локального диска и архивов, например), используемые при данном запуске системы. Пытаемся загрузить их при помощи системы плагинов, после чего посылаем туда значение локальной перменной SandBox.
При вызове функции GetFile менеджер файловой системы CFileSystem перебирает все источники из своего списка. Если ни один источник не может загрузить этот файл менеджер возвращет NULL. Если файл был загружен каким-то из источников менеджер запоминает его в своем списке и возвращает указатель на этот файл.
В деструкторе CFileSystem все открытые файлы закрываются и список файлов очищается. То же самое происходит и со списком источников файлов.

CFileSystemASN

Класс-надстройка над CFileSystem, добавляющая небольшой функционал - прежде начала поиска файла по всем известным путям он ищется в папке common. Это позволит нам использовать одни и те же ресурсы для разных миров, например - текстуры, звуки. Ах да, я же забыл вас ознакомить со структурой папки data. Так вот:

  • data/ - папка для используемых данных
    • common/ - папка для общих ресурсов всех миров
    • local/ - папка для миров, расположенных на локальной машине
      • HOME - основной стартовый каталог
      • ...

Менеджеры миров (которые на данный момент еще не реализованы) обращаясь к файловой системе будут использовать имена начинающиеся с типа менеджера (local/net) и имени мира (например, HOME). Скажем, если менеджер локального мира HOME захочет взять файл start.lua из своих ресурсов он обратится к файловой системе с запросом GetGile("local/HOME/start.lua");
Впрочем, если не требуется поддержка нескольких миров, вполне можно использовать менеджер файловой системы CFileSystem, который делает обычный поиск, без дополнительного поиска в папке common.

CFileSource, CFile

Класс источника файлов CFileSource реализует возможность чтения файлов напрямую из физической файловой системы. При вызове функции LoadFile мы пытаемся найти и открыть файл по всем путям из списка, который формируется при выполнении функции AddPath. Если такой файл не найден - возвращем NULL, если найдет - создаём файл CFile, заполняем все необходимые в нём значения и возвращем указатель на него. Класс CFileSource "видит" все приватные переменные класса CFile, т.к. он является дружественным классу CFile.

Класс файла CFile предоставляет доступ к файлу физической файловой системы и выполняет некоторые дисковые операции над ним - чтение из файла, загрузка в память.

CFileSourceZIP, CFileZIP

Класс источника файлов CFileSourceZIP реализует возможность чтения файлов из архивов формата ZIP. При выполнении функции AddPath мы перебираем все имеющиеся в указанной директории файлы и пытаеся загрузить их как ZIP-архив. В случае удачи - читаем список файлов, хранящихся в данном архиве и запоминаем его. При вызове функции LoadFile проводится поиск в списке известных нам хранящихся в архиве файлов и в случае если такой файл найден - создаём экземпляр класса CFileZIP, устанавливаем в нём необходимые переменные и отправляем указатель на него.
Класс файла CFileZIP предоставляет доступ к файлу, хранящемся в ZIP-архиве и позволяет загружать его в память.

Взаимодействие с менеджером файловой системы

Engine после разбора ini-файла читает значение переменной [Modules]FileManager и пытается загрузить через систему плагинов менеджер файловой системы с таким именем. Полученный указатель запоминается в указателях окружения с именем FileManager, получить извне доступ к нему можно например вот так:

(IFileSystem*)ENGINE->GetPtrParam("FileManager");
или воспользоваться макросами, определёнными в файле IFileSystem.h
IFile *myFile=FILER->GetFile("test.txt");
Только нужно не забывать, что даже если мы получили экземпляр класса IFile, он может быть в состоянии ASN_FILE_NOT_FOUND или в состояниях отложенной загрузки - ASN_FILE_WAIT, ASN_FILE_LOADING. Поэтому после получения указателя на файл неплохо было бы проверять его состояние функцией GetState().

В дальнейшем, если в движке появятся менеджеры миров, функционал классов файловой системы будет возможно изменен, чтобы поиск происходил только среди общих ресурсов и ресурсов текущего мира.

Изменения в исходниках

include/: Добавлен интерфейсы IFile.h, IFileSource.h, IFileSystem.h

src/asnMain/: демонстрация работы менеджера файловой системы

src/asnEngine/: добавлена инициализация менеджера файловой системы

src/asnFileSystem/: Новый плагин - менеджер файловой системы. Чтение с диска, из архивов ZIP.

Исходники этого шага выложены в SVN. Скачать их можно набрав команду:

svn co https://svn.sourceforge.net/svnroot/ambernet/tags/AmberSkyNet-0.6 ambernet_0.6
Powered by: SourceForge.net Logo