port from mysql conenctor to QTSQL module
add new classes to manage connections begin work on generic database for client and db
This commit is contained in:
24
.gitignore
vendored
24
.gitignore
vendored
@@ -1,18 +1,6 @@
|
||||
/**
|
||||
!/bin/**
|
||||
!/cmake/**
|
||||
!/etc/**
|
||||
!/include/**
|
||||
!/media/**
|
||||
!/resources/**
|
||||
!/sql/**
|
||||
!/src/**
|
||||
!/test/**
|
||||
!/.idea/**
|
||||
!.gitignore
|
||||
!.CMakeLists.txt
|
||||
!COPYING
|
||||
!Doxyfile
|
||||
!README.md
|
||||
!todo_1.4.md
|
||||
!uid_fix_concept.md
|
||||
/out
|
||||
/bin
|
||||
/build
|
||||
/.vs
|
||||
/CMakePresets.json
|
||||
/CMakeUserPresets.json
|
||||
108
CMakeLists.txt
108
CMakeLists.txt
@@ -88,7 +88,6 @@ SET(LIBARY_OUTPUT_PATH bin)
|
||||
SET(EXTERNAL_SOURCE_DIR src/external)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
# OPTION(USE_SQL "Enable sql uid save ? (require mysql installed)" OFF)
|
||||
OPTION(VALIDATE_OPENGL_PROGRAMS "Validate Opengl programs" OFF)
|
||||
|
||||
IF(VALIDATE_OPENGL_PROGRAMS)
|
||||
@@ -115,32 +114,7 @@ FetchContent_Declare(
|
||||
FetchContent_MakeAvailable(FastNoise2)
|
||||
|
||||
FIND_PACKAGE(Sol2 REQUIRED)
|
||||
FIND_PACKAGE(Qt5 COMPONENTS Widgets OpenGLExtensions Gui Network Xml Multimedia REQUIRED)
|
||||
|
||||
# IF(USE_SQL)
|
||||
FIND_LIBRARY(MYSQL_LIBRARY NAMES libmysql
|
||||
HINTS "${CMAKE_SOURCE_DIR}/../Noggit3libs/mysql")
|
||||
FIND_LIBRARY(MYSQLCPPCONN_LIBRARY NAMES mysqlcppconn
|
||||
HINTS "${CMAKE_SOURCE_DIR}/../Noggit3libs/mysql/connector")
|
||||
FIND_PATH(MYSQLCPPCONN_INCLUDE NAMES cppconn/driver.h
|
||||
HINTS "${CMAKE_SOURCE_DIR}/../Noggit3libs/mysql/connector")
|
||||
|
||||
# Ensure we always include the parent of 'cppconn' folder
|
||||
if(EXISTS "${MYSQLCPPCONN_INCLUDE}/cppconn")
|
||||
set(MYSQLCPPCONN_INCLUDE "${MYSQLCPPCONN_INCLUDE}")
|
||||
elseif(EXISTS "${MYSQLCPPCONN_INCLUDE}/driver.h")
|
||||
get_filename_component(MYSQLCPPCONN_INCLUDE "${MYSQLCPPCONN_INCLUDE}" DIRECTORY)
|
||||
endif()
|
||||
|
||||
IF(MYSQL_LIBRARY AND MYSQLCPPCONN_LIBRARY AND MYSQLCPPCONN_INCLUDE)
|
||||
ADD_DEFINITIONS(-DUSE_MYSQL_UID_STORAGE)
|
||||
SET (mysql_sources src/mysql/mysql.cpp)
|
||||
SET (mysql_headers src/mysql/mysql.h)
|
||||
SOURCE_GROUP("mysql" FILES ${mysql_sources} ${mysql_headers})
|
||||
ELSE()
|
||||
MESSAGE(FATAL_ERROR "MySQL lib or connector not found")
|
||||
ENDIF()
|
||||
# ENDIF()
|
||||
FIND_PACKAGE(Qt5 COMPONENTS Widgets OpenGLExtensions Gui Network Xml Multimedia Sql REQUIRED)
|
||||
|
||||
ADD_SUBDIRECTORY("${EXTERNAL_SOURCE_DIR}/qt-color-widgets")
|
||||
ADD_SUBDIRECTORY("${EXTERNAL_SOURCE_DIR}/framelesshelper")
|
||||
@@ -297,7 +271,6 @@ ADD_EXECUTABLE(noggit
|
||||
${opengl_sources}
|
||||
${math_sources}
|
||||
${external_sources}
|
||||
${mysql_sources}
|
||||
${os_sources}
|
||||
${util_sources}
|
||||
${util_headers}
|
||||
@@ -311,7 +284,6 @@ ADD_EXECUTABLE(noggit
|
||||
${opengl_headers}
|
||||
${math_headers}
|
||||
${external_headers}
|
||||
${mysql_headers}
|
||||
${os_headers}
|
||||
${png_blp_headers}
|
||||
${ResFiles}
|
||||
@@ -355,6 +327,7 @@ TARGET_LINK_LIBRARIES (noggit
|
||||
Qt5::Xml
|
||||
Qt5::Network
|
||||
Qt5::Multimedia
|
||||
Qt5::Sql
|
||||
ColorWidgets-qt5
|
||||
FramelessHelper
|
||||
qt_imgui_widgets
|
||||
@@ -371,6 +344,78 @@ TARGET_LINK_LIBRARIES (noggit
|
||||
blizzard-database-library
|
||||
rapidfuzz::rapidfuzz
|
||||
)
|
||||
include(ExternalProject)
|
||||
|
||||
|
||||
# Fetch qsqlmysql.dll plugin based on Qt version #####
|
||||
execute_process(
|
||||
COMMAND ${Qt5Core_DIR}/../../../bin/qmake -query QT_VERSION
|
||||
OUTPUT_VARIABLE QT_FULL_VERSION
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
message(STATUS "Qt full version: ${QT_FULL_VERSION}")
|
||||
|
||||
# prebuilds available at : https://github.com/thecodemonkey86/qt_mysql_driver/releases
|
||||
# get direct download links
|
||||
if(WIN32)
|
||||
if(MSVC)
|
||||
set(QMYSQL_PREBUILT_DRIVER_URL
|
||||
"https://github.com/thecodemonkey86/qt_mysql_driver/files/5575770/qsqlmysql.dll_Qt_SQL_driver_${QT_FULL_VERSION}_MSVC2019_64-bit.zip"
|
||||
)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # MinGW
|
||||
set(QMYSQL_PREBUILT_DRIVER_URL
|
||||
"https://github.com/thecodemonkey86/qt_mysql_driver/files/5575769/qsqlmysql.dll_Qt_SQL_driver_${QT_FULL_VERSION}_MinGW_8.1.0_64-bit.zip"
|
||||
)
|
||||
else()
|
||||
message(WARNING "Unsupported Windows compiler for prebuilt MySQL Qt driver")
|
||||
endif()
|
||||
elseif(UNIX)
|
||||
set(QMYSQL_PREBUILT_DRIVER_URL
|
||||
"https://github.com/thecodemonkey86/qt_mysql_driver/files/6388052/libqsqlmysql.so_Qt_SQL_driver_${QT_FULL_VERSION}_gcc64.zip"
|
||||
)
|
||||
else()
|
||||
message(WARNING "Unsupported platform for prebuilt MySQL Qt driver")
|
||||
endif()
|
||||
|
||||
set(QMYSQL_PREBUILT_DIR "${CMAKE_BINARY_DIR}/_deps/qt_mysql_driver")
|
||||
set(QMYSQL_EXTRACT_DIR "${QMYSQL_PREBUILT_DIR}/sqldrivers")
|
||||
set(QMYSQL_ZIP "${QMYSQL_PREBUILT_DIR}/qsqlmysql_prebuilt.zip")
|
||||
|
||||
# Download zip only if not already present
|
||||
if(NOT EXISTS "${QMYSQL_ZIP}")
|
||||
message(STATUS "qt MySQL DRIVER zip not found. Downloading qmysql from : ${QMYSQL_PREBUILT_DRIVER_URL}")
|
||||
|
||||
file(DOWNLOAD
|
||||
${QMYSQL_PREBUILT_DRIVER_URL}
|
||||
${QMYSQL_ZIP}
|
||||
# SHOW_PROGRESS
|
||||
STATUS DOWNLOAD_STATUS
|
||||
TLS_VERIFY ON
|
||||
)
|
||||
|
||||
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
|
||||
if(NOT STATUS_CODE EQUAL 0)
|
||||
message(WARNING "Failed to download prebuilt qt MySQL driver: ${DOWNLOAD_STATUS}")
|
||||
message(WARNING "You can manually download it from https://github.com/thecodemonkey86/qt_mysql_driver/releases or build qsqlmysql.dll yourself from QT source.")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Prebuilt MySQL Qt driver zip already exists, skipping download")
|
||||
endif()
|
||||
|
||||
# Extract zip
|
||||
file(ARCHIVE_EXTRACT
|
||||
INPUT ${QMYSQL_ZIP}
|
||||
DESTINATION ${QMYSQL_EXTRACT_DIR}
|
||||
)
|
||||
message(STATUS "Extracted prebuilt MySQL Qt driver at ${QMYSQL_EXTRACT_DIR}. Deps will be deploeyd after build.")
|
||||
|
||||
|
||||
# deploy
|
||||
add_custom_command(TARGET noggit POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${QMYSQL_EXTRACT_DIR}"
|
||||
"$<TARGET_FILE_DIR:noggit>"
|
||||
)
|
||||
|
||||
#add distribution themes
|
||||
add_custom_command(TARGET noggit POST_BUILD
|
||||
@@ -452,11 +497,6 @@ IF(APPLE)
|
||||
TARGET_LINK_LIBRARIES (noggit "-framework Cocoa" "-framework AppKit" "-framework Foundation")
|
||||
ENDIF()
|
||||
|
||||
IF(MYSQL_LIBRARY AND MYSQLCPPCONN_LIBRARY AND MYSQLCPPCONN_INCLUDE)
|
||||
TARGET_LINK_LIBRARIES(noggit ${MYSQL_LIBRARY} ${MYSQLCPPCONN_LIBRARY})
|
||||
TARGET_INCLUDE_DIRECTORIES(noggit SYSTEM PRIVATE ${MYSQLCPPCONN_INCLUDE})
|
||||
ENDIF()
|
||||
|
||||
IF(NOGGIT_LOGTOCONSOLE AND WIN32)
|
||||
SET_PROPERTY(TARGET noggit APPEND PROPERTY LINK_FLAGS_DEBUG "/SUBSYSTEM:CONSOLE")
|
||||
SET_PROPERTY(TARGET noggit APPEND PROPERTY COMPILE_DEFINITIONS $<$<CONFIG:Debug>:"_CONSOLE">)
|
||||
|
||||
2
src/external/blizzard-database-library
vendored
2
src/external/blizzard-database-library
vendored
Submodule src/external/blizzard-database-library updated: d0b399cb52...1cefbe958a
@@ -1,166 +0,0 @@
|
||||
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
|
||||
|
||||
#include <mysql/mysql.h>
|
||||
#include <noggit/world.h>
|
||||
|
||||
#include <QSettings>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <cppconn/driver.h>
|
||||
#include <cppconn/prepared_statement.h>
|
||||
#include <cppconn/exception.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
std::unique_ptr<sql::Connection> connect()
|
||||
{
|
||||
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
|
||||
// tcp://127.0.0.1:3306
|
||||
const sql::SQLString hostname = "tcp://" + settings.value("project/mysql/server").toString().toStdString() + ":" + settings.value("project/mysql/port", "3306").toString().toStdString();
|
||||
const sql::SQLString userName = settings.value("project/mysql/user").toString().toStdString();
|
||||
const sql::SQLString password = settings.value("project/mysql/pwd").toString().toStdString();
|
||||
const 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;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "SQL Other exception: " << e.what() << std::endl;
|
||||
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
|
||||
const sql::SQLString hostname = "tcp://" + settings.value("project/mysql/server").toString().toStdString() + ":" + settings.value("project/mysql/port", "3306").toString().toStdString();
|
||||
const sql::SQLString userName = settings.value("project/mysql/user").toString().toStdString();
|
||||
const sql::SQLString password = settings.value("project/mysql/pwd").toString().toStdString();
|
||||
|
||||
QMessageBox prompt;
|
||||
prompt.setWindowFlag(Qt::WindowStaysOnTopHint);
|
||||
try
|
||||
{
|
||||
sql::Driver* driver = get_driver_instance();
|
||||
sql::Connection* connection = driver->connect(hostname, userName, password);
|
||||
std::unique_ptr<sql::Connection> Con(connection);
|
||||
|
||||
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. \nIf you did not intend to use this feature, disable it in Noggit->settings->MySQL");
|
||||
prompt.setWindowTitle("Noggit Database Error");
|
||||
// disable if connection is not valid
|
||||
// settings.value("project/mysql/enabled") = false;
|
||||
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;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "SQL Other exception: " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasMaxUIDStoredDB(std::size_t mapID)
|
||||
{
|
||||
auto Con(connect());
|
||||
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();
|
||||
}
|
||||
|
||||
std::uint32_t getGUIDFromDB(std::size_t mapID)
|
||||
{
|
||||
auto Con(connect());
|
||||
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());
|
||||
|
||||
std::uint32_t highGUID(0);
|
||||
if (res->rowsCount() == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
while (res->next())
|
||||
{
|
||||
highGUID = res->getInt(1);
|
||||
}
|
||||
|
||||
return highGUID;
|
||||
}
|
||||
|
||||
void insertUIDinDB(std::size_t mapID, std::uint32_t NewUID)
|
||||
{
|
||||
auto Con(connect());
|
||||
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();
|
||||
}
|
||||
|
||||
void updateUIDinDB (std::size_t mapID, std::uint32_t NewUID)
|
||||
{
|
||||
auto Con(connect());
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
|
||||
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);
|
||||
void updateUIDinDB (std::size_t mapID, std::uint32_t NewUID);
|
||||
};
|
||||
@@ -76,11 +76,8 @@
|
||||
#include <noggit/tools/AreaTriggerTool.hpp>
|
||||
#include <noggit/StringHash.hpp>
|
||||
#include <noggit/application/NoggitApplication.hpp>
|
||||
#include <noggit/sql/DatabaseManager.h>
|
||||
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
#endif
|
||||
#include <QtCore/QSettings>
|
||||
|
||||
#include <noggit/scripting/scripting_tool.hpp>
|
||||
@@ -2472,17 +2469,23 @@ void MapView::createGUI()
|
||||
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());
|
||||
}
|
||||
auto& db_mgr = Noggit::Sql::DatabaseManager::instance();
|
||||
|
||||
if (db_mgr.testConnection(Noggit::Sql::SQLDbType::Noggit))
|
||||
{
|
||||
_status_database->setText("UID SQL Database is active: "
|
||||
+ _settings->value("project/mysql/server").toString() + ":"
|
||||
+ _settings->value("project/mysql/port").toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
_status_database->setText("UID SQL Database is not working: "
|
||||
+ _settings->value("project/mysql/server").toString() + ":"
|
||||
+ _settings->value("project/mysql/port").toString());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MapView::on_exit_prompt()
|
||||
|
||||
@@ -9,13 +9,11 @@
|
||||
#include <noggit/ActionManager.hpp>
|
||||
#include <noggit/Action.hpp>
|
||||
#include <noggit/project/CurrentProject.hpp>
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
#include <mysql/mysql.h>
|
||||
#endif
|
||||
#include <noggit/map_index.hpp>
|
||||
#include <noggit/uid_storage.hpp>
|
||||
#include <noggit/application/NoggitApplication.hpp>
|
||||
#include <ClientFile.hpp>
|
||||
#include <noggit/sql/SqlUIDStorage.h>
|
||||
|
||||
#include <QtCore/QSettings>
|
||||
#include <QByteArray>
|
||||
@@ -776,14 +774,13 @@ uint32_t MapIndex::newGUID()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock (_mutex);
|
||||
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
QSettings settings;
|
||||
|
||||
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)
|
||||
// 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)
|
||||
Noggit::Sql::SqlUIDStorage::updateUIDinDB(_map_id, highestGUID + 1);
|
||||
}
|
||||
#endif
|
||||
return ++highestGUID;
|
||||
}
|
||||
|
||||
@@ -1135,21 +1132,19 @@ void MapIndex::searchMaxUID()
|
||||
|
||||
void MapIndex::saveMaxUID()
|
||||
{
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
QSettings settings;
|
||||
|
||||
if (settings.value ("project/mysql/enabled", false).toBool())
|
||||
{
|
||||
if (mysql::hasMaxUIDStoredDB(_map_id))
|
||||
if (Noggit::Sql::SqlUIDStorage::hasMaxUIDStoredDB(_map_id))
|
||||
{
|
||||
mysql::updateUIDinDB(_map_id, highestGUID);
|
||||
Noggit::Sql::SqlUIDStorage::updateUIDinDB(_map_id, highestGUID);
|
||||
}
|
||||
else
|
||||
{
|
||||
mysql::insertUIDinDB(_map_id, highestGUID);
|
||||
Noggit::Sql::SqlUIDStorage::insertUIDinDB(_map_id, highestGUID);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// save the max UID on the disk (always save to sync with the db if used
|
||||
uid_storage::saveMaxUID (_map_id, highestGUID);
|
||||
}
|
||||
@@ -1157,16 +1152,14 @@ void MapIndex::saveMaxUID()
|
||||
void MapIndex::loadMaxUID()
|
||||
{
|
||||
highestGUID = uid_storage::getMaxUID (_map_id);
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
QSettings settings;
|
||||
|
||||
if (settings.value ("project/mysql/enabled", false).toBool())
|
||||
{
|
||||
highestGUID = std::max(mysql::getGUIDFromDB(_map_id), highestGUID);
|
||||
highestGUID = std::max(Noggit::Sql::SqlUIDStorage::getGUIDFromDB(_map_id), highestGUID);
|
||||
// save to make sure the db and disk uid are synced
|
||||
saveMaxUID();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MapIndex::loadMinimapMD5translate()
|
||||
|
||||
@@ -259,6 +259,11 @@ namespace Noggit::Project
|
||||
_projectWriter = std::make_shared<ApplicationProjectWriter>();
|
||||
}
|
||||
|
||||
unsigned int NoggitProject::buildId()
|
||||
{
|
||||
return ClientDatabase->getBuild();
|
||||
}
|
||||
|
||||
void NoggitProject::createBookmark(const NoggitProjectBookmarkMap& bookmark)
|
||||
{
|
||||
Bookmarks.push_back(bookmark);
|
||||
|
||||
@@ -117,6 +117,8 @@ namespace Noggit::Project
|
||||
NoggitExtraMapData ExtraMapData;
|
||||
NoggitProject();
|
||||
|
||||
unsigned int buildId();
|
||||
|
||||
void createBookmark(const NoggitProjectBookmarkMap& bookmark);
|
||||
|
||||
void deleteBookmark();
|
||||
|
||||
540
src/noggit/sql/ClientDatabase.cpp
Normal file
540
src/noggit/sql/ClientDatabase.cpp
Normal file
@@ -0,0 +1,540 @@
|
||||
#include "ClientDatabase.h"
|
||||
#include <noggit/project/CurrentProject.hpp>
|
||||
#include <noggit/application/Utils.hpp>
|
||||
|
||||
#include <QString>
|
||||
#include <QSqlRecord>
|
||||
#include <QSqlField>
|
||||
#include <QElapsedTimer>
|
||||
|
||||
namespace Noggit
|
||||
{
|
||||
|
||||
|
||||
std::optional<Structures::BlizzardDatabaseRow> ClientDatabase::getRowById(const std::string& tableName, unsigned int id)
|
||||
{
|
||||
bool setting_use_sql_db = false; // todo QSETTING
|
||||
|
||||
auto row = Structures::BlizzardDatabaseRow(-1);
|
||||
|
||||
if (setting_use_sql_db)
|
||||
row = sqlRowById(tableName, id);
|
||||
else
|
||||
row = clientRowById(tableName, id);
|
||||
|
||||
if (row.RecordId == -1)
|
||||
return std::nullopt;
|
||||
else
|
||||
return row;
|
||||
}
|
||||
|
||||
bool ClientDatabase::testUploadDBCtoDB(BlizzardDatabaseLib::BlizzardDatabaseTable& table)
|
||||
{
|
||||
|
||||
auto& db_mgr = Noggit::Sql::DatabaseManager::instance();
|
||||
bool valid_conn = db_mgr.testConnection(Noggit::Sql::SQLDbType::Noggit);
|
||||
if (!valid_conn)
|
||||
return false;
|
||||
|
||||
auto table_name = table.Name();
|
||||
// Noggit::Project::CurrentProject::get()->projectVersion; // expension, not exact build id
|
||||
unsigned int build_id = Noggit::Project::CurrentProject::get()->buildId();
|
||||
|
||||
// check if table exists
|
||||
QString sql_table_name = getSqlTableName(table_name, build_id).c_str();
|
||||
|
||||
auto noggit_db = db_mgr.noggitDatabase();
|
||||
|
||||
// table integrity check
|
||||
bool table_is_valid = true;
|
||||
bool fresh_table = false;
|
||||
QSqlRecord sql_rec = noggit_db.record(sql_table_name);
|
||||
|
||||
// noggit_db.tables().contains(sql_table_name) is bugged with current qt version and mysql 8
|
||||
QSqlQuery query_show(noggit_db);
|
||||
if (!query_show.exec("SHOW TABLES"))
|
||||
{
|
||||
qWarning() << "Failed to list tables:" << query_show.lastError().text();
|
||||
return false;
|
||||
}
|
||||
QStringList tables;
|
||||
while (query_show.next())
|
||||
{
|
||||
tables << query_show.value(0).toString();
|
||||
}
|
||||
|
||||
if (tables.contains(sql_table_name))
|
||||
{
|
||||
// this is also bugged...
|
||||
// if (sql_rec.isEmpty())
|
||||
// {
|
||||
// table_is_valid = false;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // TODO verify db structure, just column count for now
|
||||
// if (table.ColumnCount() != sql_rec.count())
|
||||
// {
|
||||
// assert(false);
|
||||
// table_is_valid = false;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
else // table doesn't exist
|
||||
{
|
||||
// create table
|
||||
table_is_valid = createSQLTableIfNotExist(table);
|
||||
fresh_table = true;
|
||||
}
|
||||
|
||||
if (!table_is_valid)
|
||||
{
|
||||
qDebug() << "Table " << sql_table_name << "does not exist or has wrong structure.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// insert if fresh_table, otherwise replace?
|
||||
|
||||
// TODOOOOOOOOOOOOOOOOOOOOOOO : fill db with data
|
||||
auto row_definition = table.GetRecordDefinition();
|
||||
auto sql_record_format = recordFormat(table_name);
|
||||
|
||||
auto client_table_iterator = table.Records();
|
||||
|
||||
QStringList column_names;
|
||||
for (auto& sql_column_format : sql_record_format)
|
||||
{
|
||||
column_names.append(sql_column_format.Name.c_str());
|
||||
}
|
||||
|
||||
// INSERT INTO table (col1, col2, ...) VALUES (?, ?, ...)
|
||||
QString sql = QString("INSERT INTO `%1` (%2) VALUES (%3)")
|
||||
.arg(sql_table_name)
|
||||
.arg(column_names.join(", "))
|
||||
.arg(QString("?, ").repeated(column_names.size()).chopped(2));
|
||||
|
||||
QSqlQuery query(noggit_db);
|
||||
if (!query.prepare(sql))
|
||||
{
|
||||
qWarning() << "Prepare failed:" << query.lastError().text();
|
||||
return false;
|
||||
}
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
|
||||
// using transaction to speed up bulk query
|
||||
// noggit_db.transaction();
|
||||
|
||||
// batching:
|
||||
// One QVariantList per column
|
||||
std::vector<QVariantList> columnData(column_names.size());
|
||||
|
||||
while (client_table_iterator.HasRecords())
|
||||
{
|
||||
auto& record = client_table_iterator.Next();
|
||||
int colIndex = 0;
|
||||
|
||||
for (auto& column_def : row_definition.ColumnDefinitions)
|
||||
{
|
||||
if (column_def.Type == "int" && column_def.isID) // id column isn't saved in the map
|
||||
{
|
||||
// query.addBindValue(record.RecordId);
|
||||
columnData[colIndex++].append(record.RecordId); // batching
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& rowColumn = record.Columns.at(column_def.Name);
|
||||
|
||||
if (column_def.Type == "int")
|
||||
{
|
||||
if (column_def.arrLength > 1)
|
||||
{
|
||||
for (int i = 0; i < column_def.arrLength; i++)
|
||||
{
|
||||
// int Value = std::stoi(rowColumn.Values[i]);
|
||||
// query.addBindValue(QString::fromStdString(rowColumn.Values[i]).toInt());
|
||||
columnData[colIndex++].append(QString::fromStdString(rowColumn.Value).toInt());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// int Value = std::stoi(rowColumn.Value);
|
||||
// query.addBindValue(QString::fromStdString(rowColumn.Value).toInt());
|
||||
columnData[colIndex++].append(QString::fromStdString(rowColumn.Value).toInt());
|
||||
}
|
||||
}
|
||||
else if (column_def.Type == "float")
|
||||
{
|
||||
if (column_def.arrLength > 1)
|
||||
{
|
||||
for (int i = 0; i < column_def.arrLength; i++)
|
||||
{
|
||||
// float Value = std::stof(rowColumn.Values[i]);
|
||||
// query.addBindValue(QString::fromStdString(rowColumn.Values[i]).toFloat());
|
||||
columnData[colIndex++].append(QString::fromStdString(rowColumn.Values[i]).toFloat());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// float Value = std::stof(rowColumn.Value);
|
||||
// query.addBindValue(QString::fromStdString(rowColumn.Value).toFloat());
|
||||
columnData[colIndex++].append(QString::fromStdString(rowColumn.Value).toFloat());
|
||||
}
|
||||
}
|
||||
else if (column_def.Type == "string")
|
||||
{
|
||||
if (column_def.arrLength > 1)
|
||||
{
|
||||
for (int i = 0; i < column_def.arrLength; i++)
|
||||
{
|
||||
// std::string value = rowColumn.Values[i];
|
||||
// query.addBindValue(QString::fromStdString(rowColumn.Values[i]));
|
||||
columnData[colIndex++].append(QString::fromStdString(rowColumn.Values[i]));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// std::string Value = rowColumn.Value;
|
||||
// query.addBindValue(QString::fromStdString(rowColumn.Value));
|
||||
columnData[colIndex++].append(QString::fromStdString(rowColumn.Value));
|
||||
}
|
||||
}
|
||||
else if (column_def.Type == "locstring")
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
// query.addBindValue(QString::fromStdString(rowColumn.Values[i]));
|
||||
columnData[colIndex++].append(QString::fromStdString(rowColumn.Values[i]));
|
||||
}
|
||||
|
||||
auto& flagValue = record.Columns.at(column_def.Name + "_flags");
|
||||
// query.addBindValue(QString::fromStdString(flagValue.Value).toInt());
|
||||
columnData[colIndex++].append(QString::fromStdString(flagValue.Value).toInt());
|
||||
}
|
||||
}
|
||||
|
||||
// if (!query.exec())
|
||||
// {
|
||||
// qWarning() << "Insert failed:" << query.lastError().text();
|
||||
// noggit_db.rollback();
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
|
||||
// Bind all columnData at once
|
||||
for (auto& col : columnData)
|
||||
query.addBindValue(col);
|
||||
|
||||
noggit_db.transaction();
|
||||
|
||||
if (!query.execBatch(QSqlQuery::ValuesAsColumns))
|
||||
{
|
||||
qWarning() << "Batch insert failed:" << query.lastError().text();
|
||||
noggit_db.rollback();
|
||||
return false;
|
||||
}
|
||||
|
||||
noggit_db.commit();
|
||||
|
||||
qint64 elapsedMs = timer.elapsed();
|
||||
|
||||
qDebug() << "Inserted" << table.RecordCount() << "rows in"
|
||||
<< elapsedMs << "ms ("
|
||||
<< (table.RecordCount() * 1000.0 / elapsedMs) << " rows/sec)";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// get from local dbc data memory stream in BlizzardDatabaseLib::BlizzardDatabase
|
||||
Structures::BlizzardDatabaseRow ClientDatabase::clientRowById(const std::string& tableName, unsigned int id)
|
||||
{
|
||||
auto& table = Noggit::Project::CurrentProject::get()->ClientDatabase->LoadTable(tableName, readFileAsIMemStream);
|
||||
auto record = table.RecordById(id);
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
// get from SQL request to noggit db
|
||||
// never use this function for more than 1 rows, implement a new bulk function
|
||||
Structures::BlizzardDatabaseRow ClientDatabase::sqlRowById(const std::string& tableName, unsigned int id)
|
||||
{
|
||||
auto& db_mgr = Noggit::Sql::DatabaseManager::instance();
|
||||
// Test connection ?
|
||||
|
||||
auto noggit_db = db_mgr.noggitDatabase();
|
||||
|
||||
auto row_definition = Noggit::Project::CurrentProject::get()->ClientDatabase->TableRecordDefinition(tableName);
|
||||
|
||||
QString sql_table_name = getSqlTableName(tableName).c_str();
|
||||
QSqlQuery query(noggit_db);
|
||||
QString sql = QString("SELECT * FROM %1 WHERE ID = :id").arg(sql_table_name);
|
||||
|
||||
query.prepare(sql);
|
||||
query.bindValue(":id", id);
|
||||
|
||||
if (!query.exec())
|
||||
{
|
||||
qWarning() << "Query exec failed:" << query.lastError().text();
|
||||
return BlizzardDatabaseLib::Structures::BlizzardDatabaseRow();
|
||||
}
|
||||
|
||||
if (query.next())
|
||||
{
|
||||
QSqlRecord record = query.record();
|
||||
|
||||
auto database_row = Structures::BlizzardDatabaseRow(id);
|
||||
|
||||
// test db def/////////////////////////
|
||||
{
|
||||
auto record_db_def = recordFormat(tableName);
|
||||
if (record.count() != record_db_def.size())
|
||||
{
|
||||
// error : definition deosn't match db structure
|
||||
assert(false);
|
||||
return Structures::BlizzardDatabaseRow(-1);
|
||||
}
|
||||
|
||||
// Tests construct row from query
|
||||
// TODOOOOOOOOOOOOOOO
|
||||
|
||||
for (int i = 0; i < record_db_def.size(); ++i)
|
||||
{
|
||||
auto& column_db_def = record_db_def[i];
|
||||
QSqlField db_field = record.field(i);
|
||||
|
||||
assert(column_db_def.Name == record.fieldName(i).toStdString());
|
||||
// assert(column_db_def.Type == db_field.type());
|
||||
}
|
||||
}/////////////////////////////////////////
|
||||
|
||||
int field_idx = 0;
|
||||
for (int column_def_idx = 0; column_def_idx < row_definition.ColumnDefinitions.size(); ++column_def_idx)
|
||||
{
|
||||
auto& column_def = row_definition.ColumnDefinitions[column_def_idx];
|
||||
auto database_column = Structures::BlizzardDatabaseColumn();
|
||||
|
||||
auto value = std::string();
|
||||
if (column_def.Type == "locstring")
|
||||
{
|
||||
std::vector<std::string> localizedValues = std::vector<std::string>();
|
||||
for (int loc_idx = 0; loc_idx < 16; loc_idx++)
|
||||
{
|
||||
localizedValues.push_back(query.value(field_idx++).toString().toStdString());
|
||||
}
|
||||
|
||||
database_column.Values = localizedValues;
|
||||
database_row.Columns[column_def.Name] = database_column;
|
||||
|
||||
// currently loc mask is set to a separate column because wdbc reader does it.
|
||||
auto loc_mask_column = Structures::BlizzardDatabaseColumn();
|
||||
loc_mask_column.Value = query.value(field_idx++).toString().toStdString();
|
||||
database_row.Columns[column_def.Name + "_flags"] = loc_mask_column;
|
||||
}
|
||||
else // every other type than locstring
|
||||
{
|
||||
if (column_def.arrLength > 1) // array
|
||||
{
|
||||
for (int i = 0; i < column_def.arrLength; i++)
|
||||
{
|
||||
auto Value = query.value(field_idx++);
|
||||
database_column.Values.push_back(Value.toString().toStdString());
|
||||
}
|
||||
}
|
||||
else // single value
|
||||
{
|
||||
auto Value = query.value(field_idx++);
|
||||
value = Value.toString().toStdString();
|
||||
}
|
||||
}
|
||||
database_column.Value = value;
|
||||
database_row.Columns[column_def.Name] = database_column;
|
||||
}
|
||||
|
||||
return database_row;
|
||||
}
|
||||
else
|
||||
{
|
||||
qWarning() << "No row found in" << tableName.c_str() << "for ID =" << id;
|
||||
return BlizzardDatabaseLib::Structures::BlizzardDatabaseRow();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ClientDatabase::createSQLTableIfNotExist(const BlizzardDatabaseLib::BlizzardDatabaseTable& table)
|
||||
{
|
||||
const std::string table_name = table.Name();
|
||||
auto row_definition = table.GetRecordDefinition();
|
||||
|
||||
const std::string sql_table_name = getSqlTableName(table_name);
|
||||
std::string statement = std::format("CREATE TABLE IF NOT EXISTS `{}` (", sql_table_name);
|
||||
|
||||
std::string primary_key_name;
|
||||
|
||||
auto db_record_format = recordFormat(table_name);
|
||||
|
||||
assert(db_record_format.size() == table.ColumnCount());
|
||||
|
||||
for (auto& db_column_format : db_record_format)
|
||||
{
|
||||
statement += std::format("`{}` {}", db_column_format.Name, db_column_format.Type);
|
||||
|
||||
if (db_column_format.Type == "TEXT")
|
||||
{
|
||||
statement += " NULL"; // allow NULL by default
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!db_column_format.isSigned && db_column_format.Type == "INT")
|
||||
{
|
||||
// assert(db_column_format.Type == "INT");
|
||||
statement += " UNSIGNED"; // only allow int to be unsigned?
|
||||
}
|
||||
statement += " NOT NULL"; // allow text to be nulled
|
||||
statement += " DEFAULT 0";
|
||||
}
|
||||
|
||||
statement += ",\n";
|
||||
|
||||
if (db_column_format.isID)
|
||||
{
|
||||
assert(primary_key_name.empty()); // more than one key ? TODO
|
||||
primary_key_name = db_column_format.Name;
|
||||
}
|
||||
}
|
||||
|
||||
if (!primary_key_name.empty())
|
||||
statement += std::format("PRIMARY KEY (`{}`)", primary_key_name);
|
||||
|
||||
// Add indexes for relations
|
||||
for (auto& db_column_format : db_record_format)
|
||||
{
|
||||
if (db_column_format.isRelation && !db_column_format.isID) {
|
||||
statement += std::format(",\nINDEX (`{}`)", db_column_format.Name);
|
||||
}
|
||||
}
|
||||
|
||||
// statement += ")\n ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 DEFAULT COLLATE='utf8mb4_general_ci';";
|
||||
statement += ")\n ENGINE = InnoDB;";
|
||||
|
||||
auto& db_mgr = Noggit::Sql::DatabaseManager::instance();
|
||||
bool valid_conn = db_mgr.testConnection(Noggit::Sql::SQLDbType::Noggit);
|
||||
if (!valid_conn)
|
||||
return false;
|
||||
auto noggit_db = db_mgr.noggitDatabase();
|
||||
|
||||
QSqlQuery query(noggit_db);
|
||||
bool success = query.exec(QString::fromStdString(statement));
|
||||
|
||||
if (!success)
|
||||
{
|
||||
qDebug() << "Failed to create table:" << query.lastError().text();
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Table " << table_name.c_str() << " created or already exists.";
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
std::string ClientDatabase::getSqlTableName(const std::string& db_name, unsigned int build_id)
|
||||
{
|
||||
if (build_id == 0)
|
||||
build_id = Noggit::Project::CurrentProject::get()->buildId();
|
||||
|
||||
std::string table = std::format("db_{}_{}", db_name, build_id);
|
||||
|
||||
// convert to lowercase for compatibility with SQL
|
||||
std::transform(table.begin(), table.end(), table.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
std::vector<DbColumnFormat> ClientDatabase::recordFormat(const std::string& table_name)
|
||||
{
|
||||
auto record_format = std::vector<DbColumnFormat>();
|
||||
|
||||
auto row_definition = Noggit::Project::CurrentProject::get()->ClientDatabase->TableRecordDefinition(table_name);
|
||||
|
||||
for (int col_idx = 0; col_idx < row_definition.ColumnDefinitions.size(); col_idx++)
|
||||
{
|
||||
auto& column_def = row_definition.ColumnDefinitions[col_idx];
|
||||
|
||||
bool is_locstring = false;
|
||||
|
||||
// convert dbd definition type names to real format
|
||||
// TODO : map types
|
||||
std::string sql_data_type = "INT";
|
||||
if (BlizzardDatabaseLib::Extension::String::Compare(column_def.Type, "int"))
|
||||
{
|
||||
sql_data_type = "INT";
|
||||
}
|
||||
else if (BlizzardDatabaseLib::Extension::String::Compare(column_def.Type, "float"))
|
||||
{
|
||||
sql_data_type = "FLOAT";
|
||||
}
|
||||
else if (BlizzardDatabaseLib::Extension::String::Compare(column_def.Type, "string"))
|
||||
{
|
||||
sql_data_type = "TEXT";
|
||||
}
|
||||
else if (BlizzardDatabaseLib::Extension::String::Compare(column_def.Type, "locstring"))
|
||||
{
|
||||
sql_data_type = "TEXT";
|
||||
is_locstring = true;
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
|
||||
int array_size = 1;
|
||||
if (column_def.arrLength > 1)
|
||||
{
|
||||
array_size = column_def.arrLength;
|
||||
}
|
||||
if (is_locstring)
|
||||
array_size = 16;
|
||||
|
||||
for (int i = 0; i < array_size; i++)
|
||||
{
|
||||
DbColumnFormat db_col_format;
|
||||
std::string col_name = "";
|
||||
|
||||
if (array_size == 1)
|
||||
{
|
||||
col_name = column_def.Name;
|
||||
}
|
||||
else if (is_locstring)
|
||||
{
|
||||
col_name = std::format("{}_{}", column_def.Name, dbc_string_loc_names[i]); // {MapName_lang}_{enUS}
|
||||
}
|
||||
else if (array_size > 1)
|
||||
{
|
||||
col_name = std::format("{}_{}", column_def.Name, i); // {MapName}_{0}
|
||||
}
|
||||
db_col_format.Name = col_name;
|
||||
db_col_format.Type = sql_data_type;
|
||||
|
||||
assert(!(column_def.isID && array_size > 1));
|
||||
db_col_format.isID = column_def.isID;
|
||||
db_col_format.isRelation = column_def.isRelation;
|
||||
db_col_format.isSigned = column_def.isSigned;
|
||||
|
||||
record_format.push_back(db_col_format);
|
||||
}
|
||||
if (is_locstring) // add lang mask column
|
||||
{
|
||||
DbColumnFormat db_col_format;
|
||||
db_col_format.Name = std::format("{}_flags", column_def.Name);
|
||||
db_col_format.Type = "INT";
|
||||
db_col_format.isSigned = false;
|
||||
db_col_format.isID = false;
|
||||
db_col_format.isRelation = false;
|
||||
record_format.push_back(db_col_format);
|
||||
}
|
||||
}
|
||||
|
||||
return record_format;
|
||||
}
|
||||
|
||||
}
|
||||
65
src/noggit/sql/ClientDatabase.h
Normal file
65
src/noggit/sql/ClientDatabase.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
#include <blizzard-database-library/include/BlizzardDatabase.h>
|
||||
#include <blizzard-database-library/include/structures/FileStructures.h>
|
||||
|
||||
#include <noggit/sql/DatabaseManager.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
|
||||
constexpr const char* dbc_string_loc_names[16] = { "enUS", "koKR", "frFR", "deDE", "zhCN",
|
||||
"zhTW", "esES", "esMX", "ruRU", "jaJP", "ptPT", "itIT",
|
||||
"unk_12", "unk_13", "unk_14", "unk_15" };
|
||||
|
||||
using namespace BlizzardDatabaseLib;
|
||||
|
||||
|
||||
|
||||
namespace Noggit
|
||||
{
|
||||
struct DbColumnFormat
|
||||
{
|
||||
std::string Type = "";
|
||||
std::string Name = "";
|
||||
// int size;
|
||||
bool isID = false;
|
||||
bool isRelation = false;
|
||||
bool isSigned = true;
|
||||
};
|
||||
|
||||
// calls client or server db adaptively. so /sql/ is not really a good location
|
||||
class ClientDatabase
|
||||
{
|
||||
public:
|
||||
// static ClientDatabase& instance()
|
||||
// {
|
||||
// static ClientDatabase _instance;
|
||||
// return _instance;
|
||||
// }
|
||||
|
||||
static std::optional<Structures::BlizzardDatabaseRow> getRowById(const std::string& tableName, unsigned int id); // constructs a row either from db or client
|
||||
|
||||
static bool testUploadDBCtoDB(BlizzardDatabaseLib::BlizzardDatabaseTable& table);
|
||||
|
||||
static void TODODeploySqlToClient();
|
||||
|
||||
private:
|
||||
// ClientDatabase() = default;
|
||||
// ~ClientDatabase() = default;
|
||||
// ClientDatabase(const ClientDatabase&) = delete;
|
||||
// ClientDatabase& operator=(const ClientDatabase&) = delete;
|
||||
|
||||
|
||||
static Structures::BlizzardDatabaseRow clientRowById(const std::string& tableName, unsigned int id);
|
||||
static Structures::BlizzardDatabaseRow sqlRowById(const std::string& tableName, unsigned int id);
|
||||
|
||||
static bool createSQLTableIfNotExist(const BlizzardDatabaseLib::BlizzardDatabaseTable& table);
|
||||
|
||||
static std::string getSqlTableName(const std::string& db_name, unsigned int build_id = 0); // get automatically from project if default(0)
|
||||
|
||||
static std::vector<DbColumnFormat> recordFormat(const std::string& table_name); // true record format for all columns, not array size/loc etc. eg returns all 17 columns for loc.
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
0
src/noggit/sql/DatabaseManager.cpp
Normal file
0
src/noggit/sql/DatabaseManager.cpp
Normal file
236
src/noggit/sql/DatabaseManager.h
Normal file
236
src/noggit/sql/DatabaseManager.h
Normal file
@@ -0,0 +1,236 @@
|
||||
#pragma once
|
||||
#include <QSqlDatabase>
|
||||
#include <QString>
|
||||
#include <QSqlError>
|
||||
#include <QDebug>
|
||||
#include <QSqlQuery>
|
||||
#include <QMap>
|
||||
#include <QStringList>
|
||||
#include <QMessageBox>
|
||||
|
||||
// Connections are currently not thread safe ! would need temporary connection copies per thread.
|
||||
namespace Noggit::Sql
|
||||
{
|
||||
enum class SQLDbType
|
||||
{
|
||||
Noggit,
|
||||
World
|
||||
};
|
||||
|
||||
enum class SqlDriver
|
||||
{
|
||||
MySQL,
|
||||
// SQLite,
|
||||
// PostgreSQL
|
||||
};
|
||||
|
||||
struct DbConfig
|
||||
{
|
||||
QString host;
|
||||
int port = 3306;
|
||||
QString dbName;
|
||||
QString user;
|
||||
QString password;
|
||||
};
|
||||
|
||||
class DatabaseManager
|
||||
{
|
||||
public:
|
||||
static DatabaseManager& instance()
|
||||
{
|
||||
static DatabaseManager _instance;
|
||||
return _instance;
|
||||
}
|
||||
|
||||
bool initializeDb(SQLDbType type,
|
||||
const QString& host, int port,
|
||||
const QString& dbName, const QString& user, const QString& password)
|
||||
{
|
||||
DbConfig newConfig{ host, port, dbName, user, password };
|
||||
|
||||
// If config is unchanged and connection is alive, nothing to do
|
||||
if (configs[type].host == host &&
|
||||
configs[type].port == port &&
|
||||
configs[type].dbName == dbName &&
|
||||
configs[type].user == user &&
|
||||
configs[type].password == password &&
|
||||
testConnection(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
configs[type] = newConfig;
|
||||
|
||||
QString connName = connectionName(type);
|
||||
|
||||
// remove old connection if it exists
|
||||
if (QSqlDatabase::contains(connName))
|
||||
{
|
||||
QSqlDatabase oldDb = QSqlDatabase::database(connName);
|
||||
if (oldDb.isOpen())
|
||||
oldDb.close();
|
||||
QSqlDatabase::removeDatabase(connName);
|
||||
}
|
||||
|
||||
// Create new connection
|
||||
QSqlDatabase db = QSqlDatabase::addDatabase(driverToString(SqlDriver::MySQL), connName);
|
||||
db.setHostName(host);
|
||||
db.setPort(port);
|
||||
// db.setDatabaseName(dbName);
|
||||
db.setUserName(user);
|
||||
db.setPassword(password);
|
||||
|
||||
// connect to my sql without a database
|
||||
if (!db.open())
|
||||
{
|
||||
qWarning() << "Failed to connect to database " << dbName << "on host" << host << ":" << db.lastError().text();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create database if it doesn't exist
|
||||
QSqlQuery query(db);
|
||||
if (!query.exec(QStringLiteral("CREATE DATABASE IF NOT EXISTS `%1`").arg(dbName)))
|
||||
{
|
||||
qWarning() << "Failed to open database " << dbName << ":" << db.lastError().text();
|
||||
return false;
|
||||
}
|
||||
|
||||
// now attempt to connect to db
|
||||
// Need to close connection first because it needs to create the connection with a databasename.
|
||||
db.close();
|
||||
db.setDatabaseName(dbName);
|
||||
if (!db.open())
|
||||
{
|
||||
qDebug() << "Failed to open database:" << db.lastError().text();
|
||||
return false;
|
||||
}
|
||||
qDebug() << db.tables();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// requires database to be initialized
|
||||
bool testConnection(SQLDbType type) const
|
||||
{
|
||||
if (!configs.contains(type))
|
||||
{
|
||||
qWarning() << "No configuration for database type" << static_cast<int>(type);
|
||||
}
|
||||
|
||||
QSqlDatabase db = database(type);
|
||||
if (db.isValid() && db.isOpen())
|
||||
return true;
|
||||
|
||||
// If the connection is invalid or closed, attempt to open it
|
||||
if (!db.open())
|
||||
{
|
||||
qWarning() << "Connection test failed for" << db.connectionName() << ":" << db.lastError().text();
|
||||
qDebug() << db.lastError().nativeErrorCode();
|
||||
qDebug() << db.lastError().databaseText();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Reconnected to database" << db.connectionName();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool testConnectionWithPopup(SQLDbType type, bool report_only_error) const
|
||||
{
|
||||
QMessageBox prompt;
|
||||
prompt.setWindowFlag(Qt::WindowStaysOnTopHint);
|
||||
bool success = false;
|
||||
std::stringstream promptText;
|
||||
try
|
||||
{
|
||||
success = testConnection(type);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
success = false;
|
||||
|
||||
promptText << "\n# ERR: " << e.what();
|
||||
}
|
||||
|
||||
if (success && !report_only_error)
|
||||
{
|
||||
prompt.setIcon(QMessageBox::Information);
|
||||
prompt.setText("Succesfully connected to MySQL database.");
|
||||
prompt.setWindowTitle("Success");
|
||||
|
||||
if (!report_only_error)
|
||||
prompt.exec();
|
||||
}
|
||||
else if (!success)
|
||||
{
|
||||
prompt.setIcon(QMessageBox::Warning);
|
||||
prompt.setText("Failed to load MySQL database, check your settings. \nIf you did not intend to use this feature, disable it in Noggit->settings->MySQL");
|
||||
prompt.setWindowTitle("Noggit Database Error");
|
||||
// disable if connection is not valid
|
||||
// settings.value("project/mysql/enabled") = false;
|
||||
|
||||
promptText << database(type).lastError().text().toStdString() << std::endl;
|
||||
promptText << database(type).lastError().nativeErrorCode().toStdString() << std::endl;
|
||||
promptText << database(type).lastError().databaseText().toStdString() << std::endl;
|
||||
|
||||
prompt.setInformativeText(promptText.str().c_str());
|
||||
prompt.exec();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool isDriverAvailable()
|
||||
{
|
||||
QStringList availableDrivers = QSqlDatabase::drivers();
|
||||
return availableDrivers.contains(driverToString(_sql_driver), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
QSqlDatabase database(SQLDbType type) const
|
||||
{
|
||||
return QSqlDatabase::database(connectionName(type));
|
||||
}
|
||||
|
||||
QSqlDatabase noggitDatabase() const
|
||||
{
|
||||
return DatabaseManager::database(SQLDbType::Noggit);
|
||||
}
|
||||
|
||||
QSqlDatabase worldDatabase() const
|
||||
{
|
||||
return DatabaseManager::database(SQLDbType::World);
|
||||
}
|
||||
|
||||
private:
|
||||
DatabaseManager() = default;
|
||||
~DatabaseManager() = default;
|
||||
DatabaseManager(const DatabaseManager&) = delete;
|
||||
DatabaseManager& operator=(const DatabaseManager&) = delete;
|
||||
|
||||
SqlDriver _sql_driver = SqlDriver::MySQL;
|
||||
|
||||
mutable QMap<SQLDbType, DbConfig> configs; // stores current configuration per DB type
|
||||
|
||||
QString connectionName(SQLDbType type) const
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SQLDbType::Noggit: return "noggit";
|
||||
case SQLDbType::World: return "world";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString driverToString(SqlDriver driver)
|
||||
{
|
||||
switch (driver)
|
||||
{
|
||||
case SqlDriver::MySQL: return "QMYSQL";
|
||||
// case SqlDriver::SQLite: return "QSQLITE";
|
||||
// case SqlDriver::PostgreSQL: return "QPSQL";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
}
|
||||
107
src/noggit/sql/SqlUIDStorage.cpp
Normal file
107
src/noggit/sql/SqlUIDStorage.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
#include "SqlUIDStorage.h"
|
||||
|
||||
bool Noggit::Sql::SqlUIDStorage::hasMaxUIDStoredDB(std::size_t mapID)
|
||||
{
|
||||
auto& db_mgr = DatabaseManager::instance();
|
||||
|
||||
bool valid_conn = db_mgr.testConnection(SQLDbType::Noggit);
|
||||
if (!valid_conn)
|
||||
return false;
|
||||
|
||||
auto noggit_db = db_mgr.noggitDatabase();
|
||||
|
||||
QSqlQuery query(noggit_db);
|
||||
query.prepare(QStringLiteral("SELECT * FROM `UIDs` WHERE `_map_id` = ?"));
|
||||
query.addBindValue(static_cast<int>(mapID));
|
||||
|
||||
if (!query.exec())
|
||||
{
|
||||
qWarning() << "Failed to check UIDs:" << query.lastError().text();
|
||||
return false;
|
||||
}
|
||||
|
||||
return query.next(); // true if at least one row exists
|
||||
}
|
||||
|
||||
std::uint32_t Noggit::Sql::SqlUIDStorage::getGUIDFromDB(std::size_t mapID)
|
||||
{
|
||||
auto& db_mgr = Sql::DatabaseManager::instance();
|
||||
|
||||
bool valid_conn = db_mgr.testConnection(SQLDbType::Noggit);
|
||||
if (!valid_conn)
|
||||
return 0;
|
||||
|
||||
QSqlDatabase noggit_db = db_mgr.noggitDatabase();
|
||||
/////
|
||||
QSqlQuery query(noggit_db);
|
||||
query.prepare(QStringLiteral("SELECT `UID` FROM `UIDs` WHERE `_map_id` = ?"));
|
||||
query.addBindValue(static_cast<int>(mapID));
|
||||
|
||||
if (!query.exec())
|
||||
{
|
||||
qWarning() << "Failed to fetch UID from db:" << query.lastError().text();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (query.next())
|
||||
return query.value(0).toUInt();
|
||||
|
||||
return 0; // no rows
|
||||
|
||||
// Optional, find highest GUID of all maps.
|
||||
// "SELECT `UID` FROM `UIDs`"
|
||||
// std::uint32_t highGUID(0);
|
||||
// if (res->rowsCount() == 0)
|
||||
// {
|
||||
// return 0;
|
||||
// }
|
||||
// while (res->next())
|
||||
// {
|
||||
// highGUID = res->getInt(1);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// return highGUID;
|
||||
}
|
||||
|
||||
void Noggit::Sql::SqlUIDStorage::insertUIDinDB(std::size_t mapID, std::uint32_t NewUID)
|
||||
{
|
||||
auto& db_mgr = Sql::DatabaseManager::instance();
|
||||
bool valid_conn = db_mgr.testConnection(SQLDbType::Noggit);
|
||||
if (!valid_conn)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QSqlDatabase noggit_db = db_mgr.noggitDatabase();
|
||||
|
||||
QSqlQuery query(noggit_db);
|
||||
query.prepare(QStringLiteral("INSERT INTO `UIDs` (`_map_id`, `UID`) VALUES (?, ?)"));
|
||||
query.addBindValue(static_cast<int>(mapID));
|
||||
query.addBindValue(static_cast<int>(NewUID));
|
||||
|
||||
if (!query.exec())
|
||||
{
|
||||
qWarning() << "Failed to insert UID in db: " << query.lastError().text();
|
||||
}
|
||||
}
|
||||
|
||||
void Noggit::Sql::SqlUIDStorage::updateUIDinDB(std::size_t mapID, std::uint32_t NewUID)
|
||||
{
|
||||
auto& db_mgr = Sql::DatabaseManager::instance();
|
||||
|
||||
if (!db_mgr.testConnection(SQLDbType::Noggit))
|
||||
return;
|
||||
|
||||
QSqlDatabase noggit_db = db_mgr.noggitDatabase();
|
||||
|
||||
QSqlQuery query(noggit_db);
|
||||
query.prepare(QStringLiteral("UPDATE `UIDs` SET `UID` = ? WHERE `_map_id` = ?"));
|
||||
query.addBindValue(static_cast<int>(NewUID));
|
||||
query.addBindValue(static_cast<int>(mapID));
|
||||
|
||||
if (!query.exec())
|
||||
{
|
||||
qWarning() << "Failed to update UID:" << query.lastError().text();
|
||||
}
|
||||
}
|
||||
16
src/noggit/sql/SqlUIDStorage.h
Normal file
16
src/noggit/sql/SqlUIDStorage.h
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <noggit/sql/DatabaseManager.h>
|
||||
|
||||
namespace Noggit::Sql
|
||||
{
|
||||
class SqlUIDStorage
|
||||
{
|
||||
public:
|
||||
|
||||
static bool hasMaxUIDStoredDB(std::size_t mapID);
|
||||
static std::uint32_t getGUIDFromDB(std::size_t mapID);
|
||||
static void insertUIDinDB(std::size_t mapID, std::uint32_t NewUID);
|
||||
static void updateUIDinDB(std::size_t mapID, std::uint32_t NewUID);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <noggit/World.h>
|
||||
|
||||
#include <blizzard-database-library/include/BlizzardDatabase.h>
|
||||
#include <noggit/sql/ClientDatabase.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QButtonGroup>
|
||||
@@ -33,6 +34,7 @@
|
||||
#include <QSpinBox>
|
||||
#include <QStackedWidget>
|
||||
#include <QWheelEvent>
|
||||
#include <QtCore/QSettings>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
@@ -629,7 +631,22 @@ void MapCreationWizard::selectMap(int map_id)
|
||||
// int map_id = world->getMapID();
|
||||
|
||||
auto table = _project->ClientDatabase->LoadTable("Map", readFileAsIMemStream);
|
||||
auto record = table.Record(map_id);
|
||||
auto record = table.RecordById(map_id);
|
||||
|
||||
|
||||
/// test area, delete later
|
||||
// auto table = _project->ClientDatabase->GetTable("Map");
|
||||
QSettings settings;
|
||||
|
||||
bool use_mysql = settings.value("project/mysql/enabled", false).toBool();
|
||||
if (use_mysql)
|
||||
{
|
||||
// bool valid_conn = mysql::testConnection(true);
|
||||
Noggit::ClientDatabase::testUploadDBCtoDB(table);
|
||||
|
||||
}
|
||||
///////////////////////////////
|
||||
|
||||
|
||||
_cur_map_id = map_id;
|
||||
|
||||
@@ -762,7 +779,7 @@ void MapCreationWizard::selectMapDifficulty()
|
||||
return;
|
||||
|
||||
auto difficulty_table = _project->ClientDatabase->LoadTable("MapDifficulty", readFileAsIMemStream);
|
||||
auto record = difficulty_table.Record(selected_difficulty_id);
|
||||
auto record = difficulty_table.RecordById(selected_difficulty_id);
|
||||
|
||||
//_difficulty_type;
|
||||
_difficulty_req_message->fill(record, "Message_lang");
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <noggit/ui/windows/settingsPanel/SettingsPanel.h>
|
||||
#include <noggit/uid_storage.hpp>
|
||||
#include <noggit/World.h>
|
||||
#include <noggit/sql/SqlUIDStorage.h>
|
||||
#include <noggit/sql/ClientDatabase.h>
|
||||
|
||||
#include <string>
|
||||
#include <blizzard-archive-library/include/Exception.hpp>
|
||||
@@ -43,16 +45,11 @@
|
||||
#include <QtWidgets/QTabWidget>
|
||||
#include <QtWidgets/QVBoxLayout>
|
||||
#include <QtWidgets/QWidget>
|
||||
#include <QtCore/QSettings>
|
||||
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
#include <QtCore/QSettings>
|
||||
#endif
|
||||
|
||||
#include "revision.h"
|
||||
|
||||
#include "ui_TitleBar.h"
|
||||
@@ -84,18 +81,32 @@ namespace Noggit::Ui::Windows
|
||||
LogError << "NoggitWindow() : Unsupported project version, skipping loading DBCs." << std::endl;
|
||||
}
|
||||
|
||||
_settings = new settings(this);
|
||||
|
||||
QSettings settings;
|
||||
// connect to databases
|
||||
bool use_mysql = settings.value("project/mysql/enabled", false).toBool();
|
||||
if (use_mysql)
|
||||
{
|
||||
auto host = settings.value("project/mysql/server").toString();
|
||||
auto port = settings.value("project/mysql/port", "3306").toInt();
|
||||
auto db_name = settings.value("project/mysql/db").toString(); // schema name
|
||||
auto user = settings.value("project/mysql/user").toString();
|
||||
auto pass = settings.value("project/mysql/pwd").toString();
|
||||
|
||||
auto& db_manager = Sql::DatabaseManager::instance();
|
||||
db_manager.initializeDb(Sql::SQLDbType::Noggit, host, port, db_name, user, pass);
|
||||
}
|
||||
|
||||
setCentralWidget(_null_widget);
|
||||
|
||||
// The default value is AnimatedDocks | AllowTabbedDocks.
|
||||
setDockOptions(AnimatedDocks | AllowNestedDocks | AllowTabbedDocks | GroupedDragging);
|
||||
|
||||
_about = new about(this);
|
||||
_settings = new settings(this);
|
||||
|
||||
_menuBar = menuBar();
|
||||
|
||||
QSettings settings;
|
||||
|
||||
if (!settings.value("systemWindowFrame", true).toBool())
|
||||
{
|
||||
QWidget* widget = new QWidget(this);
|
||||
@@ -159,16 +170,9 @@ namespace Noggit::Ui::Windows
|
||||
|
||||
unsigned int world_map_id = getWorld()->getMapID();
|
||||
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
bool use_mysql = settings.value("project/mysql/enabled", false).toBool();
|
||||
|
||||
bool valid_conn = false;
|
||||
if (use_mysql)
|
||||
{
|
||||
valid_conn = mysql::testConnection(true);
|
||||
}
|
||||
|
||||
if ((valid_conn && mysql::hasMaxUIDStoredDB(world_map_id))
|
||||
if ((Noggit::Sql::SqlUIDStorage::hasMaxUIDStoredDB(world_map_id))
|
||||
|| uid_storage::hasMaxUIDStored(world_map_id)
|
||||
)
|
||||
{
|
||||
@@ -176,7 +180,8 @@ namespace Noggit::Ui::Windows
|
||||
getWorld()->mapIndex.loadMaxUID();
|
||||
enterMapAt(pos, camera_pitch, camera_yaw, uid_fix_mode::none, from_bookmark);
|
||||
}
|
||||
#else
|
||||
// old if no mysql block
|
||||
/*
|
||||
if (uid_storage::hasMaxUIDStored(world_map_id))
|
||||
{
|
||||
if (settings.value("uid_startup_check", true).toBool())
|
||||
@@ -187,8 +192,7 @@ namespace Noggit::Ui::Windows
|
||||
getWorld()->mapIndex.loadMaxUID();
|
||||
enterMapAt(pos, camera_pitch, camera_yaw, uid_fix_mode::none, from_bookmark);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}*/
|
||||
else
|
||||
{
|
||||
auto uid_fix_window(new UidFixWindow(pos, camera_pitch, camera_yaw));
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <noggit/Log.h>
|
||||
#include <noggit/ui/FramelessWindow.hpp>
|
||||
#include <noggit/ui/windows/settingsPanel/SettingsPanel.h>
|
||||
#include <noggit/sql/DatabaseManager.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QtCore/QSettings>
|
||||
@@ -10,13 +11,10 @@
|
||||
#include <QtWidgets/QFileDialog>
|
||||
#include <QtWidgets/QPushButton>
|
||||
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
#include <mysql/mysql.h>
|
||||
#endif
|
||||
|
||||
#include <ui_SettingsPanel.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <QMessageBox>
|
||||
|
||||
|
||||
namespace Noggit
|
||||
@@ -79,11 +77,9 @@ namespace Noggit
|
||||
);
|
||||
|
||||
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
ui->MySQL_box->setEnabled(true);
|
||||
ui->MySQL_box->setCheckable(true);
|
||||
ui->mysql_warning->setVisible(false);
|
||||
#endif
|
||||
|
||||
ui->_theme->addItem("System");
|
||||
|
||||
@@ -170,10 +166,55 @@ namespace Noggit
|
||||
|
||||
connect(ui->mysql_connect_test, &QPushButton::clicked, [this]
|
||||
{
|
||||
save_changes();
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
mysql::testConnection();
|
||||
#endif
|
||||
// test button for noggit db only !
|
||||
|
||||
// save_changes(); // old method saved and only used qsetting
|
||||
|
||||
auto& db_manager = Sql::DatabaseManager::instance();
|
||||
|
||||
if (!db_manager.isDriverAvailable())
|
||||
{
|
||||
qDebug() << "Available drivers:" << QSqlDatabase::drivers();
|
||||
|
||||
// SPECIAL MISSING/currupted driver DLL POPUP
|
||||
QMessageBox prompt;
|
||||
prompt.setWindowFlag(Qt::WindowStaysOnTopHint);
|
||||
prompt.setIcon(QMessageBox::Critical);
|
||||
prompt.setText("mySQL driver could not be loaded.");
|
||||
prompt.setWindowTitle("Noggit Database Error");
|
||||
|
||||
std::stringstream promptText;
|
||||
promptText << "Make sure <Noggit/sqldrivers/qsqlmysql.dll> exists.\n";
|
||||
promptText << "If not, it can be downloaded at https://github.com/thecodemonkey86/qt_mysql_driver/releases \n";
|
||||
promptText << "Noggit Qt version :" << QT_VERSION_STR;
|
||||
|
||||
prompt.setInformativeText("Make sure Noggit/sqldrivers/qsqlmysql.dll exists."
|
||||
"If not, it can be downloaded at ");
|
||||
prompt.exec();
|
||||
return;
|
||||
}
|
||||
|
||||
QString host = ui->_mysql_server_field->text();
|
||||
QString user = ui->_mysql_user_field->text();
|
||||
QString pass = ui->_mysql_pwd_field->text();
|
||||
QString db_name = ui->_mysql_db_field->text();
|
||||
int port = ui->_mysql_port_field->value();
|
||||
|
||||
// test temporary connection from text fields
|
||||
bool test1 = db_manager.initializeDb(Sql::SQLDbType::Noggit, host, port, db_name, user, pass);
|
||||
bool test2 = db_manager.testConnectionWithPopup(Sql::SQLDbType::Noggit, false);
|
||||
|
||||
// reconnect to saved db from settings
|
||||
QSettings settings;
|
||||
host = settings.value("project/mysql/server").toString();
|
||||
port = settings.value("project/mysql/port", "3306").toInt();
|
||||
db_name = settings.value("project/mysql/db").toString(); // schema name
|
||||
user = settings.value("project/mysql/user").toString();
|
||||
pass = settings.value("project/mysql/pwd").toString();
|
||||
|
||||
bool test3 = db_manager.initializeDb(Sql::SQLDbType::Noggit, host, port, db_name, user, pass);
|
||||
assert(db_manager.testConnection(Sql::SQLDbType::Noggit));
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
@@ -234,15 +275,13 @@ namespace Noggit
|
||||
ui->_wmo_aabb->setChecked(_settings->value("render/wmo_aabb", false).toBool());
|
||||
ui->_wmo_group_bounds->setChecked(_settings->value("render/wmo_groups_bounds", false).toBool());
|
||||
|
||||
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
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();
|
||||
auto user_str = _settings->value("project/mysql/user", "root").toString();
|
||||
auto pwd_str = _settings->value("project/mysql/pwd", "root").toString();
|
||||
auto db_str = _settings->value("project/mysql/db", "noggit").toString();
|
||||
auto port_int = _settings->value("project/mysql/port", "3306").toInt();
|
||||
|
||||
// set some default
|
||||
if (server_str.isEmpty())
|
||||
@@ -261,7 +300,6 @@ namespace Noggit
|
||||
ui->_mysql_pwd_field->setText (pwd_str);
|
||||
ui->_mysql_db_field->setText (db_str);
|
||||
ui->_mysql_port_field->setValue (port_int);
|
||||
#endif
|
||||
|
||||
bool wireframe_type = _settings->value("wireframe/type", false).toBool();
|
||||
|
||||
@@ -308,14 +346,12 @@ namespace Noggit
|
||||
_settings->setValue("modern_features", ui->_modern_features->isChecked());
|
||||
_settings->setValue("use_mclq_liquids_export", ui->_use_mclq_liquids_export->isChecked());
|
||||
|
||||
#ifdef USE_MYSQL_UID_STORAGE
|
||||
_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());
|
||||
_settings->setValue("wireframe/radius", ui->_wireframe_radius->value());
|
||||
@@ -338,6 +374,18 @@ namespace Noggit
|
||||
|
||||
_settings->sync();
|
||||
|
||||
// reinitialize db on save
|
||||
auto& db_manager = Sql::DatabaseManager::instance();
|
||||
QString host = _settings->value("project/mysql/server").toString();
|
||||
int port = _settings->value("project/mysql/port", "3306").toInt();
|
||||
QString db_name = _settings->value("project/mysql/db").toString();
|
||||
QString user = _settings->value("project/mysql/user").toString();
|
||||
QString pass = _settings->value("project/mysql/pwd").toString();
|
||||
|
||||
bool test1 = db_manager.initializeDb(Sql::SQLDbType::Noggit, host, port, db_name, user, pass);
|
||||
bool test2 = db_manager.testConnectionWithPopup(Sql::SQLDbType::Noggit, true);
|
||||
|
||||
|
||||
// calls MapView::onSettingsSave()
|
||||
emit saved();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user