#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); 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) { commonClientHandler->slot_sendPacketToAllClients(PacketType::BUSY); assetManager->createCopyVersion(versionName,newVersionName); commonClientHandler->slot_sendPacketToAllClients(PacketType::FREE); } void UpdateController::deleteAssetVersion(QString versionName) { assetManager->deleteVersion(versionName); } void UpdateController::compareFiles(ClientHandler* handler, QByteArray array) { mutex->lock(); loadHash(); clientDataList.clear(); xmlFileDataParse(array); clientDataList.append(*datas); 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)); fileList->append(*calculateHash(currentStreamingPath)); assetManager->prepareLocalPathList(fileList); saveHash(hashFileName,fileList); } 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(); QDirIterator iterator(path,QDirIterator::Subdirectories); if(!QDir(path).exists()){ QDir().mkdir(path); } QDir dir(path); dir.setFilter(QDir::NoDotAndDotDot); QString hashString; QList *files = new QList; while (iterator.hasNext()) { iterator.next(); QFileInfo fileInfo = iterator.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.hash = hashString; currentFile.path = Tools::createLocalPath(fileInfo.absoluteFilePath()); files->push_back(currentFile); file.close(); } else if(fileInfo.isDir() && fileInfo.fileName() != ".." && !fileInfo.isRoot()) { currentFile.hash = "FOLDER"; currentFile.path = Tools::createLocalPath(fileInfo.path()); if(!files->contains(currentFile)){ files->push_back(currentFile); } } } std::sort(files->begin(),files->end()); serverDataList.append(*files); return files; } QString UpdateController::getPathAdditionalFile(QString name) { QString path = Tools::createSharedPath("/" + assetManager->getCurrentVersionData()->getViewName() + "/AdditionalFiles" + 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); 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 = staticDataFolderName + "/" + fileInfo.fileName() + "Hash.xml"; fileList = calculateHash(fileInfo.absoluteFilePath()); saveHash(fileName,fileList); StreamingVersionData *version = new StreamingVersionData( fileInfo.absoluteFilePath(),fileInfo.fileName(), fileInfo.birthTime(),fileInfo.size()); versionList->append(version); } createVersionListXmlAnswer(*versionList); assetManager->setVersionList(versionList); } void UpdateController::createVersionListXmlAnswer(QList version) //TODO: переименовать и перебросить в AssetManager { QList listTag; foreach(StreamingVersionData* ver,version) { SAttribute attribute1 = {"Version", ver->getViewName()}; SAttribute attribute2 = {"Created", ver->getCreateData().toString()}; QList listAttr = {attribute1, attribute2}; SXmlAnswerTag tag = {"VersionData", listAttr}; listTag.append(tag); } QFile file(versionListFile); file.open(QIODevice::WriteOnly); QXmlStreamWriter xmlWriter(&file); xmlWriter.setAutoFormatting(true); xmlWriter.writeStartDocument(); xmlWriter.writeStartElement("VersionList"); foreach(SXmlAnswerTag tag,listTag) { xmlWriter.writeStartElement(tag.elementName); foreach(SAttribute attribute,tag.attr) { xmlWriter.writeAttribute(attribute.name,attribute.value); } xmlWriter.writeEndElement(); } xmlWriter.writeEndElement(); xmlWriter.writeEndDocument(); file.close(); } void UpdateController::saveVersionToFile(StreamingVersionData *streamingVersion) //TODO: переименовать и перебросить в AssetManager { QFile file(version); file.open(QFile::WriteOnly); QXmlStreamWriter xmlWriter(&file); xmlWriter.setAutoFormatting(true); xmlWriter.writeStartDocument(); xmlWriter.writeStartElement("VersionData"); xmlWriter.writeAttribute("Version",streamingVersion->getViewName()); xmlWriter.writeAttribute("Created",streamingVersion->getCreateData().toString()); xmlWriter.writeEndElement(); xmlWriter.writeEndDocument(); file.close(); } void UpdateController::sendNewVersionList() { commonClientHandler->sendNewVersionListToAllClient(); } bool UpdateController::checkDuplicate(QString versionName) { return assetManager->findDuplicate(versionName); } void UpdateController::xmlFileDataParse(QByteArray array) { QXmlStreamReader xmlReader(array); datas = new QList; 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; } datas->append(data); } } xmlReader.readNext(); } } 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; } QList *UpdateController::getDatas() const { return datas; } void UpdateController::clearCurrentDataInfo() { delete dataInfo; } UpdateController::~UpdateController() { }