У меня есть некоторый игровой клиент, в котором после введения имени игрока и нажатия на кнопку старта, происходит подключение к socket серверу и получения с него данных (информация о игроке, карта и т.д.).
Код:
void MainWindow::on_startButton_clicked()
{
if (ui->userNameForm->toPlainText() != "") {
ui->startMenu->hide();
ui->graphview->show();
qDebug() << "clicked";
game_ = new Game(); // создание новой игры
QString userName = ui->userNameForm->toPlainText(); // чтение ника с формы
game_->connectToServer();
game_->login(userName); // отправка имени пользователя и создание game_->player()
game_->getMap(); // получение карты с сервера (3 слоя)
game_->makeMap(); // формирование карты из 3 слоёв в 1 -> game_->map()
this->setMap(game_->map()); // отрисовка в ui
this->update();
// отображение инфы о пользователе, полученной с сервера
ui->userName->setText("Name: " + game_->player().name());
ui->userTown->setText("City: " + game_->player().town().name());
ui->userPopulation->setText("Population: " +
QString::number(game_->player().town().population()) +
" / " +
QString::number(game_->player().town().populationCapacity()));
ui->userProducts->setText("Products: " +
QString::number(game_->player().town().product()) +
" / " +
QString::number(game_->player().town().productCapacity()));
ui->userSomething->setText("Rating: " + QString::number(game_->player().rating()));
game_->gameCycle(); // логика игры (много обращений к серверу и различных махинаций с game_.player())
}
}
Моя проблема в том, что при выполнении game_->gameCycle(); UI зависает и не отображает карту, до того момента, как gameCylce() закончится, я понимаю, что проблема в том, что все эти функции выполняются в одном потоке, но как мне лучше решить эту проблему? Пробовал в многопоточность, но проблема в том, что я не понимаю что из этого мне надо выполнять в другом потоке. Пробовал все методы game_ выполнять в другом потоке ( как тут https://stackoverflow.com/questions/14051276/how-to-implement-frequent-start-stop-of-a-thread-qthread), но, тогда отображение информации о пользователе выполняется быстрее, чем получение информации с сервера и это крашит клиент.
В общем не знаю что тут делать вообще... И может кто подскажет как лучше организовать структуру проекта, чтобы я мог обновлять состояние карты параллельно с логикой игры
Все предложения по улучшению кода приветствуются
Как я пробовал делать:
MainWindow.h
Код:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "Map.h"
#include "Game.h"
#include "QThread"
#include "Worker.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void setMap(std::shared_ptr<Map> m);
void setGame(Game *game) { game_ = game; };
Game& game() { return *game_; };
signals:
void startWork(Game *game);
void stopWork();
private slots:
void on_startButton_clicked();
void on_logoutButton_clicked();
private:
Ui::MainWindow *ui;
Game *game_;
Worker *worker_;
QThread *gameThread_;
};
#endif // MAINWINDOW_H
Код:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
ui->graphview->hide();
ui->postInfo->hide();
gameThread_ = new QThread;
worker_ = new Worker;
worker_->moveToThread(gameThread_);
connect(this, SIGNAL(startWork(Game*)), worker_, SLOT(StartWork(Game*)));
connect(this, SIGNAL(stopWork()), worker_, SLOT(StopWork()));
// QTimer *timer = new QTimer(this);
// connect(timer, SIGNAL(timeout()), this, SLOT(on_startButton_clicked()));
}
MainWindow::~MainWindow() {
delete ui;
}
void MainWindow::setMap(std::shared_ptr<Map> m) {
ui->graphview->setMap(m, game_->player());
}
void MainWindow::on_startButton_clicked()
{
if (ui->userNameForm->toPlainText() != "") {
ui->startMenu->hide();
ui->graphview->show();
qDebug() << "clicked";
game_ = new Game();
emit startWork(game_);
gameThread_->start();
QString userName = ui->userNameForm->toPlainText();
// game_->connectToServer();
// game_->login(userName);
// game_->getMap();
// game_->makeMap();
// Thread *thread = new Thread(this, game_->map());
// thread->start();
this->setMap(game_->map());
this->update();
ui->userName->setText("Name: " + game_->player().name());
ui->userTown->setText("City: " + game_->player().town().name());
ui->userPopulation->setText("Population: " +
QString::number(game_->player().town().population()) +
" / " +
QString::number(game_->player().town().populationCapacity()));
ui->userProducts->setText("Products: " +
QString::number(game_->player().town().product()) +
" / " +
QString::number(game_->player().town().productCapacity()));
ui->userSomething->setText("Rating: " + QString::number(game_->player().rating()));
// qDebug() << game_->map()->graph().idx();
// qDebug() << game_->map()->trains()[0].waysLength();
// qDebug() << game_->map()->graph().idx().at(game_->player().town().pointIdx());
// game_->gameCycle();
}
}
void MainWindow::on_logoutButton_clicked()
{
emit stopWork();
// game_->disconnect();
ui->startMenu->show();
ui->graphview->hide();
}
Код:
#ifndef WORKER_H
#define WORKER_H
#include <QObject>
//#include "Socket.h"
#include "Game.h"
class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
signals:
void SignalToObj_mainThreadGUI();
void running();
void stopped();
public slots:
void StopWork();
void StartWork(Game * game);
private slots:
void doWork();
private:
volatile bool isStopped, isRunning;
// Socket *socket_;
Game *game_;
};
#endif // WORKER_H
Код:
Worker::Worker(QObject *parent) : QObject(parent), isStopped(false), isRunning(false)
{}
void Worker::doWork() {
if (!isRunning || isStopped) {
return;
}
game_->connectToServer();
game_->login("Sanya");
game_->getMap();
game_->makeMap();
qDebug() << "Worker started to work";
emit SignalToObj_mainThreadGUI();
// QMetaObject::invokeMethod(this, "doWork", Qt::QueuedConnection);
}
void Worker::StopWork() {
qDebug() << "Stop";
isStopped = true;
isRunning = false;
emit stopped();
}
void Worker::StartWork(Game *game) {
game_ = game;
qDebug() << "Start";
isStopped = false;
isRunning = true;
emit running();
doWork();
}
Game.cpp
Код:
void Game::connectToServer() {
qDebug() << "Connecting";
socket_ = new Socket();
this->socket_->connect();
}
void Game::login(QString userName) {
qDebug() << "LogIn";
QJsonObject data;
data["name"] = userName;
this->socket_->sendData(Request(Action::LOGIN, data));
QJsonObject userData = socket_->getData();
qDebug() << userData;
player_ = new Player(userData);
}
void Game::logout() {
qDebug() << "LogOut";
this->socket_->close();
}
void Game::disconnect() {
qDebug() << "Connection closed";
this->socket_->close();
}
void Game::tick() {
this->socket_->sendData(Request(Action::TURN, QJsonObject()));
socket_->getData();
}
void Game::getMap() {
socket_->sendData(Request(Action::MAP, QJsonObject({{"layer", 0}})));
layer_0 = socket_->getData();
socket_->sendData(Request(Action::MAP, QJsonObject({{"layer", 1}})));
layer_1 = socket_->getData();
socket_->sendData(Request(Action::MAP, QJsonObject({{"layer", 10}})));
layer_2 = socket_->getData();
}
void Game::makeMap() {
qDebug() << "Draw map";
map_ = std::make_shared<Map>(layer_0, layer_1, layer_2, *player_);
map_->makeWays();
}