Files
RRJServer/LibServer/Systems/updatecontroller.cpp

603 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "updatecontroller.h"
UpdateController::UpdateController(QObject *parent) :
QObject(parent),
commonClientHandler(nullptr)
{
buildPath = QDir::currentPath() + "/" + applicationFolderName;
sharedDataPath = QDir::currentPath() + "/" + sharedDataFolderName;
Logger::instance().log(buildPath);
qDebug() << hashFileName;
}
void UpdateController::initialize(CommonClientHandler *commonClientHandler,DataParser *dataParser,AssetsManager *assetManager)
{
this->commonClientHandler = commonClientHandler;
this->dataParser = dataParser;
this->assetManager = assetManager;
hashCalculator = new FastHashCalculator;
if (!QDir(staticDataFolderName).exists())
{
QDir().mkdir(staticDataFolderName);
}
sizeToSend = 0;
assetManager->initialize(this,dataParser);
if (!checkRequiredFolder())
{
emit sigErrorRequired(100);
return;
}
calculateFullHash();
currentStreamingPath = assetManager->getLastVersion(); //TODO: сохрнаять предыдущую версию и загружать ее при включении
setUpCurrentServerHash();
mutex = new QMutex;
qDebug() << "UpdateController init thread ID " << QThread::currentThreadId();
emit sigInitializeFinished();
}
void UpdateController::changeAssetVersion(QString versionName)
{
//commonClientHandler->slot_sendPacketToAllClients(PacketType::BUSY);
qDebug() << "UpdateController thread ID " << QThread::currentThreadId();
currentStreamingPath = assetManager->setVersion(versionName);
setUpCurrentServerHash();
emit sigUpdateDocs();
commonClientHandler->slot_sendPacketToAllClients(PacketType::HASH_READY);
commonClientHandler->sendCurrentVersionToAllClient();
//commonClientHandler->slot_sendPacketToAllClients(PacketType::FREE);
}
void UpdateController::createCopyVersion(QString versionName,QString newVersionName,QString author)
{
//commonClientHandler->slot_sendPacketToAllClients(PacketType::BUSY);
assetManager->createCopyVersion(versionName,newVersionName,author);
//commonClientHandler->slot_sendPacketToAllClients(PacketType::FREE);
}
void UpdateController::deleteAssetVersion(QString versionName)
{
//commonClientHandler->slot_sendPacketToAllClients(PacketType::BUSY);
assetManager->deleteVersion(versionName);
//commonClientHandler->slot_sendPacketToAllClients(PacketType::FREE);
}
void UpdateController::compareFiles(ClientHandler* handler, QByteArray array)
{
mutex->lock();
serverDataList.clear();
serverDataList = *loadHash(hashFileName);
clientDataList.clear();
xmlFileDataParse(array);
checkNeedUpdate(handler);
mutex->unlock();
}
void UpdateController::showHash()
{
for(FileData& str : serverDataList){
Logger::instance().log(str.hash);
}
}
void UpdateController::calculateFullHash()
{
Logger::instance().log("Calculate hash...");
QElapsedTimer timer;
timer.start();
qDebug() << "Start calculate... ";
//commonClientHandler->slot_sendPacketToAllClients(PacketType::BUSY);
bool res = emit signal_BlockAutorization(true, "SERVER", "CalculateFullHash");
//auto *list = calculateHash(buildPath);
hashCalculator->calculateHashes(buildPath);
auto* list = hashCalculator->getHashList();
qDebug() << "Hash count: " << list->count();
saveHash(buildHashName,list);
calculateSharedHash();
Logger::instance().log("Calculate hash complete");
qDebug() << "Calculate time " << timer.elapsed();
//commonClientHandler->slot_sendPacketToAllClients(PacketType::FREE);
res = emit signal_BlockAutorization(false, "SERVER", "CalculateFullHash");
}
void UpdateController::calculateFullHashWithSetup()
{
commonClientHandler->slot_sendPacketToAllClients(PacketType::RECALCULATE_HASH);
calculateCurrentSharedHash();
setUpCurrentServerHash();
commonClientHandler->slot_sendPacketToAllClients(PacketType::HASH_READY);
}
void UpdateController::saveHash(QString fileName,QList<FileData> *fileList)
{
QFile hashFile(fileName);
hashFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
QXmlStreamWriter xmlWriter(&hashFile);
QListIterator<FileData> fileDataIterator(*fileList);
xmlWriter.setAutoFormatting(true);
xmlWriter.writeStartDocument();
xmlWriter.writeStartElement("FileDataList");
while (fileDataIterator.hasNext())
{
FileData data = fileDataIterator.next();
xmlWriter.writeStartElement("FileData");
xmlWriter.writeAttribute("Path",data.path);
xmlWriter.writeAttribute("Hash",data.hash);
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
xmlWriter.writeEndDocument();
hashFile.close();
}
QList<FileData>* UpdateController::loadHash(QString filename)
{
QList<FileData> *files = new QList<FileData>();
QFile hashFile(filename);
QByteArray array;
if(hashFile.open(QIODevice::ReadOnly)){
array = hashFile.readAll();
hashFile.close();
}
QXmlStreamReader xmlReader(array);
while (!xmlReader.atEnd())
{
if(xmlReader.isStartElement())
{
if(xmlReader.name().toUtf8() == "FileData")
{
FileData data;
foreach(const QXmlStreamAttribute &attr,xmlReader.attributes())
{
QString name = attr.name().toString();
QString value = attr.value().toString();
if(name == "Path")
data.path = value;
else if(name == "Hash")
data.hash = value;
}
files->append(data);
}
}
xmlReader.readNextStartElement();
}
Logger::instance().log("Hash load from file ");
return files;
}
void UpdateController::calculateSize()
{
QDirIterator iterator(buildPath);
quint64 total = 0;
while(iterator.hasNext()){
if(iterator.fileInfo().isFile()){
total += iterator.fileInfo().size();
}
iterator.next();
}
Logger::instance().log("Full size: ");
Logger::instance().log(QString::number(total));
}
QString UpdateController::getCommands()
{
QString commandsText;
commandsText += "check - check version ";
commandsText += "update - update files ";
return commandsText;
}
void UpdateController::setUpCurrentServerHash()
{
QList<FileData> *fileList = new QList<FileData>;
fileList->append(*loadHash(buildHashName));
FileData *streamingFolder = new FileData;
streamingFolder->hash = "FOLDER";
streamingFolder->path = buildDataPath + streamingAssetsFolderName;
fileList->append(*streamingFolder);
QString streamingHashFileName = Tools::createVersionHashFilepath(assetManager->getCurrentVersionData()->getViewName());
fileList->append(*loadHash(streamingHashFileName));
assetManager->prepareLocalPathList(fileList);
saveHash(hashFileName,fileList);
}
void UpdateController::setDataInfo(DataInfo *value)
{
dataInfo = value;
}
QString UpdateController::getCurrentStreamingPath() const
{
return currentStreamingPath;
}
QList<FileData> UpdateController::prepareRealPathList(QList<FileData> fileData)
{
return *assetManager->prepareRealPathList(&fileData);
}
void UpdateController::setLocalFileData(QList<FileData> dataList)
{
serverDataList.append(dataList);
}
bool UpdateController::checkNeedUpdate(ClientHandler *handler)
{
QList<FileData> *forSend = new QList<FileData>;
QList<FileData> *forDelete = new QList<FileData>;
Client *client = handler->getClient();
sizeToSend = 0;
bool needUpdate = false;
for (auto &item:clientDataList) //проверка на недостающие файлы по адресам
{
if(item.path.contains("Temp")) continue;
if(item.path.contains("docs.xml")) continue;
if (!serverDataList.contains(item))
{
forDelete->append(item);
}
}
for (auto &item:serverDataList)
{
if(item.path.contains("Temp")) continue;
if(item.path.contains("docs.xml")) continue;
if (!clientDataList.contains(item))
{
forSend->append(item);
}
}
if(forSend->length() > 0) //формирование сообщения об обновлении
{
QString log;
log.append(" Client: " + client->getLogin());
log.append(" Need updates: ");
log.append(QString::number(forSend->length()));
log.append(" objects");
client->setFileSendList(*forSend);
Logger::instance().log(log);
needUpdate = true;
}
else
{
QString log;
log.append(" Client: " + client->getLogin());
log.append(" no update required");
Logger::instance().log(log);
}
if(forDelete->length() > 0){
QString log;
log.append(" Client: " + client->getLogin());
log.append(" Need delete: ");
log.append(QString::number(forDelete->length()));
log.append(" objects");
client->setFileDeleteList(*forDelete);
Logger::instance().log(log);
needUpdate = true;
}
else
{
QString log;
log.append(" Client: " + client->getLogin());
log.append(" no delete required");
Logger::instance().log(log);
//handler->sendMessageBlock(log);
}
CalculateSizeToSend(*forSend);
handler->sendNeedUpdate(needUpdate,sizeToSend,forSend->length(),forDelete->length());
return needUpdate;
}
QList<FileData>* UpdateController::calculateHash(QString path)
{
serverDataList.clear();
if(!QDir(path).exists()){
QDir().mkdir(path);
}
QString hashString;
QStringList filter;
filter << "*";
QList<FileData> *files = new QList<FileData>;
QDirIterator dirIterator(path,filter, QDir::AllEntries, QDirIterator::Subdirectories);
while (dirIterator.hasNext())
{
QFileInfo fileInfo(dirIterator.next());
FileData currentFile;
if(fileInfo.isDir() && !fileInfo.fileName().startsWith(".") && fileInfo.fileName() != projectFolderName)
{
currentFile.path = Tools::createLocalPath(fileInfo.absoluteFilePath());
currentFile.hash = "FOLDER";
if(!files->contains(currentFile))
{
files->push_back(currentFile);
}
}
}
QDirIterator fileIterator(path,filter,QDir::Files | QDir::NoDotAndDotDot,QDirIterator::Subdirectories);
while (fileIterator.hasNext())
{
fileIterator.next();
QFileInfo fileInfo = fileIterator.fileInfo();
FileData currentFile;
QFile file(fileInfo.absoluteFilePath());
quint64 fileSize = file.size(); //буффер для хэширования крупных файлов
const quint64 bufferSize = 1024;
if(fileInfo.isHidden()) continue;
if(fileInfo.isFile() && file.open(QIODevice::ReadOnly) && !fileInfo.fileName().contains(".meta"))
{
char buffer[bufferSize];
int bytesRead;
int readSize = qMin(fileSize,bufferSize);
QCryptographicHash hash(QCryptographicHash::Md5);
while(readSize > 0 && (bytesRead = file.read(buffer,readSize)) > 0)
{
fileSize -= bytesRead;
hash.addData(buffer,bytesRead);
readSize = qMin(fileSize,bufferSize);
}
hashString = QString(hash.result().toHex());
currentFile.path = Tools::createLocalPath(fileInfo.absoluteFilePath());
currentFile.hash = hashString;
files->push_back(currentFile);
file.close();
}
}
serverDataList.append(*files);
return files;
}
QString UpdateController::getPathAdditionalFile(QString name)
{
QString path = Tools::createSharedPath("/" + assetManager->getCurrentVersionData()->getViewName() + "/" + additionalFilesFolderName + name);
return path;
}
QString UpdateController::getPathScensFile(QString name)
{
QString path = Tools::createSharedPath("/" + assetManager->getCurrentVersionData()->getViewName() + "/" + rusScensFolderName + name);
return path;
}
QByteArray UpdateController::getLocalHash()
{
QFile hashFile(hashFileName);
QByteArray array;
if(hashFile.open(QIODevice::ReadOnly)){
array = hashFile.readAll();
hashFile.close();
}
return array;
}
QString UpdateController::getCurrentVersionName()
{
return assetManager->getCurrentVersionData()->getViewName();
}
void UpdateController::CalculateSizeToSend(QList<FileData> diffList)
{
QListIterator<FileData> serverDiffIterator(diffList);
while (serverDiffIterator.hasNext())
{
QString path;
FileData pathForUpdate = serverDiffIterator.next();
if(pathForUpdate.path.contains(streamingAssetsFolderName))
{
path = Tools::createStreamingToRealPath(pathForUpdate.path,assetManager->getCurrentVersionData());
}
else
{
path = Tools::createRootPath(pathForUpdate.path);
}
QFile file(path);
sizeToSend += file.size();
}
}
void UpdateController::calculateSharedHash()
{
QDir sharedDir(sharedDataPath);
if(!QDir(sharedDataPath).exists())
{
QDir().mkdir(sharedDataPath);
}
QDirIterator dirIterator(sharedDir);
QList<FileData> *fileList = new QList<FileData>;
QList<StreamingVersionData*> *versionList = new QList<StreamingVersionData*>;
while (dirIterator.hasNext())
{
fileList->clear();
dirIterator.next();
QFileInfo fileInfo = dirIterator.fileInfo();
if (fileInfo.fileName() == "." || fileInfo.fileName() == "..")
continue;
QString fileName = Tools::createVersionHashFilepath(fileInfo.fileName());
hashCalculator->calculateHashes(fileInfo.filePath());
fileList = hashCalculator->getHashList();
//fileList = calculateHash(fileInfo.absoluteFilePath());
qDebug() << "Hash count: " << fileList->count();
saveHash(fileName,fileList);
StreamingVersionData *version = new StreamingVersionData(
fileInfo.absoluteFilePath(),fileInfo.fileName(),
fileInfo.birthTime(),fileInfo.size());
versionList->append(version);
}
assetManager->createFirstVersionListXML(*versionList);
}
void UpdateController::calculateCurrentSharedHash()
{
QList<FileData> *fileList = new QList<FileData>;
QString fileName = Tools::createVersionHashFilepath(assetManager->getCurrentVersionData()->getViewName());
hashCalculator->calculateHashes(currentStreamingPath);
fileList = hashCalculator->getHashList();
//fileList = calculateHash(currentStreamingPath);
qDebug() << "Hash count: " << fileList->count();
saveHash(fileName,fileList);
}
void UpdateController::sendNewVersionList()
{
commonClientHandler->sendNewVersionListToAllClient();
}
bool UpdateController::checkDuplicate(QString versionName)
{
return assetManager->findDuplicate(versionName);
}
void UpdateController::xmlFileDataParse(QByteArray array)
{
QXmlStreamReader xmlReader(array);
xmlReader.readNext();
//Крутимся в цикле до тех пор, пока не достигнем конца документа
while(!xmlReader.atEnd())
{
//Проверяем, является ли элемент началом тега
if(xmlReader.isStartElement())
{
if(xmlReader.name() == "FileData")
{
FileData data;
foreach(const QXmlStreamAttribute &attr,xmlReader.attributes())
{
QString name = attr.name().toString();
QString value = attr.value().toString();
if(name == "Path")
data.path = value;
else if(name == "Hash")
data.hash = value;
}
clientDataList.append(data);
}
}
xmlReader.readNext();
}
}
StreamingVersionData* UpdateController::getCurrentVersion()
{
return assetManager->getCurrentVersionData();
}
void UpdateController::printFileList(QList<FileData> fileData)
{
QListIterator<FileData> iterator(fileData);
while (iterator.hasNext())
{
auto next = iterator.next();
Logger::instance().log(next.path);
}
}
QList<FileData> UpdateController::getClientDataList() const
{
return clientDataList;
}
DataInfo *UpdateController::getCurrentDataInfo()
{
return dataInfo;
}
void UpdateController::clearCurrentDataInfo()
{
delete dataInfo;
}
bool UpdateController::checkRequiredFolder()
{
bool required = true;
QDir buildDir(buildPath + "/" + projectFolderName);
if (!buildDir.exists()) required = false;
QDir baseSharedDir(sharedDataPath + "/" + baseNameVersion);
if (!baseSharedDir.exists()) required = false;
return required;
}
UpdateController::~UpdateController()
{
}