mirror of
https://gitea.msk.dinamika-avia.ru/Constanta-Design/RRJServer.git
synced 2026-03-27 19:45:43 +03:00
603 lines
17 KiB
C++
603 lines
17 KiB
C++
#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->setVersion("base"); //TODO: сохрнаять предыдущую версию и загружать ее при включении
|
||
setUpCurrentServerHash();
|
||
|
||
mutex = new QMutex;
|
||
qDebug() << "UpdateController init thread ID " << QThread::currentThreadId();
|
||
}
|
||
|
||
void UpdateController::changeAssetVersion(QString versionName)
|
||
{
|
||
commonClientHandler->slot_sendPacketToAllClients(PacketType::BUSY);
|
||
qDebug() << "UpdateController thread ID " << QThread::currentThreadId();
|
||
currentStreamingPath = assetManager->setVersion(versionName);
|
||
setUpCurrentServerHash();
|
||
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);
|
||
//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);
|
||
|
||
}
|
||
|
||
void UpdateController::calculateFullHashWithSetup()
|
||
{
|
||
commonClientHandler->slot_sendPacketToAllClients(PacketType::HASH_CALCULATE_START);
|
||
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;
|
||
}
|
||
|
||
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>;
|
||
|
||
fileSendList.clear();
|
||
fileDeleteList.clear();
|
||
sizeToSend = 0;
|
||
|
||
bool needUpdate = false;
|
||
|
||
for (auto &item:clientDataList) //проверка на недостающие файлы по адресам
|
||
{
|
||
if(item.path.contains("Temp")) continue;
|
||
|
||
if (!serverDataList.contains(item))
|
||
{
|
||
forDelete->append(item);
|
||
}
|
||
}
|
||
|
||
for (auto &item:serverDataList)
|
||
{
|
||
if(item.path.contains("Temp")) continue;
|
||
|
||
if (!clientDataList.contains(item))
|
||
{
|
||
forSend->append(item);
|
||
}
|
||
}
|
||
|
||
if(forSend->length() > 0) //формирование сообщения об обновлении
|
||
{
|
||
QString log;
|
||
log.append(" Client: " + handler->getClient()->getLogin());
|
||
log.append(" Need updates: ");
|
||
log.append(QString::number(forSend->length()));
|
||
log.append(" objects");
|
||
fileSendList = *forSend;
|
||
|
||
Logger::instance().log(log);
|
||
//printFileList(*forSend);
|
||
|
||
//handler->sendMessageBlock(log);
|
||
needUpdate = true;
|
||
}
|
||
else
|
||
{
|
||
QString log;
|
||
log.append(" Client: " + handler->getClient()->getLogin());
|
||
log.append(" no update required");
|
||
Logger::instance().log(log);
|
||
//handler->sendMessageBlock(log);
|
||
}
|
||
|
||
if(forDelete->length() > 0){
|
||
QString log;
|
||
log.append(" Client: " + handler->getClient()->getLogin());
|
||
|
||
log.append(" Need delete: ");
|
||
log.append(QString::number(forDelete->length()));
|
||
log.append(" objects");
|
||
fileDeleteList = *forDelete;
|
||
|
||
Logger::instance().log(log);
|
||
//printFileList(*forDelete);
|
||
//handler->sendMessageBlock(log);
|
||
needUpdate = true;
|
||
}
|
||
else
|
||
{
|
||
QString log;
|
||
log.append(" Client: " + handler->getClient()->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;
|
||
}
|
||
|
||
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::getFileDeleteList() const
|
||
{
|
||
return fileDeleteList;
|
||
}
|
||
|
||
QList<FileData> UpdateController::getFileSendList()
|
||
{
|
||
QList<FileData> *copy = new QList<FileData>(fileSendList);
|
||
QList<FileData> sendList = *assetManager->prepareRealPathList(copy);
|
||
return sendList;
|
||
}
|
||
|
||
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()
|
||
{
|
||
|
||
}
|
||
|