#include "updatecontroller.h" UpdateController::UpdateController(QObject *parent) : QObject(parent), commonClientHandler(nullptr) { buildPath = QDir::currentPath() + "/" + applicationFolderName; sharedDataPath = QDir::currentPath() + "/" + sharedDataFolderName; emit sigLogMessage(buildPath); qDebug() << hashFileName; } void UpdateController::initialize(CommonClientHandler *commonClientHandler,DataParser *dataParser,AssetsManager *assetManager,Logger *logger) { this->commonClientHandler = commonClientHandler; this->dataParser = dataParser; this->assetManager = assetManager; sizeToSend = 0; assetManager->initialize(this,dataParser); if (!checkRequiredFolder()) { emit sigErrorRequired(100); return; } connect(this,&UpdateController::sigLogMessage,logger,&Logger::addTextToLogger,Qt::AutoConnection); calculateFullHash(); currentStreamingPath = assetManager->setVersion("base"); 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(); loadHash(); clientDataList.clear(); xmlFileDataParse(array); checkNeedUpdate(handler); mutex->unlock(); } void UpdateController::showHash() { for(FileData& str : serverDataList){ emit sigLogMessage(str.hash); } } void UpdateController::calculateFullHash() { commonClientHandler->slot_sendPacketToAllClients(PacketType::BUSY); auto *list = calculateHash(buildPath); saveHash(buildHashName,list); calculateSharedHash(); emit sigLogMessage("Calculate hash complete"); commonClientHandler->slot_sendPacketToAllClients(PacketType::FREE); } void UpdateController::saveHash(QString fileName,QList *fileList) { QFile hashFile(fileName); hashFile.open(QIODevice::WriteOnly); QXmlStreamWriter xmlWriter(&hashFile); QListIterator 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(); } void UpdateController::loadHash() { serverDataList.clear(); QFile hashFile(hashFileName); 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; } serverDataList.append(data); } } xmlReader.readNextStartElement(); } emit sigLogMessage("Hash load from file "); } void UpdateController::calculateSize() { QDirIterator iterator(buildPath); quint64 total = 0; while(iterator.hasNext()){ if(iterator.fileInfo().isFile()){ total += iterator.fileInfo().size(); } iterator.next(); } emit sigLogMessage("Full size: "); emit sigLogMessage(QString::number(total)); } QString UpdateController::getCommands() { QString commandsText; commandsText += "check - check version "; commandsText += "update - update files "; return commandsText; } void UpdateController::setUpCurrentServerHash() { QList *fileList = new QList; fileList->append(*calculateHash(buildPath)); FileData *streamingFolder = new FileData; streamingFolder->hash = "FOLDER"; streamingFolder->path = buildDataPath + streamingAssetsFolderName; fileList->append(*streamingFolder); fileList->append(*calculateHash(currentStreamingPath)); assetManager->prepareLocalPathList(fileList); saveHash(hashFileName,fileList); } void UpdateController::setDataInfo(DataInfo *value) { dataInfo = value; } QString UpdateController::getCurrentStreamingPath() const { return currentStreamingPath; } void UpdateController::setLocalFileData(QList dataList) { serverDataList.append(dataList); } bool UpdateController::checkNeedUpdate(ClientHandler *handler) { QList *forSend = new QList; QList *forDelete = new QList; 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(Tools::getTime()); log.append(" Client: " + handler->getClient()->getLogin()); log.append(" Need updates: "); log.append(QString::number(forSend->length())); log.append(" objects"); fileSendList = *forSend; emit sigLogMessage(log); //printFileList(*forSend); handler->sendMessageBlock(log); needUpdate = true; } else { QString log; log.append(Tools::getTime()); log.append(" Client: " + handler->getClient()->getLogin()); log.append(" no update required"); emit sigLogMessage(log); handler->sendMessageBlock(log); } if(forDelete->length() > 0){ QString log; log.append(Tools::getTime()); log.append(" Client: " + handler->getClient()->getLogin()); log.append(" Need delete: "); log.append(QString::number(forDelete->length())); log.append(" objects"); fileDeleteList = *forDelete; emit sigLogMessage(log); //printFileList(*forDelete); handler->sendMessageBlock(log); needUpdate = true; } else { QString log; log.append(Tools::getTime()); log.append(" Client: " + handler->getClient()->getLogin()); log.append(" no delete required"); emit sigLogMessage(log); handler->sendMessageBlock(log); } CalculateSizeToSend(*forSend); handler->sendNeedUpdate(needUpdate,sizeToSend,forSend->length(),forDelete->length()); return needUpdate; } QList* UpdateController::calculateHash(QString path) { serverDataList.clear(); if(!QDir(path).exists()){ QDir().mkdir(path); } QString hashString; QStringList filter; filter << "*"; QList *files = new QList; 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 = 10240; 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 diffList) { QListIterator 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 *fileList = new QList; QList *versionList = new QList; while (dirIterator.hasNext()) { dirIterator.next(); QFileInfo fileInfo = dirIterator.fileInfo(); if (fileInfo.fileName() == "." || fileInfo.fileName() == "..") continue; QString fileName = Tools::createVersionHashFilepath(fileInfo.fileName()); fileList = calculateHash(fileInfo.absoluteFilePath()); saveHash(fileName,fileList); StreamingVersionData *version = new StreamingVersionData( fileInfo.absoluteFilePath(),fileInfo.fileName(), fileInfo.birthTime(),fileInfo.size()); // if(fileInfo.fileName() == baseNameVersion) // { // version->setIsChangeable(false); // version->setAuthor(tr("Константа-дизайн")); // } // else // { // version->setIsChangeable(true); // } versionList->append(version); } assetManager->createFirstVersionListXML(*versionList); //assetManager->setVersionList(versionList); } 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) { QListIterator iterator(fileData); while (iterator.hasNext()) { auto next = iterator.next(); emit sigLogMessage(next.path); } } QList UpdateController::getFileDeleteList() const { return fileDeleteList; } QList UpdateController::getFileSendList() { QList sendList = *assetManager->prepareRealPathList(&fileSendList); return sendList; } QList 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() { }