mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-16 20:24:39 -06:00
Compare commits
3 Commits
magic-enum
...
serverside
| Author | SHA1 | Date | |
|---|---|---|---|
| 1151baeeac | |||
| ee1bbb7655 | |||
| 697989f6be |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -17,6 +17,3 @@
|
||||
[submodule "thirdparty/AccountManager"]
|
||||
path = thirdparty/AccountManager
|
||||
url = https://github.com/DarkflameUniverse/AccountManager
|
||||
[submodule "thirdparty/magic_enum"]
|
||||
path = thirdparty/magic_enum
|
||||
url = https://github.com/Neargye/magic_enum.git
|
||||
|
||||
@@ -214,12 +214,7 @@ set(INCLUDED_DIRECTORIES
|
||||
"dNavigation/dTerrain"
|
||||
"dZoneManager"
|
||||
"dDatabase"
|
||||
"dDatabase/CDClientDatabase"
|
||||
"dDatabase/CDClientDatabase/CDClientTables"
|
||||
"dDatabase/GameDatabase"
|
||||
"dDatabase/GameDatabase/ITables"
|
||||
"dDatabase/GameDatabase/MySQL"
|
||||
"dDatabase/GameDatabase/MySQL/Tables"
|
||||
"dDatabase/Tables"
|
||||
"dNet"
|
||||
"dScripts"
|
||||
"dScripts/02_server"
|
||||
@@ -297,7 +292,6 @@ set(INCLUDED_DIRECTORIES
|
||||
"thirdparty/recastnavigation"
|
||||
"thirdparty/SQLite"
|
||||
"thirdparty/cpplinq"
|
||||
"thirdparty/magic_enum/include"
|
||||
|
||||
"tests"
|
||||
"tests/dCommonTests"
|
||||
@@ -335,9 +329,8 @@ add_subdirectory(thirdparty)
|
||||
file(
|
||||
GLOB HEADERS_DDATABASE
|
||||
LIST_DIRECTORIES false
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/*.h
|
||||
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
|
||||
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
|
||||
)
|
||||
|
||||
|
||||
@@ -23,9 +23,6 @@ We do not recommend hosting public servers. Darkflame Universe is intended for s
|
||||
### Supply of resource files
|
||||
Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work.
|
||||
|
||||
## Step by step walkthrough for a single-player server
|
||||
If you would like a setup for a single player server only on a Windows machine, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README.
|
||||
|
||||
## Steps to setup server
|
||||
* [Clone this repository](#clone-the-repository)
|
||||
* [Install dependencies](#install-dependencies)
|
||||
|
||||
@@ -55,8 +55,14 @@ 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();
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
} catch (sql::SQLException& ex) {
|
||||
LOG("Got an error while connecting to the database: %s", ex.what());
|
||||
Database::Destroy("AuthServer");
|
||||
@@ -68,13 +74,16 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1500;
|
||||
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
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;
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
|
||||
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
|
||||
@@ -94,8 +103,6 @@ int main(int argc, char** argv) {
|
||||
uint32_t framesSinceMasterDisconnect = 0;
|
||||
uint32_t framesSinceLastSQLPing = 0;
|
||||
|
||||
AuthPackets::LoadClaimCodes();
|
||||
|
||||
while (!Game::shouldShutdown) {
|
||||
//Check if we're still connected to master:
|
||||
if (!Game::server->GetIsConnectedToMaster()) {
|
||||
@@ -127,12 +134,16 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
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;
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
|
||||
|
||||
@@ -32,11 +32,15 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
|
||||
}
|
||||
|
||||
//Read player names that are ok as well:
|
||||
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));
|
||||
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));
|
||||
}
|
||||
delete res;
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
dChatFilter::~dChatFilter() {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
set(DCHATSERVER_SOURCES
|
||||
"ChatIgnoreList.cpp"
|
||||
"ChatPacketHandler.cpp"
|
||||
"PlayerContainer.cpp"
|
||||
)
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
#include "ChatIgnoreList.h"
|
||||
#include "PlayerContainer.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "BitStreamUtils.h"
|
||||
#include "PacketUtils.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "eObjectBits.h"
|
||||
|
||||
#include "Database.h"
|
||||
|
||||
// A note to future readers, The client handles all the actual ignoring logic:
|
||||
// not allowing teams, rejecting DMs, friends requets etc.
|
||||
// The only thing not auto-handled is instance activities force joining the team on the server.
|
||||
|
||||
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
|
||||
bitStream.Write(receivingPlayer);
|
||||
|
||||
//portion that will get routed:
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, type);
|
||||
}
|
||||
|
||||
void ChatIgnoreList::GetIgnoreList(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerId;
|
||||
inStream.Read(playerId);
|
||||
|
||||
auto* receiver = Game::playerContainer.GetPlayerData(playerId);
|
||||
if (!receiver) {
|
||||
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!receiver->ignoredPlayers.empty()) {
|
||||
LOG_DEBUG("Player %llu already has an ignore list", playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
auto ignoreList = Database::Get()->GetIgnoreList(static_cast<uint32_t>(playerId));
|
||||
if (ignoreList.empty()) {
|
||||
LOG_DEBUG("Player %llu has no ignores", playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& ignoredPlayer : ignoreList) {
|
||||
receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayer.id, ignoredPlayer.name });
|
||||
GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::PERSISTENT);
|
||||
}
|
||||
|
||||
CBITSTREAM;
|
||||
WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::GET_IGNORE);
|
||||
|
||||
bitStream.Write<uint8_t>(false); // Probably is Is Free Trial, but we don't care about that
|
||||
bitStream.Write<uint16_t>(0); // literally spacing due to struct alignment
|
||||
|
||||
bitStream.Write<uint16_t>(receiver->ignoredPlayers.size());
|
||||
for (const auto& ignoredPlayer : receiver->ignoredPlayers) {
|
||||
bitStream.Write(ignoredPlayer.playerId);
|
||||
bitStream.Write(LUWString(ignoredPlayer.playerName, 36));
|
||||
}
|
||||
|
||||
Game::server->Send(&bitStream, packet->systemAddress, false);
|
||||
}
|
||||
|
||||
void ChatIgnoreList::AddIgnore(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerId;
|
||||
inStream.Read(playerId);
|
||||
|
||||
auto* receiver = Game::playerContainer.GetPlayerData(playerId);
|
||||
if (!receiver) {
|
||||
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int32_t MAX_IGNORES = 32;
|
||||
if (receiver->ignoredPlayers.size() > MAX_IGNORES) {
|
||||
LOG_DEBUG("Player %llu has too many ignores", playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
|
||||
|
||||
LUWString toIgnoreName(33);
|
||||
inStream.Read(toIgnoreName);
|
||||
std::string toIgnoreStr = toIgnoreName.GetAsString();
|
||||
|
||||
CBITSTREAM;
|
||||
WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::ADD_IGNORE);
|
||||
|
||||
// Check if the player exists
|
||||
LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY;
|
||||
if (toIgnoreStr == receiver->playerName || toIgnoreStr.find("[GM]") == 0) {
|
||||
LOG_DEBUG("Player %llu tried to ignore themselves", playerId);
|
||||
|
||||
bitStream.Write(ChatIgnoreList::AddResponse::GENERAL_ERROR);
|
||||
} else if (std::count(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), toIgnoreStr) > 0) {
|
||||
LOG_DEBUG("Player %llu is already ignoring %s", playerId, toIgnoreStr.c_str());
|
||||
|
||||
bitStream.Write(ChatIgnoreList::AddResponse::ALREADY_IGNORED);
|
||||
} else {
|
||||
// Get the playerId falling back to query if not online
|
||||
auto* playerData = Game::playerContainer.GetPlayerData(toIgnoreStr);
|
||||
if (!playerData) {
|
||||
// Fall back to query
|
||||
auto player = Database::Get()->GetCharacterInfo(toIgnoreStr);
|
||||
if (!player || player->name != toIgnoreStr) {
|
||||
LOG_DEBUG("Player %s not found", toIgnoreStr.c_str());
|
||||
} else {
|
||||
ignoredPlayerId = player->id;
|
||||
}
|
||||
} else {
|
||||
ignoredPlayerId = playerData->playerID;
|
||||
}
|
||||
|
||||
if (ignoredPlayerId != LWOOBJID_EMPTY) {
|
||||
Database::Get()->AddIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(ignoredPlayerId));
|
||||
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER);
|
||||
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::PERSISTENT);
|
||||
|
||||
receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayerId, toIgnoreStr });
|
||||
LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str());
|
||||
|
||||
bitStream.Write(ChatIgnoreList::AddResponse::SUCCESS);
|
||||
} else {
|
||||
bitStream.Write(ChatIgnoreList::AddResponse::PLAYER_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
LUWString playerNameSend(toIgnoreStr, 33);
|
||||
bitStream.Write(playerNameSend);
|
||||
bitStream.Write(ignoredPlayerId);
|
||||
|
||||
Game::server->Send(&bitStream, packet->systemAddress, false);
|
||||
}
|
||||
|
||||
void ChatIgnoreList::RemoveIgnore(Packet* packet) {
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerId;
|
||||
inStream.Read(playerId);
|
||||
|
||||
auto* receiver = Game::playerContainer.GetPlayerData(playerId);
|
||||
if (!receiver) {
|
||||
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
|
||||
return;
|
||||
}
|
||||
|
||||
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
|
||||
|
||||
LUWString removedIgnoreName(33);
|
||||
inStream.Read(removedIgnoreName);
|
||||
std::string removedIgnoreStr = removedIgnoreName.GetAsString();
|
||||
|
||||
auto toRemove = std::remove(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), removedIgnoreStr);
|
||||
if (toRemove == receiver->ignoredPlayers.end()) {
|
||||
LOG_DEBUG("Player %llu is not ignoring %s", playerId, removedIgnoreStr.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
Database::Get()->RemoveIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(toRemove->playerId));
|
||||
receiver->ignoredPlayers.erase(toRemove, receiver->ignoredPlayers.end());
|
||||
|
||||
CBITSTREAM;
|
||||
WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::REMOVE_IGNORE);
|
||||
|
||||
bitStream.Write<int8_t>(0);
|
||||
LUWString playerNameSend(removedIgnoreStr, 33);
|
||||
bitStream.Write(playerNameSend);
|
||||
|
||||
Game::server->Send(&bitStream, packet->systemAddress, false);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef __CHATIGNORELIST__H__
|
||||
#define __CHATIGNORELIST__H__
|
||||
|
||||
struct Packet;
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ChatIgnoreList {
|
||||
void GetIgnoreList(Packet* packet);
|
||||
void AddIgnore(Packet* packet);
|
||||
void RemoveIgnore(Packet* packet);
|
||||
|
||||
enum class Response : uint8_t {
|
||||
ADD_IGNORE = 32,
|
||||
REMOVE_IGNORE = 33,
|
||||
GET_IGNORE = 34,
|
||||
};
|
||||
|
||||
enum class AddResponse : uint8_t {
|
||||
SUCCESS,
|
||||
ALREADY_IGNORED,
|
||||
PLAYER_NOT_FOUND,
|
||||
GENERAL_ERROR,
|
||||
};
|
||||
};
|
||||
|
||||
#endif //!__CHATIGNORELIST__H__
|
||||
@@ -19,29 +19,46 @@
|
||||
#include "eClientMessageType.h"
|
||||
#include "eGameMessageType.h"
|
||||
|
||||
extern PlayerContainer playerContainer;
|
||||
|
||||
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
//Get from the packet which player we want to do something with:
|
||||
CINSTREAM_SKIP_HEADER;
|
||||
LWOOBJID playerID = 0;
|
||||
inStream.Read(playerID);
|
||||
|
||||
auto player = Game::playerContainer.GetPlayerData(playerID);
|
||||
auto player = playerContainer.GetPlayerData(playerID);
|
||||
if (!player) return;
|
||||
|
||||
auto friendsList = Database::Get()->GetFriendsList(playerID);
|
||||
for (const auto& friendData : friendsList) {
|
||||
//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<sql::PreparedStatement> 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<uint32_t>(playerID));
|
||||
stmt->setUInt(2, static_cast<uint32_t>(playerID));
|
||||
stmt->setUInt(3, static_cast<uint32_t>(playerID));
|
||||
|
||||
std::vector<FriendData> friends;
|
||||
|
||||
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
|
||||
while (res->next()) {
|
||||
FriendData fd;
|
||||
fd.isFTP = false; // not a thing in DLU
|
||||
fd.friendID = friendData.friendID;
|
||||
fd.friendID = res->getUInt(1);
|
||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
|
||||
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
|
||||
|
||||
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
|
||||
fd.isBestFriend = res->getInt(2) == 3; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
|
||||
if (fd.isBestFriend) player->countOfBestFriends += 1;
|
||||
fd.friendName = friendData.friendName;
|
||||
fd.friendName = res->getString(3);
|
||||
|
||||
//Now check if they're online:
|
||||
auto fr = Game::playerContainer.GetPlayerData(fd.friendID);
|
||||
auto fr = playerContainer.GetPlayerData(fd.friendID);
|
||||
|
||||
if (fr) {
|
||||
fd.isOnline = true;
|
||||
@@ -54,7 +71,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
fd.zoneID = LWOZONEID();
|
||||
}
|
||||
|
||||
player->friends.push_back(fd);
|
||||
friends.push_back(fd);
|
||||
}
|
||||
|
||||
//Now, we need to send the friendlist to the server they came from:
|
||||
@@ -66,12 +83,14 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
|
||||
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE);
|
||||
bitStream.Write<uint8_t>(0);
|
||||
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
|
||||
bitStream.Write((uint16_t)player->friends.size());
|
||||
bitStream.Write((uint16_t)friends.size());
|
||||
|
||||
for (auto& data : player->friends) {
|
||||
for (auto& data : friends) {
|
||||
data.Serialize(bitStream);
|
||||
}
|
||||
|
||||
player->friends = friends;
|
||||
|
||||
SystemAddress sysAddr = player->sysAddr;
|
||||
SEND_PACKET;
|
||||
}
|
||||
@@ -95,7 +114,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
char isBestFriendRequest{};
|
||||
inStream.Read(isBestFriendRequest);
|
||||
|
||||
auto requestor = Game::playerContainer.GetPlayerData(requestorPlayerID);
|
||||
auto requestor = playerContainer.GetPlayerData(requestorPlayerID);
|
||||
if (!requestor) {
|
||||
LOG("No requestor player %llu sent to %s found.", requestorPlayerID, playerName.c_str());
|
||||
return;
|
||||
@@ -105,7 +124,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN);
|
||||
return;
|
||||
};
|
||||
std::unique_ptr<PlayerData> requestee(Game::playerContainer.GetPlayerData(playerName));
|
||||
std::unique_ptr<PlayerData> requestee(playerContainer.GetPlayerData(playerName));
|
||||
|
||||
// Check if player is online first
|
||||
if (isBestFriendRequest && !requestee) {
|
||||
@@ -133,26 +152,35 @@ 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<sql::PreparedStatement> nameQuery(Database::CreatePreppedStmt("SELECT name from charinfo where name = ?;"));
|
||||
nameQuery->setString(1, playerName);
|
||||
std::unique_ptr<sql::ResultSet> result(nameQuery->executeQuery());
|
||||
|
||||
requestee.reset(new PlayerData());
|
||||
requestee->playerName = playerName;
|
||||
auto responseType = Database::Get()->GetCharacterInfo(playerName)
|
||||
? eAddFriendResponseType::NOTONLINE
|
||||
: eAddFriendResponseType::INVALIDCHARACTER;
|
||||
|
||||
SendFriendResponse(requestor, requestee.get(), responseType);
|
||||
SendFriendResponse(requestor, requestee.get(), result->next() ? eAddFriendResponseType::NOTONLINE : eAddFriendResponseType::INVALIDCHARACTER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isBestFriendRequest) {
|
||||
std::unique_ptr<sql::PreparedStatement> 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<uint32_t>(requestorPlayerID));
|
||||
friendUpdate->setUInt(2, static_cast<uint32_t>(requestee->playerID));
|
||||
friendUpdate->setUInt(3, static_cast<uint32_t>(requestee->playerID));
|
||||
friendUpdate->setUInt(4, static_cast<uint32_t>(requestorPlayerID));
|
||||
std::unique_ptr<sql::ResultSet> result(friendUpdate->executeQuery());
|
||||
|
||||
LWOOBJID queryPlayerID = LWOOBJID_EMPTY;
|
||||
LWOOBJID queryFriendID = LWOOBJID_EMPTY;
|
||||
uint8_t oldBestFriendStatus{};
|
||||
uint8_t bestFriendStatus{};
|
||||
auto bestFriendInfo = Database::Get()->GetBestFriendStatus(requestorPlayerID, requestee->playerID);
|
||||
if (bestFriendInfo) {
|
||||
|
||||
if (result->next()) {
|
||||
// Get the IDs
|
||||
LWOOBJID queryPlayerID = bestFriendInfo->playerCharacterId;
|
||||
LWOOBJID queryFriendID = bestFriendInfo->friendCharacterId;
|
||||
oldBestFriendStatus = bestFriendInfo->bestFriendStatus;
|
||||
queryPlayerID = result->getInt(1);
|
||||
queryFriendID = result->getInt(2);
|
||||
oldBestFriendStatus = result->getInt(3);
|
||||
bestFriendStatus = oldBestFriendStatus;
|
||||
|
||||
// Set the bits
|
||||
@@ -173,7 +201,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
|
||||
// Only do updates if there was a change in the bff status.
|
||||
if (oldBestFriendStatus != bestFriendStatus) {
|
||||
auto maxBestFriends = Game::playerContainer.GetMaxNumberOfBestFriends();
|
||||
auto maxBestFriends = playerContainer.GetMaxNumberOfBestFriends();
|
||||
if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) {
|
||||
if (requestee->countOfBestFriends >= maxBestFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
|
||||
@@ -183,7 +211,13 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
}
|
||||
} else {
|
||||
// Then update the database with this new info.
|
||||
Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee->playerID, bestFriendStatus);
|
||||
std::unique_ptr<sql::PreparedStatement> 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<uint32_t>(requestorPlayerID));
|
||||
updateQuery->setUInt(3, static_cast<uint32_t>(requestee->playerID));
|
||||
updateQuery->setUInt(4, static_cast<uint32_t>(requestee->playerID));
|
||||
updateQuery->setUInt(5, static_cast<uint32_t>(requestorPlayerID));
|
||||
updateQuery->executeUpdate();
|
||||
// Sent the best friend update here if the value is 3
|
||||
if (bestFriendStatus == 3U) {
|
||||
requestee->countOfBestFriends += 1;
|
||||
@@ -206,7 +240,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
|
||||
if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true);
|
||||
}
|
||||
} else {
|
||||
auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends();
|
||||
auto maxFriends = playerContainer.GetMaxNumberOfFriends();
|
||||
if (requestee->friends.size() >= maxFriends) {
|
||||
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
|
||||
} else if (requestor->friends.size() >= maxFriends) {
|
||||
@@ -230,8 +264,8 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
|
||||
std::string friendName = PacketUtils::ReadString(0x15, packet, true);
|
||||
|
||||
//Now to try and find both of these:
|
||||
auto requestor = Game::playerContainer.GetPlayerData(playerID);
|
||||
auto requestee = Game::playerContainer.GetPlayerData(friendName);
|
||||
auto requestor = playerContainer.GetPlayerData(playerID);
|
||||
auto requestee = playerContainer.GetPlayerData(friendName);
|
||||
if (!requestor || !requestee) return;
|
||||
|
||||
eAddFriendResponseType serverResponseCode{};
|
||||
@@ -285,7 +319,11 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
|
||||
requesteeData.isOnline = true;
|
||||
requestor->friends.push_back(requesteeData);
|
||||
|
||||
Database::Get()->AddFriend(requestor->playerID, requestee->playerID);
|
||||
std::unique_ptr<sql::PreparedStatement> statement(Database::CreatePreppedStmt("INSERT IGNORE INTO `friends` (`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?);"));
|
||||
statement->setUInt(1, static_cast<uint32_t>(requestor->playerID));
|
||||
statement->setUInt(2, static_cast<uint32_t>(requestee->playerID));
|
||||
statement->setInt(3, 0);
|
||||
statement->execute();
|
||||
}
|
||||
|
||||
if (serverResponseCode != eAddFriendResponseType::DECLINED) SendFriendResponse(requestor, requestee, serverResponseCode, isAlreadyBestFriends);
|
||||
@@ -300,20 +338,28 @@ 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<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? LIMIT 1;"));
|
||||
stmt->setString(1, friendName.c_str());
|
||||
|
||||
LWOOBJID friendID = 0;
|
||||
auto friendIdResult = Database::Get()->GetCharacterInfo(friendName);
|
||||
if (friendIdResult) {
|
||||
friendID = friendIdResult->id;
|
||||
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
|
||||
while (res->next()) {
|
||||
friendID = res->getUInt(1);
|
||||
}
|
||||
|
||||
// Convert friendID to LWOOBJID
|
||||
GeneralUtils::SetBit(friendID, eObjectBits::PERSISTENT);
|
||||
GeneralUtils::SetBit(friendID, eObjectBits::CHARACTER);
|
||||
|
||||
Database::Get()->RemoveFriend(playerID, friendID);
|
||||
std::unique_ptr<sql::PreparedStatement> 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<uint32_t>(playerID));
|
||||
deletestmt->setUInt(2, static_cast<uint32_t>(friendID));
|
||||
deletestmt->setUInt(3, static_cast<uint32_t>(friendID));
|
||||
deletestmt->setUInt(4, static_cast<uint32_t>(playerID));
|
||||
deletestmt->execute();
|
||||
|
||||
//Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended:
|
||||
auto goonA = Game::playerContainer.GetPlayerData(playerID);
|
||||
auto goonA = playerContainer.GetPlayerData(playerID);
|
||||
if (goonA) {
|
||||
// Remove the friend from our list of friends
|
||||
for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) {
|
||||
@@ -326,7 +372,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
|
||||
SendRemoveFriend(goonA, friendName, true);
|
||||
}
|
||||
|
||||
auto goonB = Game::playerContainer.GetPlayerData(friendID);
|
||||
auto goonB = playerContainer.GetPlayerData(friendID);
|
||||
if (!goonB) return;
|
||||
// Do it again for other person
|
||||
for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) {
|
||||
@@ -337,7 +383,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string goonAName = GeneralUtils::UTF16ToWTF8(Game::playerContainer.GetName(playerID));
|
||||
std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID));
|
||||
SendRemoveFriend(goonB, goonAName, true);
|
||||
}
|
||||
|
||||
@@ -346,11 +392,11 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
|
||||
auto* sender = Game::playerContainer.GetPlayerData(playerID);
|
||||
auto* sender = playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (sender == nullptr) return;
|
||||
|
||||
if (Game::playerContainer.GetIsMuted(sender)) return;
|
||||
if (playerContainer.GetIsMuted(sender)) return;
|
||||
|
||||
const auto senderName = std::string(sender->playerName.c_str());
|
||||
|
||||
@@ -365,12 +411,12 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
|
||||
|
||||
if (channel != 8) return;
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team == nullptr) return;
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
auto* otherMember = playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (otherMember == nullptr) return;
|
||||
|
||||
@@ -404,11 +450,11 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
|
||||
std::string message = PacketUtils::ReadString(0xAA, packet, true, 512);
|
||||
|
||||
//Get the bois:
|
||||
auto goonA = Game::playerContainer.GetPlayerData(senderID);
|
||||
auto goonB = Game::playerContainer.GetPlayerData(receiverName);
|
||||
auto goonA = playerContainer.GetPlayerData(senderID);
|
||||
auto goonB = playerContainer.GetPlayerData(receiverName);
|
||||
if (!goonA || !goonB) return;
|
||||
|
||||
if (Game::playerContainer.GetIsMuted(goonA)) return;
|
||||
if (playerContainer.GetIsMuted(goonA)) return;
|
||||
|
||||
std::string goonAName = goonA->playerName.c_str();
|
||||
std::string goonBName = goonB->playerName.c_str();
|
||||
@@ -466,25 +512,25 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) {
|
||||
inStream.Read(playerID);
|
||||
std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true);
|
||||
|
||||
auto* player = Game::playerContainer.GetPlayerData(playerID);
|
||||
auto* player = playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (player == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team == nullptr) {
|
||||
team = Game::playerContainer.CreateTeam(playerID);
|
||||
team = playerContainer.CreateTeam(playerID);
|
||||
}
|
||||
|
||||
auto* other = Game::playerContainer.GetPlayerData(invitedPlayer);
|
||||
auto* other = playerContainer.GetPlayerData(invitedPlayer);
|
||||
|
||||
if (other == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Game::playerContainer.GetTeam(other->playerID) != nullptr) {
|
||||
if (playerContainer.GetTeam(other->playerID) != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -517,12 +563,12 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(leaderID);
|
||||
auto* team = playerContainer.GetTeam(leaderID);
|
||||
|
||||
if (team == nullptr) {
|
||||
LOG("Failed to find team for leader (%llu)", leaderID);
|
||||
|
||||
team = Game::playerContainer.GetTeam(playerID);
|
||||
team = playerContainer.GetTeam(playerID);
|
||||
}
|
||||
|
||||
if (team == nullptr) {
|
||||
@@ -530,7 +576,7 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
|
||||
return;
|
||||
}
|
||||
|
||||
Game::playerContainer.AddMember(team, playerID);
|
||||
playerContainer.AddMember(team, playerID);
|
||||
}
|
||||
|
||||
void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
|
||||
@@ -540,12 +586,12 @@ void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
|
||||
uint32_t size = 0;
|
||||
inStream.Read(size);
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
|
||||
LOG("(%llu) leaving team", playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
Game::playerContainer.RemoveMember(team, playerID, false, false, true);
|
||||
playerContainer.RemoveMember(team, playerID, false, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,24 +604,24 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet) {
|
||||
|
||||
LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.c_str());
|
||||
|
||||
auto* kicked = Game::playerContainer.GetPlayerData(kickedPlayer);
|
||||
auto* kicked = playerContainer.GetPlayerData(kickedPlayer);
|
||||
|
||||
LWOOBJID kickedId = LWOOBJID_EMPTY;
|
||||
|
||||
if (kicked != nullptr) {
|
||||
kickedId = kicked->playerID;
|
||||
} else {
|
||||
kickedId = Game::playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer));
|
||||
kickedId = playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer));
|
||||
}
|
||||
|
||||
if (kickedId == LWOOBJID_EMPTY) return;
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID || team->leaderID == kickedId) return;
|
||||
|
||||
Game::playerContainer.RemoveMember(team, kickedId, false, true, false);
|
||||
playerContainer.RemoveMember(team, kickedId, false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,16 +634,16 @@ void ChatPacketHandler::HandleTeamPromote(Packet* packet) {
|
||||
|
||||
LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.c_str());
|
||||
|
||||
auto* promoted = Game::playerContainer.GetPlayerData(promotedPlayer);
|
||||
auto* promoted = playerContainer.GetPlayerData(promotedPlayer);
|
||||
|
||||
if (promoted == nullptr) return;
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID) return;
|
||||
|
||||
Game::playerContainer.PromoteMember(team, promoted->playerID);
|
||||
playerContainer.PromoteMember(team, promoted->playerID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,16 +657,16 @@ void ChatPacketHandler::HandleTeamLootOption(Packet* packet) {
|
||||
char option;
|
||||
inStream.Read(option);
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
|
||||
if (team != nullptr) {
|
||||
if (team->leaderID != playerID) return;
|
||||
|
||||
team->lootFlag = option;
|
||||
|
||||
Game::playerContainer.TeamStatusUpdate(team);
|
||||
playerContainer.TeamStatusUpdate(team);
|
||||
|
||||
Game::playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,18 +675,18 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
|
||||
LWOOBJID playerID = LWOOBJID_EMPTY;
|
||||
inStream.Read(playerID);
|
||||
|
||||
auto* team = Game::playerContainer.GetTeam(playerID);
|
||||
auto* data = Game::playerContainer.GetPlayerData(playerID);
|
||||
auto* team = playerContainer.GetTeam(playerID);
|
||||
auto* data = playerContainer.GetPlayerData(playerID);
|
||||
|
||||
if (team != nullptr && data != nullptr) {
|
||||
if (team->local && data->zoneID.GetMapID() != team->zoneId.GetMapID() && data->zoneID.GetCloneID() != team->zoneId.GetCloneID()) {
|
||||
Game::playerContainer.RemoveMember(team, playerID, false, false, true, true);
|
||||
playerContainer.RemoveMember(team, playerID, false, false, true, true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (team->memberIDs.size() <= 1 && !team->local) {
|
||||
Game::playerContainer.DisbandTeam(team);
|
||||
playerContainer.DisbandTeam(team);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -651,16 +697,16 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
|
||||
ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY);
|
||||
}
|
||||
|
||||
Game::playerContainer.TeamStatusUpdate(team);
|
||||
playerContainer.TeamStatusUpdate(team);
|
||||
|
||||
const auto leaderName = GeneralUtils::UTF8ToUTF16(data->playerName);
|
||||
|
||||
for (const auto memberId : team->memberIDs) {
|
||||
auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
|
||||
auto* otherMember = playerContainer.GetPlayerData(memberId);
|
||||
|
||||
if (memberId == playerID) continue;
|
||||
|
||||
const auto memberName = Game::playerContainer.GetName(memberId);
|
||||
const auto memberName = playerContainer.GetName(memberId);
|
||||
|
||||
if (otherMember != nullptr) {
|
||||
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID);
|
||||
@@ -668,7 +714,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
|
||||
ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0));
|
||||
}
|
||||
|
||||
Game::playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
playerContainer.UpdateTeamsOnWorld(team, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "eChatMessageType.h"
|
||||
#include "eChatInternalMessageType.h"
|
||||
#include "eWorldMessageType.h"
|
||||
#include "ChatIgnoreList.h"
|
||||
|
||||
#include "Game.h"
|
||||
|
||||
@@ -35,12 +34,14 @@ namespace Game {
|
||||
AssetManager* assetManager = nullptr;
|
||||
bool shouldShutdown = false;
|
||||
std::mt19937 randomEngine;
|
||||
PlayerContainer playerContainer;
|
||||
}
|
||||
|
||||
|
||||
Logger* SetupLogger();
|
||||
void HandlePacket(Packet* packet);
|
||||
|
||||
PlayerContainer playerContainer;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
constexpr uint32_t chatFramerate = mediumFramerate;
|
||||
constexpr uint32_t chatFrameDelta = mediumFrameDelta;
|
||||
@@ -77,8 +78,13 @@ 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();
|
||||
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
|
||||
} catch (sql::SQLException& ex) {
|
||||
LOG("Got an error while connecting to the database: %s", ex.what());
|
||||
Database::Destroy("ChatServer");
|
||||
@@ -90,11 +96,16 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort = 1000;
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
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;
|
||||
|
||||
//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;
|
||||
@@ -107,8 +118,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
Game::randomEngine = std::mt19937(time(0));
|
||||
|
||||
Game::playerContainer.Initialize();
|
||||
|
||||
//Run it until server gets a kill message from Master:
|
||||
auto t = std::chrono::high_resolution_clock::now();
|
||||
Packet* packet = nullptr;
|
||||
@@ -149,13 +158,16 @@ int main(int argc, char** argv) {
|
||||
//Find out the master's IP for absolutely no reason:
|
||||
std::string masterIP;
|
||||
uint32_t masterPort;
|
||||
|
||||
auto masterInfo = Database::Get()->GetMasterInfo();
|
||||
if (masterInfo) {
|
||||
masterIP = masterInfo->ip;
|
||||
masterPort = masterInfo->port;
|
||||
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;
|
||||
|
||||
framesSinceLastSQLPing = 0;
|
||||
} else framesSinceLastSQLPing++;
|
||||
|
||||
@@ -199,19 +211,19 @@ void HandlePacket(Packet* packet) {
|
||||
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) {
|
||||
switch (static_cast<eChatInternalMessageType>(packet->data[3])) {
|
||||
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
|
||||
Game::playerContainer.InsertPlayer(packet);
|
||||
playerContainer.InsertPlayer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION:
|
||||
Game::playerContainer.RemovePlayer(packet);
|
||||
playerContainer.RemovePlayer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::MUTE_UPDATE:
|
||||
Game::playerContainer.MuteUpdate(packet);
|
||||
playerContainer.MuteUpdate(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::CREATE_TEAM:
|
||||
Game::playerContainer.CreateTeamServer(packet);
|
||||
playerContainer.CreateTeamServer(packet);
|
||||
break;
|
||||
|
||||
case eChatInternalMessageType::ANNOUNCEMENT: {
|
||||
@@ -233,15 +245,7 @@ void HandlePacket(Packet* packet) {
|
||||
break;
|
||||
|
||||
case eChatMessageType::GET_IGNORE_LIST:
|
||||
ChatIgnoreList::GetIgnoreList(packet);
|
||||
break;
|
||||
|
||||
case eChatMessageType::ADD_IGNORE:
|
||||
ChatIgnoreList::AddIgnore(packet);
|
||||
break;
|
||||
|
||||
case eChatMessageType::REMOVE_IGNORE:
|
||||
ChatIgnoreList::RemoveIgnore(packet);
|
||||
LOG("Asked for ignore list, but is unimplemented right now.");
|
||||
break;
|
||||
|
||||
case eChatMessageType::TEAM_GET_STATUS:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include "ChatPackets.h"
|
||||
#include "dConfig.h"
|
||||
|
||||
void PlayerContainer::Initialize() {
|
||||
PlayerContainer::PlayerContainer() {
|
||||
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_best_friends"), m_MaxNumberOfBestFriends);
|
||||
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_friends"), m_MaxNumberOfFriends);
|
||||
}
|
||||
@@ -48,7 +48,14 @@ 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());
|
||||
|
||||
Database::Get()->UpdateActivityLog(data->playerID, eActivityType::PlayerLoggedIn, 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();
|
||||
}
|
||||
|
||||
void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
@@ -85,7 +92,14 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
|
||||
LOG("Removed user: %llu", playerID);
|
||||
m_Players.erase(playerID);
|
||||
|
||||
Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player->zoneID.GetMapID());
|
||||
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();
|
||||
}
|
||||
|
||||
void PlayerContainer::MuteUpdate(Packet* packet) {
|
||||
|
||||
@@ -7,26 +7,12 @@
|
||||
#include "dServer.h"
|
||||
#include <unordered_map>
|
||||
|
||||
struct IgnoreData {
|
||||
inline bool operator==(const std::string& other) const noexcept {
|
||||
return playerName == other;
|
||||
}
|
||||
|
||||
inline bool operator==(const LWOOBJID& other) const noexcept {
|
||||
return playerId == other;
|
||||
}
|
||||
|
||||
LWOOBJID playerId;
|
||||
std::string playerName;
|
||||
};
|
||||
|
||||
struct PlayerData {
|
||||
LWOOBJID playerID;
|
||||
std::string playerName;
|
||||
SystemAddress sysAddr;
|
||||
LWOZONEID zoneID;
|
||||
std::vector<FriendData> friends;
|
||||
std::vector<IgnoreData> ignoredPlayers;
|
||||
time_t muteExpire;
|
||||
uint8_t countOfBestFriends = 0;
|
||||
};
|
||||
@@ -43,9 +29,9 @@ struct TeamData {
|
||||
|
||||
class PlayerContainer {
|
||||
public:
|
||||
PlayerContainer();
|
||||
~PlayerContainer();
|
||||
|
||||
void Initialize();
|
||||
void InsertPlayer(Packet* packet);
|
||||
void RemovePlayer(Packet* packet);
|
||||
void MuteUpdate(Packet* packet);
|
||||
|
||||
@@ -13,8 +13,9 @@
|
||||
|
||||
//! Forward declarations
|
||||
|
||||
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase();
|
||||
void WriteSd0Magic(char* input, uint32_t chunkSize);
|
||||
bool CheckSd0Magic(std::istream& streamToCheck);
|
||||
bool CheckSd0Magic(sql::Blob* streamToCheck);
|
||||
|
||||
/**
|
||||
* @brief Truncates all models with broken data from the database.
|
||||
@@ -23,24 +24,28 @@ bool CheckSd0Magic(std::istream& streamToCheck);
|
||||
*/
|
||||
uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
uint32_t modelsTruncated{};
|
||||
auto modelsToTruncate = Database::Get()->GetAllUgcModels();
|
||||
bool previousCommitValue = Database::Get()->GetAutoCommit();
|
||||
Database::Get()->SetAutoCommit(false);
|
||||
for (auto& model : modelsToTruncate) {
|
||||
auto modelsToTruncate = GetModelsFromDatabase();
|
||||
bool previousCommitValue = Database::GetAutoCommit();
|
||||
Database::SetAutoCommit(false);
|
||||
while (modelsToTruncate->next()) {
|
||||
std::unique_ptr<sql::PreparedStatement> ugcModelToDelete(Database::CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;"));
|
||||
std::unique_ptr<sql::PreparedStatement> pcModelToDelete(Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;"));
|
||||
std::string completeUncompressedModel{};
|
||||
uint32_t chunkCount{};
|
||||
uint64_t modelId = modelsToTruncate->getInt(1);
|
||||
std::unique_ptr<sql::Blob> modelAsSd0(modelsToTruncate->getBlob(2));
|
||||
// Check that header is sd0 by checking for the sd0 magic.
|
||||
if (CheckSd0Magic(model.lxfmlData)) {
|
||||
if (CheckSd0Magic(modelAsSd0.get())) {
|
||||
while (true) {
|
||||
uint32_t chunkSize{};
|
||||
model.lxfmlData.read(reinterpret_cast<char*>(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream
|
||||
modelAsSd0->read(reinterpret_cast<char*>(&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 (!model.lxfmlData.good()) break;
|
||||
if (!modelAsSd0->good()) break;
|
||||
|
||||
std::unique_ptr<uint8_t[]> compressedChunk(new uint8_t[chunkSize]);
|
||||
for (uint32_t i = 0; i < chunkSize; i++) {
|
||||
compressedChunk[i] = model.lxfmlData.get();
|
||||
compressedChunk[i] = modelAsSd0->get();
|
||||
}
|
||||
|
||||
// Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
|
||||
@@ -54,7 +59,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, model.id, err);
|
||||
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, modelId, err);
|
||||
break;
|
||||
}
|
||||
chunkCount++;
|
||||
@@ -70,20 +75,26 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
"</LXFML>",
|
||||
completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos
|
||||
) {
|
||||
LOG("Brick-by-brick model %llu will be deleted!", model.id);
|
||||
Database::Get()->DeleteUgcModelData(model.id);
|
||||
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();
|
||||
modelsTruncated++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOG("Brick-by-brick model %llu will be deleted!", model.id);
|
||||
Database::Get()->DeleteUgcModelData(model.id);
|
||||
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();
|
||||
modelsTruncated++;
|
||||
}
|
||||
}
|
||||
|
||||
Database::Get()->Commit();
|
||||
Database::Get()->SetAutoCommit(previousCommitValue);
|
||||
Database::Commit();
|
||||
Database::SetAutoCommit(previousCommitValue);
|
||||
return modelsTruncated;
|
||||
}
|
||||
|
||||
@@ -95,17 +106,21 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
|
||||
*/
|
||||
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||
uint32_t updatedModels = 0;
|
||||
auto modelsToUpdate = Database::Get()->GetAllUgcModels();
|
||||
auto previousAutoCommitState = Database::Get()->GetAutoCommit();
|
||||
Database::Get()->SetAutoCommit(false);
|
||||
for (auto& model : modelsToUpdate) {
|
||||
auto modelsToUpdate = GetModelsFromDatabase();
|
||||
auto previousAutoCommitState = Database::GetAutoCommit();
|
||||
Database::SetAutoCommit(false);
|
||||
std::unique_ptr<sql::PreparedStatement> insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;"));
|
||||
while (modelsToUpdate->next()) {
|
||||
int64_t modelId = modelsToUpdate->getInt64(1);
|
||||
std::unique_ptr<sql::Blob> oldLxfml(modelsToUpdate->getBlob(2));
|
||||
// Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib)
|
||||
// If it does, convert it to sd0.
|
||||
if (model.lxfmlData.get() == 0x78 && model.lxfmlData.get() == 0xDA) {
|
||||
if (oldLxfml->get() == 0x78 && oldLxfml->get() == 0xDA) {
|
||||
|
||||
// Get and save size of zlib compressed chunk.
|
||||
model.lxfmlData.seekg(0, std::ios::end);
|
||||
uint32_t oldLxfmlSize = static_cast<uint32_t>(model.lxfmlData.tellg());
|
||||
model.lxfmlData.seekg(0);
|
||||
oldLxfml->seekg(0, std::ios::end);
|
||||
uint32_t oldLxfmlSize = static_cast<uint32_t>(oldLxfml->tellg());
|
||||
oldLxfml->seekg(0);
|
||||
|
||||
// Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size.
|
||||
uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9;
|
||||
@@ -113,27 +128,34 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
|
||||
|
||||
WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize);
|
||||
for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) {
|
||||
sd0ConvertedModel.get()[i] = model.lxfmlData.get();
|
||||
sd0ConvertedModel.get()[i] = oldLxfml->get();
|
||||
}
|
||||
|
||||
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
|
||||
std::istringstream outputStringStream(outputString);
|
||||
|
||||
insertionStatement->setBlob(1, static_cast<std::istream*>(&outputStringStream));
|
||||
insertionStatement->setInt64(2, modelId);
|
||||
try {
|
||||
Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
|
||||
LOG("Updated model %i to sd0", model.id);
|
||||
insertionStatement->executeUpdate();
|
||||
LOG("Updated model %i to sd0", modelId);
|
||||
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", model.id, exception.what());
|
||||
"The database error is %s", modelId, exception.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
Database::Get()->Commit();
|
||||
Database::Get()->SetAutoCommit(previousAutoCommitState);
|
||||
Database::Commit();
|
||||
Database::SetAutoCommit(previousAutoCommitState);
|
||||
return updatedModels;
|
||||
}
|
||||
|
||||
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase() {
|
||||
std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(Database::CreatePreppedStmt("SELECT id, lxfml FROM ugc;"));
|
||||
return std::unique_ptr<sql::ResultSet>(modelsRawDataQuery->executeQuery());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes sd0 magic at the front of a char*
|
||||
*
|
||||
@@ -149,6 +171,6 @@ void WriteSd0Magic(char* input, uint32_t chunkSize) {
|
||||
*reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array
|
||||
}
|
||||
|
||||
bool CheckSd0Magic(std::istream& streamToCheck) {
|
||||
return streamToCheck.get() == 's' && streamToCheck.get() == 'd' && streamToCheck.get() == '0' && streamToCheck.get() == 0x01 && streamToCheck.get() == 0xFF;
|
||||
bool CheckSd0Magic(sql::Blob* streamToCheck) {
|
||||
return streamToCheck->get() == 's' && streamToCheck->get() == 'd' && streamToCheck->get() == '0' && streamToCheck->get() == 0x01 && streamToCheck->get() == 0xFF;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ class AssetManager;
|
||||
struct SystemAddress;
|
||||
class EntityManager;
|
||||
class dZoneManager;
|
||||
class PlayerContainer;
|
||||
|
||||
namespace Game {
|
||||
extern Logger* logger;
|
||||
@@ -27,5 +26,4 @@ namespace Game {
|
||||
extern bool shouldShutdown;
|
||||
extern EntityManager* entityManager;
|
||||
extern dZoneManager* zoneManager;
|
||||
extern PlayerContainer playerContainer;
|
||||
}
|
||||
|
||||
@@ -151,11 +151,6 @@ namespace GeneralUtils {
|
||||
return std::stod(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint16_t Parse(const char* value) {
|
||||
return std::stoul(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t Parse(const char* value) {
|
||||
return std::stoul(value);
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "magic_enum/magic_enum.hpp"
|
||||
|
||||
enum class eGameMessageType : uint16_t {
|
||||
GET_POSITION = 0,
|
||||
GET_ROTATION = 1,
|
||||
@@ -1604,10 +1602,4 @@ enum class eGameMessageType : uint16_t {
|
||||
GET_IS_ON_RAIL = 1772
|
||||
};
|
||||
|
||||
template <>
|
||||
struct magic_enum::customize::enum_range<eGameMessageType> {
|
||||
static constexpr int min = 0;
|
||||
static constexpr int max = 1772;
|
||||
};
|
||||
|
||||
#endif //!__EGAMEMESSAGETYPE__H__
|
||||
|
||||
@@ -166,8 +166,7 @@ enum ePlayerFlag : int32_t {
|
||||
NJ_LIGHTNING_SPINJITZU = 2031,
|
||||
NJ_ICE_SPINJITZU = 2032,
|
||||
NJ_FIRE_SPINJITZU = 2033,
|
||||
NJ_WU_SHOW_DAILY_CHEST = 2099,
|
||||
DLU_SKIP_CINEMATICS = 1'000'000,
|
||||
NJ_WU_SHOW_DAILY_CHEST = 2099
|
||||
};
|
||||
|
||||
#endif //!__EPLAYERFLAG__H__
|
||||
|
||||
@@ -34,7 +34,7 @@ enum class eReplicaComponentType : uint32_t {
|
||||
PLATFORM_BOUNDARY,
|
||||
MODULE,
|
||||
ARCADE,
|
||||
HAVOK_VEHICLE_PHYSICS,
|
||||
VEHICLE_PHYSICS, // Havok demo based
|
||||
MOVEMENT_AI,
|
||||
EXHIBIT,
|
||||
OVERHEAD_ICON,
|
||||
@@ -50,11 +50,11 @@ enum class eReplicaComponentType : uint32_t {
|
||||
PROPERTY_ENTRANCE,
|
||||
FX,
|
||||
PROPERTY_MANAGEMENT,
|
||||
VEHICLE_PHYSICS,
|
||||
VEHICLE_PHYSICS_NEW, // internal physics based on havok
|
||||
PHYSICS_SYSTEM,
|
||||
QUICK_BUILD,
|
||||
SWITCH,
|
||||
MINI_GAME_CONTROL,
|
||||
ZONE_CONTROL, // Minigame
|
||||
CHANGLING,
|
||||
CHOICE_BUILD,
|
||||
PACKAGE,
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
#include "CDRewardCodesTable.h"
|
||||
|
||||
void CDRewardCodesTable::LoadValuesFromDatabase() {
|
||||
|
||||
// First, get the size of the table
|
||||
unsigned int size = 0;
|
||||
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RewardCodes");
|
||||
while (!tableSize.eof()) {
|
||||
size = tableSize.getIntField(0, 0);
|
||||
|
||||
tableSize.nextRow();
|
||||
}
|
||||
|
||||
tableSize.finalize();
|
||||
|
||||
// Reserve the size
|
||||
this->entries.reserve(size);
|
||||
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RewardCodes");
|
||||
while (!tableData.eof()) {
|
||||
CDRewardCode entry;
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.code = tableData.getStringField("code", "");
|
||||
entry.attachmentLOT = tableData.getIntField("attachmentLOT", -1);
|
||||
UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", -1));
|
||||
UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", ""));
|
||||
|
||||
this->entries.push_back(entry);
|
||||
tableData.nextRow();
|
||||
}
|
||||
}
|
||||
|
||||
LOT CDRewardCodesTable::GetAttachmentLOT(uint32_t rewardCodeId) const {
|
||||
for (auto const &entry : this->entries){
|
||||
if (rewardCodeId == entry.id) return entry.attachmentLOT;
|
||||
}
|
||||
return LOT_NULL;
|
||||
}
|
||||
|
||||
uint32_t CDRewardCodesTable::GetCodeID(std::string code) const {
|
||||
for (auto const &entry : this->entries){
|
||||
if (code == entry.code) return entry.id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
|
||||
struct CDRewardCode {
|
||||
uint32_t id;
|
||||
std::string code;
|
||||
LOT attachmentLOT;
|
||||
UNUSED(uint32_t locStatus);
|
||||
UNUSED(std::string gate_version);
|
||||
};
|
||||
|
||||
|
||||
class CDRewardCodesTable : public CDTable<CDRewardCodesTable> {
|
||||
private:
|
||||
std::vector<CDRewardCode> entries;
|
||||
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
const std::vector<CDRewardCode>& GetEntries() const;
|
||||
LOT GetAttachmentLOT(uint32_t rewardCodeId) const;
|
||||
uint32_t GetCodeID(std::string code) const;
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
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)
|
||||
@@ -37,7 +37,6 @@
|
||||
#include "CDPropertyTemplateTable.h"
|
||||
#include "CDFeatureGatingTable.h"
|
||||
#include "CDRailActivatorComponent.h"
|
||||
#include "CDRewardCodesTable.h"
|
||||
|
||||
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
|
||||
// A vanilla CDClient takes about 46MB of memory + the regular world data.
|
||||
@@ -83,7 +82,6 @@ CDClientManager::CDClientManager() {
|
||||
CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDRarityTableTable::Instance().LoadValuesFromDatabase();
|
||||
CDRebuildComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDRewardCodesTable::Instance().LoadValuesFromDatabase();
|
||||
CDRewardsTable::Instance().LoadValuesFromDatabase();
|
||||
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
|
||||
@@ -1,15 +1,12 @@
|
||||
set(DDATABASE_SOURCES)
|
||||
set(DDATABASE_SOURCES "CDClientDatabase.cpp"
|
||||
"CDClientManager.cpp"
|
||||
"Database.cpp"
|
||||
"MigrationRunner.cpp")
|
||||
|
||||
add_subdirectory(CDClientDatabase)
|
||||
add_subdirectory(Tables)
|
||||
|
||||
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}")
|
||||
foreach(file ${DDATABASE_TABLES_SOURCES})
|
||||
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}")
|
||||
endforeach()
|
||||
|
||||
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
|
||||
|
||||
118
dDatabase/Database.cpp
Normal file
118
dDatabase/Database.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#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);
|
||||
}
|
||||
31
dDatabase/Database.h
Normal file
31
dDatabase/Database.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <conncpp.hpp>
|
||||
|
||||
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; }
|
||||
};
|
||||
@@ -1,12 +0,0 @@
|
||||
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)
|
||||
@@ -1,40 +0,0 @@
|
||||
#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!");
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <conncpp.hpp>
|
||||
|
||||
#include "GameDatabase.h"
|
||||
|
||||
namespace Database {
|
||||
void Connect();
|
||||
GameDatabase* Get();
|
||||
void Destroy(std::string source = "");
|
||||
};
|
||||
@@ -1,57 +0,0 @@
|
||||
#ifndef __GAMEDATABASE__H__
|
||||
#define __GAMEDATABASE__H__
|
||||
|
||||
#include <optional>
|
||||
|
||||
#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"
|
||||
#include "IIgnoreList.h"
|
||||
#include "IAccountsRewardCodes.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 IAccountsRewardCodes, public IIgnoreList {
|
||||
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__
|
||||
@@ -1,37 +0,0 @@
|
||||
#ifndef __IACCOUNTS__H__
|
||||
#define __IACCOUNTS__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
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<IAccounts::Info> 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__
|
||||
@@ -1,13 +0,0 @@
|
||||
#ifndef __IACCOUNTSREWARDCODES__H__
|
||||
#define __IACCOUNTSREWARDCODES__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
class IAccountsRewardCodes {
|
||||
public:
|
||||
virtual void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) = 0;
|
||||
virtual std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) = 0;
|
||||
};
|
||||
|
||||
#endif //!__IACCOUNTSREWARDCODES__H__
|
||||
@@ -1,19 +0,0 @@
|
||||
#ifndef __IACTIVITYLOG__H__
|
||||
#define __IACTIVITYLOG__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#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__
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef __IBUGREPORTS__H__
|
||||
#define __IBUGREPORTS__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
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__
|
||||
@@ -1,49 +0,0 @@
|
||||
#ifndef __ICHARINFO__H__
|
||||
#define __ICHARINFO__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#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<std::string> GetApprovedCharacterNames() = 0;
|
||||
|
||||
// Get the character info for the given character id.
|
||||
virtual std::optional<ICharInfo::Info> GetCharacterInfo(const uint32_t charId) = 0;
|
||||
|
||||
// Get the character info for the given character name.
|
||||
virtual std::optional<ICharInfo::Info> GetCharacterInfo(const std::string_view name) = 0;
|
||||
|
||||
// Get the character ids for the given account.
|
||||
virtual std::vector<uint32_t> 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__
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef __ICHARXML__H__
|
||||
#define __ICHARXML__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
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__
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef __ICOMMANDLOG__H__
|
||||
#define __ICOMMANDLOG__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
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__
|
||||
@@ -1,32 +0,0 @@
|
||||
#ifndef __IFRIENDS__H__
|
||||
#define __IFRIENDS__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
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<FriendData> GetFriendsList(const uint32_t charId) = 0;
|
||||
|
||||
// Get the best friend status for the given player and friend character ids.
|
||||
virtual std::optional<IFriends::BestFriendStatus> 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__
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef __IIGNORELIST__H__
|
||||
#define __IIGNORELIST__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class IIgnoreList {
|
||||
public:
|
||||
struct Info {
|
||||
std::string name;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
virtual std::vector<Info> GetIgnoreList(const uint32_t playerId) = 0;
|
||||
virtual void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0;
|
||||
virtual void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0;
|
||||
};
|
||||
|
||||
#endif //!__IIGNORELIST__H__
|
||||
@@ -1,14 +0,0 @@
|
||||
#ifndef __ILEADERBOARD__H__
|
||||
#define __ILEADERBOARD__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
class ILeaderboard {
|
||||
public:
|
||||
|
||||
// Get the donation total for the given activity id.
|
||||
virtual std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) = 0;
|
||||
};
|
||||
|
||||
#endif //!__ILEADERBOARD__H__
|
||||
@@ -1,54 +0,0 @@
|
||||
#ifndef __IMAIL__H__
|
||||
#define __IMAIL__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#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<MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) = 0;
|
||||
|
||||
// Get the mail for the given mail id.
|
||||
virtual std::optional<MailInfo> 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__
|
||||
@@ -1,17 +0,0 @@
|
||||
#ifndef __IMIGRATIONHISTORY__H__
|
||||
#define __IMIGRATIONHISTORY__H__
|
||||
|
||||
#include <string_view>
|
||||
|
||||
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__
|
||||
@@ -1,19 +0,0 @@
|
||||
#ifndef __IOBJECTIDTRACKER__H__
|
||||
#define __IOBJECTIDTRACKER__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
class IObjectIdTracker {
|
||||
public:
|
||||
// Get the current persistent id.
|
||||
virtual std::optional<uint32_t> 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__
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef __IPETNAMES__H__
|
||||
#define __IPETNAMES__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
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<IPetNames::Info> GetPetNameInfo(const LWOOBJID& petId) = 0;
|
||||
};
|
||||
|
||||
#endif //!__IPETNAMES__H__
|
||||
@@ -1,15 +0,0 @@
|
||||
#ifndef __IPLAYKEYS__H__
|
||||
#define __IPLAYKEYS__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
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<bool> IsPlaykeyActive(const int32_t playkeyId) = 0;
|
||||
};
|
||||
|
||||
#endif //!__IPLAYKEYS__H__
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef __IPLAYERCHEATDETECTIONS__H__
|
||||
#define __IPLAYERCHEATDETECTIONS__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
class IPlayerCheatDetections {
|
||||
public:
|
||||
struct Info {
|
||||
std::optional<uint32_t> 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__
|
||||
@@ -1,38 +0,0 @@
|
||||
#ifndef __IPROPERTY__H__
|
||||
#define __IPROPERTY__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
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<IProperty::Info> 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__
|
||||
@@ -1,40 +0,0 @@
|
||||
#ifndef __IPROPERTIESCONTENTS__H__
|
||||
#define __IPROPERTIESCONTENTS__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
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<IPropertyContents::Model> 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__
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef __ISERVERS__H__
|
||||
#define __ISERVERS__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
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<MasterInfo> GetMasterInfo() = 0;
|
||||
};
|
||||
|
||||
#endif //!__ISERVERS__H__
|
||||
@@ -1,32 +0,0 @@
|
||||
#ifndef __IUGC__H__
|
||||
#define __IUGC__H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
class IUgc {
|
||||
public:
|
||||
struct Model {
|
||||
std::stringstream lxfmlData;
|
||||
LWOOBJID id{};
|
||||
};
|
||||
|
||||
// Gets all UGC models for the given property id.
|
||||
virtual std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) = 0;
|
||||
|
||||
// Gets all Ugcs models.
|
||||
virtual std::vector<IUgc::Model> 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__
|
||||
@@ -1,11 +0,0 @@
|
||||
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)
|
||||
@@ -1,113 +0,0 @@
|
||||
#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<sql::Statement>(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);
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
#ifndef __MYSQLDATABASE__H__
|
||||
#define __MYSQLDATABASE__H__
|
||||
|
||||
#include <conncpp.hpp>
|
||||
#include <memory>
|
||||
|
||||
#include "GameDatabase.h"
|
||||
|
||||
typedef std::unique_ptr<sql::PreparedStatement>& 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<typename ParamType>
|
||||
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<typename... Args>
|
||||
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<IServers::MasterInfo> GetMasterInfo() override;
|
||||
|
||||
std::vector<std::string> GetApprovedCharacterNames() override;
|
||||
|
||||
std::vector<FriendData> GetFriendsList(uint32_t charID) override;
|
||||
|
||||
std::optional<IFriends::BestFriendStatus> 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<IUgc::Model> GetAllUgcModels() override;
|
||||
void CreateMigrationHistoryTable() override;
|
||||
bool IsMigrationRun(const std::string_view str) override;
|
||||
void InsertMigration(const std::string_view str) override;
|
||||
std::optional<ICharInfo::Info> GetCharacterInfo(const uint32_t charId) override;
|
||||
std::optional<ICharInfo::Info> 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<IAccounts::Info> 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<uint32_t> 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<IPetNames::Info> GetPetNameInfo(const LWOOBJID& petId) override;
|
||||
std::optional<IProperty::Info> 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<IPropertyContents::Model> 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<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
|
||||
std::optional<IMail::MailInfo> 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<uint32_t> GetCurrentPersistentId() override;
|
||||
void InsertDefaultPersistentId() override;
|
||||
void UpdatePersistentId(const uint32_t id) override;
|
||||
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
|
||||
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
|
||||
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
|
||||
void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
|
||||
void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
|
||||
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
|
||||
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
|
||||
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) 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<typename... Args>
|
||||
inline std::unique_ptr<sql::ResultSet> ExecuteSelect(const std::string& query, Args&&... args) {
|
||||
std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query));
|
||||
SetParams(preppedStmt, std::forward<Args>(args)...);
|
||||
DLU_SQL_TRY_CATCH_RETHROW(return std::unique_ptr<sql::ResultSet>(preppedStmt->executeQuery()));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline void ExecuteDelete(const std::string& query, Args&&... args) {
|
||||
std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query));
|
||||
SetParams(preppedStmt, std::forward<Args>(args)...);
|
||||
DLU_SQL_TRY_CATCH_RETHROW(preppedStmt->execute());
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline int32_t ExecuteUpdate(const std::string& query, Args&&... args) {
|
||||
std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query));
|
||||
SetParams(preppedStmt, std::forward<Args>(args)...);
|
||||
DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt->executeUpdate());
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline bool ExecuteInsert(const std::string& query, Args&&... args) {
|
||||
std::unique_ptr<sql::PreparedStatement> preppedStmt(CreatePreppedStmt(query));
|
||||
SetParams(preppedStmt, std::forward<Args>(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<std::istream*>(param));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) {
|
||||
if (param) {
|
||||
// LOG("%d", param.value());
|
||||
stmt->setInt(index, param.value());
|
||||
} else {
|
||||
// LOG("Null");
|
||||
stmt->setNull(index, sql::DataType::SQLNULL);
|
||||
}
|
||||
}
|
||||
|
||||
#endif //!__MYSQLDATABASE__H__
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
#include "eGameMasterLevel.h"
|
||||
|
||||
std::optional<IAccounts::Info> 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<eGameMasterLevel>(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<int32_t>(eGameMasterLevel::OPERATOR));
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
void MySQLDatabase::InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) {
|
||||
ExecuteInsert("INSERT IGNORE INTO accounts_rewardcodes (account_id, rewardcode) VALUES (?, ?);", account_id, reward_code);
|
||||
}
|
||||
|
||||
std::vector<uint32_t> MySQLDatabase::GetRewardCodesByAccountID(const uint32_t account_id) {
|
||||
auto result = ExecuteSelect("SELECT rewardcode FROM accounts_rewardcodes WHERE account_id = ?;", account_id);
|
||||
|
||||
std::vector<uint32_t> toReturn;
|
||||
toReturn.reserve(result->rowsCount());
|
||||
while (result->next()) {
|
||||
toReturn.push_back(result->getUInt("rewardcode"));
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#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<uint32_t>(activityType), static_cast<uint32_t>(time(NULL)), mapId);
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"Accounts.cpp"
|
||||
"AccountsRewardCodes.cpp"
|
||||
"ActivityLog.cpp"
|
||||
"BugReports.cpp"
|
||||
"CharInfo.cpp"
|
||||
"CharXml.cpp"
|
||||
"CommandLog.cpp"
|
||||
"Friends.cpp"
|
||||
"IgnoreList.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
|
||||
)
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<std::string> MySQLDatabase::GetApprovedCharacterNames() {
|
||||
auto result = ExecuteSelect("SELECT name FROM charinfo;");
|
||||
|
||||
std::vector<std::string> toReturn;
|
||||
|
||||
while (result->next()) {
|
||||
toReturn.push_back(result->getString("name").c_str());
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::optional<ICharInfo::Info> CharInfoFromQueryResult(std::unique_ptr<sql::ResultSet> 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<ePermissionMap>(stmt->getUInt("permission_map"));
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::optional<ICharInfo::Info> 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<ICharInfo::Info> 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<uint32_t> 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<uint32_t> 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<uint32_t>(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<uint32_t>(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<uint32_t>(time(NULL)), characterId);
|
||||
}
|
||||
|
||||
void MySQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
|
||||
ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1", static_cast<uint32_t>(time(NULL)), characterId);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<FriendData> 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<FriendData> 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<IFriends::BestFriendStatus> 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
|
||||
);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<IIgnoreList::Info> MySQLDatabase::GetIgnoreList(const uint32_t playerId) {
|
||||
auto result = ExecuteSelect("SELECT ci.name AS name, il.ignored_player_id AS ignore_id FROM ignore_list AS il JOIN charinfo AS ci ON il.ignored_player_id = ci.id WHERE il.player_id = ?", playerId);
|
||||
|
||||
std::vector<IIgnoreList::Info> ignoreList;
|
||||
|
||||
ignoreList.reserve(result->rowsCount());
|
||||
while (result->next()) {
|
||||
ignoreList.push_back(IIgnoreList::Info{ result->getString("name").c_str(), result->getUInt("ignore_id") });
|
||||
}
|
||||
|
||||
return ignoreList;
|
||||
}
|
||||
|
||||
void MySQLDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
|
||||
ExecuteInsert("INSERT IGNORE INTO ignore_list (player_id, ignored_player_id) VALUES (?, ?)", playerId, ignoredPlayerId);
|
||||
}
|
||||
|
||||
void MySQLDatabase::RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
|
||||
ExecuteDelete("DELETE FROM ignore_list WHERE player_id = ? AND ignored_player_id = ?", playerId, ignoredPlayerId);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::optional<uint32_t> 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");
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
#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<uint32_t>(time(NULL)),
|
||||
mail.subject,
|
||||
mail.body,
|
||||
mail.itemID,
|
||||
mail.itemLOT,
|
||||
0,
|
||||
mail.itemCount);
|
||||
}
|
||||
|
||||
std::vector<IMail::MailInfo> 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<IMail::MailInfo> 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<IMail::MailInfo> 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);
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::optional<uint32_t> 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);
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
#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<IPetNames::Info> 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;
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::optional<bool> 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");
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::optional<IProperty::Info> 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()
|
||||
);
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<IPropertyContents::Model> 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<IPropertyContents::Model> toReturn;
|
||||
toReturn.reserve(result->rowsCount());
|
||||
while (result->next()) {
|
||||
IPropertyContents::Model model;
|
||||
model.id = result->getUInt64("id");
|
||||
model.lot = static_cast<LOT>(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<uint32_t>(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);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
#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<IServers::MasterInfo> 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;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#include "MySQLDatabase.h"
|
||||
|
||||
std::vector<IUgc::Model> 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<IUgc::Model> toReturn;
|
||||
|
||||
while (result->next()) {
|
||||
IUgc::Model model;
|
||||
|
||||
// blob is owned by the query, so we need to do a deep copy :/
|
||||
std::unique_ptr<std::istream> blob(result->getBlob("lxfml"));
|
||||
model.lxfmlData << blob->rdbuf();
|
||||
model.id = result->getUInt64("id");
|
||||
toReturn.push_back(std::move(model));
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
std::vector<IUgc::Model> MySQLDatabase::GetAllUgcModels() {
|
||||
auto result = ExecuteSelect("SELECT id, lxfml FROM ugc;");
|
||||
|
||||
std::vector<IUgc::Model> 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<std::istream> 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);
|
||||
}
|
||||
@@ -32,7 +32,9 @@ Migration LoadMigration(std::string path) {
|
||||
}
|
||||
|
||||
void MigrationRunner::RunMigrations() {
|
||||
Database::Get()->CreateMigrationHistoryTable();
|
||||
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;
|
||||
|
||||
sql::SQLString finalSQL = "";
|
||||
bool runSd0Migrations = false;
|
||||
@@ -43,7 +45,13 @@ void MigrationRunner::RunMigrations() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Database::Get()->IsMigrationRun(migration.name)) 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;
|
||||
|
||||
LOG("Running migration: %s", migration.name.c_str());
|
||||
if (migration.name == "dlu/5_brick_model_sd0.sql") {
|
||||
@@ -52,7 +60,10 @@ void MigrationRunner::RunMigrations() {
|
||||
finalSQL.append(migration.data.c_str());
|
||||
}
|
||||
|
||||
Database::Get()->InsertMigration(migration.name);
|
||||
stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
|
||||
stmt->setString(1, migration.name.c_str());
|
||||
stmt->execute();
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
if (finalSQL.empty() && !runSd0Migrations) {
|
||||
@@ -62,10 +73,11 @@ void MigrationRunner::RunMigrations() {
|
||||
|
||||
if (!finalSQL.empty()) {
|
||||
auto migration = GeneralUtils::SplitString(static_cast<std::string>(finalSQL), ';');
|
||||
std::unique_ptr<sql::Statement> simpleStatement(Database::CreateStmt());
|
||||
for (auto& query : migration) {
|
||||
try {
|
||||
if (query.empty()) continue;
|
||||
Database::Get()->ExecuteCustomQuery(query.c_str());
|
||||
simpleStatement->execute(query.c_str());
|
||||
} catch (sql::SQLException& e) {
|
||||
LOG("Encountered error running migration: %s", e.what());
|
||||
}
|
||||
@@ -86,7 +98,9 @@ void MigrationRunner::RunSQLiteMigrations() {
|
||||
cdstmt.execQuery().finalize();
|
||||
cdstmt.finalize();
|
||||
|
||||
Database::Get()->CreateMigrationHistoryTable();
|
||||
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;
|
||||
|
||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) {
|
||||
auto migration = LoadMigration("cdserver/" + entry);
|
||||
@@ -97,15 +111,25 @@ 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 (!cdres.eof()) continue;
|
||||
if (doExit) continue;
|
||||
|
||||
// Check first if there is entry in the migration history table on the main database.
|
||||
if (Database::Get()->IsMigrationRun(migration.name)) {
|
||||
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) {
|
||||
// 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(static_cast<int32_t>(1), migration.name.c_str());
|
||||
cdstmt.execQuery();
|
||||
cdstmt.bind((int32_t) 1, migration.name.c_str());
|
||||
cdstmt.execQuery().finalize();
|
||||
cdstmt.finalize();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -125,7 +149,8 @@ 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();
|
||||
cdstmt.execQuery().finalize();
|
||||
cdstmt.finalize();
|
||||
CDClientDatabase::ExecuteQuery("COMMIT;");
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
struct CDComponentsRegistry {
|
||||
unsigned int id; //!< The LOT is used as the ID
|
||||
@@ -42,9 +42,9 @@ std::vector<CDFeatureGating> CDFeatureGatingTable::Query(std::function<bool(CDFe
|
||||
return data;
|
||||
}
|
||||
|
||||
bool CDFeatureGatingTable::FeatureUnlocked(const CDFeatureGating& feature) const {
|
||||
bool CDFeatureGatingTable::FeatureUnlocked(const std::string& feature) const {
|
||||
for (const auto& entry : entries) {
|
||||
if (entry.featureName == feature.featureName && feature >= entry) {
|
||||
if (entry.featureName == feature) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -9,12 +9,6 @@ struct CDFeatureGating {
|
||||
int32_t current;
|
||||
int32_t minor;
|
||||
std::string description;
|
||||
|
||||
bool operator>=(const CDFeatureGating& b) const {
|
||||
return (this->major > b.major) ||
|
||||
(this->major == b.major && this->current > b.current) ||
|
||||
(this->major == b.major && this->current == b.current && this->minor >= b.minor);
|
||||
}
|
||||
};
|
||||
|
||||
class CDFeatureGatingTable : public CDTable<CDFeatureGatingTable> {
|
||||
@@ -27,7 +21,7 @@ public:
|
||||
// Queries the table with a custom "where" clause
|
||||
std::vector<CDFeatureGating> Query(std::function<bool(CDFeatureGating)> predicate);
|
||||
|
||||
bool FeatureUnlocked(const CDFeatureGating& feature) const;
|
||||
bool FeatureUnlocked(const std::string& feature) const;
|
||||
|
||||
const std::vector<CDFeatureGating>& GetEntries(void) const;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user