From 7bbe5ffc39529097c4169b94ed92ec688de18dc3 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Fri, 17 Nov 2023 18:43:01 -0600 Subject: [PATCH 1/3] Fix falling from Spider Cave not smashing you (#1302) --- dGame/dGameMessages/GameMessages.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 82a7ee77..33717059 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -4170,6 +4170,13 @@ void GameMessages::HandleRequestDie(RakNet::BitStream* inStream, Entity* entity, racingControlComponent->OnRequestDie(entity); } + else { + auto* destroyableComponent = entity->GetComponent(); + + if (!destroyableComponent) return; + + destroyableComponent->Smash(killerID, killType, deathType); + } } From b68823b4cb25a845986c6d8940d64e4c7b01dcca Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Fri, 17 Nov 2023 18:44:48 -0600 Subject: [PATCH 2/3] fix: properly check friend list limits (#1300) * fix: properly check friend list limits added a config for friend list limit for the brave that want to mod the client to sanely go over 50 moved the best friend limit config to chatconfig.ini where it should be cleanup loading these configs options a bit Tested that the BFF limit works and that the new friend limit works as well * fix typo * fix member variable naming --- dChatServer/ChatPacketHandler.cpp | 21 +++++++++++++-------- dChatServer/PlayerContainer.cpp | 18 ++++++++++-------- dChatServer/PlayerContainer.h | 18 +++++++++++------- resources/chatconfig.ini | 9 +++++++++ resources/worldconfig.ini | 4 ---- 5 files changed, 43 insertions(+), 27 deletions(-) diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index 72383c38..f9ab18c9 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -96,9 +96,6 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { } void ChatPacketHandler::HandleFriendRequest(Packet* packet) { - auto maxNumberOfBestFriendsAsString = Game::config->GetValue("max_number_of_best_friends"); - // If this config option doesn't exist, default to 5 which is what live used. - auto maxNumberOfBestFriends = maxNumberOfBestFriendsAsString != "" ? std::stoi(maxNumberOfBestFriendsAsString) : 5U; CINSTREAM_SKIP_HEADER; LWOOBJID requestorPlayerID; inStream.Read(requestorPlayerID); @@ -204,11 +201,12 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { // Only do updates if there was a change in the bff status. if (oldBestFriendStatus != bestFriendStatus) { - if (requestee->countOfBestFriends >= maxNumberOfBestFriends || requestor->countOfBestFriends >= maxNumberOfBestFriends) { - if (requestee->countOfBestFriends >= maxNumberOfBestFriends) { + auto maxBestFriends = playerContainer.GetMaxNumberOfBestFriends(); + if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) { + if (requestee->countOfBestFriends >= maxBestFriends) { SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false); } - if (requestor->countOfBestFriends >= maxNumberOfBestFriends) { + if (requestor->countOfBestFriends >= maxBestFriends) { SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false); } } else { @@ -242,8 +240,15 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true); } } else { - // Do not send this if we are requesting to be a best friend. - SendFriendRequest(requestee.get(), requestor); + auto maxFriends = playerContainer.GetMaxNumberOfFriends(); + if (requestee->friends.size() >= maxFriends) { + SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false); + } else if (requestor->friends.size() >= maxFriends) { + SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false); + } else { + // Do not send this if we are requesting to be a best friend. + SendFriendRequest(requestee.get(), requestor); + } } // If the player is actually a player and not a ghost one defined above, release it from being deleted. diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index f467cfbe..bb14a096 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -14,10 +14,12 @@ #include "dConfig.h" PlayerContainer::PlayerContainer() { + GeneralUtils::TryParse(Game::config->GetValue("max_number_of_best_friends"), m_MaxNumberOfBestFriends); + GeneralUtils::TryParse(Game::config->GetValue("max_number_of_friends"), m_MaxNumberOfFriends); } PlayerContainer::~PlayerContainer() { - mPlayers.clear(); + m_Players.clear(); } TeamData::TeamData() { @@ -41,9 +43,9 @@ void PlayerContainer::InsertPlayer(Packet* packet) { inStream.Read(data->muteExpire); data->sysAddr = packet->systemAddress; - mNames[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName); + m_Names[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName); - mPlayers.insert(std::make_pair(data->playerID, data)); + m_Players.insert(std::make_pair(data->playerID, data)); LOG("Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID()); auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);"); @@ -88,7 +90,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) { } LOG("Removed user: %llu", playerID); - mPlayers.erase(playerID); + m_Players.erase(playerID); auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);"); @@ -191,7 +193,7 @@ TeamData* PlayerContainer::CreateLocalTeam(std::vector members) { TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) { auto* team = new TeamData(); - team->teamID = ++mTeamIDCounter; + team->teamID = ++m_TeamIDCounter; team->leaderID = leader; team->local = local; @@ -376,15 +378,15 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { } std::u16string PlayerContainer::GetName(LWOOBJID playerID) { - const auto& pair = mNames.find(playerID); + const auto& pair = m_Names.find(playerID); - if (pair == mNames.end()) return u""; + if (pair == m_Names.end()) return u""; return pair->second; } LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) { - for (const auto& pair : mNames) { + for (const auto& pair : m_Names) { if (pair.second == playerName) { return pair.first; } diff --git a/dChatServer/PlayerContainer.h b/dChatServer/PlayerContainer.h index a0a40058..000164ac 100644 --- a/dChatServer/PlayerContainer.h +++ b/dChatServer/PlayerContainer.h @@ -39,13 +39,13 @@ public: void BroadcastMuteUpdate(LWOOBJID player, time_t time); PlayerData* GetPlayerData(const LWOOBJID& playerID) { - auto it = mPlayers.find(playerID); - if (it != mPlayers.end()) return it->second; + auto it = m_Players.find(playerID); + if (it != m_Players.end()) return it->second; return nullptr; } PlayerData* GetPlayerData(const std::string& playerName) { - for (auto player : mPlayers) { + for (auto player : m_Players) { if (player.second) { std::string pn = player.second->playerName.c_str(); if (pn == playerName) return player.second; @@ -67,13 +67,17 @@ public: std::u16string GetName(LWOOBJID playerID); LWOOBJID GetId(const std::u16string& playerName); bool GetIsMuted(PlayerData* data); + uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; } + uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; } - std::map& GetAllPlayerData() { return mPlayers; } + std::map& GetAllPlayerData() { return m_Players; } private: - LWOOBJID mTeamIDCounter = 0; - std::map mPlayers; + LWOOBJID m_TeamIDCounter = 0; + std::map m_Players; std::vector mTeams; - std::unordered_map mNames; + std::unordered_map m_Names; + uint32_t m_MaxNumberOfBestFriends = 5; + uint32_t m_MaxNumberOfFriends = 50; }; diff --git a/resources/chatconfig.ini b/resources/chatconfig.ini index 26b26cc7..1bb1d1b2 100644 --- a/resources/chatconfig.ini +++ b/resources/chatconfig.ini @@ -1,2 +1,11 @@ # Port number port=2005 + +# If you would like to increase the maximum number of best friends a player can have on the server +# Change the value below to what you would like this to be (5 is live accurate) +max_number_of_best_friends=5 + +# If you would like to increase the maximum number of friends a player can have on the server +# Change the value below to what you would like this to be (50 is live accurate) +# going over 50 will be allowed in some secnarios, but proper handling will require client modding +max_number_of_friends=50 diff --git a/resources/worldconfig.ini b/resources/worldconfig.ini index 0072fc51..0b15973d 100644 --- a/resources/worldconfig.ini +++ b/resources/worldconfig.ini @@ -40,10 +40,6 @@ classic_survival_scoring=0 # If this value is 1, pets will consume imagination as they did in live. if 0 they will not consume imagination at all. pets_take_imagination=1 -# If you would like to increase the maximum number of best friends a player can have on the server -# Change the value below to what you would like this to be (5 is live accurate) -max_number_of_best_friends=5 - # Disables loot drops disable_drops=0 From 7f623d358c14b38e580035105d4d71623c019091 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Fri, 17 Nov 2023 16:47:18 -0800 Subject: [PATCH 3/3] refactor: Database abstraction and organization of files (#1274) * Database: Convert to proper namespace * Database: Use base class and getter * Database: Move files around * Database: Add property Management query Database: Move over user queries Tested at gm 0 that pre-approved names are pre-approved, unapproved need moderator approval deleting characters deletes the selcted one refreshing the character page shows the last character you logged in as tested all my characters show up when i login tested that you can delete all 4 characters and the correct character is selected each time tested renaming, approving names as gm0 Database: Add ugc model getter Hey it works, look I got around the mariadb issue. Database: Add queries Database: consolidate name query Database: Add friends list query Update name of approved names query Documentation Database: Add name check Database: Add BFF Query Database: Move BFF Setter Database: Move new friend query Database: Add remove friend queries Database: Add activity log Database: Add ugc & prop content removal Database: Add model update Database: Add migration queries Database: Add character and xml queries Database: Add user queries Untested, but compiling code Need to test that new character names are properly assigned in the following scenarios gm 0 and pre-approved name gm 0 and unapproved name gm 9 and pre-approved name gm 9 and unapproved name Database: constify function arguments Database: Add pet queries * Database: Move property model queries Untested. Need to test placing a new model moving existing one removing ugc model placing ugc model moving ugc model(?) changing privacy option variously change description and name approve property can properly travel to property * Property: Move stale reference deletion * Database: Move performance update query * Database: Add bug report query * Database: Add cheat detection query * Database: Add mail send query * Untested code need to test mailing from slash command, from all users of SendMail, getting bbb of a property and sending messages to bffs * Update CDComponentsRegistryTable.h Database: Rename and add further comments Datavbase: Add comments Add some comments Build: Fix PCH directories Database: Fix time thanks apple Database: Fix compiler warnings Overload destructor Define specialty for time_t Use string instead of string_view for temp empty string Update CDTable.h Property: Update queries to use mapId Database: Reorganize Reorganize into CDClient folder and GameDatabase folder for clearer meanings and file structure Folders: Rename to GameDatabase MySQL: Remove MySQL Specifier from table Database: Move Tables to Interfaces Database: Reorder functions in header Database: Simplify property queries Database: Remove unused queries Remove extra query definitions as well Database: Consolidate User getters Database: Comment logs Update MySQLDatabase.cpp Database: Use generic code Playkey: Fix bad optional access Database: Move stuff around WorldServer: Update queries Ugc reduced by many scopes use new queries very fast tested that ugc still loads Database: Add auth queries I tested that only the correct password can sign into an account. Tested that disabled playkeys do not allow the user to play the game Database: Add donation query Database: add objectId queries Database: Add master queries Database: Fix mis-named function Database: Add slash command queries Mail: Fix itemId type CharFilter: Use new query ObjectID: Remove duplicate code SlashCommand: Update query with function Database: Add mail queries Ugc: Fix issues with saving models Resolve large scope blocks as well * Database: Add debug try catch rethrow macro * General fixes * fix play key not working * Further fixes --------- Co-authored-by: Aaron Kimbre --- CMakeLists.txt | 12 +- dAuthServer/AuthServer.cpp | 33 +-- dChatFilter/dChatFilter.cpp | 12 +- dChatServer/ChatPacketHandler.cpp | 90 ++---- dChatServer/ChatServer.cpp | 33 +-- dChatServer/PlayerContainer.cpp | 18 +- dCommon/BrickByBrickFix.cpp | 86 ++---- .../CDClientDatabase.cpp | 0 .../{ => CDClientDatabase}/CDClientDatabase.h | 0 .../CDClientManager.cpp | 0 .../{ => CDClientDatabase}/CDClientManager.h | 0 .../CDClientTables}/CDActivitiesTable.cpp | 0 .../CDClientTables}/CDActivitiesTable.h | 0 .../CDActivityRewardsTable.cpp | 0 .../CDClientTables}/CDActivityRewardsTable.h | 0 .../CDClientTables}/CDAnimationsTable.cpp | 0 .../CDClientTables}/CDAnimationsTable.h | 0 .../CDBehaviorParameterTable.cpp | 0 .../CDBehaviorParameterTable.h | 0 .../CDBehaviorTemplateTable.cpp | 0 .../CDClientTables}/CDBehaviorTemplateTable.h | 0 .../CDClientTables}/CDBrickIDTableTable.cpp | 0 .../CDClientTables}/CDBrickIDTableTable.h | 0 .../CDComponentsRegistryTable.cpp | 0 .../CDComponentsRegistryTable.h | 2 + .../CDClientTables}/CDCurrencyTableTable.cpp | 0 .../CDClientTables}/CDCurrencyTableTable.h | 0 .../CDDestructibleComponentTable.cpp | 0 .../CDDestructibleComponentTable.h | 0 .../CDClientTables}/CDEmoteTable.cpp | 0 .../CDClientTables}/CDEmoteTable.h | 0 .../CDClientTables}/CDFeatureGatingTable.cpp | 0 .../CDClientTables}/CDFeatureGatingTable.h | 0 .../CDInventoryComponentTable.cpp | 0 .../CDInventoryComponentTable.h | 0 .../CDClientTables}/CDItemComponentTable.cpp | 0 .../CDClientTables}/CDItemComponentTable.h | 0 .../CDClientTables}/CDItemSetSkillsTable.cpp | 0 .../CDClientTables}/CDItemSetSkillsTable.h | 0 .../CDClientTables}/CDItemSetsTable.cpp | 0 .../CDClientTables}/CDItemSetsTable.h | 0 .../CDLevelProgressionLookupTable.cpp | 0 .../CDLevelProgressionLookupTable.h | 0 .../CDClientTables}/CDLootMatrixTable.cpp | 0 .../CDClientTables}/CDLootMatrixTable.h | 0 .../CDClientTables}/CDLootTableTable.cpp | 0 .../CDClientTables}/CDLootTableTable.h | 0 .../CDClientTables}/CDMissionEmailTable.cpp | 0 .../CDClientTables}/CDMissionEmailTable.h | 0 .../CDMissionNPCComponentTable.cpp | 0 .../CDMissionNPCComponentTable.h | 0 .../CDClientTables}/CDMissionTasksTable.cpp | 0 .../CDClientTables}/CDMissionTasksTable.h | 0 .../CDClientTables}/CDMissionsTable.cpp | 0 .../CDClientTables}/CDMissionsTable.h | 0 .../CDMovementAIComponentTable.cpp | 0 .../CDMovementAIComponentTable.h | 0 .../CDClientTables}/CDObjectSkillsTable.cpp | 0 .../CDClientTables}/CDObjectSkillsTable.h | 0 .../CDClientTables}/CDObjectsTable.cpp | 0 .../CDClientTables}/CDObjectsTable.h | 0 .../CDPackageComponentTable.cpp | 0 .../CDClientTables}/CDPackageComponentTable.h | 0 .../CDPhysicsComponentTable.cpp | 0 .../CDClientTables}/CDPhysicsComponentTable.h | 0 .../CDPropertyEntranceComponentTable.cpp | 0 .../CDPropertyEntranceComponentTable.h | 0 .../CDPropertyTemplateTable.cpp | 0 .../CDClientTables}/CDPropertyTemplateTable.h | 0 .../CDProximityMonitorComponentTable.cpp | 0 .../CDProximityMonitorComponentTable.h | 0 .../CDRailActivatorComponent.cpp | 0 .../CDRailActivatorComponent.h | 0 .../CDClientTables}/CDRarityTableTable.cpp | 0 .../CDClientTables}/CDRarityTableTable.h | 0 .../CDRebuildComponentTable.cpp | 0 .../CDClientTables}/CDRebuildComponentTable.h | 0 .../CDClientTables}/CDRewardsTable.cpp | 0 .../CDClientTables}/CDRewardsTable.h | 0 .../CDScriptComponentTable.cpp | 0 .../CDClientTables}/CDScriptComponentTable.h | 0 .../CDClientTables}/CDSkillBehaviorTable.cpp | 0 .../CDClientTables}/CDSkillBehaviorTable.h | 0 .../CDClientTables}/CDTable.h | 0 .../CDVendorComponentTable.cpp | 0 .../CDClientTables}/CDVendorComponentTable.h | 0 .../CDClientTables}/CDZoneTableTable.cpp | 0 .../CDClientTables}/CDZoneTableTable.h | 0 .../CDClientTables}/CMakeLists.txt | 2 +- dDatabase/CDClientDatabase/CMakeLists.txt | 12 + dDatabase/CMakeLists.txt | 17 +- dDatabase/Database.cpp | 118 -------- dDatabase/Database.h | 31 -- dDatabase/GameDatabase/CMakeLists.txt | 12 + dDatabase/GameDatabase/Database.cpp | 40 +++ dDatabase/GameDatabase/Database.h | 12 + dDatabase/GameDatabase/GameDatabase.h | 55 ++++ dDatabase/GameDatabase/ITables/IAccounts.h | 37 +++ dDatabase/GameDatabase/ITables/IActivityLog.h | 19 ++ dDatabase/GameDatabase/ITables/IBugReports.h | 20 ++ dDatabase/GameDatabase/ITables/ICharInfo.h | 49 ++++ dDatabase/GameDatabase/ITables/ICharXml.h | 20 ++ dDatabase/GameDatabase/ITables/ICommandLog.h | 14 + dDatabase/GameDatabase/ITables/IFriends.h | 32 ++ dDatabase/GameDatabase/ITables/ILeaderboard.h | 14 + dDatabase/GameDatabase/ITables/IMail.h | 54 ++++ .../GameDatabase/ITables/IMigrationHistory.h | 17 ++ .../GameDatabase/ITables/IObjectIdTracker.h | 19 ++ dDatabase/GameDatabase/ITables/IPetNames.h | 21 ++ dDatabase/GameDatabase/ITables/IPlayKeys.h | 15 + .../ITables/IPlayerCheatDetections.h | 20 ++ dDatabase/GameDatabase/ITables/IProperty.h | 38 +++ .../GameDatabase/ITables/IPropertyContents.h | 40 +++ dDatabase/GameDatabase/ITables/IServers.h | 21 ++ dDatabase/GameDatabase/ITables/IUgc.h | 32 ++ .../{ => GameDatabase}/MigrationRunner.cpp | 45 +-- .../{ => GameDatabase}/MigrationRunner.h | 0 dDatabase/GameDatabase/MySQL/CMakeLists.txt | 11 + .../GameDatabase/MySQL/MySQLDatabase.cpp | 113 +++++++ dDatabase/GameDatabase/MySQL/MySQLDatabase.h | 245 ++++++++++++++++ .../GameDatabase/MySQL/Tables/Accounts.cpp | 37 +++ .../GameDatabase/MySQL/Tables/ActivityLog.cpp | 6 + .../GameDatabase/MySQL/Tables/BugReports.cpp | 6 + .../GameDatabase/MySQL/Tables/CMakeLists.txt | 22 ++ .../GameDatabase/MySQL/Tables/CharInfo.cpp | 78 +++++ .../GameDatabase/MySQL/Tables/CharXml.cpp | 19 ++ .../GameDatabase/MySQL/Tables/CommandLog.cpp | 5 + .../GameDatabase/MySQL/Tables/Friends.cpp | 73 +++++ .../GameDatabase/MySQL/Tables/Leaderboard.cpp | 11 + dDatabase/GameDatabase/MySQL/Tables/Mail.cpp | 83 ++++++ .../MySQL/Tables/MigrationHistory.cpp | 13 + .../MySQL/Tables/ObjectIdTracker.cpp | 17 ++ .../GameDatabase/MySQL/Tables/PetNames.cpp | 26 ++ .../GameDatabase/MySQL/Tables/PlayKeys.cpp | 11 + .../MySQL/Tables/PlayerCheatDetections.cpp | 7 + .../GameDatabase/MySQL/Tables/Property.cpp | 57 ++++ .../MySQL/Tables/PropertyContents.cpp | 54 ++++ .../GameDatabase/MySQL/Tables/Servers.cpp | 23 ++ dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp | 71 +++++ dGame/Character.cpp | 111 ++----- dGame/Character.h | 1 + dGame/LeaderboardManager.cpp | 10 +- dGame/User.cpp | 45 +-- dGame/UserManager.cpp | 180 ++---------- dGame/UserManager.h | 1 - dGame/dComponents/CharacterComponent.cpp | 2 +- dGame/dComponents/DonationVendorComponent.cpp | 6 +- dGame/dComponents/PetComponent.cpp | 29 +- .../dComponents/PropertyEntranceComponent.cpp | 14 +- .../PropertyManagementComponent.cpp | 277 +++++------------- dGame/dGameMessages/GameMessageHandler.h | 2 +- dGame/dGameMessages/GameMessages.cpp | 129 ++------ dGame/dUtilities/CheatDetection.cpp | 25 +- dGame/dUtilities/Mail.cpp | 173 ++++------- dGame/dUtilities/SlashCommandHandler.cpp | 103 ++----- dMasterServer/MasterServer.cpp | 60 +--- dMasterServer/ObjectIDManager.cpp | 49 +--- dMasterServer/ObjectIDManager.h | 2 +- dNet/AuthPackets.cpp | 95 +----- dNet/ClientPackets.cpp | 27 +- dWorldServer/WorldServer.cpp | 189 ++++-------- 161 files changed, 2114 insertions(+), 1516 deletions(-) rename dDatabase/{ => CDClientDatabase}/CDClientDatabase.cpp (100%) rename dDatabase/{ => CDClientDatabase}/CDClientDatabase.h (100%) rename dDatabase/{ => CDClientDatabase}/CDClientManager.cpp (100%) rename dDatabase/{ => CDClientDatabase}/CDClientManager.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDActivitiesTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDActivitiesTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDActivityRewardsTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDActivityRewardsTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDAnimationsTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDAnimationsTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDBehaviorParameterTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDBehaviorParameterTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDBehaviorTemplateTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDBehaviorTemplateTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDBrickIDTableTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDBrickIDTableTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDComponentsRegistryTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDComponentsRegistryTable.h (96%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDCurrencyTableTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDCurrencyTableTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDDestructibleComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDDestructibleComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDEmoteTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDEmoteTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDFeatureGatingTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDFeatureGatingTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDInventoryComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDInventoryComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDItemComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDItemComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDItemSetSkillsTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDItemSetSkillsTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDItemSetsTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDItemSetsTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDLevelProgressionLookupTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDLevelProgressionLookupTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDLootMatrixTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDLootMatrixTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDLootTableTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDLootTableTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMissionEmailTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMissionEmailTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMissionNPCComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMissionNPCComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMissionTasksTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMissionTasksTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMissionsTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMissionsTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMovementAIComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDMovementAIComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDObjectSkillsTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDObjectSkillsTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDObjectsTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDObjectsTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDPackageComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDPackageComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDPhysicsComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDPhysicsComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDPropertyEntranceComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDPropertyEntranceComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDPropertyTemplateTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDPropertyTemplateTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDProximityMonitorComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDProximityMonitorComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDRailActivatorComponent.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDRailActivatorComponent.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDRarityTableTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDRarityTableTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDRebuildComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDRebuildComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDRewardsTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDRewardsTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDScriptComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDScriptComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDSkillBehaviorTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDSkillBehaviorTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDVendorComponentTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDVendorComponentTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDZoneTableTable.cpp (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CDZoneTableTable.h (100%) rename dDatabase/{Tables => CDClientDatabase/CDClientTables}/CMakeLists.txt (93%) create mode 100644 dDatabase/CDClientDatabase/CMakeLists.txt delete mode 100644 dDatabase/Database.cpp delete mode 100644 dDatabase/Database.h create mode 100644 dDatabase/GameDatabase/CMakeLists.txt create mode 100644 dDatabase/GameDatabase/Database.cpp create mode 100644 dDatabase/GameDatabase/Database.h create mode 100644 dDatabase/GameDatabase/GameDatabase.h create mode 100644 dDatabase/GameDatabase/ITables/IAccounts.h create mode 100644 dDatabase/GameDatabase/ITables/IActivityLog.h create mode 100644 dDatabase/GameDatabase/ITables/IBugReports.h create mode 100644 dDatabase/GameDatabase/ITables/ICharInfo.h create mode 100644 dDatabase/GameDatabase/ITables/ICharXml.h create mode 100644 dDatabase/GameDatabase/ITables/ICommandLog.h create mode 100644 dDatabase/GameDatabase/ITables/IFriends.h create mode 100644 dDatabase/GameDatabase/ITables/ILeaderboard.h create mode 100644 dDatabase/GameDatabase/ITables/IMail.h create mode 100644 dDatabase/GameDatabase/ITables/IMigrationHistory.h create mode 100644 dDatabase/GameDatabase/ITables/IObjectIdTracker.h create mode 100644 dDatabase/GameDatabase/ITables/IPetNames.h create mode 100644 dDatabase/GameDatabase/ITables/IPlayKeys.h create mode 100644 dDatabase/GameDatabase/ITables/IPlayerCheatDetections.h create mode 100644 dDatabase/GameDatabase/ITables/IProperty.h create mode 100644 dDatabase/GameDatabase/ITables/IPropertyContents.h create mode 100644 dDatabase/GameDatabase/ITables/IServers.h create mode 100644 dDatabase/GameDatabase/ITables/IUgc.h rename dDatabase/{ => GameDatabase}/MigrationRunner.cpp (73%) rename dDatabase/{ => GameDatabase}/MigrationRunner.h (100%) create mode 100644 dDatabase/GameDatabase/MySQL/CMakeLists.txt create mode 100644 dDatabase/GameDatabase/MySQL/MySQLDatabase.cpp create mode 100644 dDatabase/GameDatabase/MySQL/MySQLDatabase.h create mode 100644 dDatabase/GameDatabase/MySQL/Tables/Accounts.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/ActivityLog.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/BugReports.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt create mode 100644 dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/CharXml.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/CommandLog.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/Friends.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/Leaderboard.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/Mail.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/MigrationHistory.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/ObjectIdTracker.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/PetNames.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/PlayKeys.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/PlayerCheatDetections.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/Property.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/Servers.cpp create mode 100644 dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f6d240a..635f5f44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,7 +214,12 @@ set(INCLUDED_DIRECTORIES "dNavigation/dTerrain" "dZoneManager" "dDatabase" - "dDatabase/Tables" + "dDatabase/CDClientDatabase" + "dDatabase/CDClientDatabase/CDClientTables" + "dDatabase/GameDatabase" + "dDatabase/GameDatabase/ITables" + "dDatabase/GameDatabase/MySQL" + "dDatabase/GameDatabase/MySQL/Tables" "dNet" "dScripts" "dScripts/02_server" @@ -329,8 +334,9 @@ add_subdirectory(thirdparty) file( GLOB HEADERS_DDATABASE LIST_DIRECTORIES false - ${PROJECT_SOURCE_DIR}/dDatabase/*.h - ${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h + ${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/*.h + ${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables/*.h + ${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables/*.h ${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h ) diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index a18657ca..c6d434b3 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -55,14 +55,8 @@ int main(int argc, char** argv) { LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); LOG("Compiled on: %s", __TIMESTAMP__); - //Connect to the MySQL Database - std::string mysql_host = Game::config->GetValue("mysql_host"); - std::string mysql_database = Game::config->GetValue("mysql_database"); - std::string mysql_username = Game::config->GetValue("mysql_username"); - std::string mysql_password = Game::config->GetValue("mysql_password"); - try { - Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); + Database::Connect(); } catch (sql::SQLException& ex) { LOG("Got an error while connecting to the database: %s", ex.what()); Database::Destroy("AuthServer"); @@ -74,15 +68,12 @@ int main(int argc, char** argv) { //Find out the master's IP: std::string masterIP; uint32_t masterPort = 1500; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); - } - delete res; - delete stmt; + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; + } Game::randomEngine = std::mt19937(time(0)); @@ -134,16 +125,12 @@ int main(int argc, char** argv) { //Find out the master's IP for absolutely no reason: std::string masterIP; uint32_t masterPort; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; } - delete res; - delete stmt; - framesSinceLastSQLPing = 0; } else framesSinceLastSQLPing++; diff --git a/dChatFilter/dChatFilter.cpp b/dChatFilter/dChatFilter.cpp index 8d2015b0..6e81db3b 100644 --- a/dChatFilter/dChatFilter.cpp +++ b/dChatFilter/dChatFilter.cpp @@ -32,15 +32,11 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) { } //Read player names that are ok as well: - auto stmt = Database::CreatePreppedStmt("select name from charinfo;"); - auto res = stmt->executeQuery(); - while (res->next()) { - std::string line = res->getString(1).c_str(); - std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase - m_ApprovedWords.push_back(CalculateHash(line)); + auto approvedNames = Database::Get()->GetApprovedCharacterNames(); + for (auto& name : approvedNames) { + std::transform(name.begin(), name.end(), name.begin(), ::tolower); //Transform to lowercase + m_ApprovedWords.push_back(CalculateHash(name)); } - delete res; - delete stmt; } dChatFilter::~dChatFilter() { diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index f9ab18c9..0a3caa8e 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -30,32 +30,17 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { auto player = playerContainer.GetPlayerData(playerID); if (!player) return; - //Get our friends list from the Db. Using a derived table since the friend of a player can be in either column. - std::unique_ptr stmt(Database::CreatePreppedStmt( - "SELECT fr.requested_player, best_friend, ci.name FROM " - "(SELECT CASE " - "WHEN player_id = ? THEN friend_id " - "WHEN friend_id = ? THEN player_id " - "END AS requested_player, best_friend FROM friends) AS fr " - "JOIN charinfo AS ci ON ci.id = fr.requested_player " - "WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ?;")); - stmt->setUInt(1, static_cast(playerID)); - stmt->setUInt(2, static_cast(playerID)); - stmt->setUInt(3, static_cast(playerID)); - - std::vector friends; - - std::unique_ptr res(stmt->executeQuery()); - while (res->next()) { + auto friendsList = Database::Get()->GetFriendsList(playerID); + for (const auto& friendData : friendsList) { FriendData fd; fd.isFTP = false; // not a thing in DLU - fd.friendID = res->getUInt(1); + fd.friendID = friendData.friendID; GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT); GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER); - fd.isBestFriend = res->getInt(2) == 3; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs + fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs if (fd.isBestFriend) player->countOfBestFriends += 1; - fd.friendName = res->getString(3); + fd.friendName = friendData.friendName; //Now check if they're online: auto fr = playerContainer.GetPlayerData(fd.friendID); @@ -71,7 +56,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { fd.zoneID = LWOZONEID(); } - friends.push_back(fd); + player->friends.push_back(fd); } //Now, we need to send the friendlist to the server they came from: @@ -83,14 +68,12 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE); bitStream.Write(0); bitStream.Write(1); //Length of packet -- just writing one as it doesn't matter, client skips it. - bitStream.Write((uint16_t)friends.size()); + bitStream.Write((uint16_t)player->friends.size()); - for (auto& data : friends) { + for (auto& data : player->friends) { data.Serialize(bitStream); } - player->friends = friends; - SystemAddress sysAddr = player->sysAddr; SEND_PACKET; } @@ -152,35 +135,26 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { // If at this point we dont have a target, then they arent online and we cant send the request. // Send the response code that corresponds to what the error is. if (!requestee) { - std::unique_ptr nameQuery(Database::CreatePreppedStmt("SELECT name from charinfo where name = ?;")); - nameQuery->setString(1, playerName); - std::unique_ptr result(nameQuery->executeQuery()); - requestee.reset(new PlayerData()); requestee->playerName = playerName; + auto responseType = Database::Get()->GetCharacterInfo(playerName) + ? eAddFriendResponseType::NOTONLINE + : eAddFriendResponseType::INVALIDCHARACTER; - SendFriendResponse(requestor, requestee.get(), result->next() ? eAddFriendResponseType::NOTONLINE : eAddFriendResponseType::INVALIDCHARACTER); + SendFriendResponse(requestor, requestee.get(), responseType); return; } if (isBestFriendRequest) { - std::unique_ptr friendUpdate(Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;")); - friendUpdate->setUInt(1, static_cast(requestorPlayerID)); - friendUpdate->setUInt(2, static_cast(requestee->playerID)); - friendUpdate->setUInt(3, static_cast(requestee->playerID)); - friendUpdate->setUInt(4, static_cast(requestorPlayerID)); - std::unique_ptr result(friendUpdate->executeQuery()); - LWOOBJID queryPlayerID = LWOOBJID_EMPTY; - LWOOBJID queryFriendID = LWOOBJID_EMPTY; uint8_t oldBestFriendStatus{}; uint8_t bestFriendStatus{}; - - if (result->next()) { + auto bestFriendInfo = Database::Get()->GetBestFriendStatus(requestorPlayerID, requestee->playerID); + if (bestFriendInfo) { // Get the IDs - queryPlayerID = result->getInt(1); - queryFriendID = result->getInt(2); - oldBestFriendStatus = result->getInt(3); + LWOOBJID queryPlayerID = bestFriendInfo->playerCharacterId; + LWOOBJID queryFriendID = bestFriendInfo->friendCharacterId; + oldBestFriendStatus = bestFriendInfo->bestFriendStatus; bestFriendStatus = oldBestFriendStatus; // Set the bits @@ -211,13 +185,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { } } else { // Then update the database with this new info. - std::unique_ptr updateQuery(Database::CreatePreppedStmt("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;")); - updateQuery->setUInt(1, bestFriendStatus); - updateQuery->setUInt(2, static_cast(requestorPlayerID)); - updateQuery->setUInt(3, static_cast(requestee->playerID)); - updateQuery->setUInt(4, static_cast(requestee->playerID)); - updateQuery->setUInt(5, static_cast(requestorPlayerID)); - updateQuery->executeUpdate(); + Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee->playerID, bestFriendStatus); // Sent the best friend update here if the value is 3 if (bestFriendStatus == 3U) { requestee->countOfBestFriends += 1; @@ -319,11 +287,7 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { requesteeData.isOnline = true; requestor->friends.push_back(requesteeData); - std::unique_ptr statement(Database::CreatePreppedStmt("INSERT IGNORE INTO `friends` (`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?);")); - statement->setUInt(1, static_cast(requestor->playerID)); - statement->setUInt(2, static_cast(requestee->playerID)); - statement->setInt(3, 0); - statement->execute(); + Database::Get()->AddFriend(requestor->playerID, requestee->playerID); } if (serverResponseCode != eAddFriendResponseType::DECLINED) SendFriendResponse(requestor, requestee, serverResponseCode, isAlreadyBestFriends); @@ -338,25 +302,17 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { //we'll have to query the db here to find the user, since you can delete them while they're offline. //First, we need to find their ID: - std::unique_ptr stmt(Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? LIMIT 1;")); - stmt->setString(1, friendName.c_str()); - LWOOBJID friendID = 0; - std::unique_ptr res(stmt->executeQuery()); - while (res->next()) { - friendID = res->getUInt(1); + auto friendIdResult = Database::Get()->GetCharacterInfo(friendName); + if (friendIdResult) { + friendID = friendIdResult->id; } // Convert friendID to LWOOBJID GeneralUtils::SetBit(friendID, eObjectBits::PERSISTENT); GeneralUtils::SetBit(friendID, eObjectBits::CHARACTER); - std::unique_ptr deletestmt(Database::CreatePreppedStmt("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;")); - deletestmt->setUInt(1, static_cast(playerID)); - deletestmt->setUInt(2, static_cast(friendID)); - deletestmt->setUInt(3, static_cast(friendID)); - deletestmt->setUInt(4, static_cast(playerID)); - deletestmt->execute(); + Database::Get()->RemoveFriend(playerID, friendID); //Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended: auto goonA = playerContainer.GetPlayerData(playerID); diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index 6fb54594..5fc861b6 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -78,13 +78,8 @@ int main(int argc, char** argv) { } //Connect to the MySQL Database - std::string mysql_host = Game::config->GetValue("mysql_host"); - std::string mysql_database = Game::config->GetValue("mysql_database"); - std::string mysql_username = Game::config->GetValue("mysql_username"); - std::string mysql_password = Game::config->GetValue("mysql_password"); - try { - Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); + Database::Connect(); } catch (sql::SQLException& ex) { LOG("Got an error while connecting to the database: %s", ex.what()); Database::Destroy("ChatServer"); @@ -96,16 +91,11 @@ int main(int argc, char** argv) { //Find out the master's IP: std::string masterIP; uint32_t masterPort = 1000; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; } - - delete res; - delete stmt; - //It's safe to pass 'localhost' here, as the IP is only used as the external IP. uint32_t maxClients = 50; uint32_t ourPort = 1501; @@ -158,15 +148,12 @@ int main(int argc, char** argv) { //Find out the master's IP for absolutely no reason: std::string masterIP; uint32_t masterPort; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); - } - delete res; - delete stmt; + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; + } framesSinceLastSQLPing = 0; } else framesSinceLastSQLPing++; diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index bb14a096..8b9eb744 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -48,14 +48,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) { m_Players.insert(std::make_pair(data->playerID, data)); LOG("Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID()); - auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);"); - - insertLog->setInt(1, data->playerID); - insertLog->setInt(2, 0); - insertLog->setUInt64(3, time(nullptr)); - insertLog->setInt(4, data->zoneID.GetMapID()); - - insertLog->executeUpdate(); + Database::Get()->UpdateActivityLog(data->playerID, eActivityType::PlayerLoggedIn, data->zoneID.GetMapID()); } void PlayerContainer::RemovePlayer(Packet* packet) { @@ -92,14 +85,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) { LOG("Removed user: %llu", playerID); m_Players.erase(playerID); - auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);"); - - insertLog->setInt(1, playerID); - insertLog->setInt(2, 1); - insertLog->setUInt64(3, time(nullptr)); - insertLog->setInt(4, player->zoneID.GetMapID()); - - insertLog->executeUpdate(); + Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player->zoneID.GetMapID()); } void PlayerContainer::MuteUpdate(Packet* packet) { diff --git a/dCommon/BrickByBrickFix.cpp b/dCommon/BrickByBrickFix.cpp index d9b589c1..f8a9e022 100644 --- a/dCommon/BrickByBrickFix.cpp +++ b/dCommon/BrickByBrickFix.cpp @@ -13,9 +13,8 @@ //! Forward declarations -std::unique_ptr GetModelsFromDatabase(); void WriteSd0Magic(char* input, uint32_t chunkSize); -bool CheckSd0Magic(sql::Blob* streamToCheck); +bool CheckSd0Magic(std::istream& streamToCheck); /** * @brief Truncates all models with broken data from the database. @@ -24,28 +23,24 @@ bool CheckSd0Magic(sql::Blob* streamToCheck); */ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { uint32_t modelsTruncated{}; - auto modelsToTruncate = GetModelsFromDatabase(); - bool previousCommitValue = Database::GetAutoCommit(); - Database::SetAutoCommit(false); - while (modelsToTruncate->next()) { - std::unique_ptr ugcModelToDelete(Database::CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;")); - std::unique_ptr pcModelToDelete(Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;")); + auto modelsToTruncate = Database::Get()->GetAllUgcModels(); + bool previousCommitValue = Database::Get()->GetAutoCommit(); + Database::Get()->SetAutoCommit(false); + for (auto& model : modelsToTruncate) { std::string completeUncompressedModel{}; uint32_t chunkCount{}; - uint64_t modelId = modelsToTruncate->getInt(1); - std::unique_ptr modelAsSd0(modelsToTruncate->getBlob(2)); // Check that header is sd0 by checking for the sd0 magic. - if (CheckSd0Magic(modelAsSd0.get())) { + if (CheckSd0Magic(model.lxfmlData)) { while (true) { uint32_t chunkSize{}; - modelAsSd0->read(reinterpret_cast(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream + model.lxfmlData.read(reinterpret_cast(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream // Check if good here since if at the end of an sd0 file, this will have eof flagged. - if (!modelAsSd0->good()) break; + if (!model.lxfmlData.good()) break; std::unique_ptr compressedChunk(new uint8_t[chunkSize]); for (uint32_t i = 0; i < chunkSize; i++) { - compressedChunk[i] = modelAsSd0->get(); + compressedChunk[i] = model.lxfmlData.get(); } // Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size. @@ -59,7 +54,7 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { completeUncompressedModel.append((char*)uncompressedChunk.get()); completeUncompressedModel.resize(previousSize + actualUncompressedSize); } else { - LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, modelId, err); + LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err); break; } chunkCount++; @@ -75,26 +70,20 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { "", completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos ) { - LOG("Brick-by-brick model %llu will be deleted!", modelId); - ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1)); - pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1)); - ugcModelToDelete->execute(); - pcModelToDelete->execute(); + LOG("Brick-by-brick model %llu will be deleted!", model.id); + Database::Get()->DeleteUgcModelData(model.id); modelsTruncated++; } } } else { - LOG("Brick-by-brick model %llu will be deleted!", modelId); - ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1)); - pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1)); - ugcModelToDelete->execute(); - pcModelToDelete->execute(); + LOG("Brick-by-brick model %llu will be deleted!", model.id); + Database::Get()->DeleteUgcModelData(model.id); modelsTruncated++; } } - Database::Commit(); - Database::SetAutoCommit(previousCommitValue); + Database::Get()->Commit(); + Database::Get()->SetAutoCommit(previousCommitValue); return modelsTruncated; } @@ -106,21 +95,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { */ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() { uint32_t updatedModels = 0; - auto modelsToUpdate = GetModelsFromDatabase(); - auto previousAutoCommitState = Database::GetAutoCommit(); - Database::SetAutoCommit(false); - std::unique_ptr insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;")); - while (modelsToUpdate->next()) { - int64_t modelId = modelsToUpdate->getInt64(1); - std::unique_ptr oldLxfml(modelsToUpdate->getBlob(2)); + auto modelsToUpdate = Database::Get()->GetAllUgcModels(); + auto previousAutoCommitState = Database::Get()->GetAutoCommit(); + Database::Get()->SetAutoCommit(false); + for (auto& model : modelsToUpdate) { // Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib) // If it does, convert it to sd0. - if (oldLxfml->get() == 0x78 && oldLxfml->get() == 0xDA) { - + if (model.lxfmlData.get() == 0x78 && model.lxfmlData.get() == 0xDA) { // Get and save size of zlib compressed chunk. - oldLxfml->seekg(0, std::ios::end); - uint32_t oldLxfmlSize = static_cast(oldLxfml->tellg()); - oldLxfml->seekg(0); + model.lxfmlData.seekg(0, std::ios::end); + uint32_t oldLxfmlSize = static_cast(model.lxfmlData.tellg()); + model.lxfmlData.seekg(0); // Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size. uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9; @@ -128,34 +113,27 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() { WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize); for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) { - sd0ConvertedModel.get()[i] = oldLxfml->get(); + sd0ConvertedModel.get()[i] = model.lxfmlData.get(); } std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader); std::istringstream outputStringStream(outputString); - insertionStatement->setBlob(1, static_cast(&outputStringStream)); - insertionStatement->setInt64(2, modelId); try { - insertionStatement->executeUpdate(); - LOG("Updated model %i to sd0", modelId); + Database::Get()->UpdateUgcModelData(model.id, outputStringStream); + LOG("Updated model %i to sd0", model.id); updatedModels++; } catch (sql::SQLException exception) { LOG("Failed to update model %i. This model should be inspected manually to see why." - "The database error is %s", modelId, exception.what()); + "The database error is %s", model.id, exception.what()); } } } - Database::Commit(); - Database::SetAutoCommit(previousAutoCommitState); + Database::Get()->Commit(); + Database::Get()->SetAutoCommit(previousAutoCommitState); return updatedModels; } -std::unique_ptr GetModelsFromDatabase() { - std::unique_ptr modelsRawDataQuery(Database::CreatePreppedStmt("SELECT id, lxfml FROM ugc;")); - return std::unique_ptr(modelsRawDataQuery->executeQuery()); -} - /** * @brief Writes sd0 magic at the front of a char* * @@ -171,6 +149,6 @@ void WriteSd0Magic(char* input, uint32_t chunkSize) { *reinterpret_cast(input + 5) = chunkSize; // Write the integer to the character array } -bool CheckSd0Magic(sql::Blob* streamToCheck) { - return streamToCheck->get() == 's' && streamToCheck->get() == 'd' && streamToCheck->get() == '0' && streamToCheck->get() == 0x01 && streamToCheck->get() == 0xFF; +bool CheckSd0Magic(std::istream& streamToCheck) { + return streamToCheck.get() == 's' && streamToCheck.get() == 'd' && streamToCheck.get() == '0' && streamToCheck.get() == 0x01 && streamToCheck.get() == 0xFF; } diff --git a/dDatabase/CDClientDatabase.cpp b/dDatabase/CDClientDatabase/CDClientDatabase.cpp similarity index 100% rename from dDatabase/CDClientDatabase.cpp rename to dDatabase/CDClientDatabase/CDClientDatabase.cpp diff --git a/dDatabase/CDClientDatabase.h b/dDatabase/CDClientDatabase/CDClientDatabase.h similarity index 100% rename from dDatabase/CDClientDatabase.h rename to dDatabase/CDClientDatabase/CDClientDatabase.h diff --git a/dDatabase/CDClientManager.cpp b/dDatabase/CDClientDatabase/CDClientManager.cpp similarity index 100% rename from dDatabase/CDClientManager.cpp rename to dDatabase/CDClientDatabase/CDClientManager.cpp diff --git a/dDatabase/CDClientManager.h b/dDatabase/CDClientDatabase/CDClientManager.h similarity index 100% rename from dDatabase/CDClientManager.h rename to dDatabase/CDClientDatabase/CDClientManager.h diff --git a/dDatabase/Tables/CDActivitiesTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp similarity index 100% rename from dDatabase/Tables/CDActivitiesTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp diff --git a/dDatabase/Tables/CDActivitiesTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h similarity index 100% rename from dDatabase/Tables/CDActivitiesTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h diff --git a/dDatabase/Tables/CDActivityRewardsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp similarity index 100% rename from dDatabase/Tables/CDActivityRewardsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp diff --git a/dDatabase/Tables/CDActivityRewardsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h similarity index 100% rename from dDatabase/Tables/CDActivityRewardsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h diff --git a/dDatabase/Tables/CDAnimationsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.cpp similarity index 100% rename from dDatabase/Tables/CDAnimationsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.cpp diff --git a/dDatabase/Tables/CDAnimationsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h similarity index 100% rename from dDatabase/Tables/CDAnimationsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h diff --git a/dDatabase/Tables/CDBehaviorParameterTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.cpp similarity index 100% rename from dDatabase/Tables/CDBehaviorParameterTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.cpp diff --git a/dDatabase/Tables/CDBehaviorParameterTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.h similarity index 100% rename from dDatabase/Tables/CDBehaviorParameterTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.h diff --git a/dDatabase/Tables/CDBehaviorTemplateTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp similarity index 100% rename from dDatabase/Tables/CDBehaviorTemplateTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp diff --git a/dDatabase/Tables/CDBehaviorTemplateTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h similarity index 100% rename from dDatabase/Tables/CDBehaviorTemplateTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h diff --git a/dDatabase/Tables/CDBrickIDTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp similarity index 100% rename from dDatabase/Tables/CDBrickIDTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp diff --git a/dDatabase/Tables/CDBrickIDTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h similarity index 100% rename from dDatabase/Tables/CDBrickIDTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h diff --git a/dDatabase/Tables/CDComponentsRegistryTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.cpp similarity index 100% rename from dDatabase/Tables/CDComponentsRegistryTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.cpp diff --git a/dDatabase/Tables/CDComponentsRegistryTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h similarity index 96% rename from dDatabase/Tables/CDComponentsRegistryTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h index fc461989..fce4f6aa 100644 --- a/dDatabase/Tables/CDComponentsRegistryTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h @@ -3,6 +3,8 @@ // Custom Classes #include "CDTable.h" +#include + enum class eReplicaComponentType : uint32_t; struct CDComponentsRegistry { unsigned int id; //!< The LOT is used as the ID diff --git a/dDatabase/Tables/CDCurrencyTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp similarity index 100% rename from dDatabase/Tables/CDCurrencyTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp diff --git a/dDatabase/Tables/CDCurrencyTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h similarity index 100% rename from dDatabase/Tables/CDCurrencyTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h diff --git a/dDatabase/Tables/CDDestructibleComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDDestructibleComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp diff --git a/dDatabase/Tables/CDDestructibleComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h similarity index 100% rename from dDatabase/Tables/CDDestructibleComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h diff --git a/dDatabase/Tables/CDEmoteTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp similarity index 100% rename from dDatabase/Tables/CDEmoteTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp diff --git a/dDatabase/Tables/CDEmoteTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h similarity index 100% rename from dDatabase/Tables/CDEmoteTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h diff --git a/dDatabase/Tables/CDFeatureGatingTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp similarity index 100% rename from dDatabase/Tables/CDFeatureGatingTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp diff --git a/dDatabase/Tables/CDFeatureGatingTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.h similarity index 100% rename from dDatabase/Tables/CDFeatureGatingTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.h diff --git a/dDatabase/Tables/CDInventoryComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDInventoryComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp diff --git a/dDatabase/Tables/CDInventoryComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h similarity index 100% rename from dDatabase/Tables/CDInventoryComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h diff --git a/dDatabase/Tables/CDItemComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDItemComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp diff --git a/dDatabase/Tables/CDItemComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h similarity index 100% rename from dDatabase/Tables/CDItemComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h diff --git a/dDatabase/Tables/CDItemSetSkillsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp similarity index 100% rename from dDatabase/Tables/CDItemSetSkillsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp diff --git a/dDatabase/Tables/CDItemSetSkillsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h similarity index 100% rename from dDatabase/Tables/CDItemSetSkillsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h diff --git a/dDatabase/Tables/CDItemSetsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp similarity index 100% rename from dDatabase/Tables/CDItemSetsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp diff --git a/dDatabase/Tables/CDItemSetsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h similarity index 100% rename from dDatabase/Tables/CDItemSetsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h diff --git a/dDatabase/Tables/CDLevelProgressionLookupTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp similarity index 100% rename from dDatabase/Tables/CDLevelProgressionLookupTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp diff --git a/dDatabase/Tables/CDLevelProgressionLookupTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h similarity index 100% rename from dDatabase/Tables/CDLevelProgressionLookupTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h diff --git a/dDatabase/Tables/CDLootMatrixTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp similarity index 100% rename from dDatabase/Tables/CDLootMatrixTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp diff --git a/dDatabase/Tables/CDLootMatrixTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h similarity index 100% rename from dDatabase/Tables/CDLootMatrixTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h diff --git a/dDatabase/Tables/CDLootTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp similarity index 100% rename from dDatabase/Tables/CDLootTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp diff --git a/dDatabase/Tables/CDLootTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h similarity index 100% rename from dDatabase/Tables/CDLootTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h diff --git a/dDatabase/Tables/CDMissionEmailTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp similarity index 100% rename from dDatabase/Tables/CDMissionEmailTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp diff --git a/dDatabase/Tables/CDMissionEmailTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h similarity index 100% rename from dDatabase/Tables/CDMissionEmailTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h diff --git a/dDatabase/Tables/CDMissionNPCComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDMissionNPCComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp diff --git a/dDatabase/Tables/CDMissionNPCComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h similarity index 100% rename from dDatabase/Tables/CDMissionNPCComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h diff --git a/dDatabase/Tables/CDMissionTasksTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp similarity index 100% rename from dDatabase/Tables/CDMissionTasksTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp diff --git a/dDatabase/Tables/CDMissionTasksTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h similarity index 100% rename from dDatabase/Tables/CDMissionTasksTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h diff --git a/dDatabase/Tables/CDMissionsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp similarity index 100% rename from dDatabase/Tables/CDMissionsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp diff --git a/dDatabase/Tables/CDMissionsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h similarity index 100% rename from dDatabase/Tables/CDMissionsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h diff --git a/dDatabase/Tables/CDMovementAIComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDMovementAIComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp diff --git a/dDatabase/Tables/CDMovementAIComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h similarity index 100% rename from dDatabase/Tables/CDMovementAIComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h diff --git a/dDatabase/Tables/CDObjectSkillsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp similarity index 100% rename from dDatabase/Tables/CDObjectSkillsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp diff --git a/dDatabase/Tables/CDObjectSkillsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h similarity index 100% rename from dDatabase/Tables/CDObjectSkillsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h diff --git a/dDatabase/Tables/CDObjectsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp similarity index 100% rename from dDatabase/Tables/CDObjectsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp diff --git a/dDatabase/Tables/CDObjectsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h similarity index 100% rename from dDatabase/Tables/CDObjectsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h diff --git a/dDatabase/Tables/CDPackageComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDPackageComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp diff --git a/dDatabase/Tables/CDPackageComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h similarity index 100% rename from dDatabase/Tables/CDPackageComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h diff --git a/dDatabase/Tables/CDPhysicsComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDPhysicsComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp diff --git a/dDatabase/Tables/CDPhysicsComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h similarity index 100% rename from dDatabase/Tables/CDPhysicsComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h diff --git a/dDatabase/Tables/CDPropertyEntranceComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDPropertyEntranceComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.cpp diff --git a/dDatabase/Tables/CDPropertyEntranceComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.h similarity index 100% rename from dDatabase/Tables/CDPropertyEntranceComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.h diff --git a/dDatabase/Tables/CDPropertyTemplateTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.cpp similarity index 100% rename from dDatabase/Tables/CDPropertyTemplateTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.cpp diff --git a/dDatabase/Tables/CDPropertyTemplateTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.h similarity index 100% rename from dDatabase/Tables/CDPropertyTemplateTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.h diff --git a/dDatabase/Tables/CDProximityMonitorComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDProximityMonitorComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp diff --git a/dDatabase/Tables/CDProximityMonitorComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h similarity index 100% rename from dDatabase/Tables/CDProximityMonitorComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h diff --git a/dDatabase/Tables/CDRailActivatorComponent.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.cpp similarity index 100% rename from dDatabase/Tables/CDRailActivatorComponent.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.cpp diff --git a/dDatabase/Tables/CDRailActivatorComponent.h b/dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.h similarity index 100% rename from dDatabase/Tables/CDRailActivatorComponent.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.h diff --git a/dDatabase/Tables/CDRarityTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp similarity index 100% rename from dDatabase/Tables/CDRarityTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp diff --git a/dDatabase/Tables/CDRarityTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h similarity index 100% rename from dDatabase/Tables/CDRarityTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h diff --git a/dDatabase/Tables/CDRebuildComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDRebuildComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp diff --git a/dDatabase/Tables/CDRebuildComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h similarity index 100% rename from dDatabase/Tables/CDRebuildComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h diff --git a/dDatabase/Tables/CDRewardsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.cpp similarity index 100% rename from dDatabase/Tables/CDRewardsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.cpp diff --git a/dDatabase/Tables/CDRewardsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.h similarity index 100% rename from dDatabase/Tables/CDRewardsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.h diff --git a/dDatabase/Tables/CDScriptComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDScriptComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp diff --git a/dDatabase/Tables/CDScriptComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h similarity index 100% rename from dDatabase/Tables/CDScriptComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h diff --git a/dDatabase/Tables/CDSkillBehaviorTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp similarity index 100% rename from dDatabase/Tables/CDSkillBehaviorTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp diff --git a/dDatabase/Tables/CDSkillBehaviorTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h similarity index 100% rename from dDatabase/Tables/CDSkillBehaviorTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h diff --git a/dDatabase/Tables/CDTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h similarity index 100% rename from dDatabase/Tables/CDTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDTable.h diff --git a/dDatabase/Tables/CDVendorComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDVendorComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp diff --git a/dDatabase/Tables/CDVendorComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h similarity index 100% rename from dDatabase/Tables/CDVendorComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h diff --git a/dDatabase/Tables/CDZoneTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp similarity index 100% rename from dDatabase/Tables/CDZoneTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp diff --git a/dDatabase/Tables/CDZoneTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h similarity index 100% rename from dDatabase/Tables/CDZoneTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h diff --git a/dDatabase/Tables/CMakeLists.txt b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt similarity index 93% rename from dDatabase/Tables/CMakeLists.txt rename to dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt index b6a02b02..43ff52b2 100644 --- a/dDatabase/Tables/CMakeLists.txt +++ b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt @@ -1,4 +1,4 @@ -set(DDATABASE_TABLES_SOURCES "CDActivitiesTable.cpp" +set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp" "CDActivityRewardsTable.cpp" "CDAnimationsTable.cpp" "CDBehaviorParameterTable.cpp" diff --git a/dDatabase/CDClientDatabase/CMakeLists.txt b/dDatabase/CDClientDatabase/CMakeLists.txt new file mode 100644 index 00000000..2645c215 --- /dev/null +++ b/dDatabase/CDClientDatabase/CMakeLists.txt @@ -0,0 +1,12 @@ +set(DDATABASE_CDCLIENTDATABASE_SOURCES + "CDClientDatabase.cpp" + "CDClientManager.cpp" +) + +add_subdirectory(CDClientTables) + +foreach(file ${DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES}) + set(DDATABASE_CDCLIENTDATABASE_SOURCES ${DDATABASE_CDCLIENTDATABASE_SOURCES} "CDClientTables/${file}") +endforeach() + +set(DDATABASE_CDCLIENTDATABASE_SOURCES ${DDATABASE_CDCLIENTDATABASE_SOURCES} PARENT_SCOPE) diff --git a/dDatabase/CMakeLists.txt b/dDatabase/CMakeLists.txt index 9e55fbe2..20f37bec 100644 --- a/dDatabase/CMakeLists.txt +++ b/dDatabase/CMakeLists.txt @@ -1,12 +1,15 @@ -set(DDATABASE_SOURCES "CDClientDatabase.cpp" - "CDClientManager.cpp" - "Database.cpp" - "MigrationRunner.cpp") +set(DDATABASE_SOURCES) -add_subdirectory(Tables) +add_subdirectory(CDClientDatabase) -foreach(file ${DDATABASE_TABLES_SOURCES}) - set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}") +foreach(file ${DDATABASE_CDCLIENTDATABASE_SOURCES}) + set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "CDClientDatabase/${file}") +endforeach() + +add_subdirectory(GameDatabase) + +foreach(file ${DDATABASE_GAMEDATABASE_SOURCES}) + set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "GameDatabase/${file}") endforeach() add_library(dDatabase STATIC ${DDATABASE_SOURCES}) diff --git a/dDatabase/Database.cpp b/dDatabase/Database.cpp deleted file mode 100644 index 5fc76786..00000000 --- a/dDatabase/Database.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "Database.h" -#include "Game.h" -#include "dConfig.h" -#include "Logger.h" -using namespace std; - -#pragma warning (disable:4251) //Disables SQL warnings - -sql::Driver* Database::driver; -sql::Connection* Database::con; -sql::Properties Database::props; -std::string Database::database; - -void Database::Connect(const string& host, const string& database, const string& username, const string& password) { - - //To bypass debug issues: - const char* szDatabase = database.c_str(); - const char* szUsername = username.c_str(); - const char* szPassword = password.c_str(); - - driver = sql::mariadb::get_driver_instance(); - - sql::Properties properties; - // The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where - // 1) it tries to parse a database from the connection string (like in tcp://localhost:3001/darkflame) based on the - // presence of a / - // 2) even avoiding that, the connector still assumes you're connecting with a tcp socket - // So, what we do in the presence of a unix socket or pipe is to set the hostname to the protocol and localhost, - // which avoids parsing errors while still ensuring the correct connection type is used, and then setting the appropriate - // property manually (which the URL parsing fails to do) - const std::string UNIX_PROTO = "unix://"; - const std::string PIPE_PROTO = "pipe://"; - if (host.find(UNIX_PROTO) == 0) { - properties["hostName"] = "unix://localhost"; - properties["localSocket"] = host.substr(UNIX_PROTO.length()).c_str(); - } else if (host.find(PIPE_PROTO) == 0) { - properties["hostName"] = "pipe://localhost"; - properties["pipe"] = host.substr(PIPE_PROTO.length()).c_str(); - } else { - properties["hostName"] = host.c_str(); - } - properties["user"] = szUsername; - properties["password"] = szPassword; - properties["autoReconnect"] = "true"; - - Database::props = properties; - Database::database = database; - - Database::Connect(); -} - -void Database::Connect() { - // `connect(const Properties& props)` segfaults in windows debug, but - // `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly - if (Database::props.find("localSocket") != Database::props.end() || Database::props.find("pipe") != Database::props.end()) { - con = driver->connect(Database::props); - } else { - con = driver->connect(Database::props["hostName"].c_str(), Database::props["user"].c_str(), Database::props["password"].c_str()); - } - con->setSchema(Database::database.c_str()); -} - -void Database::Destroy(std::string source, bool log) { - if (!con) return; - - if (log) { - if (source != "") LOG("Destroying MySQL connection from %s!", source.c_str()); - else LOG("Destroying MySQL connection!"); - } - - con->close(); - delete con; -} //Destroy - -sql::Statement* Database::CreateStmt() { - sql::Statement* toReturn = con->createStatement(); - return toReturn; -} //CreateStmt - -sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) { - const char* test = query.c_str(); - size_t size = query.length(); - sql::SQLString str(test, size); - - if (!con) { - Connect(); - LOG("Trying to reconnect to MySQL"); - } - - if (!con->isValid() || con->isClosed()) { - delete con; - - con = nullptr; - - Connect(); - LOG("Trying to reconnect to MySQL from invalid or closed connection"); - } - - auto* stmt = con->prepareStatement(str); - - return stmt; -} //CreatePreppedStmt - -void Database::Commit() { - Database::con->commit(); -} - -bool Database::GetAutoCommit() { - // TODO This should not just access a pointer. A future PR should update this - // to check for null and throw an error if the connection is not valid. - return con->getAutoCommit(); -} - -void Database::SetAutoCommit(bool value) { - // TODO This should not just access a pointer. A future PR should update this - // to check for null and throw an error if the connection is not valid. - Database::con->setAutoCommit(value); -} diff --git a/dDatabase/Database.h b/dDatabase/Database.h deleted file mode 100644 index f4d13da3..00000000 --- a/dDatabase/Database.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include - -class MySqlException : public std::runtime_error { -public: - MySqlException() : std::runtime_error("MySQL error!") {} - MySqlException(const std::string& msg) : std::runtime_error(msg.c_str()) {} -}; - -class Database { -private: - static sql::Driver* driver; - static sql::Connection* con; - static sql::Properties props; - static std::string database; -public: - static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password); - static void Connect(); - static void Destroy(std::string source = "", bool log = true); - - static sql::Statement* CreateStmt(); - static sql::PreparedStatement* CreatePreppedStmt(const std::string& query); - static void Commit(); - static bool GetAutoCommit(); - static void SetAutoCommit(bool value); - - static std::string GetDatabase() { return database; } - static sql::Properties GetProperties() { return props; } -}; diff --git a/dDatabase/GameDatabase/CMakeLists.txt b/dDatabase/GameDatabase/CMakeLists.txt new file mode 100644 index 00000000..c32007bb --- /dev/null +++ b/dDatabase/GameDatabase/CMakeLists.txt @@ -0,0 +1,12 @@ +set(DDATABASE_GAMEDATABASE_SOURCES + "Database.cpp" + "MigrationRunner.cpp" +) + +add_subdirectory(MySQL) + +foreach(file ${DDATABSE_DATABSES_MYSQL_SOURCES}) + set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "MySQL/${file}") +endforeach() + +set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} PARENT_SCOPE) diff --git a/dDatabase/GameDatabase/Database.cpp b/dDatabase/GameDatabase/Database.cpp new file mode 100644 index 00000000..cb4f989a --- /dev/null +++ b/dDatabase/GameDatabase/Database.cpp @@ -0,0 +1,40 @@ +#include "Database.h" +#include "Game.h" +#include "dConfig.h" +#include "Logger.h" +#include "MySQLDatabase.h" +#include "DluAssert.h" + +#pragma warning (disable:4251) //Disables SQL warnings + +namespace { + GameDatabase* database = nullptr; +} + +void Database::Connect() { + if (database) { + LOG("Tried to connect to database when it's already connected!"); + return; + } + + database = new MySQLDatabase(); + database->Connect(); +} + +GameDatabase* Database::Get() { + if (!database) { + LOG("Tried to get database when it's not connected!"); + Connect(); + } + return database; +} + +void Database::Destroy(std::string source) { + if (database) { + database->Destroy(source); + delete database; + database = nullptr; + } else { + LOG("Trying to destroy database when it's not connected!"); + } +} diff --git a/dDatabase/GameDatabase/Database.h b/dDatabase/GameDatabase/Database.h new file mode 100644 index 00000000..3eb292d1 --- /dev/null +++ b/dDatabase/GameDatabase/Database.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +#include "GameDatabase.h" + +namespace Database { + void Connect(); + GameDatabase* Get(); + void Destroy(std::string source = ""); +}; diff --git a/dDatabase/GameDatabase/GameDatabase.h b/dDatabase/GameDatabase/GameDatabase.h new file mode 100644 index 00000000..7d8c7de9 --- /dev/null +++ b/dDatabase/GameDatabase/GameDatabase.h @@ -0,0 +1,55 @@ +#ifndef __GAMEDATABASE__H__ +#define __GAMEDATABASE__H__ + +#include + +#include "ILeaderboard.h" +#include "IPlayerCheatDetections.h" +#include "ICommandLog.h" +#include "IMail.h" +#include "IObjectIdTracker.h" +#include "IPlayKeys.h" +#include "IServers.h" +#include "IBugReports.h" +#include "IPropertyContents.h" +#include "IProperty.h" +#include "IPetNames.h" +#include "ICharXml.h" +#include "IMigrationHistory.h" +#include "IUgc.h" +#include "IFriends.h" +#include "ICharInfo.h" +#include "IAccounts.h" +#include "IActivityLog.h" + +namespace sql { + class Statement; + class PreparedStatement; +}; + +#ifdef _DEBUG +# define DLU_SQL_TRY_CATCH_RETHROW(x) do { try { x; } catch (sql::SQLException& ex) { LOG("SQL Error: %s", ex.what()); throw; } } while(0) +#else +# define DLU_SQL_TRY_CATCH_RETHROW(x) x +#endif // _DEBUG + +class GameDatabase : + public IPlayKeys, public ILeaderboard, public IObjectIdTracker, public IServers, + public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports, + public IPropertyContents, public IProperty, public IPetNames, public ICharXml, + public IMigrationHistory, public IUgc, public IFriends, public ICharInfo, + public IAccounts, public IActivityLog { +public: + virtual ~GameDatabase() = default; + // TODO: These should be made private. + virtual void Connect() = 0; + virtual void Destroy(std::string source = "") = 0; + virtual void ExecuteCustomQuery(const std::string_view query) = 0; + virtual sql::PreparedStatement* CreatePreppedStmt(const std::string& query) = 0; + virtual void Commit() = 0; + virtual bool GetAutoCommit() = 0; + virtual void SetAutoCommit(bool value) = 0; + virtual void DeleteCharacter(const uint32_t characterId) = 0; +}; + +#endif //!__GAMEDATABASE__H__ diff --git a/dDatabase/GameDatabase/ITables/IAccounts.h b/dDatabase/GameDatabase/ITables/IAccounts.h new file mode 100644 index 00000000..1b1f85a7 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IAccounts.h @@ -0,0 +1,37 @@ +#ifndef __IACCOUNTS__H__ +#define __IACCOUNTS__H__ + +#include +#include +#include + +enum class eGameMasterLevel : uint8_t; + +class IAccounts { +public: + struct Info { + std::string bcryptPassword; + uint32_t id{}; + uint32_t playKeyId{}; + bool banned{}; + bool locked{}; + eGameMasterLevel maxGmLevel{}; + }; + + // Get the account info for the given username. + virtual std::optional GetAccountInfo(const std::string_view username) = 0; + + // Update the account's unmute time. + virtual void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) = 0; + + // Update the account's ban status. + virtual void UpdateAccountBan(const uint32_t accountId, const bool banned) = 0; + + // Update the account's password. + virtual void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) = 0; + + // Add a new account to the database. + virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0; +}; + +#endif //!__IACCOUNTS__H__ diff --git a/dDatabase/GameDatabase/ITables/IActivityLog.h b/dDatabase/GameDatabase/ITables/IActivityLog.h new file mode 100644 index 00000000..a67b61a4 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IActivityLog.h @@ -0,0 +1,19 @@ +#ifndef __IACTIVITYLOG__H__ +#define __IACTIVITYLOG__H__ + +#include + +#include "dCommonVars.h" + +enum class eActivityType : uint32_t { + PlayerLoggedIn, + PlayerLoggedOut, +}; + +class IActivityLog { +public: + // Update the activity log for the given account. + virtual void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) = 0; +}; + +#endif //!__IACTIVITYLOG__H__ diff --git a/dDatabase/GameDatabase/ITables/IBugReports.h b/dDatabase/GameDatabase/ITables/IBugReports.h new file mode 100644 index 00000000..29a6180f --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IBugReports.h @@ -0,0 +1,20 @@ +#ifndef __IBUGREPORTS__H__ +#define __IBUGREPORTS__H__ + +#include +#include + +class IBugReports { +public: + struct Info { + std::string body; + std::string clientVersion; + std::string otherPlayer; + std::string selection; + uint32_t characterId{}; + }; + + // Add a new bug report to the database. + virtual void InsertNewBugReport(const Info& info) = 0; +}; +#endif //!__IBUGREPORTS__H__ diff --git a/dDatabase/GameDatabase/ITables/ICharInfo.h b/dDatabase/GameDatabase/ITables/ICharInfo.h new file mode 100644 index 00000000..416dad2c --- /dev/null +++ b/dDatabase/GameDatabase/ITables/ICharInfo.h @@ -0,0 +1,49 @@ +#ifndef __ICHARINFO__H__ +#define __ICHARINFO__H__ + +#include +#include +#include +#include +#include + +#include "ePermissionMap.h" + +class ICharInfo { +public: + struct Info { + std::string name; + std::string pendingName; + uint32_t id{}; + uint32_t accountId{}; + bool needsRename{}; + LWOCLONEID cloneId{}; + ePermissionMap permissionMap{}; + }; + + // Get the approved names of all characters. + virtual std::vector GetApprovedCharacterNames() = 0; + + // Get the character info for the given character id. + virtual std::optional GetCharacterInfo(const uint32_t charId) = 0; + + // Get the character info for the given character name. + virtual std::optional GetCharacterInfo(const std::string_view name) = 0; + + // Get the character ids for the given account. + virtual std::vector GetAccountCharacterIds(const uint32_t accountId) = 0; + + // Insert a new character into the database. + virtual void InsertNewCharacter(const ICharInfo::Info info) = 0; + + // Set the name of the given character. + virtual void SetCharacterName(const uint32_t characterId, const std::string_view name) = 0; + + // Set the pending name of the given character. + virtual void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) = 0; + + // Updates the given character ids last login to be right now. + virtual void UpdateLastLoggedInCharacter(const uint32_t characterId) = 0; +}; + +#endif //!__ICHARINFO__H__ diff --git a/dDatabase/GameDatabase/ITables/ICharXml.h b/dDatabase/GameDatabase/ITables/ICharXml.h new file mode 100644 index 00000000..c7ada075 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/ICharXml.h @@ -0,0 +1,20 @@ +#ifndef __ICHARXML__H__ +#define __ICHARXML__H__ + +#include +#include +#include + +class ICharXml { +public: + // Get the character xml for the given character id. + virtual std::string GetCharacterXml(const uint32_t charId) = 0; + + // Update the character xml for the given character id. + virtual void UpdateCharacterXml(const uint32_t charId, const std::string_view lxfml) = 0; + + // Insert the character xml for the given character id. + virtual void InsertCharacterXml(const uint32_t characterId, const std::string_view lxfml) = 0; +}; + +#endif //!__ICHARXML__H__ diff --git a/dDatabase/GameDatabase/ITables/ICommandLog.h b/dDatabase/GameDatabase/ITables/ICommandLog.h new file mode 100644 index 00000000..63595360 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/ICommandLog.h @@ -0,0 +1,14 @@ +#ifndef __ICOMMANDLOG__H__ +#define __ICOMMANDLOG__H__ + +#include +#include + +class ICommandLog { +public: + + // Insert a new slash command log entry. + virtual void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) = 0; +}; + +#endif //!__ICOMMANDLOG__H__ diff --git a/dDatabase/GameDatabase/ITables/IFriends.h b/dDatabase/GameDatabase/ITables/IFriends.h new file mode 100644 index 00000000..6f96f441 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IFriends.h @@ -0,0 +1,32 @@ +#ifndef __IFRIENDS__H__ +#define __IFRIENDS__H__ + +#include +#include +#include + +class IFriends { +public: + struct BestFriendStatus { + uint32_t playerCharacterId{}; + uint32_t friendCharacterId{}; + uint32_t bestFriendStatus{}; + }; + + // Get the friends list for the given character id. + virtual std::vector GetFriendsList(const uint32_t charId) = 0; + + // Get the best friend status for the given player and friend character ids. + virtual std::optional GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) = 0; + + // Set the best friend status for the given player and friend character ids. + virtual void SetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId, const uint32_t bestFriendStatus) = 0; + + // Add a friend to the given character id. + virtual void AddFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) = 0; + + // Remove a friend from the given character id. + virtual void RemoveFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) = 0; +}; + +#endif //!__IFRIENDS__H__ diff --git a/dDatabase/GameDatabase/ITables/ILeaderboard.h b/dDatabase/GameDatabase/ITables/ILeaderboard.h new file mode 100644 index 00000000..84d44eb2 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/ILeaderboard.h @@ -0,0 +1,14 @@ +#ifndef __ILEADERBOARD__H__ +#define __ILEADERBOARD__H__ + +#include +#include + +class ILeaderboard { +public: + + // Get the donation total for the given activity id. + virtual std::optional GetDonationTotal(const uint32_t activityId) = 0; +}; + +#endif //!__ILEADERBOARD__H__ diff --git a/dDatabase/GameDatabase/ITables/IMail.h b/dDatabase/GameDatabase/ITables/IMail.h new file mode 100644 index 00000000..7fbc8230 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IMail.h @@ -0,0 +1,54 @@ +#ifndef __IMAIL__H__ +#define __IMAIL__H__ + +#include +#include +#include + +#include "dCommonVars.h" +#include "NiQuaternion.h" +#include "NiPoint3.h" + +class IMail { +public: + struct MailInfo { + std::string senderUsername; + std::string recipient; + std::string subject; + std::string body; + uint64_t id{}; + uint32_t senderId{}; + uint32_t receiverId{}; + uint64_t timeSent{}; + bool wasRead{}; + struct { + LWOOBJID itemID{}; + int32_t itemCount{}; + LOT itemLOT{}; + LWOOBJID itemSubkey{}; + }; + }; + + // Insert a new mail into the database. + virtual void InsertNewMail(const MailInfo& mail) = 0; + + // Get the mail for the given character id. + virtual std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) = 0; + + // Get the mail for the given mail id. + virtual std::optional GetMail(const uint64_t mailId) = 0; + + // Get the number of unread mail for the given character id. + virtual uint32_t GetUnreadMailCount(const uint32_t characterId) = 0; + + // Mark the given mail as read. + virtual void MarkMailRead(const uint64_t mailId) = 0; + + // Claim the item from the given mail. + virtual void ClaimMailItem(const uint64_t mailId) = 0; + + // Delete the given mail. + virtual void DeleteMail(const uint64_t mailId) = 0; +}; + +#endif //!__IMAIL__H__ diff --git a/dDatabase/GameDatabase/ITables/IMigrationHistory.h b/dDatabase/GameDatabase/ITables/IMigrationHistory.h new file mode 100644 index 00000000..21f27b4a --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IMigrationHistory.h @@ -0,0 +1,17 @@ +#ifndef __IMIGRATIONHISTORY__H__ +#define __IMIGRATIONHISTORY__H__ + +#include + +class IMigrationHistory { +public: + // Create the migration history table. + virtual void CreateMigrationHistoryTable() = 0; + + // Check if the given migration has been run. + virtual bool IsMigrationRun(const std::string_view str) = 0; + + // Insert the given migration into the migration history table. + virtual void InsertMigration(const std::string_view str) = 0; +}; +#endif //!__IMIGRATIONHISTORY__H__ diff --git a/dDatabase/GameDatabase/ITables/IObjectIdTracker.h b/dDatabase/GameDatabase/ITables/IObjectIdTracker.h new file mode 100644 index 00000000..cbe34b6d --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IObjectIdTracker.h @@ -0,0 +1,19 @@ +#ifndef __IOBJECTIDTRACKER__H__ +#define __IOBJECTIDTRACKER__H__ + +#include +#include + +class IObjectIdTracker { +public: + // Get the current persistent id. + virtual std::optional GetCurrentPersistentId() = 0; + + // Insert the default persistent id. + virtual void InsertDefaultPersistentId() = 0; + + // Update the persistent id. + virtual void UpdatePersistentId(const uint32_t newId) = 0; +}; + +#endif //!__IOBJECTIDTRACKER__H__ diff --git a/dDatabase/GameDatabase/ITables/IPetNames.h b/dDatabase/GameDatabase/ITables/IPetNames.h new file mode 100644 index 00000000..e82f4905 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IPetNames.h @@ -0,0 +1,21 @@ +#ifndef __IPETNAMES__H__ +#define __IPETNAMES__H__ + +#include +#include + +class IPetNames { +public: + struct Info { + std::string petName; + int32_t approvalStatus{}; + }; + + // Set the pet name moderation status for the given pet id. + virtual void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) = 0; + + // Get pet info for the given pet id. + virtual std::optional GetPetNameInfo(const LWOOBJID& petId) = 0; +}; + +#endif //!__IPETNAMES__H__ diff --git a/dDatabase/GameDatabase/ITables/IPlayKeys.h b/dDatabase/GameDatabase/ITables/IPlayKeys.h new file mode 100644 index 00000000..0a1db389 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IPlayKeys.h @@ -0,0 +1,15 @@ +#ifndef __IPLAYKEYS__H__ +#define __IPLAYKEYS__H__ + +#include +#include + +class IPlayKeys { +public: + // Get the playkey id for the given playkey. + // Optional of bool may seem pointless, however the optional indicates if the playkey exists + // and the bool indicates if the playkey is active. + virtual std::optional IsPlaykeyActive(const int32_t playkeyId) = 0; +}; + +#endif //!__IPLAYKEYS__H__ diff --git a/dDatabase/GameDatabase/ITables/IPlayerCheatDetections.h b/dDatabase/GameDatabase/ITables/IPlayerCheatDetections.h new file mode 100644 index 00000000..939f5118 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IPlayerCheatDetections.h @@ -0,0 +1,20 @@ +#ifndef __IPLAYERCHEATDETECTIONS__H__ +#define __IPLAYERCHEATDETECTIONS__H__ + +#include +#include + +class IPlayerCheatDetections { +public: + struct Info { + std::optional userId = std::nullopt; + std::string username; + std::string systemAddress; + std::string extraMessage; + }; + + // Insert a new cheat detection. + virtual void InsertCheatDetection(const IPlayerCheatDetections::Info& info) = 0; +}; + +#endif //!__IPLAYERCHEATDETECTIONS__H__ diff --git a/dDatabase/GameDatabase/ITables/IProperty.h b/dDatabase/GameDatabase/ITables/IProperty.h new file mode 100644 index 00000000..54994b51 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IProperty.h @@ -0,0 +1,38 @@ +#ifndef __IPROPERTY__H__ +#define __IPROPERTY__H__ + +#include +#include + +class IProperty { +public: + struct Info { + std::string name; + std::string description; + std::string rejectionReason; + LWOOBJID id{}; + uint32_t ownerId{}; + LWOCLONEID cloneId{}; + int32_t privacyOption{}; + uint32_t modApproved{}; + uint32_t lastUpdatedTime{}; + uint32_t claimedTime{}; + uint32_t reputation{}; + }; + + // Get the property info for the given property id. + virtual std::optional GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) = 0; + + // Update the property moderation info for the given property id. + virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0; + + // Update the property details for the given property id. + virtual void UpdatePropertyDetails(const IProperty::Info& info) = 0; + + // Update the property performance cost for the given property id. + virtual void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) = 0; + + // Insert a new property into the database. + virtual void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) = 0; +}; +#endif //!__IPROPERTY__H__ diff --git a/dDatabase/GameDatabase/ITables/IPropertyContents.h b/dDatabase/GameDatabase/ITables/IPropertyContents.h new file mode 100644 index 00000000..c862ca94 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IPropertyContents.h @@ -0,0 +1,40 @@ +#ifndef __IPROPERTIESCONTENTS__H__ +#define __IPROPERTIESCONTENTS__H__ + +#include +#include + +class IPropertyContents { +public: + struct Model { + inline bool operator==(const LWOOBJID& other) const noexcept { + return id == other; + } + + NiPoint3 position; + NiQuaternion rotation; + LWOOBJID id{}; + LOT lot{}; + uint32_t ugcId{}; + }; + + // Inserts a new UGC model into the database. + virtual void InsertNewUgcModel( + std::istringstream& sd0Data, + const uint32_t blueprintId, + const uint32_t accountId, + const uint32_t characterId) = 0; + + // Get the property models for the given property id. + virtual std::vector GetPropertyModels(const LWOOBJID& propertyId) = 0; + + // Insert a new property model into the database. + virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0; + + // Update the model position and rotation for the given property id. + virtual void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) = 0; + + // Remove the model for the given property id. + virtual void RemoveModel(const LWOOBJID& modelId) = 0; +}; +#endif //!__IPROPERTIESCONTENTS__H__ diff --git a/dDatabase/GameDatabase/ITables/IServers.h b/dDatabase/GameDatabase/ITables/IServers.h new file mode 100644 index 00000000..ee74bbb8 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IServers.h @@ -0,0 +1,21 @@ +#ifndef __ISERVERS__H__ +#define __ISERVERS__H__ + +#include +#include + +class IServers { +public: + struct MasterInfo { + std::string ip; + uint32_t port{}; + }; + + // Set the master server ip and port. + virtual void SetMasterIp(const std::string_view ip, const uint32_t port) = 0; + + // Get the master server info. + virtual std::optional GetMasterInfo() = 0; +}; + +#endif //!__ISERVERS__H__ diff --git a/dDatabase/GameDatabase/ITables/IUgc.h b/dDatabase/GameDatabase/ITables/IUgc.h new file mode 100644 index 00000000..024636ac --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IUgc.h @@ -0,0 +1,32 @@ +#ifndef __IUGC__H__ +#define __IUGC__H__ + +#include +#include +#include +#include +#include + +class IUgc { +public: + struct Model { + std::stringstream lxfmlData; + LWOOBJID id{}; + }; + + // Gets all UGC models for the given property id. + virtual std::vector GetUgcModels(const LWOOBJID& propertyId) = 0; + + // Gets all Ugcs models. + virtual std::vector GetAllUgcModels() = 0; + + // Removes ugc models that are not referenced by any property. + virtual void RemoveUnreferencedUgcModels() = 0; + + // Deletes the ugc model for the given model id. + virtual void DeleteUgcModelData(const LWOOBJID& modelId) = 0; + + // Inserts a new UGC model into the database. + virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) = 0; +}; +#endif //!__IUGC__H__ diff --git a/dDatabase/MigrationRunner.cpp b/dDatabase/GameDatabase/MigrationRunner.cpp similarity index 73% rename from dDatabase/MigrationRunner.cpp rename to dDatabase/GameDatabase/MigrationRunner.cpp index 2dbe9101..6bc3954d 100644 --- a/dDatabase/MigrationRunner.cpp +++ b/dDatabase/GameDatabase/MigrationRunner.cpp @@ -32,9 +32,7 @@ Migration LoadMigration(std::string path) { } void MigrationRunner::RunMigrations() { - auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());"); - stmt->execute(); - delete stmt; + Database::Get()->CreateMigrationHistoryTable(); sql::SQLString finalSQL = ""; bool runSd0Migrations = false; @@ -45,13 +43,7 @@ void MigrationRunner::RunMigrations() { continue; } - stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); - stmt->setString(1, migration.name.c_str()); - auto* res = stmt->executeQuery(); - bool doExit = res->next(); - delete res; - delete stmt; - if (doExit) continue; + if (Database::Get()->IsMigrationRun(migration.name)) continue; LOG("Running migration: %s", migration.name.c_str()); if (migration.name == "dlu/5_brick_model_sd0.sql") { @@ -60,10 +52,7 @@ void MigrationRunner::RunMigrations() { finalSQL.append(migration.data.c_str()); } - stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); - stmt->setString(1, migration.name.c_str()); - stmt->execute(); - delete stmt; + Database::Get()->InsertMigration(migration.name); } if (finalSQL.empty() && !runSd0Migrations) { @@ -73,11 +62,10 @@ void MigrationRunner::RunMigrations() { if (!finalSQL.empty()) { auto migration = GeneralUtils::SplitString(static_cast(finalSQL), ';'); - std::unique_ptr simpleStatement(Database::CreateStmt()); for (auto& query : migration) { try { if (query.empty()) continue; - simpleStatement->execute(query.c_str()); + Database::Get()->ExecuteCustomQuery(query.c_str()); } catch (sql::SQLException& e) { LOG("Encountered error running migration: %s", e.what()); } @@ -98,9 +86,7 @@ void MigrationRunner::RunSQLiteMigrations() { cdstmt.execQuery().finalize(); cdstmt.finalize(); - auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());"); - stmt->execute(); - delete stmt; + Database::Get()->CreateMigrationHistoryTable(); for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) { auto migration = LoadMigration("cdserver/" + entry); @@ -111,25 +97,15 @@ void MigrationRunner::RunSQLiteMigrations() { cdstmt = CDClientDatabase::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); cdstmt.bind((int32_t) 1, migration.name.c_str()); auto cdres = cdstmt.execQuery(); - bool doExit = !cdres.eof(); - cdres.finalize(); - cdstmt.finalize(); - if (doExit) continue; + if (!cdres.eof()) continue; // Check first if there is entry in the migration history table on the main database. - stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); - stmt->setString(1, migration.name.c_str()); - auto* res = stmt->executeQuery(); - doExit = res->next(); - delete res; - delete stmt; - if (doExit) { + if (Database::Get()->IsMigrationRun(migration.name)) { // Insert into cdclient database if there is an entry in the main database but not the cdclient database. cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); - cdstmt.bind((int32_t) 1, migration.name.c_str()); - cdstmt.execQuery().finalize(); - cdstmt.finalize(); + cdstmt.bind(static_cast(1), migration.name.c_str()); + cdstmt.execQuery(); continue; } @@ -149,8 +125,7 @@ void MigrationRunner::RunSQLiteMigrations() { // Insert into cdclient database. cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); cdstmt.bind((int32_t) 1, migration.name.c_str()); - cdstmt.execQuery().finalize(); - cdstmt.finalize(); + cdstmt.execQuery(); CDClientDatabase::ExecuteQuery("COMMIT;"); } diff --git a/dDatabase/MigrationRunner.h b/dDatabase/GameDatabase/MigrationRunner.h similarity index 100% rename from dDatabase/MigrationRunner.h rename to dDatabase/GameDatabase/MigrationRunner.h diff --git a/dDatabase/GameDatabase/MySQL/CMakeLists.txt b/dDatabase/GameDatabase/MySQL/CMakeLists.txt new file mode 100644 index 00000000..9114445d --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/CMakeLists.txt @@ -0,0 +1,11 @@ +SET(DDATABSE_DATABSES_MYSQL_SOURCES + "MySQLDatabase.cpp" +) + +add_subdirectory(Tables) + +foreach(file ${DDATABASES_DATABASES_MYSQL_TABLES_SOURCES}) + set(DDATABSE_DATABSES_MYSQL_SOURCES ${DDATABSE_DATABSES_MYSQL_SOURCES} "Tables/${file}") +endforeach() + +set(DDATABSE_DATABSES_MYSQL_SOURCES ${DDATABSE_DATABSES_MYSQL_SOURCES} PARENT_SCOPE) diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.cpp b/dDatabase/GameDatabase/MySQL/MySQLDatabase.cpp new file mode 100644 index 00000000..259c3866 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.cpp @@ -0,0 +1,113 @@ +#include "MySQLDatabase.h" + +#include "Database.h" +#include "Game.h" +#include "dConfig.h" +#include "Logger.h" + +namespace { + std::string databaseName; + sql::Properties properties; + sql::Driver* driver = nullptr; + sql::Connection* con = nullptr; +}; + +void MySQLDatabase::Connect() { + driver = sql::mariadb::get_driver_instance(); + + // The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where + // 1) it tries to parse a database from the connection string (like in tcp://localhost:3001/darkflame) based on the + // presence of a / + // 2) even avoiding that, the connector still assumes you're connecting with a tcp socket + // So, what we do in the presence of a unix socket or pipe is to set the hostname to the protocol and localhost, + // which avoids parsing errors while still ensuring the correct connection type is used, and then setting the appropriate + // property manually (which the URL parsing fails to do) + const std::string UNIX_PROTO = "unix://"; + const std::string PIPE_PROTO = "pipe://"; + std::string mysql_host = Game::config->GetValue("mysql_host"); + if (mysql_host.find(UNIX_PROTO) == 0) { + properties["hostName"] = "unix://localhost"; + properties["localSocket"] = mysql_host.substr(UNIX_PROTO.length()).c_str(); + } else if (mysql_host.find(PIPE_PROTO) == 0) { + properties["hostName"] = "pipe://localhost"; + properties["pipe"] = mysql_host.substr(PIPE_PROTO.length()).c_str(); + } else { + properties["hostName"] = mysql_host.c_str(); + } + properties["user"] = Game::config->GetValue("mysql_username").c_str(); + properties["password"] = Game::config->GetValue("mysql_password").c_str(); + properties["autoReconnect"] = "true"; + + databaseName = Game::config->GetValue("mysql_database").c_str(); + + // `connect(const Properties& props)` segfaults in windows debug, but + // `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly + if (properties.find("localSocket") != properties.end() || properties.find("pipe") != properties.end()) { + con = driver->connect(properties); + } else { + con = driver->connect(properties["hostName"].c_str(), properties["user"].c_str(), properties["password"].c_str()); + } + con->setSchema(databaseName.c_str()); +} + +void MySQLDatabase::Destroy(std::string source) { + if (!con) return; + + if (source.empty()) LOG("Destroying MySQL connection!"); + else LOG("Destroying MySQL connection from %s!", source.c_str()); + + con->close(); + delete con; + con = nullptr; +} + +void MySQLDatabase::ExecuteCustomQuery(const std::string_view query) { + std::unique_ptr(con->createStatement())->execute(query.data()); +} + +sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& query) { + if (!con) { + Connect(); + LOG("Trying to reconnect to MySQL"); + } + + if (!con->isValid() || con->isClosed()) { + delete con; + + con = nullptr; + + Connect(); + LOG("Trying to reconnect to MySQL from invalid or closed connection"); + } + + return con->prepareStatement(sql::SQLString(query.c_str(), query.length())); +} + +void MySQLDatabase::Commit() { + con->commit(); +} + +bool MySQLDatabase::GetAutoCommit() { + // TODO This should not just access a pointer. A future PR should update this + // to check for null and throw an error if the connection is not valid. + return con->getAutoCommit(); +} + +void MySQLDatabase::SetAutoCommit(bool value) { + // TODO This should not just access a pointer. A future PR should update this + // to check for null and throw an error if the connection is not valid. + con->setAutoCommit(value); +} + +void MySQLDatabase::DeleteCharacter(const uint32_t characterId) { + ExecuteDelete("DELETE FROM charxml WHERE id=? LIMIT 1;", characterId); + ExecuteDelete("DELETE FROM command_log WHERE character_id=?;", characterId); + ExecuteDelete("DELETE FROM friends WHERE player_id=? OR friend_id=?;", characterId, characterId); + ExecuteDelete("DELETE FROM leaderboard WHERE character_id=?;", characterId); + ExecuteDelete("DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);", characterId); + ExecuteDelete("DELETE FROM properties WHERE owner_id=?;", characterId); + ExecuteDelete("DELETE FROM ugc WHERE character_id=?;", characterId); + ExecuteDelete("DELETE FROM activity_log WHERE character_id=?;", characterId); + ExecuteDelete("DELETE FROM mail WHERE receiver_id=?;", characterId); + ExecuteDelete("DELETE FROM charinfo WHERE id=? LIMIT 1;", characterId); +} diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h new file mode 100644 index 00000000..bed79bb7 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -0,0 +1,245 @@ +#ifndef __MYSQLDATABASE__H__ +#define __MYSQLDATABASE__H__ + +#include +#include + +#include "GameDatabase.h" + +typedef std::unique_ptr& UniquePreppedStmtRef; + +// Purposefully no definition for this to provide linker errors in the case someone tries to +// bind a parameter to a type that isn't defined. +template +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const ParamType param); + +// This is a function to set each parameter in a prepared statement. +// This is accomplished with a combination of parameter packing and Fold Expressions. +// The constexpr if statement is used to prevent the compiler from trying to call SetParam with 0 arguments. +template +void SetParams(UniquePreppedStmtRef stmt, Args&&... args) { + if constexpr (sizeof...(args) != 0) { + int i = 1; + (SetParam(stmt, i++, args), ...); + } +} + +class MySQLDatabase : public GameDatabase { +public: + void Connect() override; + void Destroy(std::string source = "") override; + + sql::PreparedStatement* CreatePreppedStmt(const std::string& query) override; + void Commit() override; + bool GetAutoCommit() override; + void SetAutoCommit(bool value) override; + void ExecuteCustomQuery(const std::string_view query) override; + + // Overloaded queries + std::optional GetMasterInfo() override; + + std::vector GetApprovedCharacterNames() override; + + std::vector GetFriendsList(uint32_t charID) override; + + std::optional GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) override; + void SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) override; + void AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override; + void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override; + void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override; + void DeleteUgcModelData(const LWOOBJID& modelId) override; + void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override; + std::vector GetAllUgcModels() override; + void CreateMigrationHistoryTable() override; + bool IsMigrationRun(const std::string_view str) override; + void InsertMigration(const std::string_view str) override; + std::optional GetCharacterInfo(const uint32_t charId) override; + std::optional GetCharacterInfo(const std::string_view charId) override; + std::string GetCharacterXml(const uint32_t accountId) override; + void UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) override; + std::optional GetAccountInfo(const std::string_view username) override; + void InsertNewCharacter(const ICharInfo::Info info) override; + void InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) override; + std::vector GetAccountCharacterIds(uint32_t accountId) override; + void DeleteCharacter(const uint32_t characterId) override; + void SetCharacterName(const uint32_t characterId, const std::string_view name) override; + void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) override; + void UpdateLastLoggedInCharacter(const uint32_t characterId) override; + void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) override; + std::optional GetPetNameInfo(const LWOOBJID& petId) override; + std::optional GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override; + void UpdatePropertyModerationInfo(const IProperty::Info& info) override; + void UpdatePropertyDetails(const IProperty::Info& info) override; + void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override; + std::vector GetPropertyModels(const LWOOBJID& propertyId) override; + void RemoveUnreferencedUgcModels() override; + void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override; + void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) override; + void RemoveModel(const LWOOBJID& modelId) override; + void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; + void InsertNewBugReport(const IBugReports::Info& info) override; + void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; + void InsertNewMail(const IMail::MailInfo& mail) override; + void InsertNewUgcModel( + std::istringstream& sd0Data, + const uint32_t blueprintId, + const uint32_t accountId, + const uint32_t characterId) override; + std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override; + std::optional GetMail(const uint64_t mailId) override; + uint32_t GetUnreadMailCount(const uint32_t characterId) override; + void MarkMailRead(const uint64_t mailId) override; + void DeleteMail(const uint64_t mailId) override; + void ClaimMailItem(const uint64_t mailId) override; + void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) override; + void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) override; + void UpdateAccountBan(const uint32_t accountId, const bool banned) override; + void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override; + void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override; + void SetMasterIp(const std::string_view ip, const uint32_t port) override; + std::optional GetCurrentPersistentId() override; + void InsertDefaultPersistentId() override; + void UpdatePersistentId(const uint32_t id) override; + std::optional GetDonationTotal(const uint32_t activityId) override; + std::optional IsPlaykeyActive(const int32_t playkeyId) override; + std::vector GetUgcModels(const LWOOBJID& propertyId) override; +private: + + // Generic query functions that can be used for any query. + // Return type may be different depending on the query, so it is up to the caller to check the return type. + // The first argument is the query string, and the rest are the parameters to bind to the query. + // The return type is a unique_ptr to the result set, which is deleted automatically when it goes out of scope + template + inline std::unique_ptr ExecuteSelect(const std::string& query, Args&&... args) { + std::unique_ptr preppedStmt(CreatePreppedStmt(query)); + SetParams(preppedStmt, std::forward(args)...); + DLU_SQL_TRY_CATCH_RETHROW(return std::unique_ptr(preppedStmt->executeQuery())); + } + + template + inline void ExecuteDelete(const std::string& query, Args&&... args) { + std::unique_ptr preppedStmt(CreatePreppedStmt(query)); + SetParams(preppedStmt, std::forward(args)...); + DLU_SQL_TRY_CATCH_RETHROW(preppedStmt->execute()); + } + + template + inline int32_t ExecuteUpdate(const std::string& query, Args&&... args) { + std::unique_ptr preppedStmt(CreatePreppedStmt(query)); + SetParams(preppedStmt, std::forward(args)...); + DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt->executeUpdate()); + } + + template + inline bool ExecuteInsert(const std::string& query, Args&&... args) { + std::unique_ptr preppedStmt(CreatePreppedStmt(query)); + SetParams(preppedStmt, std::forward(args)...); + DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt->execute()); + } +}; + +// Below are each of the definitions of SetParam for each supported type. + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string_view param) { + // LOG("%s", param.data()); + stmt->setString(index, param.data()); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const char* param) { + // LOG("%s", param); + stmt->setString(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string param) { + // LOG("%s", param.c_str()); + stmt->setString(index, param.c_str()); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int8_t param) { + // LOG("%u", param); + stmt->setByte(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint8_t param) { + // LOG("%d", param); + stmt->setByte(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int16_t param) { + // LOG("%u", param); + stmt->setShort(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint16_t param) { + // LOG("%d", param); + stmt->setShort(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint32_t param) { + // LOG("%u", param); + stmt->setUInt(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int32_t param) { + // LOG("%d", param); + stmt->setInt(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int64_t param) { + // LOG("%llu", param); + stmt->setInt64(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint64_t param) { + // LOG("%llu", param); + stmt->setUInt64(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const float param) { + // LOG("%f", param); + stmt->setFloat(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const double param) { + // LOG("%f", param); + stmt->setDouble(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const bool param) { + // LOG("%d", param); + stmt->setBoolean(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istream* param) { + // LOG("Blob"); + // This is the one time you will ever see me use const_cast. + stmt->setBlob(index, const_cast(param)); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional param) { + if (param) { + // LOG("%d", param.value()); + stmt->setInt(index, param.value()); + } else { + // LOG("Null"); + stmt->setNull(index, sql::DataType::SQLNULL); + } +} + +#endif //!__MYSQLDATABASE__H__ diff --git a/dDatabase/GameDatabase/MySQL/Tables/Accounts.cpp b/dDatabase/GameDatabase/MySQL/Tables/Accounts.cpp new file mode 100644 index 00000000..801f444d --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Accounts.cpp @@ -0,0 +1,37 @@ +#include "MySQLDatabase.h" + +#include "eGameMasterLevel.h" + +std::optional MySQLDatabase::GetAccountInfo(const std::string_view username) { + auto result = ExecuteSelect("SELECT id, password, banned, locked, play_key_id, gm_level FROM accounts WHERE name = ? LIMIT 1;", username); + + if (!result->next()) { + return std::nullopt; + } + + IAccounts::Info toReturn; + toReturn.id = result->getUInt("id"); + toReturn.maxGmLevel = static_cast(result->getInt("gm_level")); + toReturn.bcryptPassword = result->getString("password").c_str(); + toReturn.banned = result->getBoolean("banned"); + toReturn.locked = result->getBoolean("locked"); + toReturn.playKeyId = result->getUInt("play_key_id"); + + return toReturn; +} + +void MySQLDatabase::UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) { + ExecuteUpdate("UPDATE accounts SET mute_expire = ? WHERE id = ?;", timeToUnmute, accountId); +} + +void MySQLDatabase::UpdateAccountBan(const uint32_t accountId, const bool banned) { + ExecuteUpdate("UPDATE accounts SET banned = ? WHERE id = ?;", banned, accountId); +} + +void MySQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) { + ExecuteUpdate("UPDATE accounts SET password = ? WHERE id = ?;", bcryptpassword, accountId); +} + +void MySQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) { + ExecuteInsert("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);", username, bcryptpassword, static_cast(eGameMasterLevel::OPERATOR)); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/ActivityLog.cpp b/dDatabase/GameDatabase/MySQL/Tables/ActivityLog.cpp new file mode 100644 index 00000000..50fd6b79 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/ActivityLog.cpp @@ -0,0 +1,6 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) { + ExecuteInsert("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);", + characterId, static_cast(activityType), static_cast(time(NULL)), mapId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/BugReports.cpp b/dDatabase/GameDatabase/MySQL/Tables/BugReports.cpp new file mode 100644 index 00000000..4f23941d --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/BugReports.cpp @@ -0,0 +1,6 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertNewBugReport(const IBugReports::Info& info) { + ExecuteInsert("INSERT INTO `bug_reports`(body, client_version, other_player_id, selection, reporter_id) VALUES (?, ?, ?, ?, ?)", + info.body, info.clientVersion, info.otherPlayer, info.selection, info.characterId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt new file mode 100644 index 00000000..e9593ba9 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt @@ -0,0 +1,22 @@ +set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES + "Accounts.cpp" + "ActivityLog.cpp" + "BugReports.cpp" + "CharInfo.cpp" + "CharXml.cpp" + "CommandLog.cpp" + "Friends.cpp" + "Leaderboard.cpp" + "Mail.cpp" + "MigrationHistory.cpp" + "ObjectIdTracker.cpp" + "PetNames.cpp" + "PlayerCheatDetections.cpp" + "PlayKeys.cpp" + "Property.cpp" + "PropertyContents.cpp" + "Servers.cpp" + "Ugc.cpp" + PARENT_SCOPE +) + diff --git a/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp b/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp new file mode 100644 index 00000000..7406e69b --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp @@ -0,0 +1,78 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetApprovedCharacterNames() { + auto result = ExecuteSelect("SELECT name FROM charinfo;"); + + std::vector toReturn; + + while (result->next()) { + toReturn.push_back(result->getString("name").c_str()); + } + + return toReturn; +} + +std::optional CharInfoFromQueryResult(std::unique_ptr stmt) { + if (!stmt->next()) { + return std::nullopt; + } + + ICharInfo::Info toReturn; + + toReturn.id = stmt->getUInt("id"); + toReturn.name = stmt->getString("name").c_str(); + toReturn.pendingName = stmt->getString("pending_name").c_str(); + toReturn.needsRename = stmt->getBoolean("needs_rename"); + toReturn.cloneId = stmt->getUInt64("prop_clone_id"); + toReturn.accountId = stmt->getUInt("account_id"); + toReturn.permissionMap = static_cast(stmt->getUInt("permission_map")); + + return toReturn; +} + +std::optional MySQLDatabase::GetCharacterInfo(const uint32_t charId) { + return CharInfoFromQueryResult( + ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE id = ? LIMIT 1;", charId) + ); +} + +std::optional MySQLDatabase::GetCharacterInfo(const std::string_view name) { + return CharInfoFromQueryResult( + ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE name = ? LIMIT 1;", name) + ); +} + +std::vector MySQLDatabase::GetAccountCharacterIds(const uint32_t accountId) { + auto result = ExecuteSelect("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 4;", accountId); + + std::vector toReturn; + toReturn.reserve(result->rowsCount()); + while (result->next()) { + toReturn.push_back(result->getUInt("id")); + } + + return toReturn; +} + +void MySQLDatabase::InsertNewCharacter(const ICharInfo::Info info) { + ExecuteInsert( + "INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)", + info.id, + info.accountId, + info.name, + info.pendingName, + false, + static_cast(time(NULL))); +} + +void MySQLDatabase::SetCharacterName(const uint32_t characterId, const std::string_view name) { + ExecuteUpdate("UPDATE charinfo SET name = ?, pending_name = '', needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1;", name, static_cast(time(NULL)), characterId); +} + +void MySQLDatabase::SetPendingCharacterName(const uint32_t characterId, const std::string_view name) { + ExecuteUpdate("UPDATE charinfo SET pending_name = ?, needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1", name, static_cast(time(NULL)), characterId); +} + +void MySQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) { + ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1", static_cast(time(NULL)), characterId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/CharXml.cpp b/dDatabase/GameDatabase/MySQL/Tables/CharXml.cpp new file mode 100644 index 00000000..91a6351e --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/CharXml.cpp @@ -0,0 +1,19 @@ +#include "MySQLDatabase.h" + +std::string MySQLDatabase::GetCharacterXml(const uint32_t charId) { + auto result = ExecuteSelect("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;", charId); + + if (!result->next()) { + return ""; + } + + return result->getString("xml_data").c_str(); +} + +void MySQLDatabase::UpdateCharacterXml(const uint32_t charId, const std::string_view lxfml) { + ExecuteUpdate("UPDATE charxml SET xml_data = ? WHERE id = ?;", lxfml, charId); +} + +void MySQLDatabase::InsertCharacterXml(const uint32_t characterId, const std::string_view lxfml) { + ExecuteInsert("INSERT INTO `charxml` (`id`, `xml_data`) VALUES (?,?)", characterId, lxfml); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/CommandLog.cpp b/dDatabase/GameDatabase/MySQL/Tables/CommandLog.cpp new file mode 100644 index 00000000..c8ae365a --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/CommandLog.cpp @@ -0,0 +1,5 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) { + ExecuteInsert("INSERT INTO command_log (character_id, command) VALUES (?, ?);", characterId, command); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Friends.cpp b/dDatabase/GameDatabase/MySQL/Tables/Friends.cpp new file mode 100644 index 00000000..da9b34a3 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Friends.cpp @@ -0,0 +1,73 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetFriendsList(const uint32_t charId) { + auto friendsList = ExecuteSelect( + R"QUERY( + SELECT fr.requested_player AS player, best_friend AS bff, ci.name AS name FROM + ( + SELECT CASE + WHEN player_id = ? THEN friend_id + WHEN friend_id = ? THEN player_id + END AS requested_player, best_friend FROM friends + ) AS fr + JOIN charinfo AS ci ON ci.id = fr.requested_player + WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ?; + )QUERY", charId, charId, charId); + + std::vector toReturn; + toReturn.reserve(friendsList->rowsCount()); + + while (friendsList->next()) { + FriendData fd; + fd.friendID = friendsList->getUInt("player"); + fd.isBestFriend = friendsList->getInt("bff") == 3; // 0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs + fd.friendName = friendsList->getString("name").c_str(); + + toReturn.push_back(fd); + } + + return toReturn; +} + +std::optional MySQLDatabase::GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) { + auto result = ExecuteSelect("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;", + playerCharacterId, + friendCharacterId, + friendCharacterId, + playerCharacterId + ); + + if (!result->next()) { + return std::nullopt; + } + + IFriends::BestFriendStatus toReturn; + toReturn.playerCharacterId = result->getUInt("player_id"); + toReturn.friendCharacterId = result->getUInt("friend_id"); + toReturn.bestFriendStatus = result->getUInt("best_friend"); + + return toReturn; +} + +void MySQLDatabase::SetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId, const uint32_t bestFriendStatus) { + ExecuteUpdate("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;", + bestFriendStatus, + playerCharacterId, + friendCharacterId, + friendCharacterId, + playerCharacterId + ); +} + +void MySQLDatabase::AddFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) { + ExecuteInsert("INSERT IGNORE INTO friends (player_id, friend_id, best_friend) VALUES (?, ?, 0);", playerCharacterId, friendCharacterId); +} + +void MySQLDatabase::RemoveFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) { + ExecuteDelete("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;", + playerCharacterId, + friendCharacterId, + friendCharacterId, + playerCharacterId + ); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Leaderboard.cpp b/dDatabase/GameDatabase/MySQL/Tables/Leaderboard.cpp new file mode 100644 index 00000000..22403abb --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Leaderboard.cpp @@ -0,0 +1,11 @@ +#include "MySQLDatabase.h" + +std::optional MySQLDatabase::GetDonationTotal(const uint32_t activityId) { + auto donation_total = ExecuteSelect("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;", activityId); + + if (!donation_total->next()) { + return std::nullopt; + } + + return donation_total->getUInt("donation_total"); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp new file mode 100644 index 00000000..63f5ceca --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp @@ -0,0 +1,83 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertNewMail(const IMail::MailInfo& mail) { + ExecuteInsert( + "INSERT INTO `mail` " + "(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`)" + " VALUES (?,?,?,?,?,?,?,?,?,?,?,0)", + mail.senderId, + mail.senderUsername, + mail.receiverId, + mail.recipient, + static_cast(time(NULL)), + mail.subject, + mail.body, + mail.itemID, + mail.itemLOT, + 0, + mail.itemCount); +} + +std::vector MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) { + auto res = ExecuteSelect( + "SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent" + " FROM mail WHERE receiver_id=? limit ?;", + characterId, numberOfMail); + + std::vector toReturn; + toReturn.reserve(res->rowsCount()); + + while (res->next()) { + IMail::MailInfo mail; + mail.id = res->getUInt64("id"); + mail.subject = res->getString("subject").c_str(); + mail.body = res->getString("body").c_str(); + mail.senderUsername = res->getString("sender_name").c_str(); + mail.itemID = res->getUInt("attachment_id"); + mail.itemLOT = res->getInt("attachment_lot"); + mail.itemSubkey = res->getInt("attachment_subkey"); + mail.itemCount = res->getInt("attachment_count"); + mail.timeSent = res->getUInt64("time_sent"); + mail.wasRead = res->getBoolean("was_read"); + + toReturn.push_back(std::move(mail)); + } + + return toReturn; +} + +std::optional MySQLDatabase::GetMail(const uint64_t mailId) { + auto res = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId); + + if (!res->next()) { + return std::nullopt; + } + + IMail::MailInfo toReturn; + toReturn.itemLOT = res->getInt("attachment_lot"); + toReturn.itemCount = res->getInt("attachment_count"); + + return toReturn; +} + +uint32_t MySQLDatabase::GetUnreadMailCount(const uint32_t characterId) { + auto res = ExecuteSelect("SELECT COUNT(*) AS number_unread FROM mail WHERE receiver_id=? AND was_read=0;", characterId); + + if (!res->next()) { + return 0; + } + + return res->getInt("number_unread"); +} + +void MySQLDatabase::MarkMailRead(const uint64_t mailId) { + ExecuteUpdate("UPDATE mail SET was_read=1 WHERE id=? LIMIT 1;", mailId); +} + +void MySQLDatabase::ClaimMailItem(const uint64_t mailId) { + ExecuteUpdate("UPDATE mail SET attachment_lot=0 WHERE id=? LIMIT 1;", mailId); +} + +void MySQLDatabase::DeleteMail(const uint64_t mailId) { + ExecuteDelete("DELETE FROM mail WHERE id=? LIMIT 1;", mailId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/MigrationHistory.cpp b/dDatabase/GameDatabase/MySQL/Tables/MigrationHistory.cpp new file mode 100644 index 00000000..a0afc341 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/MigrationHistory.cpp @@ -0,0 +1,13 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::CreateMigrationHistoryTable() { + ExecuteInsert("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());"); +} + +bool MySQLDatabase::IsMigrationRun(const std::string_view str) { + return ExecuteSelect("SELECT name FROM migration_history WHERE name = ?;", str)->next(); +} + +void MySQLDatabase::InsertMigration(const std::string_view str) { + ExecuteInsert("INSERT INTO migration_history (name) VALUES (?);", str); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/ObjectIdTracker.cpp b/dDatabase/GameDatabase/MySQL/Tables/ObjectIdTracker.cpp new file mode 100644 index 00000000..f22cd855 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/ObjectIdTracker.cpp @@ -0,0 +1,17 @@ +#include "MySQLDatabase.h" + +std::optional MySQLDatabase::GetCurrentPersistentId() { + auto result = ExecuteSelect("SELECT last_object_id FROM object_id_tracker"); + if (!result->next()) { + return std::nullopt; + } + return result->getUInt("last_object_id"); +} + +void MySQLDatabase::InsertDefaultPersistentId() { + ExecuteInsert("INSERT INTO object_id_tracker VALUES (1);"); +} + +void MySQLDatabase::UpdatePersistentId(const uint32_t newId) { + ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = ?;", newId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/PetNames.cpp b/dDatabase/GameDatabase/MySQL/Tables/PetNames.cpp new file mode 100644 index 00000000..dd2a3a6c --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/PetNames.cpp @@ -0,0 +1,26 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) { + ExecuteInsert( + "INSERT INTO `pet_names` (`id`, `pet_name`, `approved`) VALUES (?, ?, ?) " + "ON DUPLICATE KEY UPDATE pet_name = ?, approved = ?;", + petId, + info.petName, + info.approvalStatus, + info.petName, + info.approvalStatus); +} + +std::optional MySQLDatabase::GetPetNameInfo(const LWOOBJID& petId) { + auto result = ExecuteSelect("SELECT pet_name, approved FROM pet_names WHERE id = ? LIMIT 1;", petId); + + if (!result->next()) { + return std::nullopt; + } + + IPetNames::Info toReturn; + toReturn.petName = result->getString("pet_name").c_str(); + toReturn.approvalStatus = result->getInt("approved"); + + return toReturn; +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/PlayKeys.cpp b/dDatabase/GameDatabase/MySQL/Tables/PlayKeys.cpp new file mode 100644 index 00000000..63f2822a --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/PlayKeys.cpp @@ -0,0 +1,11 @@ +#include "MySQLDatabase.h" + +std::optional MySQLDatabase::IsPlaykeyActive(const int32_t playkeyId) { + auto keyCheckRes = ExecuteSelect("SELECT active FROM `play_keys` WHERE id=?", playkeyId); + + if (!keyCheckRes->next()) { + return std::nullopt; + } + + return keyCheckRes->getBoolean("active"); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/PlayerCheatDetections.cpp b/dDatabase/GameDatabase/MySQL/Tables/PlayerCheatDetections.cpp new file mode 100644 index 00000000..753630c3 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/PlayerCheatDetections.cpp @@ -0,0 +1,7 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertCheatDetection(const IPlayerCheatDetections::Info& info) { + ExecuteInsert( + "INSERT INTO player_cheat_detections (account_id, name, violation_msg, violation_system_address) VALUES (?, ?, ?, ?)", + info.userId, info.username, info.extraMessage, info.systemAddress); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Property.cpp b/dDatabase/GameDatabase/MySQL/Tables/Property.cpp new file mode 100644 index 00000000..5d72a3b5 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Property.cpp @@ -0,0 +1,57 @@ +#include "MySQLDatabase.h" + +std::optional MySQLDatabase::GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) { + auto propertyEntry = ExecuteSelect( + "SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved " + "FROM properties WHERE zone_id = ? AND clone_id = ?;", mapId, cloneId); + + if (!propertyEntry->next()) { + return std::nullopt; + } + + IProperty::Info toReturn; + toReturn.id = propertyEntry->getUInt64("id"); + toReturn.ownerId = propertyEntry->getUInt64("owner_id"); + toReturn.cloneId = propertyEntry->getUInt64("clone_id"); + toReturn.name = propertyEntry->getString("name").c_str(); + toReturn.description = propertyEntry->getString("description").c_str(); + toReturn.privacyOption = propertyEntry->getInt("privacy_option"); + toReturn.rejectionReason = propertyEntry->getString("rejection_reason").c_str(); + toReturn.lastUpdatedTime = propertyEntry->getUInt("last_updated"); + toReturn.claimedTime = propertyEntry->getUInt("time_claimed"); + toReturn.reputation = propertyEntry->getUInt("reputation"); + toReturn.modApproved = propertyEntry->getUInt("mod_approved"); + + return toReturn; +} + +void MySQLDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) { + ExecuteUpdate("UPDATE properties SET privacy_option = ?, rejection_reason = ?, mod_approved = ? WHERE id = ? LIMIT 1;", + info.privacyOption, + info.rejectionReason, + info.modApproved, + info.id); +} + +void MySQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) { + ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ? LIMIT 1;", info.name, info.description, info.id); +} + +void MySQLDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) { + ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ? LIMIT 1;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID()); +} + +void MySQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) { + auto insertion = ExecuteInsert( + "INSERT INTO properties" + "(id, owner_id, template_id, clone_id, name, description, zone_id, rent_amount, rent_due, privacy_option, last_updated, time_claimed, rejection_reason, reputation, performance_cost)" + "VALUES (?, ?, ?, ?, ?, ?, ?, 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '', 0, 0.0)", + info.id, + info.ownerId, + templateId, + zoneId.GetCloneID(), + info.name, + info.description, + zoneId.GetMapID() + ); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp new file mode 100644 index 00000000..dba82d56 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp @@ -0,0 +1,54 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) { + auto result = ExecuteSelect("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;", propertyId); + + std::vector toReturn; + toReturn.reserve(result->rowsCount()); + while (result->next()) { + IPropertyContents::Model model; + model.id = result->getUInt64("id"); + model.lot = static_cast(result->getUInt("lot")); + model.position.x = result->getFloat("x"); + model.position.y = result->getFloat("y"); + model.position.z = result->getFloat("z"); + model.rotation.w = result->getFloat("rw"); + model.rotation.x = result->getFloat("rx"); + model.rotation.y = result->getFloat("ry"); + model.rotation.z = result->getFloat("rz"); + model.ugcId = result->getUInt64("ugc_id"); + toReturn.push_back(std::move(model)); + } + return toReturn; +} + +void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) { + try { + ExecuteInsert( + "INSERT INTO properties_contents" + "(id, property_id, ugc_id, lot, x, y, z, rx, ry, rz, rw, model_name, model_description, behavior_1, behavior_2, behavior_3, behavior_4, behavior_5)" + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 18 + model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast(model.lot), + model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w, + name, "", // Model description. TODO implement this. + 0, // behavior 1. TODO implement this. + 0, // behavior 2. TODO implement this. + 0, // behavior 3. TODO implement this. + 0, // behavior 4. TODO implement this. + 0 // behavior 5. TODO implement this. + ); + } catch (sql::SQLException& e) { + LOG("Error inserting new property model: %s", e.what()); + } +} + +void MySQLDatabase::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) { + ExecuteUpdate( + "UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;", + position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId); +} + +void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) { + ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp b/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp new file mode 100644 index 00000000..4411ad21 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp @@ -0,0 +1,23 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) { + // We only want our 1 entry anyways, so we can just delete all and reinsert the one we want + // since it would be two queries anyways. + ExecuteDelete("TRUNCATE TABLE servers;"); + ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port); +} + +std::optional MySQLDatabase::GetMasterInfo() { + auto result = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;"); + + if (!result->next()) { + return std::nullopt; + } + + MasterInfo toReturn; + + toReturn.ip = result->getString("ip").c_str(); + toReturn.port = result->getInt("port"); + + return toReturn; +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp b/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp new file mode 100644 index 00000000..3b62a51b --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp @@ -0,0 +1,71 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) { + auto result = ExecuteSelect( + "SELECT lxfml, u.id FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;", + propertyId); + + std::vector toReturn; + + while (result->next()) { + IUgc::Model model; + + // blob is owned by the query, so we need to do a deep copy :/ + std::unique_ptr blob(result->getBlob("lxfml")); + model.lxfmlData << blob->rdbuf(); + model.id = result->getUInt64("id"); + toReturn.push_back(std::move(model)); + } + + return toReturn; +} + +std::vector MySQLDatabase::GetAllUgcModels() { + auto result = ExecuteSelect("SELECT id, lxfml FROM ugc;"); + + std::vector models; + models.reserve(result->rowsCount()); + while (result->next()) { + IUgc::Model model; + model.id = result->getInt64("id"); + + // blob is owned by the query, so we need to do a deep copy :/ + std::unique_ptr blob(result->getBlob("lxfml")); + model.lxfmlData << blob->rdbuf(); + models.push_back(std::move(model)); + } + + return models; +} + +void MySQLDatabase::RemoveUnreferencedUgcModels() { + ExecuteDelete("DELETE FROM ugc WHERE id NOT IN (SELECT ugc_id FROM properties_contents WHERE ugc_id IS NOT NULL);"); +} + +void MySQLDatabase::InsertNewUgcModel( + std::istringstream& sd0Data, // cant be const sad + const uint32_t blueprintId, + const uint32_t accountId, + const uint32_t characterId) { + const std::istream stream(sd0Data.rdbuf()); + ExecuteInsert( + "INSERT INTO `ugc`(`id`, `account_id`, `character_id`, `is_optimized`, `lxfml`, `bake_ao`, `filename`) VALUES (?,?,?,?,?,?,?)", + blueprintId, + accountId, + characterId, + 0, + &stream, + false, + "weedeater.lxfml" + ); +} + +void MySQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) { + ExecuteDelete("DELETE FROM ugc WHERE id = ?;", modelId); + ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId); +} + +void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) { + const std::istream stream(lxfml.rdbuf()); + ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId); +} diff --git a/dGame/Character.cpp b/dGame/Character.cpp index 7259602f..ee04710a 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -25,104 +25,36 @@ Character::Character(uint32_t id, User* parentUser) { //First load the name, etc: m_ID = id; - - sql::PreparedStatement* stmt = Database::CreatePreppedStmt( - "SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;" - ); - - stmt->setInt64(1, id); - - sql::ResultSet* res = stmt->executeQuery(); - - while (res->next()) { - m_Name = res->getString(1).c_str(); - m_UnapprovedName = res->getString(2).c_str(); - m_NameRejected = res->getBoolean(3); - m_PropertyCloneID = res->getUInt(4); - m_PermissionMap = static_cast(res->getUInt64(5)); - } - - delete res; - delete stmt; - - //Load the xmlData now: - sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt( - "SELECT xml_data FROM charxml WHERE id=? LIMIT 1;" - ); - - xmlStmt->setInt64(1, id); - - sql::ResultSet* xmlRes = xmlStmt->executeQuery(); - while (xmlRes->next()) { - m_XMLData = xmlRes->getString(1).c_str(); - } - - delete xmlRes; - delete xmlStmt; - - m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing. - m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused. - m_ZoneCloneID = 0; - - m_Doc = nullptr; - - //Quickly and dirtly parse the xmlData to get the info we need: - DoQuickXMLDataParse(); - - //Set our objectID: - m_ObjectID = m_ID; - GeneralUtils::SetBit(m_ObjectID, eObjectBits::CHARACTER); - GeneralUtils::SetBit(m_ObjectID, eObjectBits::PERSISTENT); - m_ParentUser = parentUser; m_OurEntity = nullptr; - m_BuildMode = false; + m_Doc = nullptr; } Character::~Character() { - delete m_Doc; + if (m_Doc) delete m_Doc; m_Doc = nullptr; + m_OurEntity = nullptr; + m_ParentUser = nullptr; } -void Character::UpdateFromDatabase() { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt( - "SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;" - ); +void Character::UpdateInfoFromDatabase() { + auto charInfo = Database::Get()->GetCharacterInfo(m_ID); - stmt->setInt64(1, m_ID); - - sql::ResultSet* res = stmt->executeQuery(); - - while (res->next()) { - m_Name = res->getString(1).c_str(); - m_UnapprovedName = res->getString(2).c_str(); - m_NameRejected = res->getBoolean(3); - m_PropertyCloneID = res->getUInt(4); - m_PermissionMap = static_cast(res->getUInt64(5)); + if (charInfo) { + m_Name = charInfo->name; + m_UnapprovedName = charInfo->pendingName; + m_NameRejected = charInfo->needsRename; + m_PropertyCloneID = charInfo->cloneId; + m_PermissionMap = charInfo->permissionMap; } - delete res; - delete stmt; - //Load the xmlData now: - sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt( - "SELECT xml_data FROM charxml WHERE id=? LIMIT 1;" - ); - xmlStmt->setInt64(1, m_ID); - - sql::ResultSet* xmlRes = xmlStmt->executeQuery(); - while (xmlRes->next()) { - m_XMLData = xmlRes->getString(1).c_str(); - } - - delete xmlRes; - delete xmlStmt; + m_XMLData = Database::Get()->GetCharacterXml(m_ID); m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing. m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused. m_ZoneCloneID = 0; - delete m_Doc; m_Doc = nullptr; //Quickly and dirtly parse the xmlData to get the info we need: @@ -137,6 +69,11 @@ void Character::UpdateFromDatabase() { m_BuildMode = false; } +void Character::UpdateFromDatabase() { + if (m_Doc) delete m_Doc; + UpdateInfoFromDatabase(); +} + void Character::DoQuickXMLDataParse() { if (m_XMLData.size() == 0) return; @@ -406,17 +343,11 @@ void Character::SetIsNewLogin() { void Character::WriteToDatabase() { //Dump our xml into m_XMLData: - auto* printer = new tinyxml2::XMLPrinter(0, true, 0); - m_Doc->Print(printer); - m_XMLData = printer->CStr(); + tinyxml2::XMLPrinter printer(0, true, 0); + m_Doc->Print(&printer); //Finally, save to db: - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charxml SET xml_data=? WHERE id=?"); - stmt->setString(1, m_XMLData.c_str()); - stmt->setUInt(2, m_ID); - stmt->execute(); - delete stmt; - delete printer; + Database::Get()->UpdateCharacterXml(m_ID, printer.CStr()); } void Character::SetPlayerFlag(const uint32_t flagId, const bool value) { diff --git a/dGame/Character.h b/dGame/Character.h index 79ce0c0c..77a52f44 100644 --- a/dGame/Character.h +++ b/dGame/Character.h @@ -458,6 +458,7 @@ public: void SetBillboardVisible(bool visible); private: + void UpdateInfoFromDatabase(); /** * The ID of this character. First 32 bits of the ObjectID. */ diff --git a/dGame/LeaderboardManager.cpp b/dGame/LeaderboardManager.cpp index b7083ea8..6d928aaa 100644 --- a/dGame/LeaderboardManager.cpp +++ b/dGame/LeaderboardManager.cpp @@ -237,7 +237,7 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r } baseLookup += " LIMIT 1"; LOG_DEBUG("query is %s", baseLookup.c_str()); - std::unique_ptr baseQuery(Database::CreatePreppedStmt(baseLookup)); + std::unique_ptr baseQuery(Database::Get()->CreatePreppedStmt(baseLookup)); baseQuery->setInt(1, this->gameID); std::unique_ptr baseResult(baseQuery->executeQuery()); @@ -250,7 +250,7 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r std::unique_ptr lookupBuffer = std::make_unique(STRING_LENGTH); int32_t res = snprintf(lookupBuffer.get(), STRING_LENGTH, queryBase.c_str(), orderBase.data(), filter.c_str(), resultStart, resultEnd); DluAssert(res != -1); - std::unique_ptr query(Database::CreatePreppedStmt(lookupBuffer.get())); + std::unique_ptr query(Database::Get()->CreatePreppedStmt(lookupBuffer.get())); LOG_DEBUG("Query is %s vars are %i %i %i", lookupBuffer.get(), this->gameID, this->relatedPlayer, relatedPlayerLeaderboardId); query->setInt(1, this->gameID); if (this->infoType == InfoType::Friends) { @@ -301,7 +301,7 @@ std::string FormatInsert(const Leaderboard::Type& type, const Score& score, cons void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore, const float tertiaryScore) { const Leaderboard::Type leaderboardType = GetLeaderboardType(activityId); - std::unique_ptr query(Database::CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;")); + std::unique_ptr query(Database::Get()->CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;")); query->setInt(1, playerID); query->setInt(2, activityId); std::unique_ptr myScoreResult(query->executeQuery()); @@ -378,14 +378,14 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi saveQuery = FormatInsert(leaderboardType, newScore, false); } LOG("save query %s %i %i", saveQuery.c_str(), playerID, activityId); - std::unique_ptr saveStatement(Database::CreatePreppedStmt(saveQuery)); + std::unique_ptr saveStatement(Database::Get()->CreatePreppedStmt(saveQuery)); saveStatement->setInt(1, playerID); saveStatement->setInt(2, activityId); saveStatement->execute(); // track wins separately if (leaderboardType == Leaderboard::Type::Racing && tertiaryScore != 0.0f) { - std::unique_ptr winUpdate(Database::CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;")); + std::unique_ptr winUpdate(Database::Get()->CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;")); winUpdate->setInt(1, playerID); winUpdate->setInt(2, activityId); winUpdate->execute(); diff --git a/dGame/User.cpp b/dGame/User.cpp index afde75f6..ce440bd7 100644 --- a/dGame/User.cpp +++ b/dGame/User.cpp @@ -23,41 +23,23 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std: m_IsBestFriendMap = std::unordered_map(); - //HACK HACK HACK - //This needs to be re-enabled / updated whenever the mute stuff is moved to another table. - //This was only done because otherwise the website's account page dies and the website is waiting on a migration to wordpress. - - //sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gmlevel, mute_expire FROM accounts WHERE name=? LIMIT 1;"); - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gm_level FROM accounts WHERE name=? LIMIT 1;"); - stmt->setString(1, username.c_str()); - - sql::ResultSet* res = stmt->executeQuery(); - while (res->next()) { - m_AccountID = res->getUInt(1); - m_MaxGMLevel = static_cast(res->getInt(2)); + auto userInfo = Database::Get()->GetAccountInfo(username); + if (userInfo) { + m_AccountID = userInfo->id; + m_MaxGMLevel = userInfo->maxGmLevel; m_MuteExpire = 0; //res->getUInt64(3); } - delete res; - delete stmt; - //If we're loading a zone, we'll load the last used (aka current) character: if (Game::server->GetZoneID() != 0) { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 1;"); - stmt->setUInt(1, m_AccountID); - - sql::ResultSet* res = stmt->executeQuery(); - if (res->rowsCount() > 0) { - while (res->next()) { - LWOOBJID objID = res->getUInt64(1); - Character* character = new Character(uint32_t(objID), this); - m_Characters.push_back(character); - LOG("Loaded %llu as it is the last used char", objID); - } + auto characterList = Database::Get()->GetAccountCharacterIds(m_AccountID); + if (!characterList.empty()) { + const uint32_t lastUsedCharacterId = characterList.front(); + Character* character = new Character(lastUsedCharacterId, this); + character->UpdateFromDatabase(); + m_Characters.push_back(character); + LOG("Loaded %i as it is the last used char", lastUsedCharacterId); } - - delete res; - delete stmt; } } @@ -92,10 +74,7 @@ User& User::operator= (const User& other) { } bool User::operator== (const User& other) const { - if (m_Username == other.m_Username || m_SessionKey == other.m_SessionKey || m_SystemAddress == other.m_SystemAddress) - return true; - - return false; + return m_Username == other.m_Username || m_SessionKey == other.m_SessionKey || m_SystemAddress == other.m_SystemAddress; } Character* User::GetLastUsedChar() { diff --git a/dGame/UserManager.cpp b/dGame/UserManager.cpp index 0b26822d..9820636a 100644 --- a/dGame/UserManager.cpp +++ b/dGame/UserManager.cpp @@ -158,20 +158,6 @@ void UserManager::DeletePendingRemovals() { m_UsersToDelete.clear(); } -bool UserManager::IsNameAvailable(const std::string& requestedName) { - bool toReturn = false; //To allow for a clean exit - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? OR pending_name=? LIMIT 1;"); - stmt->setString(1, requestedName.c_str()); - stmt->setString(2, requestedName.c_str()); - - sql::ResultSet* res = stmt->executeQuery(); - if (res->rowsCount() == 0) toReturn = true; - - delete stmt; - delete res; - return toReturn; -} - std::string UserManager::GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex) { if (firstNameIndex > m_FirstNames.size() || middleNameIndex > m_MiddleNames.size() || lastNameIndex > m_LastNames.size()) return std::string("INVALID"); return std::string(m_FirstNames[firstNameIndex] + m_MiddleNames[middleNameIndex] + m_LastNames[lastNameIndex]); @@ -200,11 +186,6 @@ bool UserManager::IsNamePreapproved(const std::string& requestedName) { void UserManager::RequestCharacterList(const SystemAddress& sysAddr) { User* u = GetUser(sysAddr); if (!u) return; - - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 4;"); - stmt->setUInt(1, u->GetAccountID()); - - sql::ResultSet* res = stmt->executeQuery(); std::vector& chars = u->GetCharacters(); for (size_t i = 0; i < chars.size(); ++i) { @@ -232,16 +213,13 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) { chars.clear(); - while (res->next()) { - LWOOBJID objID = res->getUInt64(1); - Character* character = new Character(uint32_t(objID), u); + for (const auto& characterId : Database::Get()->GetAccountCharacterIds(u->GetAccountID())) { + Character* character = new Character(characterId, u); + character->UpdateFromDatabase(); character->SetIsNewLogin(); chars.push_back(character); } - delete res; - delete stmt; - WorldPackets::SendCharacterList(sysAddr, u); } @@ -270,19 +248,19 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle); LOT pantsLOT = FindCharPantsID(pantsColor); - if (name != "" && !UserManager::IsNameAvailable(name)) { + if (!name.empty() && Database::Get()->GetCharacterInfo(name)) { LOG("AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str()); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE); return; } - if (!IsNameAvailable(predefinedName)) { + if (Database::Get()->GetCharacterInfo(predefinedName)) { LOG("AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str()); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::PREDEFINED_NAME_IN_USE); return; } - if (name == "") { + if (name.empty()) { LOG("AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str()); } else { LOG("AccountID: %i is creating a character with name: %s (temporary: %s)", u->GetAccountID(), name.c_str(), predefinedName.c_str()); @@ -290,13 +268,8 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) //Now that the name is ok, we can get an objectID from Master: ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) { - sql::PreparedStatement* overlapStmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE id = ?"); - overlapStmt->setUInt(1, objectID); - - auto* overlapResult = overlapStmt->executeQuery(); - - if (overlapResult->next()) { - LOG("Character object id unavailable, check objectidtracker!"); + if (Database::Get()->GetCharacterInfo(objectID)) { + LOG("Character object id unavailable, check object_id_tracker!"); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE); return; } @@ -337,41 +310,19 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) bool nameOk = IsNamePreapproved(name); if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true; - if (name != "") { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)"); - stmt->setUInt(1, objectID); - stmt->setUInt(2, u->GetAccountID()); - stmt->setString(3, predefinedName.c_str()); - stmt->setString(4, name.c_str()); - stmt->setBoolean(5, false); - stmt->setUInt64(6, time(NULL)); + std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName; + std::string pendingName = !name.empty() && !nameOk ? name : ""; - if (nameOk) { - stmt->setString(3, name.c_str()); - stmt->setString(4, ""); - } + ICharInfo::Info info; + info.name = nameToAssign; + info.pendingName = pendingName; + info.id = objectID; + info.accountId = u->GetAccountID(); - stmt->execute(); - delete stmt; - } else { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)"); - stmt->setUInt(1, objectID); - stmt->setUInt(2, u->GetAccountID()); - stmt->setString(3, predefinedName.c_str()); - stmt->setString(4, ""); - stmt->setBoolean(5, false); - stmt->setUInt64(6, time(NULL)); - - stmt->execute(); - delete stmt; - } + Database::Get()->InsertNewCharacter(info); //Now finally insert our character xml: - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charxml`(`id`, `xml_data`) VALUES (?,?)"); - stmt->setUInt(1, objectID); - stmt->setString(2, xml3.str().c_str()); - stmt->execute(); - delete stmt; + Database::Get()->InsertCharacterXml(objectID, xml3.str()); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS); UserManager::RequestCharacterList(sysAddr); @@ -403,73 +354,12 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) WorldPackets::SendCharacterDeleteResponse(sysAddr, false); } else { LOG("Deleting character %i", charID); - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charxml WHERE id=? LIMIT 1;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM command_log WHERE character_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE player_id=? OR friend_id=?;"); - stmt->setUInt(1, charID); - stmt->setUInt(2, charID); - stmt->execute(); - delete stmt; - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION); - bitStream.Write(objectID); - Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM leaderboard WHERE character_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt( - "DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);" - ); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM properties WHERE owner_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM ugc WHERE character_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM activity_log WHERE character_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM mail WHERE receiver_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charinfo WHERE id=? LIMIT 1;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } + Database::Get()->DeleteCharacter(charID); + + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION); + bitStream.Write(objectID); + Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); WorldPackets::SendCharacterDeleteResponse(sysAddr, true); } @@ -517,26 +407,14 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) return; } - if (IsNameAvailable(newName)) { + if (Database::Get()->GetCharacterInfo(newName)) { if (IsNamePreapproved(newName)) { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET name=?, pending_name='', needs_rename=0, last_login=? WHERE id=? LIMIT 1"); - stmt->setString(1, newName); - stmt->setUInt64(2, time(NULL)); - stmt->setUInt(3, character->GetID()); - stmt->execute(); - delete stmt; - + Database::Get()->SetCharacterName(charID, newName); LOG("Character %s now known as %s", character->GetName().c_str(), newName.c_str()); WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS); UserManager::RequestCharacterList(sysAddr); } else { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET pending_name=?, needs_rename=0, last_login=? WHERE id=? LIMIT 1"); - stmt->setString(1, newName); - stmt->setUInt64(2, time(NULL)); - stmt->setUInt(3, character->GetID()); - stmt->execute(); - delete stmt; - + Database::Get()->SetPendingCharacterName(charID, newName); LOG("Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.c_str()); WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS); UserManager::RequestCharacterList(sysAddr); @@ -566,11 +444,7 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID } if (hasCharacter && character) { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET last_login=? WHERE id=? LIMIT 1"); - stmt->setUInt64(1, time(NULL)); - stmt->setUInt(2, playerID); - stmt->execute(); - delete stmt; + Database::Get()->UpdateLastLoggedInCharacter(playerID); uint32_t zoneID = character->GetZoneID(); if (zoneID == LWOZONEID_INVALID) zoneID = 1000; //Send char to VE diff --git a/dGame/UserManager.h b/dGame/UserManager.h index bf9985a1..94bf5e95 100644 --- a/dGame/UserManager.h +++ b/dGame/UserManager.h @@ -28,7 +28,6 @@ public: bool DeleteUser(const SystemAddress& sysAddr); //Returns true on succesful deletion void DeletePendingRemovals(); - bool IsNameAvailable(const std::string& requestedName); std::string GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex); bool IsNamePreapproved(const std::string& requestedName); diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index 7a30d821..8c59b28e 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -700,7 +700,7 @@ std::string CharacterComponent::StatisticsToString() const { } uint64_t CharacterComponent::GetStatisticFromSplit(std::vector split, uint32_t index) { - return split.size() > index ? std::stoul(split.at(index)) : 0; + return split.size() > index ? std::stoull(split.at(index)) : 0; } ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) { diff --git a/dGame/dComponents/DonationVendorComponent.cpp b/dGame/dComponents/DonationVendorComponent.cpp index 7f85ea97..6abc959a 100644 --- a/dGame/dComponents/DonationVendorComponent.cpp +++ b/dGame/dComponents/DonationVendorComponent.cpp @@ -22,10 +22,8 @@ DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorCompone return; } - std::unique_ptr query(Database::CreatePreppedStmt("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;")); - query->setInt(1, m_ActivityId); - std::unique_ptr donation_total(query->executeQuery()); - if (donation_total->next()) m_TotalDonated = donation_total->getInt("donation_total"); + auto donationTotal = Database::Get()->GetDonationTotal(m_ActivityId); + if (donationTotal) m_TotalDonated = donationTotal.value(); m_TotalRemaining = m_Goal - m_TotalDonated; m_PercentComplete = m_TotalDonated/static_cast(m_Goal); } diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index df473055..1780437e 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -1097,37 +1097,18 @@ void PetComponent::SetPetNameForModeration(const std::string& petName) { approved = 2; //approved } - auto deleteStmt = Database::CreatePreppedStmt("DELETE FROM pet_names WHERE id = ? LIMIT 1;"); - deleteStmt->setUInt64(1, m_DatabaseId); - - deleteStmt->execute(); - - delete deleteStmt; - //Save to db: - auto stmt = Database::CreatePreppedStmt("INSERT INTO `pet_names` (`id`, `pet_name`, `approved`) VALUES (?, ?, ?);"); - stmt->setUInt64(1, m_DatabaseId); - stmt->setString(2, petName); - stmt->setInt(3, approved); - stmt->execute(); - delete stmt; + Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{petName, approved}); } void PetComponent::LoadPetNameFromModeration() { - auto stmt = Database::CreatePreppedStmt("SELECT pet_name, approved FROM pet_names WHERE id = ? LIMIT 1;"); - stmt->setUInt64(1, m_DatabaseId); - - auto res = stmt->executeQuery(); - while (res->next()) { - m_ModerationStatus = res->getInt(2); - + auto petNameInfo = Database::Get()->GetPetNameInfo(m_DatabaseId); + if (petNameInfo) { + m_ModerationStatus = petNameInfo->approvalStatus; if (m_ModerationStatus == 2) { - m_Name = res->getString(1); + m_Name = petNameInfo->petName; } } - - delete res; - delete stmt; } void PetComponent::SetPreconditions(std::string& preconditions) { diff --git a/dGame/dComponents/PropertyEntranceComponent.cpp b/dGame/dComponents/PropertyEntranceComponent.cpp index d8b3eb95..4d7ea16d 100644 --- a/dGame/dComponents/PropertyEntranceComponent.cpp +++ b/dGame/dComponents/PropertyEntranceComponent.cpp @@ -103,7 +103,7 @@ std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMe if (sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS) { std::string friendsList = " AND p.owner_id IN ("; - auto friendsListQuery = Database::CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;"); + auto friendsListQuery = Database::Get()->CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;"); friendsListQuery->setUInt(1, character->GetID()); friendsListQuery->setUInt(2, character->GetID()); @@ -147,7 +147,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl if (!character) return; // Player property goes in index 1 of the vector. This is how the client expects it. - auto playerPropertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?"); + auto playerPropertyLookup = Database::Get()->CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?"); playerPropertyLookup->setInt(1, character->GetID()); playerPropertyLookup->setInt(2, this->m_MapID); @@ -180,7 +180,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl const auto query = BuildQuery(entity, sortMethod, character); - auto propertyLookup = Database::CreatePreppedStmt(query); + auto propertyLookup = Database::Get()->CreatePreppedStmt(query); const auto searchString = "%" + filterText + "%"; propertyLookup->setUInt(1, this->m_MapID); @@ -209,7 +209,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl std::string ownerName = ""; bool isOwned = true; - auto nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;"); + auto nameLookup = Database::Get()->CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;"); nameLookup->setUInt64(1, cloneId); @@ -245,7 +245,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl GeneralUtils::SetBit(ownerObjId, eObjectBits::PERSISTENT); // Query to get friend and best friend fields - auto friendCheck = Database::CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)"); + auto friendCheck = Database::Get()->CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)"); friendCheck->setUInt(1, character->GetID()); friendCheck->setUInt(2, ownerObjId); @@ -278,7 +278,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl bool isAlt = false; // Query to determine whether this property is an alt character of the entity. - auto isAltQuery = Database::CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;"); + auto isAltQuery = Database::Get()->CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;"); isAltQuery->setInt(1, character->GetID()); isAltQuery->setInt(2, owner); @@ -312,7 +312,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl int32_t numberOfProperties = 0; auto buttonQuery = BuildQuery(entity, sortMethod, character, "SELECT COUNT(*) FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ", false); - auto propertiesLeft = Database::CreatePreppedStmt(buttonQuery); + auto propertiesLeft = Database::Get()->CreatePreppedStmt(buttonQuery); propertiesLeft->setUInt(1, this->m_MapID); propertiesLeft->setString(2, searchString.c_str()); diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index 1fc38119..7877b2c9 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -39,13 +39,12 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo instance = this; const auto& worldId = Game::zoneManager->GetZone()->GetZoneID(); - const auto zoneId = worldId.GetMapID(); const auto cloneId = worldId.GetCloneID(); - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT id FROM PropertyTemplate WHERE mapID = ?;"); - query.bind(1, (int)zoneId); + auto query = CDClientDatabase::CreatePreppedStmt("SELECT id FROM PropertyTemplate WHERE mapID = ?;"); + + query.bind(1, static_cast(zoneId)); auto result = query.execQuery(); @@ -55,34 +54,25 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo templateId = result.getIntField(0); - result.finalize(); + auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId); - auto* propertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE template_id = ? AND clone_id = ?;"); - - propertyLookup->setInt(1, templateId); - propertyLookup->setInt64(2, cloneId); - - auto* propertyEntry = propertyLookup->executeQuery(); - - if (propertyEntry->next()) { - this->propertyId = propertyEntry->getUInt64(1); - this->owner = propertyEntry->getUInt64(2); + if (propertyInfo) { + this->propertyId = propertyInfo->id; + this->owner = propertyInfo->ownerId; GeneralUtils::SetBit(this->owner, eObjectBits::CHARACTER); GeneralUtils::SetBit(this->owner, eObjectBits::PERSISTENT); - this->clone_Id = propertyEntry->getInt(2); - this->propertyName = propertyEntry->getString(5).c_str(); - this->propertyDescription = propertyEntry->getString(6).c_str(); - this->privacyOption = static_cast(propertyEntry->getUInt(9)); - this->moderatorRequested = propertyEntry->getInt(10) == 0 && rejectionReason == "" && privacyOption == PropertyPrivacyOption::Public; - this->LastUpdatedTime = propertyEntry->getUInt64(11); - this->claimedTime = propertyEntry->getUInt64(12); - this->rejectionReason = std::string(propertyEntry->getString(13).c_str()); - this->reputation = propertyEntry->getUInt(14); + this->clone_Id = propertyInfo->cloneId; + this->propertyName = propertyInfo->name; + this->propertyDescription = propertyInfo->description; + this->privacyOption = static_cast(propertyInfo->privacyOption); + this->rejectionReason = propertyInfo->rejectionReason; + this->moderatorRequested = propertyInfo->modApproved == 0 && rejectionReason == "" && privacyOption == PropertyPrivacyOption::Public; + this->LastUpdatedTime = propertyInfo->lastUpdatedTime; + this->claimedTime = propertyInfo->claimedTime; + this->reputation = propertyInfo->reputation; Load(); } - - delete propertyLookup; } LWOOBJID PropertyManagementComponent::GetOwnerId() const { @@ -152,14 +142,13 @@ void PropertyManagementComponent::SetPrivacyOption(PropertyPrivacyOption value) } privacyOption = value; - auto* propertyUpdate = Database::CreatePreppedStmt("UPDATE properties SET privacy_option = ?, rejection_reason = ?, mod_approved = ? WHERE id = ?;"); + IProperty::Info info; + info.id = propertyId; + info.privacyOption = static_cast(privacyOption); + info.rejectionReason = rejectionReason; + info.modApproved = 0; - propertyUpdate->setInt(1, static_cast(value)); - propertyUpdate->setString(2, ""); - propertyUpdate->setInt(3, 0); - propertyUpdate->setInt64(4, propertyId); - - propertyUpdate->executeUpdate(); + Database::Get()->UpdatePropertyModerationInfo(info); } void PropertyManagementComponent::UpdatePropertyDetails(std::string name, std::string description) { @@ -169,13 +158,12 @@ void PropertyManagementComponent::UpdatePropertyDetails(std::string name, std::s propertyDescription = description; - auto* propertyUpdate = Database::CreatePreppedStmt("UPDATE properties SET name = ?, description = ? WHERE id = ?;"); + IProperty::Info info; + info.id = propertyId; + info.name = propertyName; + info.description = propertyDescription; - propertyUpdate->setString(1, name.c_str()); - propertyUpdate->setString(2, description.c_str()); - propertyUpdate->setInt64(3, propertyId); - - propertyUpdate->executeUpdate(); + Database::Get()->UpdatePropertyDetails(info); OnQueryPropertyData(GetOwner(), UNASSIGNED_SYSTEM_ADDRESS); } @@ -217,28 +205,14 @@ bool PropertyManagementComponent::Claim(const LWOOBJID playerId) { propertyId = ObjectIDManager::GenerateRandomObjectID(); - auto* insertion = Database::CreatePreppedStmt( - "INSERT INTO properties" - "(id, owner_id, template_id, clone_id, name, description, rent_amount, rent_due, privacy_option, last_updated, time_claimed, rejection_reason, reputation, zone_id, performance_cost)" - "VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '', 0, ?, 0.0)" - ); - insertion->setUInt64(1, propertyId); - insertion->setUInt64(2, (uint32_t)playerId); - insertion->setUInt(3, templateId); - insertion->setUInt64(4, playerCloneId); - insertion->setString(5, name.c_str()); - insertion->setString(6, description.c_str()); - insertion->setInt(7, propertyZoneId); + IProperty::Info info; + info.id = propertyId; + info.ownerId = character->GetID(); + info.cloneId = playerCloneId; + info.name = name; + info.description = description; - // Try and execute the query, print an error if it fails. - try { - insertion->execute(); - } catch (sql::SQLException& exception) { - LOG("Failed to execute query: (%s)!", exception.what()); - - throw exception; - return false; - } + Database::Get()->InsertNewProperty(info, templateId, worldId); auto* zoneControlObject = Game::zoneManager->GetZoneControlObject(); for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControlObject)) { @@ -545,7 +519,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet { item->SetCount(item->GetCount() - 1); - LOG("YES IT GOES HERE"); + LOG("BODGE TIME, YES IT GOES HERE"); break; } @@ -569,14 +543,13 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet void PropertyManagementComponent::UpdateApprovedStatus(const bool value) { if (owner == LWOOBJID_EMPTY) return; - auto* update = Database::CreatePreppedStmt("UPDATE properties SET mod_approved = ? WHERE id = ?;"); + IProperty::Info info; + info.id = propertyId; + info.modApproved = value; + info.privacyOption = static_cast(privacyOption); + info.rejectionReason = ""; - update->setBoolean(1, value); - update->setInt64(2, propertyId); - - update->executeUpdate(); - - delete update; + Database::Get()->UpdatePropertyModerationInfo(info); } void PropertyManagementComponent::Load() { @@ -584,39 +557,17 @@ void PropertyManagementComponent::Load() { return; } - auto* lookup = Database::CreatePreppedStmt("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;"); - - lookup->setUInt64(1, propertyId); - - auto* lookupResult = lookup->executeQuery(); - - while (lookupResult->next()) { - const LWOOBJID id = lookupResult->getUInt64(1); - const LOT lot = lookupResult->getInt(2); - - const NiPoint3 position = - { - static_cast(lookupResult->getDouble(3)), - static_cast(lookupResult->getDouble(4)), - static_cast(lookupResult->getDouble(5)) - }; - - const NiQuaternion rotation = - { - static_cast(lookupResult->getDouble(9)), - static_cast(lookupResult->getDouble(6)), - static_cast(lookupResult->getDouble(7)), - static_cast(lookupResult->getDouble(8)) - }; + auto propertyModels = Database::Get()->GetPropertyModels(propertyId); + for (const auto& databaseModel : propertyModels) { auto* node = new SpawnerNode(); - node->position = position; - node->rotation = rotation; + node->position = databaseModel.position; + node->rotation = databaseModel.rotation; SpawnerInfo info{}; - info.templateID = lot; + info.templateID = databaseModel.lot; info.nodes = { node }; info.templateScale = 1.0f; info.activeOnLoad = true; @@ -626,13 +577,13 @@ void PropertyManagementComponent::Load() { //info.emulated = true; //info.emulator = Game::entityManager->GetZoneControlEntity()->GetObjectID(); - info.spawnerID = id; + info.spawnerID = databaseModel.id; std::vector settings; //BBB property models need to have extra stuff set for them: - if (lot == 14) { - LWOOBJID blueprintID = lookupResult->getUInt(10); + if (databaseModel.lot == 14) { + LWOOBJID blueprintID = databaseModel.ugcId; GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER); GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT); @@ -640,7 +591,7 @@ void PropertyManagementComponent::Load() { LDFBaseData* componentWhitelist = new LDFData(u"componentWhitelist", 1); LDFBaseData* modelType = new LDFData(u"modelType", 2); LDFBaseData* propertyObjectID = new LDFData(u"propertyObjectID", true); - LDFBaseData* userModelID = new LDFData(u"userModelID", id); + LDFBaseData* userModelID = new LDFData(u"userModelID", databaseModel.id); settings.push_back(ldfBlueprintID); settings.push_back(componentWhitelist); @@ -649,7 +600,7 @@ void PropertyManagementComponent::Load() { settings.push_back(userModelID); } else { auto modelType = new LDFData(u"modelType", 2); - auto userModelID = new LDFData(u"userModelID", id); + auto userModelID = new LDFData(u"userModelID", databaseModel.id); auto ldfModelBehavior = new LDFData(u"modelBehaviors", 0); auto propertyObjectID = new LDFData(u"propertyObjectID", true); auto componentWhitelist = new LDFData(u"componentWhitelist", 1); @@ -671,8 +622,6 @@ void PropertyManagementComponent::Load() { models.insert_or_assign(model->GetObjectID(), spawnerId); } - - delete lookup; } void PropertyManagementComponent::Save() { @@ -680,27 +629,7 @@ void PropertyManagementComponent::Save() { return; } - auto* insertion = Database::CreatePreppedStmt("INSERT INTO properties_contents VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - auto* update = Database::CreatePreppedStmt("UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;"); - auto* lookup = Database::CreatePreppedStmt("SELECT id FROM properties_contents WHERE property_id = ?;"); - auto* remove = Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE id = ?;"); - - lookup->setUInt64(1, propertyId); - sql::ResultSet* lookupResult = nullptr; - try { - lookupResult = lookup->executeQuery(); - } catch (sql::SQLException& ex) { - LOG("lookup error %s", ex.what()); - } - std::vector present; - - while (lookupResult->next()) { - const auto dbId = lookupResult->getUInt64(1); - - present.push_back(dbId); - } - - delete lookupResult; + auto present = Database::Get()->GetPropertyModels(propertyId); std::vector modelIds; @@ -719,69 +648,26 @@ void PropertyManagementComponent::Save() { const auto rotation = entity->GetRotation(); if (std::find(present.begin(), present.end(), id) == present.end()) { - insertion->setInt64(1, id); - insertion->setUInt64(2, propertyId); - insertion->setNull(3, 0); - insertion->setInt(4, entity->GetLOT()); - insertion->setDouble(5, position.x); - insertion->setDouble(6, position.y); - insertion->setDouble(7, position.z); - insertion->setDouble(8, rotation.x); - insertion->setDouble(9, rotation.y); - insertion->setDouble(10, rotation.z); - insertion->setDouble(11, rotation.w); - insertion->setString(12, ("Objects_" + std::to_string(entity->GetLOT()) + "_name").c_str()); // Model name. TODO make this customizable - insertion->setString(13, ""); // Model description. TODO implement this. - insertion->setDouble(14, 0); // behavior 1. TODO implement this. - insertion->setDouble(15, 0); // behavior 2. TODO implement this. - insertion->setDouble(16, 0); // behavior 3. TODO implement this. - insertion->setDouble(17, 0); // behavior 4. TODO implement this. - insertion->setDouble(18, 0); // behavior 5. TODO implement this. - try { - insertion->execute(); - } catch (sql::SQLException& ex) { - LOG("Error inserting into properties_contents. Error %s", ex.what()); - } - } else { - update->setDouble(1, position.x); - update->setDouble(2, position.y); - update->setDouble(3, position.z); - update->setDouble(4, rotation.x); - update->setDouble(5, rotation.y); - update->setDouble(6, rotation.z); - update->setDouble(7, rotation.w); + IPropertyContents::Model model; + model.id = id; + model.lot = entity->GetLOT(); + model.position = position; + model.rotation = rotation; + model.ugcId = 0; - update->setInt64(8, id); - try { - update->executeUpdate(); - } catch (sql::SQLException& ex) { - LOG("Error updating properties_contents. Error: %s", ex.what()); - } + Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name"); + } else { + Database::Get()->UpdateModelPositionRotation(id, position, rotation); } } - for (auto id : present) { - if (std::find(modelIds.begin(), modelIds.end(), id) != modelIds.end()) { + for (auto model : present) { + if (std::find(modelIds.begin(), modelIds.end(), model.id) != modelIds.end()) { continue; } - remove->setInt64(1, id); - try { - remove->execute(); - } catch (sql::SQLException& ex) { - LOG("Error removing from properties_contents. Error %s", ex.what()); - } + Database::Get()->RemoveModel(model.id); } - - auto* removeUGC = Database::CreatePreppedStmt("DELETE FROM ugc WHERE id NOT IN (SELECT ugc_id FROM properties_contents);"); - - removeUGC->execute(); - - delete removeUGC; - delete insertion; - delete update; - delete lookup; - delete remove; } void PropertyManagementComponent::AddModel(LWOOBJID modelId, LWOOBJID spawnerId) { @@ -799,6 +685,7 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const const auto& worldId = Game::zoneManager->GetZone()->GetZoneID(); const auto zoneId = worldId.GetMapID(); + const auto cloneId = worldId.GetCloneID(); LOG("Getting property info for %d", zoneId); GameMessages::PropertyDataMessage message = GameMessages::PropertyDataMessage(zoneId); @@ -806,45 +693,25 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const const auto isClaimed = GetOwnerId() != LWOOBJID_EMPTY; LWOOBJID ownerId = GetOwnerId(); - std::string ownerName = ""; + std::string ownerName; + auto charInfo = Database::Get()->GetCharacterInfo(ownerId); + if (charInfo) ownerName = charInfo->name; std::string name = ""; std::string description = ""; uint64_t claimed = 0; char privacy = 0; if (isClaimed) { - const auto cloneId = worldId.GetCloneID(); - - auto* nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;"); - nameLookup->setUInt64(1, cloneId); - - auto* nameResult = nameLookup->executeQuery(); - if (nameResult->next()) { - ownerName = nameResult->getString(1).c_str(); - } - - delete nameResult; - delete nameLookup; - name = propertyName; description = propertyDescription; claimed = claimedTime; privacy = static_cast(this->privacyOption); if (moderatorRequested) { - auto checkStatus = Database::CreatePreppedStmt("SELECT rejection_reason, mod_approved FROM properties WHERE id = ?;"); - - checkStatus->setInt64(1, propertyId); - - auto result = checkStatus->executeQuery(); - - result->next(); - - const auto reason = std::string(result->getString(1).c_str()); - const auto modApproved = result->getInt(2); - if (reason != "") { + auto moderationInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId); + if (moderationInfo->rejectionReason != "") { moderatorRequested = false; - rejectionReason = reason; - } else if (reason == "" && modApproved == 1) { + rejectionReason = moderationInfo->rejectionReason; + } else if (moderationInfo->rejectionReason == "" && moderationInfo->modApproved == 1) { moderatorRequested = false; rejectionReason = ""; } else { diff --git a/dGame/dGameMessages/GameMessageHandler.h b/dGame/dGameMessages/GameMessageHandler.h index 821d80be..61378f80 100644 --- a/dGame/dGameMessages/GameMessageHandler.h +++ b/dGame/dGameMessages/GameMessageHandler.h @@ -18,7 +18,7 @@ #include "Game.h" #include "Logger.h" #include "GameMessages.h" -#include "../dDatabase/CDClientDatabase.h" +#include "CDClientDatabase.h" enum class eGameMessageType : uint16_t; diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 33717059..7fb9c259 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -2577,70 +2577,23 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent const auto zoneId = worldId.GetMapID(); const auto cloneId = worldId.GetCloneID(); - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT id FROM PropertyTemplate WHERE mapID = ?;"); - query.bind(1, (int)zoneId); - - auto result = query.execQuery(); - - if (result.eof() || result.fieldIsNull(0)) return; - - int templateId = result.getIntField(0); - - auto* propertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE template_id = ? AND clone_id = ?;"); - - propertyLookup->setInt(1, templateId); - propertyLookup->setInt64(2, cloneId); - - auto* propertyEntry = propertyLookup->executeQuery(); - uint64_t propertyId = 0; - - if (propertyEntry->next()) { - propertyId = propertyEntry->getUInt64(1); - } - - delete propertyEntry; - delete propertyLookup; + auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId); + LWOOBJID propertyId = LWOOBJID_EMPTY; + if (propertyInfo) propertyId = propertyInfo->id; //Insert into ugc: - auto ugcs = Database::CreatePreppedStmt("INSERT INTO `ugc`(`id`, `account_id`, `character_id`, `is_optimized`, `lxfml`, `bake_ao`, `filename`) VALUES (?,?,?,?,?,?,?)"); - ugcs->setUInt(1, blueprintIDSmall); - ugcs->setInt(2, entity->GetParentUser()->GetAccountID()); - ugcs->setInt(3, entity->GetCharacter()->GetID()); - ugcs->setInt(4, 0); - - //whacky stream biz - std::string s(sd0Data.get(), sd0Size); - std::istringstream iss(s); - - ugcs->setBlob(5, &iss); - ugcs->setBoolean(6, false); - ugcs->setString(7, "weedeater.lxfml"); - ugcs->execute(); - delete ugcs; + std::string str(sd0Data.get(), sd0Size); + std::istringstream sd0DataStream(str); + Database::Get()->InsertNewUgcModel(sd0DataStream, blueprintIDSmall, entity->GetParentUser()->GetAccountID(), entity->GetCharacter()->GetID()); //Insert into the db as a BBB model: - auto* stmt = Database::CreatePreppedStmt("INSERT INTO `properties_contents` VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); - stmt->setUInt64(1, newIDL); - stmt->setUInt64(2, propertyId); - stmt->setUInt(3, blueprintIDSmall); - stmt->setUInt(4, 14); // 14 is the lot the BBB models use - stmt->setDouble(5, 0.0f); // x - stmt->setDouble(6, 0.0f); // y - stmt->setDouble(7, 0.0f); // z - stmt->setDouble(8, 0.0f); // rx - stmt->setDouble(9, 0.0f); // ry - stmt->setDouble(10, 0.0f); // rz - stmt->setDouble(11, 0.0f); // rw - stmt->setString(12, "Objects_14_name"); // Model name. TODO make this customizable - stmt->setString(13, ""); // Model description. TODO implement this. - stmt->setDouble(14, 0); // behavior 1. TODO implement this. - stmt->setDouble(15, 0); // behavior 2. TODO implement this. - stmt->setDouble(16, 0); // behavior 3. TODO implement this. - stmt->setDouble(17, 0); // behavior 4. TODO implement this. - stmt->setDouble(18, 0); // behavior 5. TODO implement this. - stmt->execute(); - delete stmt; + IPropertyContents::Model model; + model.id = newIDL; + model.ugcId = blueprintIDSmall; + model.position = NiPoint3::ZERO; + model.rotation = NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f); + model.lot = 14; + Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_14_name"); /* Commented out until UGC server would be updated to use a sd0 file instead of lxfml stream. @@ -4232,20 +4185,12 @@ void GameMessages::HandleUpdatePropertyPerformanceCost(RakNet::BitStream* inStre if (performanceCost == 0.0f) return; auto zone = Game::zoneManager->GetZone(); - const auto& worldId = zone->GetZoneID(); - const auto cloneId = worldId.GetCloneID(); - const auto zoneId = worldId.GetMapID(); + if (!zone) { + LOG("If you see this message, something is very wrong."); + return; + } - auto updatePerformanceCostQuery = Database::CreatePreppedStmt("UPDATE properties SET performance_cost = ? WHERE clone_id = ? AND zone_id = ?"); - - updatePerformanceCostQuery->setDouble(1, performanceCost); - updatePerformanceCostQuery->setInt(2, cloneId); - updatePerformanceCostQuery->setInt(3, zoneId); - - updatePerformanceCostQuery->executeUpdate(); - - delete updatePerformanceCostQuery; - updatePerformanceCostQuery = nullptr; + Database::Get()->UpdatePerformanceCost(zone->GetZoneID(), performanceCost); } void GameMessages::HandleVehicleNotifyHitImaginationServer(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { @@ -5126,7 +5071,7 @@ void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, E item->Disassemble(TEMP_MODELS); - std::unique_ptr stmt(Database::CreatePreppedStmt("DELETE FROM ugc_modular_build where ugc_id = ?")); + std::unique_ptr stmt(Database::Get()->CreatePreppedStmt("DELETE FROM ugc_modular_build where ugc_id = ?")); stmt->setUInt64(1, item->GetSubKey()); stmt->execute(); @@ -5629,7 +5574,7 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity* inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig); } - std::unique_ptr stmt(Database::CreatePreppedStmt("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)")); + std::unique_ptr stmt(Database::Get()->CreatePreppedStmt("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)")); stmt->setUInt64(1, newIdBig); stmt->setString(2, GeneralUtils::UTF16ToWTF8(modules)); auto* pCharacter = character->GetCharacter(); @@ -5986,31 +5931,27 @@ void GameMessages::SendGetHotPropertyData(RakNet::BitStream* inStream, Entity* e void GameMessages::HandleReportBug(RakNet::BitStream* inStream, Entity* entity) { //Definitely not stolen from autogenerated code, no sir: - std::u16string body; - std::string clientVersion; - std::string nOtherPlayerID; - std::string selection; - uint32_t messageLength; - int32_t reporterID = 0; + IBugReports::Info reportInfo; //Reading: + uint32_t messageLength; inStream->Read(messageLength); for (uint32_t i = 0; i < (messageLength); ++i) { uint16_t character; inStream->Read(character); - body.push_back(character); + reportInfo.body.push_back(static_cast(character)); } auto character = entity->GetCharacter(); - if (character) reporterID = character->GetID(); + if (character) reportInfo.characterId = character->GetID(); uint32_t clientVersionLength; inStream->Read(clientVersionLength); for (unsigned int k = 0; k < clientVersionLength; k++) { unsigned char character; inStream->Read(character); - clientVersion.push_back(character); + reportInfo.clientVersion.push_back(character); } uint32_t nOtherPlayerIDLength; @@ -6018,32 +5959,18 @@ void GameMessages::HandleReportBug(RakNet::BitStream* inStream, Entity* entity) for (unsigned int k = 0; k < nOtherPlayerIDLength; k++) { unsigned char character; inStream->Read(character); - nOtherPlayerID.push_back(character); + reportInfo.otherPlayer.push_back(character); } - // Convert other player id from LWOOBJID to the database id. - uint32_t otherPlayer = LWOOBJID_EMPTY; - if (nOtherPlayerID != "") otherPlayer = std::atoi(nOtherPlayerID.c_str()); uint32_t selectionLength; inStream->Read(selectionLength); for (unsigned int k = 0; k < selectionLength; k++) { unsigned char character; inStream->Read(character); - selection.push_back(character); + reportInfo.selection.push_back(character); } - try { - sql::PreparedStatement* insertBug = Database::CreatePreppedStmt("INSERT INTO `bug_reports`(body, client_version, other_player_id, selection, reporter_id) VALUES (?, ?, ?, ?, ?)"); - insertBug->setString(1, GeneralUtils::UTF16ToWTF8(body)); - insertBug->setString(2, clientVersion); - insertBug->setString(3, std::to_string(otherPlayer)); - insertBug->setString(4, selection); - insertBug->setInt(5, reporterID); - insertBug->execute(); - delete insertBug; - } catch (sql::SQLException& e) { - LOG("Couldn't save bug report! (%s)", e.what()); - } + Database::Get()->InsertNewBugReport(reportInfo); } void diff --git a/dGame/dUtilities/CheatDetection.cpp b/dGame/dUtilities/CheatDetection.cpp index ce841710..a43cdaeb 100644 --- a/dGame/dUtilities/CheatDetection.cpp +++ b/dGame/dUtilities/CheatDetection.cpp @@ -9,6 +9,7 @@ #include "User.h" #include "UserManager.h" #include "dConfig.h" +#include Entity* GetPossessedEntity(const LWOOBJID& objId) { auto* entity = Game::entityManager->GetEntity(objId); @@ -26,20 +27,22 @@ void ReportCheat(User* user, const SystemAddress& sysAddr, const char* messageIf if (!user) { LOG("WARNING: User is null, using defaults."); } - std::unique_ptr stmt(Database::CreatePreppedStmt( - "INSERT INTO player_cheat_detections (account_id, name, violation_msg, violation_system_address) VALUES (?, ?, ?, ?)") - ); - user ? stmt->setInt(1, user->GetAccountID()) : stmt->setNull(1, sql::DataType::INTEGER); - stmt->setString(2, user ? user->GetUsername().c_str() : "User is null."); + + IPlayerCheatDetections::Info info; + if (user) info.userId = user->GetAccountID(); + info.username = user ? user->GetUsername().c_str() : "User is null."; + + // user string here because ToString is static and may change. + info.systemAddress = sysAddr.ToString(); constexpr int32_t bufSize = 4096; - char buffer[bufSize]; - vsnprintf(buffer, bufSize, messageIfNotSender, args); + char extraMsg[bufSize]; + vsnprintf(extraMsg, bufSize, messageIfNotSender, args); + info.extraMessage = extraMsg; - stmt->setString(3, buffer); - stmt->setString(4, Game::config->GetValue("log_ip_addresses_for_anti_cheat") == "1" ? sysAddr.ToString() : "IP logging disabled."); - stmt->execute(); - LOG("Anti-cheat message: %s", buffer); + Database::Get()->InsertCheatDetection(info); + + LOG("Anti-cheat message: %s", extraMsg); } void LogAndSaveFailedAntiCheatCheck(const LWOOBJID& id, const SystemAddress& sysAddr, const CheckType checkType, const char* messageIfNotSender, va_list args) { diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index 961dc5f6..2677c9cc 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -76,22 +76,19 @@ void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, const void Mail::SendMail(const LWOOBJID sender, const std::string& senderName, LWOOBJID recipient, const std::string& recipientName, const std::string& subject, const std::string& body, const LOT attachment, const uint16_t attachmentCount, const SystemAddress& sysAddr) { - auto* ins = Database::CreatePreppedStmt("INSERT INTO `mail`(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`) VALUES (?,?,?,?,?,?,?,?,?,?,?,0)"); + IMail::MailInfo mailInsert; + mailInsert.senderUsername = senderName; + mailInsert.recipient = recipientName; + mailInsert.subject = subject; + mailInsert.body = body; + mailInsert.senderId = sender; + mailInsert.receiverId = recipient; + mailInsert.itemCount = attachmentCount; + mailInsert.itemID = LWOOBJID_EMPTY; + mailInsert.itemLOT = attachment; + mailInsert.itemSubkey = LWOOBJID_EMPTY; - ins->setUInt(1, sender); - ins->setString(2, senderName.c_str()); - ins->setUInt(3, recipient); - ins->setString(4, recipientName.c_str()); - ins->setUInt64(5, time(nullptr)); - ins->setString(6, subject.c_str()); - ins->setString(7, body.c_str()); - ins->setUInt(8, 0); - ins->setInt(9, attachment); - ins->setInt(10, 0); - ins->setInt(11, attachmentCount); - ins->execute(); - - delete ins; + Database::Get()->InsertNewMail(mailInsert); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) return; // TODO: Echo to chat server @@ -220,43 +217,30 @@ void Mail::HandleSendMail(RakNet::BitStream* packet, const SystemAddress& sysAdd } //Get the receiver's id: - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id from charinfo WHERE name=? LIMIT 1;"); - stmt->setString(1, recipient); - sql::ResultSet* res = stmt->executeQuery(); - uint32_t receiverID = 0; + auto receiverID = Database::Get()->GetCharacterInfo(recipient); - if (res->rowsCount() > 0) { - while (res->next()) receiverID = res->getUInt(1); - } else { + if (!receiverID) { Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::RecipientNotFound); - delete stmt; - delete res; return; } - delete stmt; - delete res; - //Check if we have a valid receiver: - if (GeneralUtils::CaseInsensitiveStringCompare(recipient, character->GetName()) || receiverID == character->GetObjectID()) { + if (GeneralUtils::CaseInsensitiveStringCompare(recipient, character->GetName()) || receiverID->id == character->GetID()) { Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::CannotMailSelf); return; } else { - uint64_t currentTime = time(NULL); - sql::PreparedStatement* ins = Database::CreatePreppedStmt("INSERT INTO `mail`(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`) VALUES (?,?,?,?,?,?,?,?,?,?,?,0)"); - ins->setUInt(1, character->GetObjectID()); - ins->setString(2, character->GetName()); - ins->setUInt(3, receiverID); - ins->setString(4, recipient); - ins->setUInt64(5, currentTime); - ins->setString(6, subject); - ins->setString(7, body); - ins->setUInt(8, itemID); - ins->setInt(9, itemLOT); - ins->setInt(10, 0); - ins->setInt(11, attachmentCount); - ins->execute(); - delete ins; + IMail::MailInfo mailInsert; + mailInsert.senderUsername = character->GetName(); + mailInsert.recipient = recipient; + mailInsert.subject = subject; + mailInsert.body = body; + mailInsert.senderId = character->GetID(); + mailInsert.receiverId = receiverID->id; + mailInsert.itemCount = attachmentCount; + mailInsert.itemID = itemID; + mailInsert.itemLOT = itemLOT; + mailInsert.itemSubkey = LWOOBJID_EMPTY; + Database::Get()->InsertNewMail(mailInsert); } Mail::SendSendResponse(sysAddr, Mail::MailSendResponse::Success); @@ -279,61 +263,49 @@ void Mail::HandleSendMail(RakNet::BitStream* packet, const SystemAddress& sysAdd } void Mail::HandleDataRequest(RakNet::BitStream* packet, const SystemAddress& sysAddr, Entity* player) { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT * FROM mail WHERE receiver_id=? limit 20;"); - stmt->setUInt(1, player->GetCharacter()->GetObjectID()); - sql::ResultSet* res = stmt->executeQuery(); + auto playerMail = Database::Get()->GetMailForPlayer(player->GetCharacter()->GetID(), 20); RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::MAIL); bitStream.Write(int(MailMessageID::MailData)); bitStream.Write(int(0)); - bitStream.Write(uint16_t(res->rowsCount())); - bitStream.Write(uint16_t(0)); + bitStream.Write(playerMail.size()); + bitStream.Write(0); - if (res->rowsCount() > 0) { - while (res->next()) { - bitStream.Write(res->getUInt64(1)); //MailID + for (const auto& mail : playerMail) { + bitStream.Write(mail.id); //MailID - /*std::u16string subject = GeneralUtils::UTF8ToUTF16(res->getString(7)); - std::u16string body = GeneralUtils::UTF8ToUTF16(res->getString(8)); - std::u16string sender = GeneralUtils::UTF8ToUTF16(res->getString(3)); + WriteStringAsWString(&bitStream, mail.subject.c_str(), 50); //subject + WriteStringAsWString(&bitStream, mail.body.c_str(), 400); //body + WriteStringAsWString(&bitStream, mail.senderUsername.c_str(), 32); //sender - WriteToPacket(&bitStream, subject, 50); - WriteToPacket(&bitStream, body, 400); - WriteToPacket(&bitStream, sender, 32);*/ + bitStream.Write(uint32_t(0)); + bitStream.Write(uint64_t(0)); - WriteStringAsWString(&bitStream, res->getString(7).c_str(), 50); //subject - WriteStringAsWString(&bitStream, res->getString(8).c_str(), 400); //body - WriteStringAsWString(&bitStream, res->getString(3).c_str(), 32); //sender + bitStream.Write(mail.itemID); //Attachment ID + LOT lot = mail.itemLOT; + if (lot <= 0) bitStream.Write(LOT(-1)); + else bitStream.Write(lot); + bitStream.Write(uint32_t(0)); - bitStream.Write(uint32_t(0)); - bitStream.Write(uint64_t(0)); + bitStream.Write(mail.itemSubkey); //Attachment subKey + bitStream.Write(mail.itemCount); //Attachment count - bitStream.Write(res->getUInt64(9)); //Attachment ID - LOT lot = res->getInt(10); - if (lot <= 0) bitStream.Write(LOT(-1)); - else bitStream.Write(lot); - bitStream.Write(uint32_t(0)); + bitStream.Write(uint32_t(0)); + bitStream.Write(uint16_t(0)); - bitStream.Write(res->getInt64(11)); //Attachment subKey - bitStream.Write(uint16_t(res->getInt(12))); //Attachment count + bitStream.Write(mail.timeSent); //time sent (twice?) + bitStream.Write(mail.timeSent); + bitStream.Write(mail.wasRead); //was read - bitStream.Write(uint32_t(0)); - bitStream.Write(uint16_t(0)); - - bitStream.Write(uint64_t(res->getUInt64(6))); //time sent (twice?) - bitStream.Write(uint64_t(res->getUInt64(6))); - bitStream.Write(uint8_t(res->getBoolean(13))); //was read - - bitStream.Write(uint8_t(0)); - bitStream.Write(uint16_t(0)); - bitStream.Write(uint32_t(0)); - } + bitStream.Write(uint8_t(0)); + bitStream.Write(uint16_t(0)); + bitStream.Write(uint32_t(0)); } Game::server->Send(&bitStream, sysAddr, false); - PacketUtils::SavePacket("Max_Mail_Data.bin", (const char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); + // PacketUtils::SavePacket("Max_Mail_Data.bin", (const char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); } void Mail::HandleAttachmentCollect(RakNet::BitStream* packet, const SystemAddress& sysAddr, Entity* player) { @@ -345,31 +317,24 @@ void Mail::HandleAttachmentCollect(RakNet::BitStream* packet, const SystemAddres packet->Read(playerID); if (mailID > 0 && playerID == player->GetObjectID()) { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;"); - stmt->setUInt64(1, mailID); - sql::ResultSet* res = stmt->executeQuery(); + auto playerMail = Database::Get()->GetMail(mailID); LOT attachmentLOT = 0; uint32_t attachmentCount = 0; - while (res->next()) { - attachmentLOT = res->getInt(1); - attachmentCount = res->getInt(2); + if (playerMail) { + attachmentLOT = playerMail->itemLOT; + attachmentCount = playerMail->itemCount; } - auto inv = static_cast(player->GetComponent(eReplicaComponentType::INVENTORY)); + auto inv = player->GetComponent(); if (!inv) return; inv->AddItem(attachmentLOT, attachmentCount, eLootSourceType::MAIL); Mail::SendAttachmentRemoveConfirm(sysAddr, mailID); - sql::PreparedStatement* up = Database::CreatePreppedStmt("UPDATE mail SET attachment_lot=0 WHERE id=?;"); - up->setUInt64(1, mailID); - up->execute(); - delete up; - delete res; - delete stmt; + Database::Get()->ClaimMailItem(mailID); } } @@ -394,15 +359,9 @@ void Mail::HandleMailRead(RakNet::BitStream* packet, const SystemAddress& sysAdd } void Mail::HandleNotificationRequest(const SystemAddress& sysAddr, uint32_t objectID) { - auto returnVal = std::async(std::launch::async, [&]() { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM mail WHERE receiver_id=? AND was_read=0"); - stmt->setUInt(1, objectID); - sql::ResultSet* res = stmt->executeQuery(); + auto unreadMailCount = Database::Get()->GetUnreadMailCount(objectID); - if (res->rowsCount() > 0) Mail::SendNotification(sysAddr, res->rowsCount()); - delete res; - delete stmt; - }); + if (unreadMailCount > 0) Mail::SendNotification(sysAddr, unreadMailCount); } void Mail::SendSendResponse(const SystemAddress& sysAddr, MailSendResponse response) { @@ -449,10 +408,7 @@ void Mail::SendDeleteConfirm(const SystemAddress& sysAddr, uint64_t mailID, LWOO bitStream.Write(mailID); Game::server->Send(&bitStream, sysAddr, false); - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM mail WHERE id=? LIMIT 1;"); - stmt->setUInt64(1, mailID); - stmt->execute(); - delete stmt; + Database::Get()->DeleteMail(mailID); } void Mail::SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID) { @@ -463,8 +419,5 @@ void Mail::SendReadConfirm(const SystemAddress& sysAddr, uint64_t mailID) { bitStream.Write(mailID); Game::server->Send(&bitStream, sysAddr, false); - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE mail SET was_read=1 WHERE id=?"); - stmt->setUInt64(1, mailID); - stmt->execute(); - delete stmt; + Database::Get()->MarkMailRead(mailID); } diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index db27ee7b..71f0e38b 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -360,11 +360,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit } // Log command to database - auto stmt = Database::CreatePreppedStmt("INSERT INTO command_log (character_id, command) VALUES (?, ?);"); - stmt->setInt(1, entity->GetCharacter()->GetID()); - stmt->setString(2, GeneralUtils::UTF16ToWTF8(command).c_str()); - stmt->execute(); - delete stmt; + Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), chatCommand); if (chatCommand == "setminifig" && args.size() == 2 && entity->GetGMLevel() >= eGameMasterLevel::FORUM_MODERATOR) { // could break characters so only allow if GM > 0 int32_t minifigItemId; @@ -816,46 +812,36 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (chatCommand == "mailitem" && entity->GetGMLevel() >= eGameMasterLevel::MODERATOR && args.size() >= 2) { const auto& playerName = args[0]; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id from charinfo WHERE name=? LIMIT 1;"); - stmt->setString(1, playerName); - sql::ResultSet* res = stmt->executeQuery(); + auto playerInfo = Database::Get()->GetCharacterInfo(playerName); + uint32_t receiverID = 0; - - if (res->rowsCount() > 0) { - while (res->next()) receiverID = res->getUInt(1); - } - - delete stmt; - delete res; - - if (receiverID == 0) { + if (!playerInfo) { ChatPackets::SendSystemMessage(sysAddr, u"Failed to find that player"); return; } - uint32_t lot; + receiverID = playerInfo->id; + + LOT lot; if (!GeneralUtils::TryParse(args[1], lot)) { ChatPackets::SendSystemMessage(sysAddr, u"Invalid item lot."); return; } - uint64_t currentTime = time(NULL); - sql::PreparedStatement* ins = Database::CreatePreppedStmt("INSERT INTO `mail`(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`) VALUES (?,?,?,?,?,?,?,?,?,?,?,0)"); - ins->setUInt(1, entity->GetObjectID()); - ins->setString(2, "Darkflame Universe"); - ins->setUInt(3, receiverID); - ins->setString(4, playerName); - ins->setUInt64(5, currentTime); - ins->setString(6, "Lost item"); - ins->setString(7, "This is a replacement item for one you lost."); - ins->setUInt(8, 0); - ins->setInt(9, lot); - ins->setInt(10, 0); - ins->setInt(11, 1); - ins->execute(); - delete ins; + IMail::MailInfo mailInsert; + mailInsert.senderId = entity->GetObjectID(); + mailInsert.senderUsername = "Darkflame Universe"; + mailInsert.receiverId = receiverID; + mailInsert.recipient = playerName; + mailInsert.subject = "Lost item"; + mailInsert.body = "This is a replacement item for one you lost."; + mailInsert.itemID = LWOOBJID_EMPTY; + mailInsert.itemLOT = lot; + mailInsert.itemSubkey = LWOOBJID_EMPTY; + mailInsert.itemCount = 1; + Database::Get()->InsertNewMail(mailInsert); ChatPackets::SendSystemMessage(sysAddr, u"Mail sent"); @@ -1015,25 +1001,16 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit LWOOBJID characterId = 0; if (player == nullptr) { - auto* accountQuery = Database::CreatePreppedStmt("SELECT account_id, id FROM charinfo WHERE name=? LIMIT 1;"); + auto characterInfo = Database::Get()->GetCharacterInfo(args[0]); - accountQuery->setString(1, args[0]); + if (characterInfo) { + accountId = characterInfo->accountId; + characterId = characterInfo->id; - auto result = accountQuery->executeQuery(); - - if (result->rowsCount() > 0) { - while (result->next()) { - accountId = result->getUInt(1); - characterId = result->getUInt64(2); - - GeneralUtils::SetBit(characterId, eObjectBits::CHARACTER); - GeneralUtils::SetBit(characterId, eObjectBits::PERSISTENT); - } + GeneralUtils::SetBit(characterId, eObjectBits::CHARACTER); + GeneralUtils::SetBit(characterId, eObjectBits::PERSISTENT); } - delete accountQuery; - delete result; - if (accountId == 0) { ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(args[0])); @@ -1044,8 +1021,6 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit characterId = player->GetObjectID(); } - auto* userUpdate = Database::CreatePreppedStmt("UPDATE accounts SET mute_expire = ? WHERE id = ?;"); - time_t expire = 1; // Default to indefinate mute if (args.size() >= 2) { @@ -1070,12 +1045,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit expire += 60 * 60 * hours; } - userUpdate->setUInt64(1, expire); - userUpdate->setInt(2, accountId); - - userUpdate->executeUpdate(); - - delete userUpdate; + Database::Get()->UpdateAccountUnmuteTime(accountId, expire); char buffer[32] = "brought up for review.\0"; @@ -1127,19 +1097,12 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit uint32_t accountId = 0; if (player == nullptr) { - auto* accountQuery = Database::CreatePreppedStmt("SELECT account_id FROM charinfo WHERE name=? LIMIT 1;"); + auto characterInfo = Database::Get()->GetCharacterInfo(args[0]); - accountQuery->setString(1, args[0]); - - auto result = accountQuery->executeQuery(); - - if (result->rowsCount() > 0) { - while (result->next()) accountId = result->getUInt(1); + if (characterInfo) { + accountId = characterInfo->accountId; } - delete accountQuery; - delete result; - if (accountId == 0) { ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(args[0])); @@ -1149,13 +1112,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit accountId = player->GetParentUser()->GetAccountID(); } - auto* userUpdate = Database::CreatePreppedStmt("UPDATE accounts SET banned = true WHERE id = ?;"); - - userUpdate->setInt(1, accountId); - - userUpdate->executeUpdate(); - - delete userUpdate; + Database::Get()->UpdateAccountBan(accountId, true); if (player != nullptr) { Game::server->Disconnect(player->GetSystemAddress(), eServerDisconnectIdentifiers::FREE_TRIAL_EXPIRED); diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 72eb4e59..e5947009 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -130,13 +130,8 @@ int main(int argc, char** argv) { LOG("Compiled on: %s", __TIMESTAMP__); //Connect to the MySQL Database - std::string mysql_host = Game::config->GetValue("mysql_host"); - std::string mysql_database = Game::config->GetValue("mysql_database"); - std::string mysql_username = Game::config->GetValue("mysql_username"); - std::string mysql_password = Game::config->GetValue("mysql_password"); - try { - Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); + Database::Connect(); } catch (sql::SQLException& ex) { LOG("Got an error while connecting to the database: %s", ex.what()); LOG("Migrations not run"); @@ -225,19 +220,14 @@ int main(int argc, char** argv) { std::cout << "Enter a username: "; std::cin >> username; - std::unique_ptr userLookupStatement(Database::CreatePreppedStmt("SELECT id FROM accounts WHERE name=? LIMIT 1;")); - userLookupStatement->setString(1, username.c_str()); - std::unique_ptr res(userLookupStatement->executeQuery()); - if (res->rowsCount() > 0) { + auto accountId = Database::Get()->GetAccountInfo(username); + if (accountId) { LOG("Account with name \"%s\" already exists", username.c_str()); std::cout << "Do you want to change the password of that account? [y/n]?"; std::string prompt = ""; std::cin >> prompt; if (prompt == "y" || prompt == "yes") { - uint32_t accountId = 0; - res->next(); - accountId = res->getUInt(1); - if (accountId == 0) return EXIT_FAILURE; + if (accountId->id == 0) return EXIT_FAILURE; //Read the password from the console without echoing it. #ifdef __linux__ @@ -257,10 +247,7 @@ int main(int argc, char** argv) { bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash); assert(bcryptState == 0); - std::unique_ptr userUpdateStatement(Database::CreatePreppedStmt("UPDATE accounts SET password = ? WHERE id = ?;")); - userUpdateStatement->setString(1, std::string(hash, BCRYPT_HASHSIZE).c_str()); - userUpdateStatement->setUInt(2, accountId); - userUpdateStatement->execute(); + Database::Get()->UpdateAccountPassword(accountId->id, std::string(hash, BCRYPT_HASHSIZE)); LOG("Account \"%s\" password updated successfully!", username.c_str()); } else { @@ -289,11 +276,7 @@ int main(int argc, char** argv) { //Create account try { - std::unique_ptr statement(Database::CreatePreppedStmt("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);")); - statement->setString(1, username.c_str()); - statement->setString(2, std::string(hash, BCRYPT_HASHSIZE).c_str()); - statement->setInt(3, 9); - statement->execute(); + Database::Get()->InsertNewAccount(username, std::string(hash, BCRYPT_HASHSIZE)); } catch(sql::SQLException& e) { LOG("A SQL error occurred!:\n %s", e.what()); return EXIT_FAILURE; @@ -312,8 +295,6 @@ int main(int argc, char** argv) { Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::shouldShutdown); //Query for the database for a server labeled "master" - auto* masterLookupStatement = Database::CreatePreppedStmt("SELECT id FROM `servers` WHERE `name` = 'master'"); - auto* result = masterLookupStatement->executeQuery(); auto master_server_ip = Game::config->GetValue("master_ip"); @@ -321,22 +302,7 @@ int main(int argc, char** argv) { master_server_ip = Game::server->GetIP(); } - //If we found a server, update it's IP and port to the current one. - if (result->next()) { - auto* updateStatement = Database::CreatePreppedStmt("UPDATE `servers` SET `ip` = ?, `port` = ? WHERE `id` = ?"); - updateStatement->setString(1, master_server_ip.c_str()); - updateStatement->setInt(2, Game::server->GetPort()); - updateStatement->setInt(3, result->getInt("id")); - updateStatement->execute(); - delete updateStatement; - } else { - //If we didn't find a server, create one. - auto* insertStatement = Database::CreatePreppedStmt("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171023)"); - insertStatement->setString(1, master_server_ip.c_str()); - insertStatement->setInt(2, Game::server->GetPort()); - insertStatement->execute(); - delete insertStatement; - } + Database::Get()->SetMasterIp(master_server_ip, Game::server->GetPort()); //Create additional objects here: ObjectIDManager::Instance()->Initialize(Game::logger); @@ -384,16 +350,12 @@ int main(int argc, char** argv) { //Find out the master's IP for absolutely no reason: std::string masterIP; uint32_t masterPort; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; } - delete res; - delete stmt; - framesSinceLastSQLPing = 0; } else framesSinceLastSQLPing++; diff --git a/dMasterServer/ObjectIDManager.cpp b/dMasterServer/ObjectIDManager.cpp index df0613cc..4739addf 100644 --- a/dMasterServer/ObjectIDManager.cpp +++ b/dMasterServer/ObjectIDManager.cpp @@ -14,58 +14,35 @@ void ObjectIDManager::Initialize(Logger* logger) { this->currentPersistentID = 1; try { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt( - "SELECT last_object_id FROM object_id_tracker"); - - sql::ResultSet* result = stmt->executeQuery(); - auto next = result->next(); - - if (!next) { - sql::PreparedStatement* insertStmt = Database::CreatePreppedStmt( - "INSERT INTO object_id_tracker VALUES (1)"); - - insertStmt->execute(); - - delete insertStmt; + auto lastObjectId = Database::Get()->GetCurrentPersistentId(); + if (!lastObjectId) { + Database::Get()->InsertDefaultPersistentId(); return; + } else { + this->currentPersistentID = lastObjectId.value(); } - while (next) { - this->currentPersistentID = - result->getInt(1) > 0 ? result->getInt(1) : 1; - next = result->next(); + if (this->currentPersistentID <= 0) { + LOG("Invalid persistent object ID in database. Aborting to prevent bad id generation."); + throw std::runtime_error("Invalid persistent object ID in database. Aborting to prevent bad id generation."); } - - delete result; - delete stmt; } catch (sql::SQLException& e) { - LOG("Unable to fetch max persistent object ID in use. Defaulting to 1."); + LOG("Unable to fetch max persistent object ID in use. This will cause issues. Aborting to prevent collisions."); LOG("SQL error: %s", e.what()); - this->currentPersistentID = 1; + throw; } } //! Generates a new persistent ID -uint32_t ObjectIDManager::GeneratePersistentID(void) { +uint32_t ObjectIDManager::GeneratePersistentID() { uint32_t toReturn = ++this->currentPersistentID; - // So we peroidically save our ObjID to the database: - // if (toReturn % 25 == 0) { // TEMP: DISABLED FOR DEBUG / DEVELOPMENT! - sql::PreparedStatement* stmt = Database::CreatePreppedStmt( - "UPDATE object_id_tracker SET last_object_id=?"); - stmt->setUInt(1, toReturn); - stmt->execute(); - delete stmt; - // } + SaveToDatabase(); return toReturn; } void ObjectIDManager::SaveToDatabase() { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt( - "UPDATE object_id_tracker SET last_object_id=?"); - stmt->setUInt(1, currentPersistentID); - stmt->execute(); - delete stmt; + Database::Get()->UpdatePersistentId(this->currentPersistentID); } diff --git a/dMasterServer/ObjectIDManager.h b/dMasterServer/ObjectIDManager.h index 1454d08f..cdb6dcdf 100644 --- a/dMasterServer/ObjectIDManager.h +++ b/dMasterServer/ObjectIDManager.h @@ -41,7 +41,7 @@ public: /*! \return The new persistent ID */ - uint32_t GeneratePersistentID(void); + uint32_t GeneratePersistentID(); void SaveToDatabase(); }; diff --git a/dNet/AuthPackets.cpp b/dNet/AuthPackets.cpp index 1445be37..12f140d6 100644 --- a/dNet/AuthPackets.cpp +++ b/dNet/AuthPackets.cpp @@ -27,6 +27,7 @@ #include "eConnectionType.h" #include "eServerMessageType.h" #include "eMasterMessageType.h" +#include "eGameMasterLevel.h" void AuthPackets::HandleHandshake(dServer* server, Packet* packet) { RakNet::BitStream inStream(packet->data, packet->length, false); @@ -64,120 +65,54 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) { const char* szUsername = username.c_str(); // Fetch account details - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT password, banned, locked, play_key_id, gm_level FROM accounts WHERE name=? LIMIT 1;"); - stmt->setString(1, szUsername); + auto accountInfo = Database::Get()->GetAccountInfo(username); - sql::ResultSet* res = stmt->executeQuery(); - - if (res->rowsCount() == 0) { - LOG("No user found!"); + if (!accountInfo) { + LOG("No user by name %s found!", username.c_str()); AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username); return; } - std::string sqlPass = ""; - bool sqlBanned = false; - bool sqlLocked = false; - uint32_t sqlPlayKey = 0; - uint32_t sqlGmLevel = 0; - - while (res->next()) { - sqlPass = res->getString(1).c_str(); - sqlBanned = res->getBoolean(2); - sqlLocked = res->getBoolean(3); - sqlPlayKey = res->getInt(4); - sqlGmLevel = res->getInt(5); - } - - delete stmt; - delete res; - //If we aren't running in live mode, then only GMs are allowed to enter: const auto& closedToNonDevs = Game::config->GetValue("closed_to_non_devs"); - if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && sqlGmLevel == 0) { + if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) { AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username); return; } - if (Game::config->GetValue("dont_use_keys") != "1") { + if (Game::config->GetValue("dont_use_keys") != "1" && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) { + LOG(""); //Check to see if we have a play key: - if (sqlPlayKey == 0 && sqlGmLevel == 0) { + if (accountInfo->playKeyId == 0) { AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username); LOG("User %s tried to log in, but they don't have a play key.", username.c_str()); return; } //Check if the play key is _valid_: - auto keyCheckStmt = Database::CreatePreppedStmt("SELECT active FROM `play_keys` WHERE id=?"); - keyCheckStmt->setInt(1, sqlPlayKey); - auto keyRes = keyCheckStmt->executeQuery(); - bool isKeyActive = false; + auto playKeyStatus = Database::Get()->IsPlaykeyActive(accountInfo->playKeyId); - if (keyRes->rowsCount() == 0 && sqlGmLevel == 0) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username); + if (!playKeyStatus) { + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a valid play key associated with it!", "", 2001, username); return; } - while (keyRes->next()) { - isKeyActive = (bool)keyRes->getInt(1); - } - - if (!isKeyActive && sqlGmLevel == 0) { + if (!playKeyStatus.value()) { AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username); LOG("User %s tried to log in, but their play key was disabled", username.c_str()); return; } } - if (sqlBanned) { + if (accountInfo->banned) { AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username); return; } - if (sqlLocked) { + if (accountInfo->locked) { AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username); return; } - /* - * Updated hashing method: - * First attempt bcrypt. - * If that fails, fallback to old method and setup bcrypt for new login. - */ - - bool loginSuccess = true; - - int32_t bcryptState = ::bcrypt_checkpw(password.c_str(), sqlPass.c_str()); - - if (bcryptState != 0) { - // Fallback on old method - - std::string oldPassword = sha512(password + username); - - if (sqlPass != oldPassword) { - loginSuccess = false; - } else { - // Generate new hash for bcrypt - - char salt[BCRYPT_HASHSIZE]; - char hash[BCRYPT_HASHSIZE]; - - bcryptState = ::bcrypt_gensalt(12, salt); - - assert(bcryptState == 0); - - bcryptState = ::bcrypt_hashpw(password.c_str(), salt, hash); - - assert(bcryptState == 0); - - sql::PreparedStatement* accountUpdate = Database::CreatePreppedStmt("UPDATE accounts SET password = ? WHERE name = ? LIMIT 1;"); - - accountUpdate->setString(1, std::string(hash, BCRYPT_HASHSIZE).c_str()); - accountUpdate->setString(2, szUsername); - - accountUpdate->executeUpdate(); - } - } else { - // Login success with bcrypt - } + bool loginSuccess = ::bcrypt_checkpw(password.c_str(), accountInfo->bcryptPassword.c_str()) == 0; if (!loginSuccess) { AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username); diff --git a/dNet/ClientPackets.cpp b/dNet/ClientPackets.cpp index daf0221a..18b2f3d1 100644 --- a/dNet/ClientPackets.cpp +++ b/dNet/ClientPackets.cpp @@ -353,30 +353,18 @@ void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Pa LWOOBJID idOfReceiver = LWOOBJID_EMPTY; { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE name = ?"); - stmt->setString(1, receiver); + auto characterIdFetch = Database::Get()->GetCharacterInfo(receiver); - sql::ResultSet* res = stmt->executeQuery(); - - if (res->next()) { - idOfReceiver = res->getInt("id"); + if (characterIdFetch) { + idOfReceiver = characterIdFetch->id; } - - delete stmt; - delete res; } if (user->GetIsBestFriendMap().find(receiver) == user->GetIsBestFriendMap().end() && idOfReceiver != LWOOBJID_EMPTY) { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"); - stmt->setInt(1, entity->GetObjectID()); - stmt->setInt(2, idOfReceiver); - stmt->setInt(3, idOfReceiver); - stmt->setInt(4, entity->GetObjectID()); + auto bffInfo = Database::Get()->GetBestFriendStatus(entity->GetObjectID(), idOfReceiver); - sql::ResultSet* res = stmt->executeQuery(); - - if (res->next()) { - isBestFriend = res->getInt("best_friend") == 3; + if (bffInfo) { + isBestFriend = bffInfo->bestFriendStatus == 3; } if (isBestFriend) { @@ -384,9 +372,6 @@ void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Pa tmpBestFriendMap[receiver] = true; user->SetIsBestFriendMap(tmpBestFriendMap); } - - delete res; - delete stmt; } else if (user->GetIsBestFriendMap().find(receiver) != user->GetIsBestFriendMap().end()) { isBestFriend = true; } diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index e8f5915d..cfd7d157 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -74,6 +74,7 @@ #include "ZCompression.h" #include "EntityManager.h" #include "CheatDetection.h" +#include "eGameMasterLevel.h" namespace Game { Logger* logger = nullptr; @@ -180,20 +181,15 @@ int main(int argc, char** argv) { CDClientManager::Instance(); - //Connect to the MySQL Database - std::string mysql_host = Game::config->GetValue("mysql_host"); - std::string mysql_database = Game::config->GetValue("mysql_database"); - std::string mysql_username = Game::config->GetValue("mysql_username"); - std::string mysql_password = Game::config->GetValue("mysql_password"); - Diagnostics::SetProduceMemoryDump(Game::config->GetValue("generate_dump") == "1"); if (!Game::config->GetValue("dump_folder").empty()) { Diagnostics::SetOutDirectory(Game::config->GetValue("dump_folder")); } + //Connect to the MySQL Database: try { - Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); + Database::Connect(); } catch (sql::SQLException& ex) { LOG("Got an error while connecting to the database: %s", ex.what()); return EXIT_FAILURE; @@ -202,15 +198,12 @@ int main(int argc, char** argv) { //Find out the master's IP: std::string masterIP = "localhost"; uint32_t masterPort = 1000; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); - } + auto masterInfo = Database::Get()->GetMasterInfo(); - delete res; - delete stmt; + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; + } ObjectIDManager::Instance()->Initialize(); UserManager::Instance()->Initialize(); @@ -485,16 +478,12 @@ int main(int argc, char** argv) { //Find out the master's IP for absolutely no reason: std::string masterIP; uint32_t masterPort; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; } - delete res; - delete stmt; - framesSinceLastSQLPing = 0; } else framesSinceLastSQLPing++; @@ -886,20 +875,15 @@ void HandlePacket(Packet* packet) { // If the check is turned on, validate the client's database checksum. if (Game::config->GetValue("check_fdb") == "1" && !databaseChecksum.empty()) { - uint32_t gmLevel = 0; - auto* stmt = Database::CreatePreppedStmt("SELECT gm_level FROM accounts WHERE name=? LIMIT 1;"); - stmt->setString(1, username.c_str()); - - auto* res = stmt->executeQuery(); - while (res->next()) { - gmLevel = res->getInt(1); + auto accountInfo = Database::Get()->GetAccountInfo(username); + if (!accountInfo) { + LOG("Client's account does not exist in the database, aborting connection."); + Game::server->Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::CHARACTER_NOT_FOUND); + return; } - delete stmt; - delete res; - // Developers may skip this check - if (gmLevel < 8 && clientDatabaseChecksum != databaseChecksum) { + if (accountInfo->maxGmLevel < eGameMasterLevel::DEVELOPER && clientDatabaseChecksum != databaseChecksum) { LOG("Client's database checksum does not match the server's, aborting connection."); Game::server->Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::WRONG_GAME_VERSION); return; @@ -963,7 +947,7 @@ void HandlePacket(Packet* packet) { CheckType::Entity, "Sending GM with a sending player that does not match their own. GM ID: %i", static_cast(messageID) - ); + ); if (isSender) GameMessageHandler::HandleMessage(&dataStream, packet->systemAddress, objectID, messageID); break; @@ -987,7 +971,7 @@ void HandlePacket(Packet* packet) { CheckType::User, "Sending login request with a sending player that does not match their own. Player ID: %llu", playerID - ); + ); if (!valid) return; @@ -1058,23 +1042,23 @@ void HandlePacket(Packet* packet) { if (!levelComponent) return; auto version = levelComponent->GetCharacterVersion(); - switch(version) { - case eCharacterVersion::RELEASE: - // TODO: Implement, super low priority - case eCharacterVersion::LIVE: - LOG("Updating Character Flags"); - c->SetRetroactiveFlags(); - levelComponent->SetCharacterVersion(eCharacterVersion::PLAYER_FACTION_FLAGS); - case eCharacterVersion::PLAYER_FACTION_FLAGS: - LOG("Updating Vault Size"); - player->RetroactiveVaultSize(); - levelComponent->SetCharacterVersion(eCharacterVersion::VAULT_SIZE); - case eCharacterVersion::VAULT_SIZE: - LOG("Updaing Speedbase"); - levelComponent->SetRetroactiveBaseSpeed(); - levelComponent->SetCharacterVersion(eCharacterVersion::UP_TO_DATE); - case eCharacterVersion::UP_TO_DATE: - break; + switch (version) { + case eCharacterVersion::RELEASE: + // TODO: Implement, super low priority + case eCharacterVersion::LIVE: + LOG("Updating Character Flags"); + c->SetRetroactiveFlags(); + levelComponent->SetCharacterVersion(eCharacterVersion::PLAYER_FACTION_FLAGS); + case eCharacterVersion::PLAYER_FACTION_FLAGS: + LOG("Updating Vault Size"); + player->RetroactiveVaultSize(); + levelComponent->SetCharacterVersion(eCharacterVersion::VAULT_SIZE); + case eCharacterVersion::VAULT_SIZE: + LOG("Updaing Speedbase"); + levelComponent->SetRetroactiveBaseSpeed(); + levelComponent->SetCharacterVersion(eCharacterVersion::UP_TO_DATE); + case eCharacterVersion::UP_TO_DATE: + break; } player->GetCharacter()->SetTargetScene(""); @@ -1090,89 +1074,45 @@ void HandlePacket(Packet* packet) { if (g_CloneID != 0) { const auto& worldId = Game::zoneManager->GetZone()->GetZoneID(); - const auto zoneId = Game::server->GetZoneID(); + const auto zoneId = worldId.GetMapID(); const auto cloneId = g_CloneID; - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT id FROM PropertyTemplate WHERE mapID = ?;"); - query.bind(1, (int)zoneId); + //Check for BBB models: + auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId); - auto result = query.execQuery(); - - if (result.eof() || result.fieldIsNull(0)) { - LOG("No property templates found for zone %d, not sending BBB", zoneId); + LWOOBJID propertyId = LWOOBJID_EMPTY; + if (propertyInfo) propertyId = propertyInfo->id; + else { + LOG("Couldn't find property ID for zone %i, clone %i", zoneId, cloneId); goto noBBB; } + for (auto& bbbModel : Database::Get()->GetUgcModels(propertyId)) { + LOG("Getting lxfml ugcID: %llu", bbbModel.id); - //Check for BBB models: - auto stmt = Database::CreatePreppedStmt("SELECT ugc_id FROM properties_contents WHERE lot=14 AND property_id=?"); + bbbModel.lxfmlData.seekg(0, std::ios::end); + size_t lxfmlSize = bbbModel.lxfmlData.tellg(); + bbbModel.lxfmlData.seekg(0); - int32_t templateId = result.getIntField(0); + //Send message: + LWOOBJID blueprintID = bbbModel.id; + GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER); + GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT); - result.finalize(); + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE); + bitStream.Write(LWOOBJID_EMPTY); //always zero so that a check on the client passes + bitStream.Write(eBlueprintSaveResponseType::EverythingWorked); + bitStream.Write(1); + bitStream.Write(blueprintID); - auto* propertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE template_id = ? AND clone_id = ?;"); + bitStream.Write(lxfmlSize); - propertyLookup->setInt(1, templateId); - propertyLookup->setInt64(2, g_CloneID); + bitStream.WriteAlignedBytes(reinterpret_cast(bbbModel.lxfmlData.str().c_str()), lxfmlSize); - auto* propertyEntry = propertyLookup->executeQuery(); - uint64_t propertyId = 0; - - if (propertyEntry->next()) { - propertyId = propertyEntry->getUInt64(1); + SystemAddress sysAddr = packet->systemAddress; + SEND_PACKET; + // PacketUtils::SavePacket("lxfml packet " + std::to_string(bbbModel.id) + ".bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); } - - delete propertyLookup; - - stmt->setUInt64(1, propertyId); - auto res = stmt->executeQuery(); - while (res->next()) { - LOG("Getting lxfml ugcID: %u", res->getUInt(1)); - - //Get lxfml: - auto stmtL = Database::CreatePreppedStmt("SELECT lxfml from ugc where id=?"); - stmtL->setUInt(1, res->getUInt(1)); - - auto lxres = stmtL->executeQuery(); - - while (lxres->next()) { - auto lxfml = lxres->getBlob(1); - - lxfml->seekg(0, std::ios::end); - size_t lxfmlSize = lxfml->tellg(); - lxfml->seekg(0); - - //Send message: - { - LWOOBJID blueprintID = res->getUInt(1); - GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER); - GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT); - - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE); - bitStream.Write(LWOOBJID_EMPTY); //always zero so that a check on the client passes - bitStream.Write(eBlueprintSaveResponseType::EverythingWorked); - bitStream.Write(1); - bitStream.Write(blueprintID); - - bitStream.Write(lxfmlSize); - - for (size_t i = 0; i < lxfmlSize; ++i) - bitStream.Write(lxfml->get()); - - SystemAddress sysAddr = packet->systemAddress; - SEND_PACKET; - PacketUtils::SavePacket("lxfml packet " + std::to_string(res->getUInt(1)) + ".bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); - } - } - - delete stmtL; - delete lxres; - } - - delete stmt; - delete res; } noBBB: @@ -1330,6 +1270,7 @@ void WorldShutdownProcess(uint32_t zoneId) { if (PropertyManagementComponent::Instance() != nullptr) { LOG("Saving ALL property data for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId()); PropertyManagementComponent::Instance()->Save(); + Database::Get()->RemoveUnreferencedUgcModels(); LOG("ALL property data saved for zone %i clone %i!", zoneId, PropertyManagementComponent::Instance()->GetCloneId()); }