Merge branch 'sql_update' into 'noggit-shadowlands'

update and fix MySQL UID feature.

See merge request prophecy-rp/noggit-red!37
This commit is contained in:
Kaev
2023-05-07 07:01:36 +00:00
9 changed files with 249 additions and 30 deletions

View File

@@ -13,25 +13,94 @@ namespace
std::unique_ptr<sql::Connection> connect()
{
QSettings settings;
std::unique_ptr<sql::Connection> Con
(get_driver_instance()->connect
( settings.value("project/mysql/server").toString().toStdString()
, settings.value("project/mysql/user").toString().toStdString()
, settings.value("project/mysql/pwd").toString().toStdString()
)
);
Con->setSchema(settings.value("project/mysql/db").toString().toStdString());
return Con;
// if using release SQL binaries in debug mode it will crash https://bugs.mysql.com/bug.php?id=91238 unless using sql strings
// tcp://127.0.0.1:3306
sql::SQLString hostname = "tcp://" + settings.value("project/mysql/server").toString().toStdString() + ":" + settings.value("project/mysql/port", "3306").toString().toStdString();
sql::SQLString userName = settings.value("project/mysql/user").toString().toStdString();
sql::SQLString password = settings.value("project/mysql/pwd").toString().toStdString();
sql::SQLString schema = settings.value("project/mysql/db").toString().toStdString();
try
{
std::unique_ptr<sql::Connection> Con(get_driver_instance()->connect(hostname, userName, password));
// crete database if it doesn't exist
std::string createdb_statement = "CREATE DATABASE IF NOT EXISTS " + schema;
std::unique_ptr<sql::PreparedStatement> dbpstmt(Con->prepareStatement(createdb_statement));
std::unique_ptr<sql::ResultSet> res(dbpstmt->executeQuery());
Con->setSchema(schema);
// create table if it doesn't exist, querries from src/sql
std::unique_ptr<sql::PreparedStatement> tablepstmt(Con->prepareStatement("CREATE TABLE IF NOT EXISTS `UIDs` ("
"`_map_id` int(11) NOT NULL,"
"`UID` int(11) NOT NULL,"
"PRIMARY KEY(`_map_id`)"
") ENGINE = InnoDB DEFAULT CHARSET = latin1;"));
std::unique_ptr<sql::ResultSet> tableres(tablepstmt->executeQuery());
return Con;
}
catch (sql::SQLException& e)
{
return nullptr;
}
}
}
namespace mysql
{
bool testConnection(bool report_only_err)
{
QSettings settings;
// if using release SQL binaries in debug mode it will crash https://bugs.mysql.com/bug.php?id=91238 unless using sql strings
sql::SQLString hostname = "tcp://" + settings.value("project/mysql/server").toString().toStdString() + ":" + settings.value("project/mysql/port", "3306").toString().toStdString();
sql::SQLString userName = settings.value("project/mysql/user").toString().toStdString();
sql::SQLString password = settings.value("project/mysql/pwd").toString().toStdString();
sql::SQLString schema = settings.value("project/mysql/db").toString().toStdString();
QMessageBox prompt;
prompt.setWindowFlag(Qt::WindowStaysOnTopHint);
try
{
std::unique_ptr<sql::Connection> Con(get_driver_instance()->connect(hostname, userName, password));
prompt.setIcon(QMessageBox::Information);
prompt.setText("Succesfully connected to MySQL database.");
prompt.setWindowTitle("Success");
if (!report_only_err)
prompt.exec();
return true;
}
catch (sql::SQLException& e)
{
prompt.setIcon(QMessageBox::Warning);
prompt.setText("Failed to load MySQL database, check your settings.");
prompt.setWindowTitle("Noggit Database Error");
std::stringstream promptText;
promptText << "\n# ERR: " << e.what();
promptText << "\n (MySQL error code: " << e.getErrorCode() + ")";
prompt.setInformativeText(promptText.str().c_str());
prompt.exec();
return false;
}
}
bool hasMaxUIDStoredDB(std::size_t mapID)
{
auto Con(connect());
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("SELECT * FROM UIDs WHERE MapId=(?)"));
if (Con == nullptr)
return false;
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("SELECT * FROM `UIDs` WHERE `_map_id`=(?)"));
pstmt->setInt(1, mapID);
std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
return res->rowsCount();
@@ -40,7 +109,9 @@ namespace mysql
std::uint32_t getGUIDFromDB(std::size_t mapID)
{
auto Con(connect());
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("SELECT UID FROM UIDs WHERE MapId=(?)"));
if (Con == nullptr)
return 0;
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("SELECT `UID` FROM `UIDs` WHERE `_map_id`=(?)"));
pstmt->setInt(1, mapID);
std::unique_ptr<sql::ResultSet> res(pstmt->executeQuery());
@@ -60,7 +131,9 @@ namespace mysql
void insertUIDinDB(std::size_t mapID, std::uint32_t NewUID)
{
auto Con(connect());
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("INSERT INTO UIDs SET MapId=(?), UID=(?)"));
if (Con == nullptr)
return;
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("INSERT INTO `UIDs` SET `_map_id`=(?), `UID`=(?)"));
pstmt->setInt(1, mapID);
pstmt->setInt(2, NewUID);
pstmt->executeUpdate();
@@ -69,7 +142,9 @@ namespace mysql
void updateUIDinDB (std::size_t mapID, std::uint32_t NewUID)
{
auto Con(connect());
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("UPDATE UIDs SET UID=(?) WHERE MapId=(?)"));
if (Con == nullptr)
return;
std::unique_ptr<sql::PreparedStatement> pstmt(Con->prepareStatement("UPDATE `UIDs` SET `UID`=(?) WHERE `_map_id`=(?)"));
pstmt->setInt(1, NewUID);
pstmt->setInt(2, mapID);
pstmt->executeUpdate();

View File

@@ -7,6 +7,7 @@
namespace mysql
{
bool testConnection(bool report_only_err = false);
bool hasMaxUIDStoredDB(std::size_t mapID);
std::uint32_t getGUIDFromDB(std::size_t mapID);
void insertUIDinDB(std::size_t mapID, std::uint32_t NewUID);

View File

@@ -302,6 +302,26 @@ public:
static std::vector<std::string> getWMOAreaNames(int WMOId);
};
class GameObjectDisplayInfoDB : public DBCFile
{
public:
GameObjectDisplayInfoDB() :
DBCFile("DBFilesClient\\GameObjectDisplayInfo.dbc")
{ }
/// Fields
static const size_t ID = 0; // uint
static const size_t ModelName = 1; // string
static const size_t Sounds = 2; // int[10]
static const size_t GeoBoxMinX = 12; // float
static const size_t GeoBoxMinY = 13; // float
static const size_t GeoBoxMinZ = 14; // float
static const size_t GeoBoxMaxX = 15; // float
static const size_t GeoBoxMaxY = 16; // float
static const size_t GeoBoxMaxZ = 17; // float
static const size_t ObjectEffectPackageID = 18; // int
};
void OpenDBs(std::shared_ptr<BlizzardArchive::ClientData> clientData);
const char * getGroundEffectDoodad(unsigned int effectID, int DoodadNum);
@@ -323,3 +343,4 @@ extern ZoneMusicDB gZoneMusicDB;
extern ZoneIntroMusicTableDB gZoneIntroMusicTableDB;
extern SoundEntriesDB gSoundEntriesDB;
extern WMOAreaTableDB gWMOAreaTableDB;
extern GameObjectDisplayInfoDB gGameObjectDisplayInfoDB;

View File

@@ -50,6 +50,12 @@
#include <variant>
#include <noggit/Selection.h>
#ifdef USE_MYSQL_UID_STORAGE
#include <mysql/mysql.h>
#include <QtCore/QSettings>
#endif
#include <noggit/scripting/scripting_tool.hpp>
#include <noggit/scripting/script_settings.hpp>
@@ -2597,6 +2603,19 @@ void MapView::createGUI()
connect(_main_window, &Noggit::Ui::Windows::NoggitWindow::exitPromptOpened, this, &MapView::on_exit_prompt);
set_editing_mode (editing_mode::ground);
// do we need to do this every tick ?
#ifdef USE_MYSQL_UID_STORAGE
if (_settings->value("project/mysql/enabled").toBool())
{
if (mysql::hasMaxUIDStoredDB(_world->getMapID()))
{
_status_database->setText("MySQL UID sync enabled: "
+ _settings->value("project/mysql/server").toString() + ":"
+ _settings->value("project/mysql/port").toString());
}
}
#endif
}
void MapView::on_exit_prompt()
@@ -2638,6 +2657,7 @@ MapView::MapView( math::degrees camera_yaw0
, _status_time (new QLabel (this))
, _status_fps (new QLabel (this))
, _status_culling (new QLabel (this))
, _status_database(new QLabel(this))
, _texBrush{new OpenGL::texture{}}
, _transform_gizmo(Noggit::Ui::Tools::ViewportGizmo::GizmoContext::MAP_VIEW)
, _tablet_manager(Noggit::TabletManager::instance()),
@@ -2695,6 +2715,12 @@ MapView::MapView( math::degrees camera_yaw0
, _main_window
, [=] { _main_window->statusBar()->removeWidget (_status_culling); }
);
_main_window->statusBar()->addWidget(_status_database);
connect(this
, &QObject::destroyed
, _main_window
, [=] { _main_window->statusBar()->removeWidget(_status_database); }
);
moving = strafing = updown = lookat = turn = 0.0f;

View File

@@ -367,6 +367,7 @@ private:
QLabel* _status_time;
QLabel* _status_fps;
QLabel* _status_culling;
QLabel* _status_database;
Noggit::BoolToggleProperty _locked_cursor_mode = {false};
Noggit::BoolToggleProperty _move_model_to_cursor_position = {true};

View File

@@ -681,7 +681,7 @@ uint32_t MapIndex::newGUID()
#ifdef USE_MYSQL_UID_STORAGE
QSettings settings;
if (settings->value ("project/mysql/enabled", false).toBool())
if (settings.value ("project/mysql/enabled", false).toBool())
{
mysql::updateUIDinDB(_map_id, highestGUID + 1); // update the highest uid in db, note that if the user don't save these uid won't be used (not really a problem tho)
}
@@ -1025,7 +1025,7 @@ void MapIndex::saveMaxUID()
#ifdef USE_MYSQL_UID_STORAGE
QSettings settings;
if (settings->value ("project/mysql/enabled", false).toBool())
if (settings.value ("project/mysql/enabled", false).toBool())
{
if (mysql::hasMaxUIDStoredDB(_map_id))
{
@@ -1047,9 +1047,9 @@ void MapIndex::loadMaxUID()
#ifdef USE_MYSQL_UID_STORAGE
QSettings settings;
if (settings->value ("project/mysql/enabled", false).toBool())
if (settings.value ("project/mysql/enabled", false).toBool())
{
highestGUID = std::max(mysql::getGUIDFromDB(map_id), highestGUID);
highestGUID = std::max(mysql::getGUIDFromDB(_map_id), highestGUID);
// save to make sure the db and disk uid are synced
saveMaxUID();
}

View File

@@ -126,12 +126,14 @@ namespace Noggit::Ui::Windows
#ifdef USE_MYSQL_UID_STORAGE
bool use_mysql = settings.value("project/mysql/enabled", false).toBool();
if ((use_myqsl && mysql::hasMaxUIDStoredDB(_world->getMapID()))
mysql::testConnection(true);
if ((use_mysql && mysql::hasMaxUIDStoredDB(_world->getMapID()))
|| uid_storage::hasMaxUIDStored(_world->getMapID())
)
{
_world->mapIndex.loadMaxUID();
enterMapAt(pos, camera_pitch, camera_yaw, from_bookmark);
enterMapAt(pos, camera_pitch, camera_yaw, uid_fix_mode::none, from_bookmark);
}
#else
if (uid_storage::hasMaxUIDStored(_world->getMapID()))

View File

@@ -19,6 +19,10 @@
#include <QDir>
#include <QApplication>
#ifdef USE_MYSQL_UID_STORAGE
#include <mysql/mysql.h>
#endif
#include <ui_SettingsPanel.h>
#include <ui_TitleBar.h>
@@ -85,6 +89,7 @@ namespace Noggit
#ifdef USE_MYSQL_UID_STORAGE
ui->MySQL_box->setEnabled(true);
ui->MySQL_box->setCheckable(true);
ui->mysql_warning->setVisible(false);
#endif
@@ -157,6 +162,13 @@ namespace Noggit
}
);
connect(ui->mysql_connect_test, &QPushButton::clicked, [this]
{
save_changes();
mysql::testConnection();
}
);
// load the values in the fields
discard_changes();
}
@@ -200,11 +212,31 @@ namespace Noggit
#ifdef USE_MYSQL_UID_STORAGE
ui->_mysql_box->setChecked (_settings->value ("project/mysql/enabled").toBool());
ui->_mysql_server_field->setText (_settings->value ("project/mysql/server").toString());
ui->_mysql_user_field->setText(_settings->value ("project/mysql/user").toString());
ui->_mysql_pwd_field->setText (_settings->value ("project/mysql/pwd").toString());
ui->_mysql_db_field->setText (_settings->value ("project/mysql/db").toString());
ui->MySQL_box->setChecked (_settings->value ("project/mysql/enabled").toBool());
auto server_str = _settings->value("project/mysql/server", "127.0.0.1").toString();
auto user_str = _settings->value("project/mysql/user", "127.0.0.1").toString();
auto pwd_str = _settings->value("project/mysql/pwd", "127.0.0.1").toString();
auto db_str = _settings->value("project/mysql/db", "127.0.0.1").toString();
auto port_int = _settings->value("project/mysql/port", "127.0.0.1").toInt();
// set some default
if (server_str.isEmpty())
server_str = "127.0.0.1";
if (user_str.isEmpty())
user_str = "root";
if (pwd_str.isEmpty())
pwd_str = "root";
if (db_str.isEmpty())
db_str = "noggit";
if (!port_int)
port_int = 3306;
ui->_mysql_server_field->setText (server_str);
ui->_mysql_user_field->setText(user_str);
ui->_mysql_pwd_field->setText (pwd_str);
ui->_mysql_db_field->setText (db_str);
ui->_mysql_port_field->setValue (port_int);
#endif
int wireframe_type = _settings->value("wireframe/type", 0).toInt();
@@ -245,11 +277,12 @@ namespace Noggit
_settings->setValue("nativeMenubar", ui->_nativeMenubar->isChecked());
#ifdef USE_MYSQL_UID_STORAGE
_settings->setValue ("project/mysql/enabled", _mysql_box->isChecked());
_settings->setValue ("project/mysql/server", _mysql_server_field->text());
_settings->setValue ("project/mysql/user", _mysql_user_field->text());
_settings->setValue ("project/mysql/pwd", _mysql_pwd_field->text());
_settings->setValue ("project/mysql/db", _mysql_db_field->text());
_settings->setValue ("project/mysql/enabled", ui->MySQL_box->isChecked());
_settings->setValue ("project/mysql/server", ui->_mysql_server_field->text());
_settings->setValue ("project/mysql/user", ui->_mysql_user_field->text());
_settings->setValue ("project/mysql/pwd", ui->_mysql_pwd_field->text());
_settings->setValue ("project/mysql/db", ui->_mysql_db_field->text());
_settings->setValue ("project/mysql/port", ui->_mysql_port_field->text());
#endif
_settings->setValue("wireframe/type", ui->radio_wire_cursor->isChecked());

View File

@@ -1201,7 +1201,7 @@
<bool>false</bool>
</property>
<property name="title">
<string>MySQL</string>
<string>MySQL (Noggit UID storage)</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
@@ -1295,6 +1295,36 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_66">
<item>
<widget class="QLabel" name="label_48">
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Port</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="_mysql_port_field">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>65535</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="mysql_warning">
<property name="minimumSize">
@@ -1311,6 +1341,36 @@
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_12">
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="mysql_connect_test">
<property name="minimumSize">
<size>
<width>93</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Test Connection</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>