Files
RRJServer/ServerLMS/Systems/updatecontroller.cpp
2025-01-22 10:14:43 +03:00

551 lines
15 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;
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,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<FileData> *fileList)
{
QFile hashFile(fileName);
hashFile.open(QIODevice::WriteOnly);
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();
}
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<FileData> *fileList = new QList<FileData>;
fileList->append(*calculateHash(buildPath));
fileList->append(*calculateHash(currentStreamingPath));
assetManager->prepareLocalPathList(fileList);
saveHash(hashFileName,fileList);
}
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(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<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 | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
while (dirIterator.hasNext())
{
QFileInfo fileInfo(dirIterator.next());
FileData currentFile;
if(fileInfo.isDir() && !fileInfo.fileName().startsWith(".") && fileInfo.fileName() != "RRJLoader")
{
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<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())
{
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> fileData)
{
QListIterator<FileData> iterator(fileData);
while (iterator.hasNext())
{
auto next = iterator.next();
emit sigLogMessage(next.path);
}
}
QList<FileData> UpdateController::getFileDeleteList() const
{
return fileDeleteList;
}
QList<FileData> UpdateController::getFileSendList()
{
QList<FileData> sendList = *assetManager->prepareRealPathList(&fileSendList);
return sendList;
}
QList<FileData> UpdateController::getClientDataList() const
{
return clientDataList;
}
DataInfo *UpdateController::getCurrentDataInfo()
{
return dataInfo;
}
void UpdateController::clearCurrentDataInfo()
{
delete dataInfo;
}
UpdateController::~UpdateController()
{
}