Compare commits

..

9 Commits

Author SHA1 Message Date
David Markowitz
7d62ec543a add back header 2023-07-27 20:06:12 -07:00
David Markowitz
3bab2af1a4 Merge branch 'main' into cdclient-rework 2023-07-27 20:03:12 -07:00
Wincent Holm
633b87a5e7 Merge branch 'main' into cdclient-rework 2022-10-29 21:52:08 +02:00
wincent
3031f942dc Removed log statement 2022-10-29 21:46:09 +02:00
wincent
e3e9718db5 Reverted local hardcoded NET_VERSION value. 2022-08-11 16:40:17 +02:00
wincent
56f371216b Abstracted the CDClient tables
There is now an option to utilize shared memory for some CDClient tables by adding `CD_PROVIDER_MEMORY=1` to the CMakeVariables.txt file.

Allows masterconfig.ini to specify another run command for the world server, to allow for easier debugging through `valgrind`.
2022-08-11 16:36:03 +02:00
Wincent Holm
45ae46f6f1 Merge branch 'main' into cdclient-rework 2022-07-18 16:44:53 +02:00
wincent
db7f7c60b2 Optimizations to shared memory
Load the context at start in readonly mode instead of fetching for every entry.
2022-07-10 00:40:13 +02:00
wincent
34fa43ed93 Initial concept for CDClient rework.
Utilizes `boost::interprocess` to share a map between worlds.
The master server loads the table into memory, and the worlds hook into it when they want to get an entry.

This solves the issue of skill delay, but introduces the third-party library Boost.
There should be a conversation about the introduction of Boost — it is a large library and adds extra steps to the installation.
2022-07-09 22:59:36 +02:00
311 changed files with 5134 additions and 6810 deletions

View File

@@ -14,4 +14,4 @@ EXTERNAL_IP=localhost
MARIADB_USER=darkflame
MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_DATABASE=darkflame
MARIADB_DATABASE=darkflame

View File

@@ -322,6 +322,15 @@ link_directories(${PROJECT_BINARY_DIR})
# Load all of our third party directories
add_subdirectory(thirdparty)
# Include Boost
find_package(Boost COMPONENTS interprocess)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(dGame ${Boost_LIBRARIES})
endif()
# Glob together all headers that need to be precompiled
file(
GLOB HEADERS_DDATABASE

View File

@@ -1,8 +1,12 @@
PROJECT_VERSION_MAJOR=1
PROJECT_VERSION_MINOR=1
PROJECT_VERSION_PATCH=1
PROJECT_VERSION_PATCH=0
# LICENSE
LICENSE=AGPL-3.0
# The network version.
# 171023 - Darkflame Universe client
# 171022 - Unmodded client
NET_VERSION=171022
# Debugging
# Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
__dynamic=1
@@ -18,5 +22,3 @@ __maria_db_connector_compile_jobs__=1
__enable_testing__=1
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/
# Uncomment the below line to cache the entire CDClient into memory
# CDCLIENT_CACHE_ALL=1

View File

@@ -179,7 +179,7 @@ If you would like to build the server faster, append `-j<number>` where number i
### Notes
Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building:
* If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory.
* If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023.
* If you are using a Darkflame Universe client, ensure NET_VERSION is changed to 171023.
## Configuring your server
This server has a few steps that need to be taken to configure the server for your use case.

View File

@@ -55,10 +55,34 @@ int main(int argc, char** argv) {
Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
Game::logger->Log("AuthServer", "Compiled on: %s", __TIMESTAMP__);
Database::Connect(Game::config);
//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");
// Get Master server IP and port
SocketDescriptor masterSock = Database::Connection->GetMasterServerIP();
try {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
} catch (sql::SQLException& ex) {
Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s", ex.what());
Database::Destroy("AuthServer");
delete Game::server;
delete Game::logger;
return EXIT_FAILURE;
}
//Find out the master's IP:
std::string masterIP;
uint32_t masterPort = 1500;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto res = stmt->executeQuery();
while (res->next()) {
masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
}
delete res;
delete stmt;
Game::randomEngine = std::mt19937(time(0));
@@ -68,7 +92,7 @@ int main(int argc, char** argv) {
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterSock.hostAddress, masterSock.port, ServerType::Auth, Game::config, &Game::shouldShutdown);
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown);
//Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now();
@@ -107,7 +131,18 @@ int main(int argc, char** argv) {
//Every 10 min we ping our sql server to keep it alive hopefully:
if (framesSinceLastSQLPing >= sqlPingTime) {
Database::Connection->GetMasterServerIP();
//Find out the master's IP for absolutely no reason:
std::string masterIP;
uint32_t masterPort;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto res = stmt->executeQuery();
while (res->next()) {
masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
}
delete res;
delete stmt;
framesSinceLastSQLPing = 0;
} else framesSinceLastSQLPing++;
@@ -116,7 +151,9 @@ int main(int argc, char** argv) {
t += std::chrono::milliseconds(authFrameDelta); //Auth can run at a lower "fps"
std::this_thread::sleep_until(t);
}
//Delete our objects here:
Database::Destroy("AuthServer");
delete Game::server;
delete Game::logger;
delete Game::config;

View File

@@ -31,13 +31,16 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
ReadWordlistDCF("blacklist.dcf", false);
}
// Read player names that are ok as well:
auto names = Database::Connection->GetAllCharacterNames();
for (auto& name : names) {
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
m_ApprovedWords.push_back(CalculateHash(name));
//Read player names that are ok as well:
auto stmt = Database::CreatePreppedStmt("select name from charinfo;");
auto res = stmt->executeQuery();
while (res->next()) {
std::string line = res->getString(1).c_str();
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
m_ApprovedWords.push_back(CalculateHash(line));
}
delete res;
delete stmt;
}
dChatFilter::~dChatFilter() {

View File

@@ -3,7 +3,6 @@
#include "Database.h"
#include <vector>
#include "PacketUtils.h"
#include "BitStreamUtils.h"
#include "Game.h"
#include "dServer.h"
#include "GeneralUtils.h"
@@ -76,11 +75,11 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Now, we need to send the friendlist to the server they came from:
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE);
PacketUtils::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)friends.size());
@@ -413,21 +412,21 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
const auto otherName = std::string(otherMember->playerName.c_str());
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(otherMember->playerID);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
bitStream.Write(otherMember->playerID);
bitStream.Write<uint8_t>(8);
bitStream.Write<unsigned int>(69);
bitStream.Write(LUWString(senderName));
PacketUtils::WritePacketWString(senderName, 33, &bitStream);
bitStream.Write(sender->playerID);
bitStream.Write<uint16_t>(0);
bitStream.Write<uint8_t>(0); //not mythran nametag
bitStream.Write(LUWString(otherName));
PacketUtils::WritePacketWString(otherName, 33, &bitStream);
bitStream.Write<uint8_t>(0); //not mythran for receiver
bitStream.Write<uint8_t>(0); //teams?
bitStream.Write(LUWString(message, 512));
PacketUtils::WritePacketWString(message, 512, &bitStream);
SystemAddress sysAddr = otherMember->sysAddr;
SEND_PACKET;
@@ -435,7 +434,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
}
void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
LWOOBJID senderID = PacketUtils::ReadS64(0x08, packet);
LWOOBJID senderID = PacketUtils::ReadPacketS64(0x08, packet);
std::string receiverName = PacketUtils::ReadString(0x66, packet, true);
std::string message = PacketUtils::ReadString(0xAA, packet, true, 512);
@@ -452,21 +451,21 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
//To the sender:
{
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(goonA->playerID);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
bitStream.Write(goonA->playerID);
bitStream.Write<uint8_t>(7);
bitStream.Write<unsigned int>(69);
bitStream.Write(LUWString(goonAName));
PacketUtils::WritePacketWString(goonAName, 33, &bitStream);
bitStream.Write(goonA->playerID);
bitStream.Write<uint16_t>(0);
bitStream.Write<uint8_t>(0); //not mythran nametag
bitStream.Write(LUWString(goonBName));
PacketUtils::WritePacketWString(goonBName, 33, &bitStream);
bitStream.Write<uint8_t>(0); //not mythran for receiver
bitStream.Write<uint8_t>(0); //success
bitStream.Write(LUWString(message, 512));
PacketUtils::WritePacketWString(message, 512, &bitStream);
SystemAddress sysAddr = goonA->sysAddr;
SEND_PACKET;
@@ -475,21 +474,21 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
//To the receiver:
{
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(goonB->playerID);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
bitStream.Write(goonA->playerID);
bitStream.Write<uint8_t>(7);
bitStream.Write<unsigned int>(69);
bitStream.Write(LUWString(goonAName));
PacketUtils::WritePacketWString(goonAName, 33, &bitStream);
bitStream.Write(goonA->playerID);
bitStream.Write<uint16_t>(0);
bitStream.Write<uint8_t>(0); //not mythran nametag
bitStream.Write(LUWString(goonBName));
PacketUtils::WritePacketWString(goonBName, 33, &bitStream);
bitStream.Write<uint8_t>(0); //not mythran for receiver
bitStream.Write<uint8_t>(3); //new whisper
bitStream.Write(LUWString(message, 512));
PacketUtils::WritePacketWString(message, 512, &bitStream);
SystemAddress sysAddr = goonB->sysAddr;
SEND_PACKET;
@@ -710,13 +709,13 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE);
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE);
bitStream.Write(LUWString(sender->playerName.c_str()));
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
bitStream.Write(sender->playerID);
SystemAddress sysAddr = receiver->sysAddr;
@@ -725,7 +724,7 @@ void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender)
void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -752,7 +751,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeader
void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -777,7 +776,7 @@ void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderI
void ChatPacketHandler::SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -794,7 +793,7 @@ void ChatPacketHandler::SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64Play
void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -823,7 +822,7 @@ void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTria
void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -849,7 +848,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband
void ChatPacketHandler::SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -883,16 +882,16 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla
[bool] - is FTP*/
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(friendData->playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY);
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY);
bitStream.Write<uint8_t>(notifyType);
std::string playerName = playerData->playerName.c_str();
bitStream.Write(LUWString(playerName));
PacketUtils::WritePacketWString(playerName, 33, &bitStream);
bitStream.Write(playerData->zoneID.GetMapID());
bitStream.Write(playerData->zoneID.GetInstanceID());
@@ -922,12 +921,12 @@ void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* send
}
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST);
bitStream.Write(LUWString(sender->playerName.c_str()));
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST);
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
SystemAddress sysAddr = receiver->sysAddr;
@@ -938,16 +937,16 @@ void ChatPacketHandler::SendFriendResponse(PlayerData* receiver, PlayerData* sen
if (!receiver || !sender) return;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
// Portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE);
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE);
bitStream.Write(responseCode);
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender->sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
// Then write the player name
bitStream.Write(LUWString(sender->playerName.c_str()));
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
// Then if this is an acceptance code, write the following extra info.
if (responseCode == eAddFriendResponseType::ACCEPTED) {
bitStream.Write(sender->playerID);
@@ -963,13 +962,13 @@ void ChatPacketHandler::SendRemoveFriend(PlayerData* receiver, std::string& pers
if (!receiver) return;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE);
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE);
bitStream.Write<uint8_t>(isSuccessful); //isOnline
bitStream.Write(LUWString(personToRemove));
PacketUtils::WritePacketWString(personToRemove, 33, &bitStream);
SystemAddress sysAddr = receiver->sysAddr;
SEND_PACKET;

View File

@@ -33,7 +33,6 @@ namespace Game {
dChatFilter* chatFilter = nullptr;
AssetManager* assetManager = nullptr;
bool shouldShutdown = false;
std::mt19937 randomEngine;
}
@@ -77,10 +76,34 @@ int main(int argc, char** argv) {
return EXIT_FAILURE;
}
Database::Connect(Game::config);
//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");
// Get Master server IP and port
SocketDescriptor masterSock = Database::Connection->GetMasterServerIP();
try {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
} catch (sql::SQLException& ex) {
Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s", ex.what());
Database::Destroy("ChatServer");
delete Game::server;
delete Game::logger;
return EXIT_FAILURE;
}
//Find out the master's IP:
std::string masterIP;
uint32_t masterPort = 1000;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto res = stmt->executeQuery();
while (res->next()) {
masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
}
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;
@@ -88,11 +111,9 @@ int main(int argc, char** argv) {
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterSock.hostAddress, masterSock.port, ServerType::Chat, Game::config, &Game::shouldShutdown);
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
Game::randomEngine = std::mt19937(time(0));
//Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now();
@@ -131,7 +152,18 @@ int main(int argc, char** argv) {
//Every 10 min we ping our sql server to keep it alive hopefully:
if (framesSinceLastSQLPing >= sqlPingTime) {
Database::Connection->GetMasterServerIP();
//Find out the master's IP for absolutely no reason:
std::string masterIP;
uint32_t masterPort;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto res = stmt->executeQuery();
while (res->next()) {
masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
}
delete res;
delete stmt;
framesSinceLastSQLPing = 0;
} else framesSinceLastSQLPing++;
@@ -141,8 +173,8 @@ int main(int argc, char** argv) {
std::this_thread::sleep_until(t);
}
// Delete our objects here:
Database::Destroy();
//Delete our objects here:
Database::Destroy("ChatServer");
delete Game::server;
delete Game::logger;
delete Game::config;

View File

@@ -6,11 +6,10 @@
#include "dLogger.h"
#include "ChatPacketHandler.h"
#include "GeneralUtils.h"
#include "BitStreamUtils.h"
#include "PacketUtils.h"
#include "Database.h"
#include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "ChatPackets.h"
PlayerContainer::PlayerContainer() {
}
@@ -41,7 +40,14 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
mPlayers.insert(std::make_pair(data->playerID, data));
Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
Database::Connection->InsertIntoActivityLog(data->playerID, 0, time(nullptr), 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) {
@@ -78,7 +84,14 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
Game::logger->Log("PlayerContainer", "Removed user: %llu", playerID);
mPlayers.erase(playerID);
Database::Connection->InsertIntoActivityLog(playerID, 1, time(nullptr), 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) {
@@ -133,7 +146,7 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
bitStream.Write(player);
bitStream.Write(time);
@@ -194,14 +207,6 @@ TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
}
void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
if (team->memberIDs.size() >= 4){
Game::logger->Log("PlayerContainer", "Tried to add player to team that already had 4 players");
auto* player = GetPlayerData(playerID);
if (!player) return;
ChatPackets::SendSystemMessage(player->sysAddr, u"The teams is full! You have not been added to a team!");
return;
}
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
if (index != team->memberIDs.end()) return;
@@ -340,7 +345,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
bitStream.Write(team->teamID);
bitStream.Write(deleteTeam);

View File

@@ -1,93 +1,35 @@
#include "MySQLMigration.h"
#include "BrickByBrickFix.h"
#include <istream>
#include <memory>
#include <iostream>
#include <sstream>
#include "CDClientDatabase.h"
#include "Game.h"
#include "GeneralUtils.h"
#include "dLogger.h"
#include "BinaryPathFinder.h"
#include "ZCompression.h"
#include "tinyxml2.h"
#include "../MySQL.h"
#include "Database.h"
#include "Game.h"
#include "ZCompression.h"
#include "dLogger.h"
void MigrationRunner::RunMigrations(DatabaseBase* db) {
auto database = dynamic_cast<MySQLDatabase*>(db);
//! Forward declarations
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;
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase();
void WriteSd0Magic(char* input, uint32_t chunkSize);
bool CheckSd0Magic(sql::Blob* streamToCheck);
sql::SQLString finalSQL = "";
bool runSd0Migrations = false;
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/mysql").string())) {
auto migration = Migration::LoadMigration("dlu", "mysql", entry);
if (migration.data.empty()) {
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;
Game::logger->Log("MigrationRunner", "Running migration: %s", migration.name.c_str());
if (migration.name == "dlu/5_brick_model_sd0.sql") {
runSd0Migrations = true;
} else {
finalSQL.append(migration.data.c_str());
}
stmt = database->CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
stmt->setString(1, migration.name.c_str());
stmt->execute();
delete stmt;
}
if (finalSQL.empty() && !runSd0Migrations) {
Game::logger->Log("MigrationRunner", "Server database is up to date.");
return;
}
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;
simpleStatement->execute(query.c_str());
} catch (sql::SQLException& e) {
Game::logger->Log("MigrationRunner", "Encountered error running migration: %s", e.what());
}
}
}
// Do this last on the off chance none of the other migrations have been run yet.
if (runSd0Migrations) {
uint32_t numberOfUpdatedModels = UpdateBrickByBrickModelsToSd0();
Game::logger->Log("MasterServer", "%i models were updated from zlib to sd0.", numberOfUpdatedModels);
uint32_t numberOfTruncatedModels = TruncateBrokenBrickByBrickXml();
Game::logger->Log("MasterServer", "%i models were truncated from the database.", numberOfTruncatedModels);
}
}
uint32_t TruncateBrokenBrickByBrickXml(MySQLDatabase* database) {
/**
* @brief Truncates all models with broken data from the database.
*
* @return The number of models deleted
*/
uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
uint32_t modelsTruncated{};
auto modelsToTruncate = GetModelsFromDatabase(database);
bool previousCommitValue = database->GetAutoCommit();
database->SetAutoCommit(false);
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::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);
@@ -153,8 +95,8 @@ uint32_t TruncateBrokenBrickByBrickXml(MySQLDatabase* database) {
}
}
database->Commit();
database->SetAutoCommit(previousCommitValue);
Database::Commit();
Database::SetAutoCommit(previousCommitValue);
return modelsTruncated;
}
@@ -164,14 +106,12 @@ uint32_t TruncateBrokenBrickByBrickXml(MySQLDatabase* database) {
*
* @return The number of models updated to SD0
*/
uint32_t UpdateBrickByBrickModelsToSd0(MySQLDatabase* database) {
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
uint32_t updatedModels = 0;
auto modelsToUpdate = GetModelsFromDatabase();
auto previousAutoCommitState = database->GetAutoCommit();
database->SetAutoCommit(false);
std::unique_ptr<sql::PreparedStatement> insertionStatement(database->CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;"));
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));
@@ -210,14 +150,13 @@ uint32_t UpdateBrickByBrickModelsToSd0(MySQLDatabase* database) {
}
}
}
database->Commit();
database->SetAutoCommit(previousAutoCommitState);
Database::Commit();
Database::SetAutoCommit(previousAutoCommitState);
return updatedModels;
}
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase(MySQLDatabase* database) {
std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(database->CreatePreppedStmt("SELECT id, lxfml FROM ugc;"));
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());
}

20
dCommon/BrickByBrickFix.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
namespace BrickByBrickFix {
/**
* @brief Deletes all broken BrickByBrick models that have invalid XML
*
* @return The number of BrickByBrick models that were truncated
*/
uint32_t TruncateBrokenBrickByBrickXml();
/**
* @brief Updates all BrickByBrick models in the database to be
* in the sd0 format as opposed to a zlib compressed format.
*
* @return The number of BrickByBrick models that were updated
*/
uint32_t UpdateBrickByBrickModelsToSd0();
};

View File

@@ -12,8 +12,9 @@ set(DCOMMON_SOURCES
"NiPoint3.cpp"
"NiQuaternion.cpp"
"SHA512.cpp"
"Demangler.cpp"
"Type.cpp"
"ZCompression.cpp"
"BrickByBrickFix.cpp"
"BinaryPathFinder.cpp"
"FdbToSqlite.cpp"
)

View File

@@ -1,29 +0,0 @@
#include "Demangler.h"
#ifdef __GNUG__
#include <cstdlib>
#include <cxxabi.h>
#include <memory>
#include <typeinfo>
std::string Demangler::Demangle(const char* name) {
// some arbitrary value to eliminate the compiler warning
// -4 is not a valid return value for __cxa_demangle so we'll use that.
int status = -4;
// __cxa_demangle requires that we free the returned char*
std::unique_ptr<char, void (*)(void*)> res{
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status == 0) ? res.get() : "";
}
#else // __GNUG__
// does nothing if not g++
std::string Demangler::Demangle(const char* name) {
return name;
}
#endif // __GNUG__

View File

@@ -1,9 +0,0 @@
#pragma once
#include <string>
namespace Demangler {
// Given a char* containing a mangled name, return a std::string containing the demangled name.
// If the function fails for any reason, it returns an empty string.
std::string Demangle(const char* name);
}

View File

@@ -107,7 +107,7 @@ static void ErrorCallback(void* data, const char* msg, int errnum) {
}
#endif
#include "Demangler.h"
#include "Type.h"
void GenerateDump() {
std::string cmd = "sudo gcore " + std::to_string(getpid());
@@ -122,43 +122,41 @@ void CatchUnhandled(int sig) {
if (Diagnostics::GetProduceMemoryDump()) {
GenerateDump();
}
constexpr uint8_t MaxStackTrace = 32;
void* array[MaxStackTrace];
void* array[10];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, MaxStackTrace);
size = backtrace(array, 10);
# if defined(__GNUG__)
#if defined(__GNUG__) and defined(__dynamic)
// Loop through the returned addresses, and get the symbols to be demangled
char** strings = backtrace_symbols(array, size);
// Print the stack trace
for (size_t i = 0; i < size; i++) {
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]'
// and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' and extract the function name
std::string functionName = strings[i];
std::string::size_type start = functionName.find('(');
std::string::size_type end = functionName.find('+');
if (start != std::string::npos && end != std::string::npos) {
std::string demangled = functionName.substr(start + 1, end - start - 1);
demangled = Demangler::Demangle(demangled.c_str());
demangled = demangle(functionName.c_str());
// If the demangled string is not empty, then we can replace the mangled string with the demangled one
if (!demangled.empty()) {
demangled.push_back('(');
demangled += functionName.substr(end);
functionName = demangled;
if (demangled.empty()) {
Game::logger->Log("Diagnostics", "[%02zu] %s", i, demangled.c_str());
} else {
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
}
} else {
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
}
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
}
# else // defined(__GNUG__)
#else
backtrace_symbols_fd(array, size, STDOUT_FILENO);
# endif // defined(__GNUG__)
#endif
FILE* file = fopen(fileName.c_str(), "w+");
if (file != NULL) {
@@ -168,7 +166,7 @@ void CatchUnhandled(int sig) {
fclose(file);
}
#else // __include_backtrace__
#else
struct backtrace_state* state = backtrace_create_state(
Diagnostics::GetProcessFileName().c_str(),
@@ -179,7 +177,7 @@ void CatchUnhandled(int sig) {
struct bt_ctx ctx = { state, 0 };
Bt(state);
#endif // __include_backtrace__
#endif
exit(EXIT_FAILURE);
}

View File

@@ -126,11 +126,6 @@ namespace GeneralUtils {
template <typename T>
T Parse(const char* value);
template <>
inline bool Parse(const char* value) {
return std::stoi(value);
}
template <>
inline int32_t Parse(const char* value) {
return std::stoi(value);

View File

@@ -61,33 +61,35 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
}
case LDF_TYPE_S32: {
int32_t data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
try {
int32_t data = static_cast<int32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10));
returnValue = new LDFData<int32_t>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<int32_t>(key, data);
break;
}
case LDF_TYPE_FLOAT: {
float data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
try {
float data = strtof(ldfTypeAndValue.second.data(), &storage);
returnValue = new LDFData<float>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<float>(key, data);
break;
}
case LDF_TYPE_DOUBLE: {
double data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
try {
double data = strtod(ldfTypeAndValue.second.data(), &storage);
returnValue = new LDFData<double>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<double>(key, data);
break;
}
@@ -100,7 +102,9 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") {
data = 0;
} else {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
try {
data = static_cast<uint32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10));
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
@@ -118,7 +122,9 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") {
data = false;
} else {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
try {
data = static_cast<bool>(strtol(ldfTypeAndValue.second.data(), &storage, 10));
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
@@ -129,22 +135,24 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
}
case LDF_TYPE_U64: {
uint64_t data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
try {
uint64_t data = static_cast<uint64_t>(strtoull(ldfTypeAndValue.second.data(), &storage, 10));
returnValue = new LDFData<uint64_t>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<uint64_t>(key, data);
break;
}
case LDF_TYPE_OBJID: {
LWOOBJID data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
try {
LWOOBJID data = static_cast<LWOOBJID>(strtoll(ldfTypeAndValue.second.data(), &storage, 10));
returnValue = new LDFData<LWOOBJID>(key, data);
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<LWOOBJID>(key, data);
break;
}

View File

@@ -129,19 +129,10 @@ NiPoint3 NiPoint3::operator+(const NiPoint3& point) const {
}
//! Operator for addition of vectors
NiPoint3& NiPoint3::operator+=(const NiPoint3& point) {
this->x += point.x;
this->y += point.y;
this->z += point.z;
return *this;
NiPoint3 NiPoint3::operator+=(const NiPoint3& point) const {
return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z);
}
NiPoint3& NiPoint3::operator*=(const float scalar) {
this->x *= scalar;
this->y *= scalar;
this->z *= scalar;
return *this;
}
//! Operator for subtraction of vectors
NiPoint3 NiPoint3::operator-(const NiPoint3& point) const {

View File

@@ -136,9 +136,7 @@ public:
NiPoint3 operator+(const NiPoint3& point) const;
//! Operator for addition of vectors
NiPoint3& operator+=(const NiPoint3& point);
NiPoint3& operator*=(const float scalar);
NiPoint3 operator+=(const NiPoint3& point) const;
//! Operator for subtraction of vectors
NiPoint3 operator-(const NiPoint3& point) const;

27
dCommon/Type.cpp Normal file
View File

@@ -0,0 +1,27 @@
#include "Type.h"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
std::string demangle(const char* name) {
int status = -4; // some arbitrary value to eliminate the compiler warning
// enable c++11 by passing the flag -std=c++11 to g++
std::unique_ptr<char, void(*)(void*)> res{
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status == 0) ? res.get() : name;
}
#else
// does nothing if not g++
std::string demangle(const char* name) {
return name;
}
#endif

12
dCommon/Type.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <string>
#include <typeinfo>
std::string demangle(const char* name);
template <class T>
std::string type(const T& t) {
return demangle(typeid(t).name());
}

View File

@@ -42,7 +42,7 @@ struct AssetMemoryBuffer : std::streambuf {
}
void close() {
free(m_Base);
delete m_Base;
}
};

View File

@@ -9,7 +9,6 @@
#include "BitStream.h"
#include "eConnectionType.h"
#include "eClientMessageType.h"
#include "BitStreamUtils.h"
#pragma warning (disable:4251) //Disables SQL warnings
@@ -33,7 +32,7 @@ constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate);
#define CBITSTREAM RakNet::BitStream bitStream;
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false);
#define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits());
#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
#define CMSGHEADER PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
#define SEND_PACKET Game::server->Send(&bitStream, sysAddr, false);
#define SEND_PACKET_BROADCAST Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);

File diff suppressed because it is too large Load Diff

View File

@@ -101,7 +101,7 @@ enum class eReplicaComponentType : uint32_t {
TRADE,
USER_CONTROL,
IGNORE_LIST,
MULTI_ZONE_ENTRANCE,
ROCKET_LAUNCH_LUP,
BUFF_REAL, // the real buff component, should just be name BUFF
INTERACTION_MANAGER,
DONATION_VENDOR,

View File

@@ -0,0 +1,22 @@
#pragma once
#include "GeneralUtils.h"
#include "Game.h"
#include "dLogger.h"
#include "dServer.h"
#include "CDTable.h"
template <
typename KeyType,
typename MappedType
>
class CDAbstractProvider
{
public:
virtual void LoadClient() = 0;
virtual void LoadHost() = 0;
virtual const MappedType& GetEntry(const KeyType& key, const MappedType& defaultValue) = 0;
};

View File

@@ -0,0 +1,124 @@
#pragma once
#include "GeneralUtils.h"
#include "Game.h"
#include "dLogger.h"
#include "dServer.h"
#include "CDTable.h"
#include "CDAbstractProvider.h"
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <functional>
#include <utility>
#include <chrono>
template <
typename KeyType,
typename MappedType
>
class CDAbstractSharedMemoryMap : public CDAbstractProvider<KeyType, MappedType>
{
typedef std::pair<const KeyType, MappedType> ValueType;
typedef boost::interprocess::allocator<ValueType, boost::interprocess::managed_shared_memory::segment_manager> ShmemAllocator;
typedef boost::interprocess::map<KeyType, MappedType, std::less<KeyType>, ShmemAllocator> Map;
public:
std::map<KeyType, MappedType> m_CacheMap;
std::string m_Name;
size_t m_Size;
bool m_Host;
Map* m_HostEntries;
boost::interprocess::managed_shared_memory m_ClientSegment;
ShmemAllocator* m_ClientAllocInst;
boost::interprocess::offset_ptr<Map> m_ClientEntires;
CDAbstractSharedMemoryMap(std::string name, size_t size)
{
m_Name = name;
m_Size = size;
m_Host = false;
m_HostEntries = nullptr;
m_ClientAllocInst = nullptr;
m_ClientEntires = nullptr;
LoadClient();
}
const MappedType& GetEntry(const KeyType& key, const MappedType& defaultValue) override {
const auto& cacheIt = m_CacheMap.find(key);
if (cacheIt != m_CacheMap.end()) {
return cacheIt->second;
}
const auto& it = m_ClientEntires->find(key);
if (it == m_ClientEntires->end()) {
return defaultValue;
}
return it->second;
}
const void SetEntry(const KeyType& key, const MappedType& value) {
if (m_Host) {
// If we are already hosting, we cannot add to the map, throw an error
throw std::runtime_error("Can not add to a map that is already being hosted");
}
m_CacheMap.emplace(key, value);
}
void LoadClient() override {
try {
m_ClientSegment = boost::interprocess::managed_shared_memory(boost::interprocess::open_read_only, m_Name.c_str());
m_ClientAllocInst = new ShmemAllocator(m_ClientSegment.get_segment_manager());
m_ClientEntires = m_ClientSegment.find<Map>(m_Name.c_str()).first;
if (m_ClientEntires == nullptr) {
throw std::runtime_error("Could not find shared memory segment " + m_Name);
}
} catch (std::exception &e) {
// Not open
}
}
void LoadHost() override {
try {
boost::interprocess::shared_memory_object::remove(m_Name.c_str());
boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, m_Name.c_str(), m_Size);
ShmemAllocator alloc(segment.get_segment_manager());
m_HostEntries = segment.construct<Map>(m_Name.c_str()) (std::less<KeyType>(), alloc);
// Copy cache
for (const auto& pair : m_CacheMap) {
m_HostEntries->insert(std::make_pair(pair.first, pair.second));
}
m_Host = true;
LoadClient();
} catch (std::exception &e) {
// Make sure the smemory is removed
boost::interprocess::shared_memory_object::remove(m_Name.c_str());
throw e;
}
}
};

View File

@@ -0,0 +1,150 @@
#pragma once
#include "CDAbstractProvider.h"
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <functional>
#include <utility>
#include <chrono>
#include <mutex>
template <
typename KeyType,
typename MappedType
>
class CDAbstractSharedMemoryProvider : public CDAbstractProvider<KeyType, MappedType>
{
typedef std::pair<const KeyType, MappedType> ValueType;
typedef boost::interprocess::allocator<ValueType, boost::interprocess::managed_shared_memory::segment_manager> ShmemAllocator;
typedef boost::interprocess::map<KeyType, MappedType, std::less<KeyType>, ShmemAllocator> Map;
public:
std::string m_Name;
std::string m_MapName;
std::function <ValueType(CppSQLite3Query&)> m_ParseEntry;
std::function <int32_t(int32_t)> m_CalculateSize;
bool m_Cache;
std::unordered_map<KeyType, MappedType> m_CacheMap;
bool m_Host;
Map* m_HostEntries;
boost::interprocess::managed_shared_memory m_ClientSegment;
ShmemAllocator* m_ClientAllocInst;
boost::interprocess::offset_ptr<Map> m_ClientEntires;
CDAbstractSharedMemoryProvider(std::string name, std::function <ValueType(CppSQLite3Query&)> parseEntry, std::function <int32_t(int32_t)> calculateSize, bool cache)
{
m_Name = name;
m_MapName = name + "Map";
m_ParseEntry = parseEntry;
m_CalculateSize = calculateSize;
m_Cache = cache;
m_Host = false;
m_HostEntries = nullptr;
LoadClient();
}
const MappedType& GetEntry(const KeyType& key, const MappedType& defaultValue) override {
if (m_Host) {
auto it = m_HostEntries->find(key);
if (it == m_HostEntries->end())
{
return defaultValue;
}
return it->second;
}
if (m_Cache) {
auto it = m_CacheMap.find(key);
if (it != m_CacheMap.end()) {
return it->second;
}
}
const auto& it = m_ClientEntires->find(key);
if (it == m_ClientEntires->end())
{
if (m_Cache) {
m_CacheMap.emplace(key, defaultValue);
}
return defaultValue;
}
if (m_Cache) {
m_CacheMap.emplace(key, it->second);
}
return it->second;
}
void LoadClient() override {
try {
m_ClientSegment = boost::interprocess::managed_shared_memory(boost::interprocess::open_read_only, m_MapName.c_str());
m_ClientAllocInst = new ShmemAllocator(m_ClientSegment.get_segment_manager());
m_ClientEntires = m_ClientSegment.find<Map>(m_Name.c_str()).first;
if (m_ClientEntires == nullptr) {
throw std::runtime_error("Could not find shared memory segment " + m_Name);
}
} catch (boost::interprocess::interprocess_exception& e) {
// Not open
}
}
void LoadHost() override {
try {
boost::interprocess::shared_memory_object::remove(m_MapName.c_str());
auto sizeQuery = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM " + m_Name);
if (sizeQuery.eof()) {
throw std::runtime_error("Could not get size of table " + m_Name);
return;
}
int32_t size = sizeQuery.getIntField(0);
size = m_CalculateSize(size);
boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, m_MapName.c_str(), size);
ShmemAllocator alloc_inst (segment.get_segment_manager());
m_HostEntries = segment.construct<Map>(m_Name.c_str()) (std::less<KeyType>(), alloc_inst);
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM " + m_Name);
while (!tableData.eof()) {
ValueType entry = m_ParseEntry(tableData);
m_HostEntries->insert(entry);
tableData.nextRow();
}
tableData.finalize();
m_Host = true;
LoadClient();
} catch (std::exception &e) {
// Make sure the smemory is removed
boost::interprocess::shared_memory_object::remove(m_MapName.c_str());
throw e;
}
}
};

View File

@@ -0,0 +1,74 @@
#pragma once
#include "GeneralUtils.h"
#include "Game.h"
#include "dLogger.h"
#include "dServer.h"
#include "CDTable.h"
#include "CDAbstractProvider.h"
template <
typename KeyType,
typename MappedType
>
class CDAbstractSqliteProvider : public CDAbstractProvider<KeyType, MappedType>
{
typedef std::pair<const KeyType, MappedType> ValueType;
public:
std::string m_Name;
std::function <ValueType(CppSQLite3Query&)> m_ParseEntry;
std::unordered_map<KeyType, MappedType> m_Entries;
CDAbstractSqliteProvider(std::string name, std::function <ValueType(CppSQLite3Query&)> parseEntry, bool cache)
{
m_Name = name;
m_ParseEntry = parseEntry;
LoadClient();
}
void LoadClient() override {
// First, get the size of the table
uint32_t size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM " + m_Name);
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
m_Entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM " + m_Name);
while (!tableData.eof()) {
auto entry = m_ParseEntry(tableData);
m_Entries.insert(entry);
tableData.nextRow();
}
tableData.finalize();
}
void LoadHost() override {
return;
}
const MappedType& GetEntry(const KeyType& key, const MappedType& defaultValue) override {
auto it = m_Entries.find(key);
if (it != m_Entries.end()) {
return it->second;
}
return defaultValue;
}
};

View File

@@ -13,7 +13,15 @@
#include <sstream>
#include <iostream>
//! The CDClient Database namespace
// Enable this to cache all entries in each table for fast access, comes with more memory cost
//#define CDCLIENT_CACHE_ALL
/*!
\file CDClientDatabase.hpp
\brief An interface between the CDClient.sqlite file and the server
*/
//! The CDClient Database namespace
namespace CDClientDatabase {
//! Opens a connection with the CDClient

View File

@@ -38,53 +38,51 @@
#include "CDFeatureGatingTable.h"
#include "CDRailActivatorComponent.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.
// #define CDCLIENT_CACHE_ALL
#ifdef CDCLIENT_CACHE_ALL
#define CDCLIENT_DONT_CACHE_TABLE(x) x
#else
#define CDCLIENT_DONT_CACHE_TABLE(x)
#endif
CDClientManager::CDClientManager() {
CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
CDActivitiesTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDAnimationsTable::Instance().LoadValuesFromDatabase());
CDBehaviorParameterTable::Instance().LoadValuesFromDatabase();
CDBehaviorTemplateTable::Instance().LoadValuesFromDatabase();
CDBrickIDTableTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDComponentsRegistryTable::Instance().LoadValuesFromDatabase());
CDCurrencyTableTable::Instance().LoadValuesFromDatabase();
CDDestructibleComponentTable::Instance().LoadValuesFromDatabase();
CDEmoteTableTable::Instance().LoadValuesFromDatabase();
CDFeatureGatingTable::Instance().LoadValuesFromDatabase();
CDInventoryComponentTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDItemComponentTable::Instance().LoadValuesFromDatabase());
CDItemSetSkillsTable::Instance().LoadValuesFromDatabase();
CDItemSetsTable::Instance().LoadValuesFromDatabase();
CDLevelProgressionLookupTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDLootMatrixTable::Instance().LoadValuesFromDatabase());
CDCLIENT_DONT_CACHE_TABLE(CDLootTableTable::Instance().LoadValuesFromDatabase());
CDMissionEmailTable::Instance().LoadValuesFromDatabase();
CDMissionNPCComponentTable::Instance().LoadValuesFromDatabase();
CDMissionTasksTable::Instance().LoadValuesFromDatabase();
CDMissionsTable::Instance().LoadValuesFromDatabase();
CDMovementAIComponentTable::Instance().LoadValuesFromDatabase();
CDObjectSkillsTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDObjectsTable::Instance().LoadValuesFromDatabase());
CDPhysicsComponentTable::Instance().LoadValuesFromDatabase();
CDPackageComponentTable::Instance().LoadValuesFromDatabase();
CDProximityMonitorComponentTable::Instance().LoadValuesFromDatabase();
CDPropertyEntranceComponentTable::Instance().LoadValuesFromDatabase();
CDPropertyTemplateTable::Instance().LoadValuesFromDatabase();
CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase();
CDRarityTableTable::Instance().LoadValuesFromDatabase();
CDRebuildComponentTable::Instance().LoadValuesFromDatabase();
CDRewardsTable::Instance().LoadValuesFromDatabase();
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
CDZoneTableTable::Instance().LoadValuesFromDatabase();
CDActivityRewardsTable::Instance();
CDAnimationsTable::Instance();
CDBehaviorParameterTable::Instance();
CDBehaviorTemplateTable::Instance();
CDComponentsRegistryTable::Instance();
CDCurrencyTableTable::Instance();
CDDestructibleComponentTable::Instance();
CDEmoteTableTable::Instance();
CDInventoryComponentTable::Instance();
CDItemComponentTable::Instance();
CDItemSetsTable::Instance();
CDItemSetSkillsTable::Instance();
CDLevelProgressionLookupTable::Instance();
CDLootMatrixTable::Instance();
CDLootTableTable::Instance();
CDMissionNPCComponentTable::Instance();
CDMissionTasksTable::Instance();
CDMissionsTable::Instance();
CDObjectSkillsTable::Instance();
CDObjectsTable::Instance();
CDPhysicsComponentTable::Instance();
CDRebuildComponentTable::Instance();
CDScriptComponentTable::Instance();
CDSkillBehaviorTable::Instance();
CDZoneTableTable::Instance();
CDVendorComponentTable::Instance();
CDActivitiesTable::Instance();
CDPackageComponentTable::Instance();
CDProximityMonitorComponentTable::Instance();
CDMovementAIComponentTable::Instance();
CDBrickIDTableTable::Instance();
CDRarityTableTable::Instance();
CDMissionEmailTable::Instance();
CDRewardsTable::Instance();
CDPropertyEntranceComponentTable::Instance();
CDPropertyTemplateTable::Instance();
CDFeatureGatingTable::Instance();
CDRailActivatorComponentTable::Instance();
}
void CDClientManager::LoadHost() {
for (auto itr = this->tables.begin(); itr != this->tables.end(); ++itr) {
itr->second->LoadHost();
}
CDTable::InitalizeHost();
}

41
dDatabase/CDProvider.h Normal file
View File

@@ -0,0 +1,41 @@
#pragma once
#include "CDAbstractProvider.h"
/**
* Shared memory provider with CDAbstractSharedMemoryProvider<key, value>
*
* Depends on the boost::interprocess library — header only
*
* Requires that CD_PROVIDER_MEMORY is defined and CD_PROVIDER_SQLITE is not defined
*/
#if defined(CD_PROVIDER_MEMORY) && !defined(CD_PROVIDER_SQLITE)
#include "CDAbstractSharedMemoryProvider.h"
#define CD_PROVIDER(provider, key, value) CDAbstractSharedMemoryProvider<key, value>* provider; typedef key CD_KEY; typedef value CD_VALUE
#define NEW_CD_PROVIDER(provider, name, parser, size_calculator, cache) provider = new CDAbstractSharedMemoryProvider<CD_KEY, CD_VALUE>(name, parser, size_calculator, cache)
template<typename KeyType, typename MappedType>
using CDProvider = CDAbstractSharedMemoryProvider<KeyType, MappedType>;
#endif
/**
* SQLite provider with CDAbstractSqliteProvider<key, value>
*
* No extra dependencies
*
* Requires that CD_PROVIDER_SQLITE or CD_PROVIDER_MEMORY is not defined — the default option
*/
#if defined(CD_PROVIDER_SQLITE) || !defined(CD_PROVIDER_MEMORY)
#include "CDAbstractSqliteProvider.h"
#define CD_PROVIDER(provider, key, value) CDAbstractSqliteProvider<key, value>* provider; typedef key CD_KEY; typedef value CD_VALUE
#define NEW_CD_PROVIDER(provider, name, parser, size_calculator, cache) provider = new CDAbstractSqliteProvider<CD_KEY, CD_VALUE>(name, parser, cache)
template<typename KeyType, typename MappedType>
using CDProvider = CDAbstractSqliteProvider<KeyType, MappedType>;
#endif

View File

@@ -1,6 +1,7 @@
set(DDATABASE_SOURCES "CDClientDatabase.cpp"
"CDClientManager.cpp"
"Database.cpp")
"Database.cpp"
"MigrationRunner.cpp")
add_subdirectory(Tables)
@@ -8,17 +9,5 @@ foreach(file ${DDATABASE_TABLES_SOURCES})
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}")
endforeach()
add_subdirectory(Databases)
foreach (file ${DDATABASE_DATABASES_SOURCES})
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Databases/${file}")
endforeach()
add_subdirectory(Databases/Migrations)
foreach (file ${DDATABASE_DATABASES_MIGRATIONS_SOURCES})
set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Databases/Migrations/${file}")
endforeach()
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
target_link_libraries(dDatabase sqlite3 mariadbConnCpp)

View File

@@ -1,26 +1,118 @@
#include "Database.h"
#include "Game.h"
#include "dConfig.h"
#include "dLogger.h"
using namespace std;
#include "Databases/MySQL.h"
#pragma warning (disable:4251) //Disables SQL warnings
void Database::Connect(dConfig* config) {
bool useSqlite = true;
if (config->GetValue("mysql_host") != "" && config->GetValue("mysql_database") != "" && config->GetValue("mysql_username") != "" && config->GetValue("mysql_password") != "") {
useSqlite = false;
}
sql::Driver* Database::driver;
sql::Connection* Database::con;
sql::Properties Database::props;
std::string Database::database;
if (useSqlite) {
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 {
Database::Connection = new MySQLDatabase(config->GetValue("mysql_host"), config->GetValue("mysql_database"), config->GetValue("mysql_username"), config->GetValue("mysql_password"));
Database::ConnectionType = eConnectionTypes::MYSQL;
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 != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!", source.c_str());
else Game::logger->Log("Database", "Destroying MySQL connection!");
}
Database::Connection->Connect();
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();
Game::logger->Log("Database", "Trying to reconnect to MySQL");
}
if (!con->isValid() || con->isClosed()) {
delete con;
con = nullptr;
Connect();
Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection");
}
auto* stmt = con->prepareStatement(str);
return stmt;
} //CreatePreppedStmt
void Database::Commit() {
Database::con->commit();
}
void Database::Destroy() {
Database::Connection->Destroy();
delete Database::Connection;
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);
}

View File

@@ -1,16 +1,31 @@
#pragma once
#include <string>
#include <conncpp.hpp>
#include "Databases/DatabaseBase.h"
class dConfig;
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 DatabaseBase* Connection;
static eConnectionTypes ConnectionType;
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 void Connect(dConfig* config);
static void Destroy();
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; }
};

View File

@@ -1 +0,0 @@
set(DDATABASE_DATABASES_SOURCES "MySQL.cpp")

View File

@@ -1,100 +0,0 @@
#pragma once
#include <string>
#include <vector>
#include "RakNetTypes.h"
#include "Structures.h"
enum eConnectionTypes {
NONE,
MYSQL,
SQLITE
};
class DatabaseBase {
public:
virtual void Connect() = 0;
virtual void Destroy() = 0;
// Server Get
virtual SocketDescriptor GetMasterServerIP() = 0;
// Server Set
virtual void CreateServer(const std::string& name, const std::string& ip, uint16_t port, uint32_t state, uint32_t version) = 0;
virtual void SetServerIpAndPortByName(const std::string& name, const std::string& ip, uint16_t port) = 0;
// Misc
virtual void InsertIntoActivityLog(uint32_t playerId, uint32_t activityId, uint32_t timestamp, uint32_t zoneId) = 0;
virtual void InsertIntoCommandLog(uint32_t playerId, const std::string& command) = 0;
// Character Get
virtual CharacterInfo GetCharacterInfoByID(uint32_t id) = 0;
virtual CharacterInfo GetCharacterInfoByName(const std::string& name) = 0;
virtual std::string GetCharacterXMLByID(uint32_t id) = 0;
virtual std::vector<std::string> GetAllCharacterNames() = 0;
virtual std::vector<CharacterInfo> GetAllCharactersByAccountID(uint32_t accountId) = 0;
virtual bool IsCharacterNameAvailable(const std::string& name) = 0;
// Charater Write
virtual void CreateCharacterXML(uint32_t id, const std::string& xml) = 0;
virtual void UpdateCharacterXML(uint32_t id, const std::string& xml) = 0;
virtual void CreateCharacter(uint32_t id, uint32_t account_id, const std::string& name, const std::string& pending_name, bool needs_rename, uint64_t last_login) = 0;
virtual void ApproveCharacterName(uint32_t id, const std::string& newName) = 0;
virtual void SetPendingCharacterName(uint32_t id, const std::string& pendingName) = 0;
virtual void UpdateCharacterLastLogin(uint32_t id, uint64_t time) = 0;
// Character Delete
virtual void DeleteCharacter(uint32_t id) = 0;
// Friends Get
virtual bool AreBestFriends(uint32_t charId1, uint32_t charId2) = 0;
// Account Get
virtual AccountInfo GetAccountByName(const std::string& name) = 0;
virtual AccountInfo GetAccountByID(uint32_t id) = 0;
virtual uint32_t GetLatestCharacterOfAccount(uint32_t id) = 0;
// Account Set
virtual void BanAccount(uint32_t id) = 0;
virtual void MuteAccount(uint32_t id, uint64_t muteExpireDate) = 0;
// Pet Write
virtual void CreatePetName(uint64_t id, const std::string& name, bool approved) = 0;
// Pet Delete
virtual void DeletePetName(uint64_t id) = 0;
// Pet Get
virtual PetName GetPetName(uint64_t id) = 0;
// Keys Get
virtual bool IsKeyActive(uint32_t id) = 0;
// Object ID tracker Get
virtual uint32_t GetObjectIDTracker() = 0;
// Object ID tracker Set
virtual void SetObjectIDTracker(uint32_t id) = 0;
// Mail Get
virtual MailInfo GetMailByID(uint64_t id) = 0;
virtual std::vector<MailInfo> GetAllRecentMailOfUser(uint32_t id) = 0;
virtual uint32_t GetUnreadMailCountForUser(uint32_t id) = 0;
// Mail Write
virtual void WriteMail(uint32_t senderId, const std::string& senderName, uint32_t receiverId, const std::string& receiverName, uint64_t sendTime, const std::string& subject, const std::string& body, uint32_t attachmentId = 0, uint32_t attachmentLot = 0, uint64_t attachmentSubkey = 0, uint32_t attachmentCount = 0, bool wasRead = false) = 0;
virtual void SetMailAsRead(uint64_t id) = 0;
virtual void RemoveAttachmentFromMail(uint64_t id) = 0;
// Mail Delete
virtual void DeleteMail(uint64_t id) = 0;
// Property Get
virtual uint64_t GetPropertyFromTemplateAndClone(uint32_t templateId, uint32_t cloneId) = 0;
virtual std::vector<uint32_t> GetBBBModlesForProperty(uint32_t propertyId) = 0;
virtual std::istream GetLXFMLFromID(uint32_t id) = 0;
private:
};

View File

@@ -1 +0,0 @@
set(DDATABASE_DATABASES_MIGRATIONS_SOURCES "MigrationManager.cpp" "MySQLMigration.cpp")

View File

@@ -1,90 +0,0 @@
#include "MigrationManager.h"
#include <fstream>
#include <string>
#include "dLogger.h"
#include "GeneralUtils.h"
#include "CDClientDatabase.h"
#include "BinaryPathFinder.h"
Migration Migration::LoadMigration(const std::string& type, const std::string& dbType, const std::string& name) {
Migration migration{};
std::ifstream file(BinaryPathFinder::GetBinaryDir() / "migrations/" / type / dbType / name);
if (file.is_open()) {
std::string line;
std::string total = "";
while (std::getline(file, line)) {
total += line;
}
file.close();
migration.name = ;
migration.data = total;
}
return migration;
}
void MigrationManager::RunSQLiteMigrations() {
auto cdstmt = CDClientDatabase::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);");
cdstmt.execQuery().finalize();
cdstmt.finalize();
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) {
auto migration = Migration::LoadMigration("cdserver", "", entry);
if (migration.data.empty()) continue;
// Check if there is an entry in the migration history table on the cdclient database.
cdstmt = CDClientDatabase::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
cdstmt.bind((int32_t)1, migration.name.c_str());
auto cdres = cdstmt.execQuery();
bool doExit = !cdres.eof();
cdres.finalize();
cdstmt.finalize();
if (doExit) continue;
// Check first if there is entry in the migration history table on the main database.
stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
stmt->setString(1, migration.name.c_str());
auto* res = stmt->executeQuery();
doExit = res->next();
delete res;
delete stmt;
if (doExit) {
// Insert into cdclient database if there is an entry in the main database but not the cdclient database.
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
cdstmt.bind((int32_t)1, migration.name.c_str());
cdstmt.execQuery().finalize();
cdstmt.finalize();
continue;
}
// Doing these 1 migration at a time since one takes a long time and some may think it is crashing.
// This will at the least guarentee that the full migration needs to be run in order to be counted as "migrated".
Game::logger->Log("MigrationRunner", "Executing migration: %s. This may take a while. Do not shut down server.", migration.name.c_str());
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
for (const auto& dml : GeneralUtils::SplitString(migration.data, ';')) {
if (dml.empty()) continue;
try {
CDClientDatabase::ExecuteDML(dml.c_str());
} catch (CppSQLite3Exception& e) {
Game::logger->Log("MigrationRunner", "Encountered error running DML command: (%i) : %s", e.errorCode(), e.errorMessage());
}
}
// Insert into cdclient database.
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
cdstmt.bind((int32_t)1, migration.name.c_str());
cdstmt.execQuery().finalize();
cdstmt.finalize();
CDClientDatabase::ExecuteQuery("COMMIT;");
}
Game::logger->Log("MigrationRunner", "CDServer database is up to date.");
}

View File

@@ -1,17 +0,0 @@
#pragma once
class DatabaseBase;
struct Migration {
std::string data;
std::string name;
static Migration LoadMigration(const std::string& type, const std::string& dbType, const std::string& name);
};
class MigrationManager {
public:
void RunMigrations(DatabaseBase* db);
void RunSQLiteMigrations();
private:
};

View File

@@ -1,13 +0,0 @@
#pragma once
#include <string>
#include "MigrationManager.h"
class MySQLMigrationManager : public MigrationManager {
public:
void RunMigrations(DatabaseBase* db) override;
void RunSQLiteMigrations() override;
private:
};

View File

@@ -1,651 +0,0 @@
#include "MySQL.h"
#pragma warning (disable:4251)
#include "Game.h"
#include "dConfig.h"
#include "dLogger.h"
MySQLDatabase::MySQLDatabase(const std::string& host, const std::string& database, const std::string& username, const std::string& password) {
this->m_Host = host;
this->m_Database = database;
this->m_Username = username;
this->m_Password = password;
m_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 (this->m_Host.find(UNIX_PROTO) == 0) {
properties["hostName"] = "unix://localhost";
properties["localSocket"] = this->m_Host.substr(UNIX_PROTO.length()).c_str();
} else if (this->m_Host.find(PIPE_PROTO) == 0) {
properties["hostName"] = "pipe://localhost";
properties["pipe"] = this->m_Host.substr(PIPE_PROTO.length()).c_str();
} else {
properties["hostName"] = this->m_Host.c_str();
}
properties["user"] = this->m_Username.c_str();
properties["password"] = this->m_Password.c_str();
properties["autoReconnect"] = "true";
this->m_Properties = properties;
this->m_Database = database;
}
MySQLDatabase::~MySQLDatabase() {
this->Destroy();
}
void MySQLDatabase::Connect() {
if (this->m_Properties.find("localSocket") != this->m_Properties.end() || this->m_Properties.find("pipe") != this->m_Properties.end()) {
this->m_Connection = m_Driver->connect(this->m_Properties);
} else {
this->m_Connection = m_Driver->connect(
this->m_Properties["hostName"].c_str(),
this->m_Properties["user"].c_str(),
this->m_Properties["password"].c_str()
);
}
this->m_Connection->setSchema(this->m_Database.c_str());
}
void MySQLDatabase::Destroy() {
if (this->m_Connection != nullptr) {
this->m_Connection->close();
delete this->m_Connection;
this->m_Connection = nullptr;
}
}
sql::Statement* MySQLDatabase::CreateStmt() {
sql::Statement* toReturn = this->m_Connection->createStatement();
return toReturn;
}
sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& query) {
const char* test = query.c_str();
size_t size = query.length();
sql::SQLString str(test, size);
if (!this->m_Connection) {
Connect();
Game::logger->Log("Database", "Trying to reconnect to MySQL");
}
if (!this->m_Connection->isValid() || this->m_Connection->isClosed()) {
delete this->m_Connection;
this->m_Connection = nullptr;
Connect();
Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection");
}
auto* stmt = this->m_Connection->prepareStatement(str);
return stmt;
}
std::unique_ptr<sql::PreparedStatement> MySQLDatabase::CreatePreppedStmtUnique(const std::string& query) {
std::unique_ptr<sql::PreparedStatement> stmt(CreatePreppedStmt(query));
return stmt;
}
std::unique_ptr<sql::ResultSet> MySQLDatabase::GetResultsOfStatement(sql::Statement* stmt) {
auto* res = stmt->executeQuery();
std::unique_ptr<sql::ResultSet> result(res);
return result;
}
void MySQLDatabase::Commit() {
this->m_Connection->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 m_Connection->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.
m_Connection->setAutoCommit(value);
}
SocketDescriptor MySQLDatabase::GetMasterServerIP() {
auto stmt = CreatePreppedStmtUnique("SELECT ip, port FROM servers WHERE name = 'master';");
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
return SocketDescriptor(res->getInt("port"), res->getString("ip"));
}
return SocketDescriptor(0, "");
}
void MySQLDatabase::CreateServer(const std::string& name, const std::string& ip, uint16_t port, uint32_t state, uint32_t version) {
auto stmt = CreatePreppedStmtUnique("INSERT INTO servers (name, ip, port, state, version) VALUES (?, ?, ?, ?, ?);");
stmt->setString(1, name);
stmt->setString(2, ip);
stmt->setInt(3, port);
stmt->setInt(4, state);
stmt->setInt(5, version);
stmt->execute();
}
void MySQLDatabase::SetServerIpAndPortByName(const std::string& name, const std::string& ip, uint16_t port) {
auto stmt = CreatePreppedStmtUnique("UPDATE servers SET ip = ?, port = ? WHERE name = ?;");
stmt->setString(1, ip);
stmt->setInt(2, port);
stmt->setString(3, name);
stmt->execute();
}
std::vector<std::string> MySQLDatabase::GetAllCharacterNames() {
auto stmt = CreatePreppedStmtUnique("SELECT name FROM charinfo;");
auto res = GetResultsOfStatement(stmt.get());
std::vector<std::string> names;
while (res->next()) {
names.push_back(res->getString("name").c_str());
}
return names;
}
bool MySQLDatabase::IsCharacterNameAvailable(const std::string& name) {
auto stmt = CreatePreppedStmtUnique("SELECT name FROM charinfo WHERE name = ? OR pending_name = ?;");
stmt->setString(1, name);
stmt->setString(2, name);
auto res = GetResultsOfStatement(stmt.get());
return !res->next();
}
void MySQLDatabase::InsertIntoActivityLog(uint32_t playerId, uint32_t activityId, uint32_t timestamp, uint32_t zoneId) {
auto stmt = CreatePreppedStmtUnique("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
stmt->setUInt(1, playerId);
stmt->setUInt(2, activityId);
stmt->setUInt(3, timestamp);
stmt->setUInt(4, zoneId);
stmt->executeUpdate();
}
void MySQLDatabase::InsertIntoCommandLog(uint32_t playerId, const std::string& command) {
auto stmt = CreatePreppedStmtUnique("INSERT INTO command_log (character_id, command) VALUES (?, ?);");
stmt->setUInt(1, playerId);
stmt->setString(2, command);
stmt->executeUpdate();
}
CharacterInfo MySQLDatabase::GetCharacterInfoByID(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT account_id, name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id = ? LIMIT 1;");
stmt->setUInt(1, id);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
CharacterInfo info{};
info.AccountID = res->getUInt("account_id");
info.ID = id;
info.Name = res->getString("name");
info.PendingName = res->getString("pending_name");
info.NameRejected = res->getBoolean("needs_rename");
info.PropertyCloneID = res->getUInt("prop_clone_id");
info.PermissionMap = (ePermissionMap)res->getUInt("permission_map");
return info;
}
return CharacterInfo{};
}
CharacterInfo MySQLDatabase::GetCharacterInfoByName(const std::string& name) {
auto stmt = CreatePreppedStmtUnique("SELECT id FROM charinfo WHERE name = ?");
stmt->setString(1, name);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
return GetCharacterInfoByID(res->getUInt("id"));
}
return CharacterInfo{};
}
uint32_t MySQLDatabase::GetLatestCharacterOfAccount(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 1;");
stmt->setUInt(1, id);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
return res->getUInt("id");
}
return 0;
}
std::string MySQLDatabase::GetCharacterXMLByID(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;");
stmt->setUInt(1, id);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
return res->getString("xml_data").c_str();
}
return "";
}
void MySQLDatabase::CreateCharacterXML(uint32_t id, const std::string& xml) {
auto stmt = CreatePreppedStmtUnique("INSERT INTO charxml (id, xml_data) VALUES (?, ?);");
stmt->setUInt(1, id);
stmt->setString(2, xml);
stmt->executeUpdate();
}
void MySQLDatabase::UpdateCharacterXML(uint32_t id, const std::string& xml) {
auto stmt = CreatePreppedStmtUnique("UPDATE charxml SET xml_data = ? WHERE id = ?;");
stmt->setString(1, xml);
stmt->setUInt(2, id);
stmt->executeUpdate();
}
void MySQLDatabase::CreateCharacter(uint32_t id, uint32_t account_id, const std::string& name, const std::string& pending_name, bool needs_rename, uint64_t last_login) {
auto stmt = CreatePreppedStmtUnique("INSERT INTO charinfo (id, account_id, name, pending_name, needs_rename, last_login) VALUES (?, ?, ?, ?, ?, ?);");
stmt->setUInt(1, id);
stmt->setUInt(2, account_id);
stmt->setString(3, name);
stmt->setString(4, pending_name);
stmt->setBoolean(5, needs_rename);
stmt->setUInt64(6, last_login);
stmt->execute();
}
void MySQLDatabase::ApproveCharacterName(uint32_t id, const std::string& newName) {
auto stmt = CreatePreppedStmtUnique("UPDATE charinfo SET name = ?, pending_name = '', needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1");
stmt->setString(1, newName);
stmt->setUInt64(2, time(NULL));
stmt->setUInt(3, id);
stmt->execute();
}
void MySQLDatabase::SetPendingCharacterName(uint32_t id, const std::string& pendingName) {
auto stmt = CreatePreppedStmtUnique("UPDATE charinfo SET pending_name=?, needs_rename=0, last_login=? WHERE id=? LIMIT 1;");
stmt->setString(1, pendingName);
stmt->setUInt64(2, time(NULL));
stmt->setUInt(3, id);
stmt->execute();
}
void MySQLDatabase::UpdateCharacterLastLogin(uint32_t id, uint64_t time) {
auto stmt = CreatePreppedStmtUnique("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1;");
stmt->setUInt64(1, time);
stmt->setUInt(2, id);
stmt->execute();
}
void MySQLDatabase::DeleteCharacter(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("DELETE FROM charxml WHERE id = ?;");
stmt->setUInt(1, id);
stmt->execute();
stmt = CreatePreppedStmtUnique("DELETE FROM command_log WHERE character_id = ?;");
stmt->setUInt(1, id);
stmt->execute();
stmt = CreatePreppedStmtUnique("DELETE FROM friends WHERE player_id = ? OR friend_id = ?;");
stmt->setUInt(1, id);
stmt->setUInt(2, id);
stmt->execute();
stmt = CreatePreppedStmtUnique("DELETE FROM leaderboard WHERE character_id = ?;");
stmt->setUInt(1, id);
stmt->execute();
stmt = CreatePreppedStmtUnique("DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id = ?)");
stmt->setUInt(1, id);
stmt->execute();
stmt = CreatePreppedStmtUnique("DELETE FROM properties WHERE owner_id = ?;");
stmt->setUInt(1, id);
stmt->execute();
stmt = CreatePreppedStmtUnique("DELETE FROM ugc WHERE character_id = ?;");
stmt->setUInt(1, id);
stmt->execute();
stmt = CreatePreppedStmtUnique("DELETE FROM activity_log WHERE character_id = ?;");
stmt->setUInt(1, id);
stmt->execute();
stmt = CreatePreppedStmtUnique("DELETE FROM mail WHERE receiver_id = ?;");
stmt->setUInt(1, id);
stmt->execute();
stmt = CreatePreppedStmtUnique("DELETE FROM charinfo WHERE id = ?;");
stmt->setUInt(1, id);
stmt->execute();
}
AccountInfo MySQLDatabase::GetAccountByName(const std::string& name) {
auto stmt = CreatePreppedStmtUnique("SELECT id FROM accounts WHERE name = ? LIMIT 1;");
stmt->setString(1, name);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
return GetAccountByID(res->getUInt("id"));
}
return AccountInfo{};
}
bool MySQLDatabase::AreBestFriends(uint32_t goon1, uint32_t goon2) {
auto stmt = CreatePreppedStmtUnique("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;");
stmt->setUInt(1, goon1);
stmt->setUInt(2, goon2);
stmt->setUInt(3, goon2);
stmt->setUInt(4, goon1);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
return res->getInt("best_friend") == 3;
}
return false;
}
AccountInfo MySQLDatabase::GetAccountByID(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT name, password, gm_level, locked, banned, play_key_id, created_at, mute_expire FROM accounts WHERE id = ? LIMIT 1;");
stmt->setUInt(1, id);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
AccountInfo info{};
info.ID = id;
info.Name = res->getString("name");
info.Password = res->getString("password");
info.MaxGMLevel = res->getUInt("gm_level");
info.Locked = res->getBoolean("locked");
info.Banned = res->getBoolean("banned");
info.PlayKeyID = res->getUInt("play_key_id");
info.CreatedAt = res->getUInt64("created_at");
info.MuteExpire = res->getUInt64("mute_expire");
return info;
}
return AccountInfo{};
}
void MySQLDatabase::BanAccount(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("UPDATE accounts SET banned = 1 WHERE id = ?;");
stmt->setUInt(1, id);
stmt->execute();
}
void MySQLDatabase::MuteAccount(uint32_t id, uint64_t muteExpireDate) {
auto stmt = CreatePreppedStmtUnique("UPDATE accounts SET mute_expire = ? WHERE id = ?;");
stmt->setUInt64(1, muteExpireDate);
stmt->setUInt(2, id);
stmt->execute();
}
std::vector<CharacterInfo> MySQLDatabase::GetAllCharactersByAccountID(uint32_t accountId) {
auto stmt = CreatePreppedStmtUnique("SELECT id FROM charinfo WHERE account_id = ? LIMIT 4;");
stmt->setUInt(1, accountId);
auto res = GetResultsOfStatement(stmt.get());
std::vector<CharacterInfo> characters;
while (res->next()) {
characters.push_back(GetCharacterInfoByID(res->getUInt("id")));
}
return characters;
}
void MySQLDatabase::CreatePetName(uint64_t id, const std::string& name, bool approved) {
auto stmt = CreatePreppedStmtUnique("INSERT INTO pet_names (id, name, approved) VALUES (?, ?, ?);");
stmt->setUInt64(1, id);
stmt->setString(2, name);
stmt->setBoolean(3, approved);
stmt->execute();
}
void MySQLDatabase::DeletePetName(uint64_t id) {
auto stmt = CreatePreppedStmtUnique("DELETE FROM pet_names WHERE id = ?;");
stmt->setUInt64(1, id);
stmt->execute();
}
PetName MySQLDatabase::GetPetName(uint64_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT name, approved FROM pet_names WHERE id = ? LIMIT 1;");
stmt->setUInt64(1, id);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
PetName name{};
name.ID = id;
name.Name = res->getString("name");
name.Approved = res->getBoolean("approved");
return name;
}
return PetName{};
}
bool MySQLDatabase::IsKeyActive(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT * FROM play_keys WHERE id = ? AND active = 1 LIMIT 1;");
stmt->setUInt(1, id);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
return true;
}
return false;
}
uint32_t MySQLDatabase::GetObjectIDTracker() {
auto stmt = CreatePreppedStmtUnique("SELECT last_object_id FROM object_id_tracker;");
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
uint32_t id = res->getUInt("last_object_id");
return id;
}
auto stmt = CreatePreppedStmtUnique("INSERT INTO object_id_tracker (last_object_id) VALUES (1);");
stmt->execute();
return 1;
}
void MySQLDatabase::SetObjectIDTracker(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("UPDATE object_id_tracker SET last_object_id = ?;");
stmt->setUInt(1, id);
stmt->execute();
}
MailInfo MySQLDatabase::GetMailByID(uint64_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT * FROM mail WHERE id = ?;");
stmt->setUInt64(1, id);
auto res = GetResultsOfStatement(stmt.get());
if (res->next()) {
MailInfo mail{};
mail.ID = id;
mail.SenderID = res->getUInt("sender_id");
mail.SenderName = res->getString("sender_name");
mail.ReceiverID = res->getUInt("receiver_id");
mail.ReceiverName = res->getString("receiver_name");
mail.TimeSent = res->getUInt64("time_sent");
mail.Subject = res->getString("subject");
mail.Body = res->getString("body");
mail.AttachmentID = res->getUInt("attachment_id");
mail.AttachmentLOT = res->getUInt("attachment_lot");
mail.AttachmentSubkey = res->getUInt64("attachment_subkey");
mail.AttachmentCount = res->getUInt("attachment_count");
mail.WasRead = res->getBoolean("was_read");
return mail;
}
return MailInfo{};
}
std::vector<MailInfo> MySQLDatabase::GetAllRecentMailOfUser(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT * FROM mail WHERE receiver_id = ? LIMIT 20;");
stmt->setUInt(1, id);
auto res = GetResultsOfStatement(stmt.get());
std::vector<MailInfo> mail;
while (res->next()) {
MailInfo mailInfo{};
mailInfo.ID = res->getUInt64("id");
mailInfo.SenderID = res->getUInt("sender_id");
mailInfo.SenderName = res->getString("sender_name");
mailInfo.ReceiverID = res->getUInt("receiver_id");
mailInfo.ReceiverName = res->getString("receiver_name");
mailInfo.TimeSent = res->getUInt64("time_sent");
mailInfo.Subject = res->getString("subject");
mailInfo.Body = res->getString("body");
mailInfo.AttachmentID = res->getUInt("attachment_id");
mailInfo.AttachmentLOT = res->getUInt("attachment_lot");
mailInfo.AttachmentSubkey = res->getUInt64("attachment_subkey");
mailInfo.AttachmentCount = res->getUInt("attachment_count");
mailInfo.WasRead = res->getBoolean("was_read");
mail.push_back(mailInfo);
}
return mail;
}
uint32_t MySQLDatabase::GetUnreadMailCountForUser(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT COUNT(*) FROM mail WHERE receiver_id = ? AND was_read = 0;");
stmt->setUInt(1, id);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
return res->getUInt(1);
}
return 0;
}
void MySQLDatabase::WriteMail(uint32_t senderId, const std::string& senderName, uint32_t receiverId, const std::string& receiverName, uint64_t sendTime, const std::string& subject, const std::string& body, uint32_t attachmentId, uint32_t attachmentLot, uint64_t attachmentSubkey, uint32_t attachmentCount, bool wasRead) {
auto stmt = CreatePreppedStmtUnique("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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
stmt->setUInt(1, senderId);
stmt->setString(2, senderName);
stmt->setUInt(3, receiverId);
stmt->setString(4, receiverName);
stmt->setUInt64(5, sendTime);
stmt->setString(6, subject);
stmt->setString(7, body);
stmt->setUInt(8, attachmentId);
stmt->setUInt(9, attachmentLot);
stmt->setUInt64(10, attachmentSubkey);
stmt->setUInt(11, attachmentCount);
stmt->setBoolean(12, wasRead);
stmt->execute();
}
void MySQLDatabase::DeleteMail(uint64_t id) {
auto stmt = CreatePreppedStmtUnique("DELETE FROM mail WHERE id = ?;");
stmt->setUInt64(1, id);
stmt->execute();
}
void MySQLDatabase::SetMailAsRead(uint64_t id) {
auto stmt = CreatePreppedStmtUnique("UPDATE mail SET was_read = 1 WHERE id = ?;");
stmt->setUInt64(1, id);
stmt->execute();
}
void MySQLDatabase::RemoveAttachmentFromMail(uint64_t id) {
auto stmt = CreatePreppedStmtUnique("UPDATE mail SET attachment_lot = 0 WHERE id = ?;");
stmt->setUInt64(1, id);
stmt->execute();
}
uint64_t MySQLDatabase::GetPropertyFromTemplateAndClone(uint32_t templateId, uint32_t cloneId) {
auto stmt = CreatePreppedStmtUnique("SELECT id FROM properties WHERE template_id = ? AND clone_id = ? LIMIT 1;");
stmt->setUInt(1, templateId);
stmt->setUInt(2, cloneId);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
return res->getUInt64("id");
}
return 0;
}
std::vector<uint32_t> MySQLDatabase::GetBBBModlesForProperty(uint32_t propertyId) {
auto stmt = CreatePreppedStmtUnique("SELECT ugc_id FROM properties_contents WHERE lot = 14 AND property_id = ?;");
stmt->setUInt(1, propertyId);
auto res = GetResultsOfStatement(stmt.get());
std::vector<uint32_t> models;
while (res->next()) {
models.push_back(res->getUInt("ugc_id"));
}
return models;
}
std::istream* MySQLDatabase::GetLXFMLFromID(uint32_t id) {
auto stmt = CreatePreppedStmtUnique("SELECT lxfml FROM ugc WHERE id = ? LIMIT 1;");
stmt->setUInt(1, id);
auto res = GetResultsOfStatement(stmt.get());
while (res->next()) {
std::istream* blob = res->getBlob("lxfml");
return blob;
}
return new std::istream();
}

View File

@@ -1,99 +0,0 @@
#pragma once
#include "DatabaseBase.h"
#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 MySQLDatabase : public DatabaseBase {
public:
MySQLDatabase(const std::string& host, const std::string& database, const std::string& username, const std::string& password);
~MySQLDatabase();
void Connect() override;
void Destroy() override;
sql::Statement* CreateStmt();
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
std::unique_ptr<sql::PreparedStatement> CreatePreppedStmtUnique(const std::string& query);
std::unique_ptr<sql::ResultSet> GetResultsOfStatement(sql::Statement* stmt);
void Commit();
bool GetAutoCommit();
void SetAutoCommit(bool value);
SocketDescriptor GetMasterServerIP() override;
void CreateServer(const std::string& name, const std::string& ip, uint16_t port, uint32_t state, uint32_t version) override;
void SetServerIpAndPortByName(const std::string& name, const std::string& ip, uint16_t port) override;
void InsertIntoActivityLog(uint32_t playerId, uint32_t activityId, uint32_t timestamp, uint32_t zoneId) override;
void InsertIntoCommandLog(uint32_t playerId, const std::string& command) override;
CharacterInfo GetCharacterInfoByID(uint32_t id) override;
CharacterInfo GetCharacterInfoByName(const std::string& name) override;
std::string GetCharacterXMLByID(uint32_t id) override;
std::vector<std::string> GetAllCharacterNames() override;
std::vector<CharacterInfo> GetAllCharactersByAccountID(uint32_t accountId) override;
bool IsCharacterNameAvailable(const std::string& name) override;
void CreateCharacterXML(uint32_t id, const std::string& xml) override;
void UpdateCharacterXML(uint32_t id, const std::string& xml) override;
void CreateCharacter(uint32_t id, uint32_t account_id, const std::string& name, const std::string& pending_name, bool needs_rename, uint64_t last_login) override;
void ApproveCharacterName(uint32_t id, const std::string& newName) override;
void SetPendingCharacterName(uint32_t id, const std::string& pendingName) override;
void UpdateCharacterLastLogin(uint32_t id, uint64_t time) override;
void DeleteCharacter(uint32_t id) override;
bool AreBestFriends(uint32_t charId1, uint32_t charId2) override;
AccountInfo GetAccountByName(const std::string& name) override;
AccountInfo GetAccountByID(uint32_t id) override;
uint32_t GetLatestCharacterOfAccount(uint32_t id) override;
void BanAccount(uint32_t id) override;
void MuteAccount(uint32_t id, uint64_t muteExpireDate) override;
void CreatePetName(uint64_t id, const std::string& name, bool approved) override;
void DeletePetName(uint64_t id) override;
PetName GetPetName(uint64_t id) override;
bool IsKeyActive(uint32_t id) override;
uint32_t GetObjectIDTracker() override;
void SetObjectIDTracker(uint32_t id) override;
MailInfo GetMailByID(uint64_t id) override;
std::vector<MailInfo> GetAllRecentMailOfUser(uint32_t id) override;
uint32_t GetUnreadMailCountForUser(uint32_t id) override;
void WriteMail(uint32_t senderId, const std::string& senderName, uint32_t receiverId, const std::string& receiverName, uint64_t sendTime, const std::string& subject, const std::string& body, uint32_t attachmentId = 0, uint32_t attachmentLot = 0, uint64_t attachmentSubkey = 0, uint32_t attachmentCount = 0, bool wasRead = false) override;
void SetMailAsRead(uint64_t id) override;
void RemoveAttachmentFromMail(uint64_t id) override;
void DeleteMail(uint64_t id) override;
uint64_t GetPropertyFromTemplateAndClone(uint32_t templateId, uint32_t cloneId) override;
std::vector<uint32_t> GetBBBModlesForProperty(uint32_t propertyId) override;
std::istream GetLXFMLFromID(uint32_t id) override;
private:
std::string m_Host;
std::string m_Database;
std::string m_Username;
std::string m_Password;
sql::Connection* m_Connection;
sql::Driver* m_Driver;
sql::Properties m_Properties;
};

View File

@@ -1,49 +0,0 @@
#pragma once
#include <string>
#include "ePermissionMap.h"
struct CharacterInfo {
uint32_t AccountID;
uint32_t ID;
std::string Name;
std::string PendingName;
bool NameRejected;
uint32_t PropertyCloneID;
ePermissionMap PermissionMap;
};
struct AccountInfo {
uint32_t ID;
std::string Name;
std::string Password;
uint32_t MaxGMLevel;
bool Locked;
bool Banned;
uint32_t PlayKeyID;
uint64_t CreatedAt;
uint64_t MuteExpire;
};
struct PetName {
uint64_t ID;
std::string Name;
bool Approved;
};
struct MailInfo {
uint64_t ID;
uint32_t SenderID;
std::string SenderName;
uint32_t ReceiverID;
std::string ReceiverName;
uint64_t TimeSent;
std::string Subject;
std::string Body;
uint32_t AttachmentID;
uint32_t AttachmentLOT;
uint64_t AttachmentSubkey;
uint32_t AttachmentCount;
bool WasRead;
};

View File

@@ -0,0 +1,158 @@
#include "MigrationRunner.h"
#include "BrickByBrickFix.h"
#include "CDClientDatabase.h"
#include "Database.h"
#include "Game.h"
#include "GeneralUtils.h"
#include "dLogger.h"
#include "BinaryPathFinder.h"
#include <istream>
Migration LoadMigration(std::string path) {
Migration migration{};
std::ifstream file(BinaryPathFinder::GetBinaryDir() / "migrations/" / path);
if (file.is_open()) {
std::string line;
std::string total = "";
while (std::getline(file, line)) {
total += line;
}
file.close();
migration.name = path;
migration.data = total;
}
return migration;
}
void MigrationRunner::RunMigrations() {
auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());");
stmt->execute();
delete stmt;
sql::SQLString finalSQL = "";
bool runSd0Migrations = false;
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/").string())) {
auto migration = LoadMigration("dlu/" + entry);
if (migration.data.empty()) {
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;
Game::logger->Log("MigrationRunner", "Running migration: %s", migration.name.c_str());
if (migration.name == "dlu/5_brick_model_sd0.sql") {
runSd0Migrations = true;
} else {
finalSQL.append(migration.data.c_str());
}
stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
stmt->setString(1, migration.name.c_str());
stmt->execute();
delete stmt;
}
if (finalSQL.empty() && !runSd0Migrations) {
Game::logger->Log("MigrationRunner", "Server database is up to date.");
return;
}
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;
simpleStatement->execute(query.c_str());
} catch (sql::SQLException& e) {
Game::logger->Log("MigrationRunner", "Encountered error running migration: %s", e.what());
}
}
}
// Do this last on the off chance none of the other migrations have been run yet.
if (runSd0Migrations) {
uint32_t numberOfUpdatedModels = BrickByBrickFix::UpdateBrickByBrickModelsToSd0();
Game::logger->Log("MasterServer", "%i models were updated from zlib to sd0.", numberOfUpdatedModels);
uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml();
Game::logger->Log("MasterServer", "%i models were truncated from the database.", numberOfTruncatedModels);
}
}
void MigrationRunner::RunSQLiteMigrations() {
auto cdstmt = CDClientDatabase::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP);");
cdstmt.execQuery().finalize();
cdstmt.finalize();
auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());");
stmt->execute();
delete stmt;
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) {
auto migration = LoadMigration("cdserver/" + entry);
if (migration.data.empty()) continue;
// Check if there is an entry in the migration history table on the cdclient database.
cdstmt = CDClientDatabase::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
cdstmt.bind((int32_t) 1, migration.name.c_str());
auto cdres = cdstmt.execQuery();
bool doExit = !cdres.eof();
cdres.finalize();
cdstmt.finalize();
if (doExit) continue;
// Check first if there is entry in the migration history table on the main database.
stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;");
stmt->setString(1, migration.name.c_str());
auto* res = stmt->executeQuery();
doExit = res->next();
delete res;
delete stmt;
if (doExit) {
// Insert into cdclient database if there is an entry in the main database but not the cdclient database.
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
cdstmt.bind((int32_t) 1, migration.name.c_str());
cdstmt.execQuery().finalize();
cdstmt.finalize();
continue;
}
// Doing these 1 migration at a time since one takes a long time and some may think it is crashing.
// This will at the least guarentee that the full migration needs to be run in order to be counted as "migrated".
Game::logger->Log("MigrationRunner", "Executing migration: %s. This may take a while. Do not shut down server.", migration.name.c_str());
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
for (const auto& dml : GeneralUtils::SplitString(migration.data, ';')) {
if (dml.empty()) continue;
try {
CDClientDatabase::ExecuteDML(dml.c_str());
} catch (CppSQLite3Exception& e) {
Game::logger->Log("MigrationRunner", "Encountered error running DML command: (%i) : %s", e.errorCode(), e.errorMessage());
}
}
// Insert into cdclient database.
cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);");
cdstmt.bind((int32_t) 1, migration.name.c_str());
cdstmt.execQuery().finalize();
cdstmt.finalize();
CDClientDatabase::ExecuteQuery("COMMIT;");
}
Game::logger->Log("MigrationRunner", "CDServer database is up to date.");
}

View File

@@ -0,0 +1,13 @@
#pragma once
#include <string>
struct Migration {
std::string data;
std::string name;
};
namespace MigrationRunner {
void RunMigrations();
void RunSQLiteMigrations();
};

View File

@@ -1,6 +1,7 @@
#include "CDActivitiesTable.h"
void CDActivitiesTable::LoadValuesFromDatabase() {
CDActivitiesTable::CDActivitiesTable(void) {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Activities");
@@ -54,3 +55,8 @@ std::vector<CDActivities> CDActivitiesTable::Query(std::function<bool(CDActiviti
return data;
}
std::vector<CDActivities> CDActivitiesTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -30,10 +30,9 @@ private:
std::vector<CDActivities> entries;
public:
void LoadValuesFromDatabase();
CDActivitiesTable();
// Queries the table with a custom "where" clause
std::vector<CDActivities> Query(std::function<bool(CDActivities)> predicate);
const std::vector<CDActivities>& GetEntries() const { return this->entries; }
std::vector<CDActivities> GetEntries(void) const;
};

View File

@@ -1,6 +1,6 @@
#include "CDActivityRewardsTable.h"
void CDActivityRewardsTable::LoadValuesFromDatabase() {
CDActivityRewardsTable::CDActivityRewardsTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -43,3 +43,8 @@ std::vector<CDActivityRewards> CDActivityRewardsTable::Query(std::function<bool(
return data;
}
std::vector<CDActivityRewards> CDActivityRewardsTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -18,10 +18,10 @@ private:
std::vector<CDActivityRewards> entries;
public:
void LoadValuesFromDatabase();
CDActivityRewardsTable();
// Queries the table with a custom "where" clause
std::vector<CDActivityRewards> Query(std::function<bool(CDActivityRewards)> predicate);
std::vector<CDActivityRewards> GetEntries() const;
std::vector<CDActivityRewards> GetEntries(void) const;
};

View File

@@ -2,35 +2,6 @@
#include "GeneralUtils.h"
#include "Game.h"
void CDAnimationsTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Animations");
while (!tableData.eof()) {
std::string animation_type = tableData.getStringField("animation_type", "");
DluAssert(!animation_type.empty());
AnimationGroupID animationGroupID = tableData.getIntField("animationGroupID", -1);
DluAssert(animationGroupID != -1);
CDAnimation entry;
entry.animation_name = tableData.getStringField("animation_name", "");
entry.chance_to_play = tableData.getFloatField("chance_to_play", 1.0f);
UNUSED_COLUMN(entry.min_loops = tableData.getIntField("min_loops", 0);)
UNUSED_COLUMN(entry.max_loops = tableData.getIntField("max_loops", 0);)
entry.animation_length = tableData.getFloatField("animation_length", 0.0f);
UNUSED_COLUMN(entry.hideEquip = tableData.getIntField("hideEquip", 0) == 1;)
UNUSED_COLUMN(entry.ignoreUpperBody = tableData.getIntField("ignoreUpperBody", 0) == 1;)
UNUSED_COLUMN(entry.restartable = tableData.getIntField("restartable", 0) == 1;)
UNUSED_COLUMN(entry.face_animation_name = tableData.getStringField("face_animation_name", "");)
UNUSED_COLUMN(entry.priority = tableData.getFloatField("priority", 0.0f);)
UNUSED_COLUMN(entry.blendTime = tableData.getFloatField("blendTime", 0.0f);)
this->animations[CDAnimationKey(animation_type, animationGroupID)].push_back(entry);
tableData.nextRow();
}
tableData.finalize();
}
bool CDAnimationsTable::CacheData(CppSQLite3Statement& queryToCache) {
auto tableData = queryToCache.execQuery();
// If we received a bad lookup, cache it anyways so we do not run the query again.

View File

@@ -27,7 +27,6 @@ class CDAnimationsTable : public CDTable<CDAnimationsTable> {
typedef std::string AnimationID;
typedef std::pair<std::string, AnimationGroupID> CDAnimationKey;
public:
void LoadValuesFromDatabase();
/**
* Given an animationType and the previousAnimationName played, return the next animationType to play.
* If there are more than 1 animationTypes that can be played, one is selected at random but also does not allow

View File

@@ -1,55 +1,53 @@
#include "CDBehaviorParameterTable.h"
#include "GeneralUtils.h"
uint64_t GetKey(const uint32_t behaviorID, const uint32_t parameterID) {
uint64_t key = behaviorID;
key <<= 31U;
key |= parameterID;
return key;
}
void CDBehaviorParameterTable::LoadValuesFromDatabase() {
CDBehaviorParameterTable::CDBehaviorParameterTable(void) {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter");
uint32_t uniqueParameterId = 0;
uint64_t hash = 0;
while (!tableData.eof()) {
uint32_t behaviorID = tableData.getIntField("behaviorID", -1);
CDBehaviorParameter entry;
entry.behaviorID = tableData.getIntField("behaviorID", -1);
auto candidateStringToAdd = std::string(tableData.getStringField("parameterID", ""));
auto parameter = m_ParametersList.find(candidateStringToAdd);
uint32_t parameterId;
if (parameter != m_ParametersList.end()) {
parameterId = parameter->second;
entry.parameterID = parameter;
} else {
parameterId = m_ParametersList.insert(std::make_pair(candidateStringToAdd, m_ParametersList.size())).first->second;
entry.parameterID = m_ParametersList.insert(std::make_pair(candidateStringToAdd, uniqueParameterId)).first;
uniqueParameterId++;
}
uint64_t hash = GetKey(behaviorID, parameterId);
float value = tableData.getFloatField("value", -1.0f);
hash = entry.behaviorID;
hash = (hash << 31U) | entry.parameterID->second;
entry.value = tableData.getFloatField("value", -1.0f);
m_Entries.insert(std::make_pair(hash, value));
tableData.nextRow();
m_Entries.insert(std::make_pair(hash, entry));
}
tableData.finalize();
}
float CDBehaviorParameterTable::GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue) {
auto parameterID = this->m_ParametersList.find(name);
if (parameterID == this->m_ParametersList.end()) return defaultValue;
auto hash = GetKey(behaviorID, parameterID->second);
uint64_t hash = behaviorID;
hash = (hash << 31U) | parameterID->second;
// Search for specific parameter
auto it = m_Entries.find(hash);
return it != m_Entries.end() ? it->second : defaultValue;
const auto& it = m_Entries.find(hash);
return it != m_Entries.end() ? it->second.value : defaultValue;
}
std::map<std::string, float> CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) {
uint64_t hashBase = behaviorID;
std::map<std::string, float> returnInfo;
for (auto& [parameterString, parameterId] : m_ParametersList) {
uint64_t hash = GetKey(hashBase, parameterId);
uint64_t hash;
for (auto& parameterCandidate : m_ParametersList) {
hash = (hashBase << 31U) | parameterCandidate.second;
auto infoCandidate = m_Entries.find(hash);
if (infoCandidate != m_Entries.end()) {
returnInfo.insert(std::make_pair(parameterString, infoCandidate->second));
returnInfo.insert(std::make_pair(infoCandidate->second.parameterID->first, infoCandidate->second.value));
}
}
return returnInfo;
}

View File

@@ -5,15 +5,18 @@
#include <unordered_map>
#include <unordered_set>
struct CDBehaviorParameter {
unsigned int behaviorID; //!< The Behavior ID
std::unordered_map<std::string, uint32_t>::iterator parameterID; //!< The Parameter ID
float value; //!< The value of the behavior template
};
class CDBehaviorParameterTable : public CDTable<CDBehaviorParameterTable> {
private:
typedef uint64_t BehaviorParameterHash;
typedef float BehaviorParameterValue;
std::unordered_map<BehaviorParameterHash, BehaviorParameterValue> m_Entries;
std::unordered_map<uint64_t, CDBehaviorParameter> m_Entries;
std::unordered_map<std::string, uint32_t> m_ParametersList;
public:
void LoadValuesFromDatabase();
CDBehaviorParameterTable();
float GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue = 0);
std::map<std::string, float> GetParametersByBehaviorID(uint32_t behaviorID);

View File

@@ -1,6 +1,6 @@
#include "CDBehaviorTemplateTable.h"
void CDBehaviorTemplateTable::LoadValuesFromDatabase() {
CDBehaviorTemplateTable::CDBehaviorTemplateTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -48,7 +48,7 @@ std::vector<CDBehaviorTemplate> CDBehaviorTemplateTable::Query(std::function<boo
return data;
}
const std::vector<CDBehaviorTemplate>& CDBehaviorTemplateTable::GetEntries() const {
std::vector<CDBehaviorTemplate> CDBehaviorTemplateTable::GetEntries(void) const {
return this->entries;
}
@@ -64,3 +64,4 @@ const CDBehaviorTemplate CDBehaviorTemplateTable::GetByBehaviorID(uint32_t behav
return entry->second;
}
}

View File

@@ -6,10 +6,10 @@
#include <unordered_set>
struct CDBehaviorTemplate {
unsigned int behaviorID; //!< The Behavior ID
unsigned int templateID; //!< The Template ID (LOT)
unsigned int effectID; //!< The Effect ID attached
std::unordered_set<std::string>::iterator effectHandle; //!< The effect handle
unsigned int behaviorID; //!< The Behavior ID
unsigned int templateID; //!< The Template ID (LOT)
unsigned int effectID; //!< The Effect ID attached
size_t effectHandle; //!< The effect handle
};
@@ -19,12 +19,11 @@ private:
std::unordered_map<uint32_t, CDBehaviorTemplate> entriesMappedByBehaviorID;
std::unordered_set<std::string> m_EffectHandles;
public:
void LoadValuesFromDatabase();
CDBehaviorTemplateTable();
// Queries the table with a custom "where" clause
std::vector<CDBehaviorTemplate> Query(std::function<bool(CDBehaviorTemplate)> predicate);
const std::vector<CDBehaviorTemplate>& GetEntries(void) const;
std::vector<CDBehaviorTemplate> GetEntries(void) const;
const CDBehaviorTemplate GetByBehaviorID(uint32_t behaviorID);
};

View File

@@ -1,6 +1,6 @@
#include "CDBrickIDTableTable.h"
void CDBrickIDTableTable::LoadValuesFromDatabase() {
CDBrickIDTableTable::CDBrickIDTableTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -39,7 +39,7 @@ std::vector<CDBrickIDTable> CDBrickIDTableTable::Query(std::function<bool(CDBric
return data;
}
const std::vector<CDBrickIDTable>& CDBrickIDTableTable::GetEntries() const {
std::vector<CDBrickIDTable> CDBrickIDTableTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -21,9 +21,9 @@ private:
std::vector<CDBrickIDTable> entries;
public:
void LoadValuesFromDatabase();
CDBrickIDTableTable();
// Queries the table with a custom "where" clause
std::vector<CDBrickIDTable> Query(std::function<bool(CDBrickIDTable)> predicate);
const std::vector<CDBrickIDTable>& GetEntries() const;
std::vector<CDBrickIDTable> GetEntries(void) const;
};

View File

@@ -1,7 +1,26 @@
#include "CDComponentsRegistryTable.h"
#include "eReplicaComponentType.h"
void CDComponentsRegistryTable::LoadValuesFromDatabase() {
#define CDCLIENT_CACHE_ALL
CDComponentsRegistryTable::CDComponentsRegistryTable(void) {
#ifdef CDCLIENT_CACHE_ALL
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ComponentsRegistry");
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 ComponentsRegistry");
while (!tableData.eof()) {
@@ -11,42 +30,65 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() {
entry.component_id = tableData.getIntField("component_id", -1);
this->mappedEntries.insert_or_assign(((uint64_t)entry.component_type) << 32 | ((uint64_t)entry.id), entry.component_id);
this->mappedEntries.insert_or_assign(entry.id, 0);
tableData.nextRow();
}
tableData.finalize();
#endif
}
int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) {
auto exists = mappedEntries.find(id);
if (exists != mappedEntries.end()) {
auto iter = mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id));
return iter == mappedEntries.end() ? defaultValue : iter->second;
const auto& iter = this->mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id));
if (iter == this->mappedEntries.end()) {
return defaultValue;
}
// Now get the data. Get all components of this entity so we dont do a query for each component
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ComponentsRegistry WHERE id = ?;");
query.bind(1, static_cast<int32_t>(id));
return iter->second;
auto tableData = query.execQuery();
#ifndef CDCLIENT_CACHE_ALL
// Now get the data
std::stringstream query;
query << "SELECT * FROM ComponentsRegistry WHERE id = " << std::to_string(id);
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
while (!tableData.eof()) {
CDComponentsRegistry entry;
entry.id = tableData.getIntField("id", -1);
entry.component_type = static_cast<eReplicaComponentType>(tableData.getIntField("component_type", 0));
entry.component_type = tableData.getIntField("component_type", -1);
entry.component_id = tableData.getIntField("component_id", -1);
this->mappedEntries.insert_or_assign(((uint64_t)entry.component_type) << 32 | ((uint64_t)entry.id), entry.component_id);
//this->entries.push_back(entry);
//Darwin's stuff:
const auto& it = this->mappedEntries.find(entry.id);
if (it != mappedEntries.end()) {
const auto& iter = it->second.find(entry.component_type);
if (iter == it->second.end()) {
it->second.insert(std::make_pair(entry.component_type, entry.component_id));
}
} else {
std::map<unsigned int, unsigned int> map;
map.insert(std::make_pair(entry.component_type, entry.component_id));
this->mappedEntries.insert(std::make_pair(entry.id, map));
}
tableData.nextRow();
}
mappedEntries.insert_or_assign(id, 0);
tableData.finalize();
auto iter = this->mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id));
const auto& it2 = this->mappedEntries.find(id);
if (it2 != mappedEntries.end()) {
const auto& iter = it2->second.find(componentType);
if (iter != it2->second.end()) {
return iter->second;
}
}
return iter == this->mappedEntries.end() ? defaultValue : iter->second;
return defaultValue;
#endif
}

View File

@@ -13,9 +13,9 @@ struct CDComponentsRegistry {
class CDComponentsRegistryTable : public CDTable<CDComponentsRegistryTable> {
private:
std::unordered_map<uint64_t, uint32_t> mappedEntries; //id, component_type, component_id
std::map<uint64_t, uint32_t> mappedEntries; //id, component_type, component_id
public:
void LoadValuesFromDatabase();
CDComponentsRegistryTable();
int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0);
};

View File

@@ -1,7 +1,7 @@
#include "CDCurrencyTableTable.h"
//! Constructor
void CDCurrencyTableTable::LoadValuesFromDatabase() {
CDCurrencyTableTable::CDCurrencyTableTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -43,7 +43,7 @@ std::vector<CDCurrencyTable> CDCurrencyTableTable::Query(std::function<bool(CDCu
return data;
}
const std::vector<CDCurrencyTable>& CDCurrencyTableTable::GetEntries() const {
std::vector<CDCurrencyTable> CDCurrencyTableTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -23,9 +23,9 @@ private:
std::vector<CDCurrencyTable> entries;
public:
void LoadValuesFromDatabase();
CDCurrencyTableTable();
// Queries the table with a custom "where" clause
std::vector<CDCurrencyTable> Query(std::function<bool(CDCurrencyTable)> predicate);
const std::vector<CDCurrencyTable>& GetEntries() const;
std::vector<CDCurrencyTable> GetEntries(void) const;
};

View File

@@ -1,6 +1,8 @@
#include "CDDestructibleComponentTable.h"
void CDDestructibleComponentTable::LoadValuesFromDatabase() {
//! Constructor
CDDestructibleComponentTable::CDDestructibleComponentTable(void) {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM DestructibleComponent");
@@ -50,7 +52,7 @@ std::vector<CDDestructibleComponent> CDDestructibleComponentTable::Query(std::fu
return data;
}
const std::vector<CDDestructibleComponent>& CDDestructibleComponentTable::GetEntries() const {
std::vector<CDDestructibleComponent> CDDestructibleComponentTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -25,9 +25,9 @@ private:
std::vector<CDDestructibleComponent> entries;
public:
void LoadValuesFromDatabase();
CDDestructibleComponentTable();
// Queries the table with a custom "where" clause
std::vector<CDDestructibleComponent> Query(std::function<bool(CDDestructibleComponent)> predicate);
const std::vector<CDDestructibleComponent>& GetEntries(void) const;
std::vector<CDDestructibleComponent> GetEntries(void) const;
};

View File

@@ -1,26 +1,40 @@
#include "CDEmoteTable.h"
void CDEmoteTableTable::LoadValuesFromDatabase() {
//! Constructor
CDEmoteTableTable::CDEmoteTableTable(void) {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Emotes");
while (!tableData.eof()) {
CDEmoteTable entry;
entry.ID = tableData.getIntField("id", -1);
entry.animationName = tableData.getStringField("animationName", "");
entry.iconFilename = tableData.getStringField("iconFilename", "");
entry.channel = tableData.getIntField("channel", -1);
entry.locked = tableData.getIntField("locked", -1) != 0;
entry.localize = tableData.getIntField("localize", -1) != 0;
entry.locState = tableData.getIntField("locStatus", -1);
entry.gateVersion = tableData.getStringField("gate_version", "");
CDEmoteTable* entry = new CDEmoteTable();
entry->ID = tableData.getIntField("id", -1);
entry->animationName = tableData.getStringField("animationName", "");
entry->iconFilename = tableData.getStringField("iconFilename", "");
entry->channel = tableData.getIntField("channel", -1);
entry->locked = tableData.getIntField("locked", -1) != 0;
entry->localize = tableData.getIntField("localize", -1) != 0;
entry->locState = tableData.getIntField("locStatus", -1);
entry->gateVersion = tableData.getStringField("gate_version", "");
entries.insert(std::make_pair(entry.ID, entry));
entries.insert(std::make_pair(entry->ID, entry));
tableData.nextRow();
}
tableData.finalize();
}
CDEmoteTable* CDEmoteTableTable::GetEmote(int id) {
auto itr = entries.find(id);
return itr != entries.end() ? &itr->second : nullptr;
//! Destructor
CDEmoteTableTable::~CDEmoteTableTable(void) {
for (auto e : entries) {
if (e.second) delete e.second;
}
entries.clear();
}
CDEmoteTable* CDEmoteTableTable::GetEmote(int id) {
for (auto e : entries) {
if (e.first == id) return e.second;
}
return nullptr;
}

View File

@@ -28,10 +28,11 @@ struct CDEmoteTable {
class CDEmoteTableTable : public CDTable<CDEmoteTableTable> {
private:
std::map<int, CDEmoteTable> entries;
std::map<int, CDEmoteTable*> entries;
public:
void LoadValuesFromDatabase();
CDEmoteTableTable();
~CDEmoteTableTable();
// Returns an emote by ID
CDEmoteTable* GetEmote(int id);
};

View File

@@ -1,6 +1,7 @@
#include "CDFeatureGatingTable.h"
void CDFeatureGatingTable::LoadValuesFromDatabase() {
//! Constructor
CDFeatureGatingTable::CDFeatureGatingTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -52,7 +53,7 @@ bool CDFeatureGatingTable::FeatureUnlocked(const std::string& feature) const {
return false;
}
const std::vector<CDFeatureGating>& CDFeatureGatingTable::GetEntries() const {
std::vector<CDFeatureGating> CDFeatureGatingTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -16,12 +16,11 @@ private:
std::vector<CDFeatureGating> entries;
public:
void LoadValuesFromDatabase();
CDFeatureGatingTable();
// Queries the table with a custom "where" clause
std::vector<CDFeatureGating> Query(std::function<bool(CDFeatureGating)> predicate);
bool FeatureUnlocked(const std::string& feature) const;
const std::vector<CDFeatureGating>& GetEntries(void) const;
std::vector<CDFeatureGating> GetEntries(void) const;
};

View File

@@ -1,6 +1,7 @@
#include "CDInventoryComponentTable.h"
void CDInventoryComponentTable::LoadValuesFromDatabase() {
//! Constructor
CDInventoryComponentTable::CDInventoryComponentTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -41,7 +42,7 @@ std::vector<CDInventoryComponent> CDInventoryComponentTable::Query(std::function
return data;
}
const std::vector<CDInventoryComponent>& CDInventoryComponentTable::GetEntries() const {
std::vector<CDInventoryComponent> CDInventoryComponentTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -15,9 +15,9 @@ private:
std::vector<CDInventoryComponent> entries;
public:
void LoadValuesFromDatabase();
CDInventoryComponentTable();
// Queries the table with a custom "where" clause
std::vector<CDInventoryComponent> Query(std::function<bool(CDInventoryComponent)> predicate);
const std::vector<CDInventoryComponent>& GetEntries() const;
std::vector<CDInventoryComponent> GetEntries(void) const;
};

View File

@@ -3,7 +3,11 @@
CDItemComponent CDItemComponentTable::Default = {};
void CDItemComponentTable::LoadValuesFromDatabase() {
//! Constructor
CDItemComponentTable::CDItemComponentTable(void) {
Default = CDItemComponent();
#ifdef CDCLIENT_CACHE_ALL
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ItemComponent");
@@ -51,13 +55,13 @@ void CDItemComponentTable::LoadValuesFromDatabase() {
entry.currencyLOT = tableData.getIntField("currencyLOT", -1);
entry.altCurrencyCost = tableData.getIntField("altCurrencyCost", -1);
entry.subItems = tableData.getStringField("subItems", "");
UNUSED_COLUMN(entry.audioEventUse = tableData.getStringField("audioEventUse", ""));
entry.audioEventUse = tableData.getStringField("audioEventUse", "");
entry.noEquipAnimation = tableData.getIntField("noEquipAnimation", -1) == 1 ? true : false;
entry.commendationLOT = tableData.getIntField("commendationLOT", -1);
entry.commendationCost = tableData.getIntField("commendationCost", -1);
UNUSED_COLUMN(entry.audioEquipMetaEventSet = tableData.getStringField("audioEquipMetaEventSet", ""));
entry.audioEquipMetaEventSet = tableData.getStringField("audioEquipMetaEventSet", "");
entry.currencyCosts = tableData.getStringField("currencyCosts", "");
UNUSED_COLUMN(entry.ingredientInfo = tableData.getStringField("ingredientInfo", ""));
entry.ingredientInfo = tableData.getStringField("ingredientInfo", "");
entry.locStatus = tableData.getIntField("locStatus", -1);
entry.forgeType = tableData.getIntField("forgeType", -1);
entry.SellMultiplier = tableData.getFloatField("SellMultiplier", -1.0f);
@@ -67,6 +71,7 @@ void CDItemComponentTable::LoadValuesFromDatabase() {
}
tableData.finalize();
#endif
}
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) {
@@ -75,10 +80,12 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
return it->second;
}
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ItemComponent WHERE id = ?;");
query.bind(1, static_cast<int32_t>(skillID));
#ifndef CDCLIENT_CACHE_ALL
std::stringstream query;
auto tableData = query.execQuery();
query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(skillID);
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
if (tableData.eof()) {
entries.insert(std::make_pair(skillID, Default));
return Default;
@@ -137,6 +144,7 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s
if (it2 != this->entries.end()) {
return it2->second;
}
#endif
return Default;
}

View File

@@ -54,7 +54,7 @@ private:
std::map<unsigned int, CDItemComponent> entries;
public:
void LoadValuesFromDatabase();
CDItemComponentTable();
static std::map<LOT, uint32_t> ParseCraftingCurrencies(const CDItemComponent& itemComponent);
// Gets an entry by ID

View File

@@ -1,6 +1,7 @@
#include "CDItemSetSkillsTable.h"
void CDItemSetSkillsTable::LoadValuesFromDatabase() {
//! Constructor
CDItemSetSkillsTable::CDItemSetSkillsTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -40,7 +41,7 @@ std::vector<CDItemSetSkills> CDItemSetSkillsTable::Query(std::function<bool(CDIt
return data;
}
const std::vector<CDItemSetSkills>& CDItemSetSkillsTable::GetEntries() const {
std::vector<CDItemSetSkills> CDItemSetSkillsTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -14,11 +14,11 @@ private:
std::vector<CDItemSetSkills> entries;
public:
void LoadValuesFromDatabase();
CDItemSetSkillsTable();
// Queries the table with a custom "where" clause
std::vector<CDItemSetSkills> Query(std::function<bool(CDItemSetSkills)> predicate);
const std::vector<CDItemSetSkills>& GetEntries() const;
std::vector<CDItemSetSkills> GetEntries(void) const;
std::vector<CDItemSetSkills> GetBySkillID(unsigned int SkillSetID);
};

View File

@@ -1,6 +1,7 @@
#include "CDItemSetsTable.h"
void CDItemSetsTable::LoadValuesFromDatabase() {
//! Constructor
CDItemSetsTable::CDItemSetsTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -52,7 +53,7 @@ std::vector<CDItemSets> CDItemSetsTable::Query(std::function<bool(CDItemSets)> p
return data;
}
const std::vector<CDItemSets>& CDItemSetsTable::GetEntries() const {
std::vector<CDItemSets> CDItemSetsTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -26,10 +26,10 @@ private:
std::vector<CDItemSets> entries;
public:
void LoadValuesFromDatabase();
CDItemSetsTable();
// Queries the table with a custom "where" clause
std::vector<CDItemSets> Query(std::function<bool(CDItemSets)> predicate);
const std::vector<CDItemSets>& GetEntries(void) const;
std::vector<CDItemSets> GetEntries(void) const;
};

View File

@@ -1,6 +1,7 @@
#include "CDLevelProgressionLookupTable.h"
void CDLevelProgressionLookupTable::LoadValuesFromDatabase() {
//! Constructor
CDLevelProgressionLookupTable::CDLevelProgressionLookupTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -31,6 +32,7 @@ void CDLevelProgressionLookupTable::LoadValuesFromDatabase() {
tableData.finalize();
}
//! Queries the table with a custom "where" clause
std::vector<CDLevelProgressionLookup> CDLevelProgressionLookupTable::Query(std::function<bool(CDLevelProgressionLookup)> predicate) {
std::vector<CDLevelProgressionLookup> data = cpplinq::from(this->entries)
@@ -40,7 +42,8 @@ std::vector<CDLevelProgressionLookup> CDLevelProgressionLookupTable::Query(std::
return data;
}
const std::vector<CDLevelProgressionLookup>& CDLevelProgressionLookupTable::GetEntries() const {
//! Gets all the entries in the table
std::vector<CDLevelProgressionLookup> CDLevelProgressionLookupTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -14,10 +14,10 @@ private:
std::vector<CDLevelProgressionLookup> entries;
public:
void LoadValuesFromDatabase();
CDLevelProgressionLookupTable();
// Queries the table with a custom "where" clause
std::vector<CDLevelProgressionLookup> Query(std::function<bool(CDLevelProgressionLookup)> predicate);
const std::vector<CDLevelProgressionLookup>& GetEntries() const;
// Gets all the entries in the table
std::vector<CDLevelProgressionLookup> GetEntries(void) const;
};

View File

@@ -1,19 +1,7 @@
#include "CDLootMatrixTable.h"
CDLootMatrix CDLootMatrixTable::ReadRow(CppSQLite3Query& tableData) const {
CDLootMatrix entry{};
if (tableData.eof()) return entry;
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
entry.percent = tableData.getFloatField("percent", -1.0f);
entry.minToDrop = tableData.getIntField("minToDrop", -1);
entry.maxToDrop = tableData.getIntField("maxToDrop", -1);
entry.flagID = tableData.getIntField("flagID", -1);
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
return entry;
}
void CDLootMatrixTable::LoadValuesFromDatabase() {
//! Constructor
CDLootMatrixTable::CDLootMatrixTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -24,6 +12,8 @@ void CDLootMatrixTable::LoadValuesFromDatabase() {
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
@@ -31,28 +21,33 @@ void CDLootMatrixTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootMatrix");
while (!tableData.eof()) {
CDLootMatrix entry;
uint32_t lootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
entry.percent = tableData.getFloatField("percent", -1.0f);
entry.minToDrop = tableData.getIntField("minToDrop", -1);
entry.maxToDrop = tableData.getIntField("maxToDrop", -1);
entry.id = tableData.getIntField("id", -1);
entry.flagID = tableData.getIntField("flagID", -1);
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
this->entries[lootMatrixIndex].push_back(ReadRow(tableData));
tableData.nextRow();
}
}
const LootMatrixEntries& CDLootMatrixTable::GetMatrix(uint32_t matrixId) {
auto itr = this->entries.find(matrixId);
if (itr != this->entries.end()) {
return itr->second;
}
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootMatrix where LootMatrixIndex = ?;");
query.bind(1, static_cast<int32_t>(matrixId));
auto tableData = query.execQuery();
while (!tableData.eof()) {
this->entries[matrixId].push_back(ReadRow(tableData));
this->entries.push_back(entry);
tableData.nextRow();
}
return this->entries[matrixId];
tableData.finalize();
}
std::vector<CDLootMatrix> CDLootMatrixTable::Query(std::function<bool(CDLootMatrix)> predicate) {
std::vector<CDLootMatrix> data = cpplinq::from(this->entries)
>> cpplinq::where(predicate)
>> cpplinq::to_vector();
return data;
}
const std::vector<CDLootMatrix>& CDLootMatrixTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -4,26 +4,26 @@
#include "CDTable.h"
struct CDLootMatrix {
unsigned int LootMatrixIndex; //!< The Loot Matrix Index
unsigned int LootTableIndex; //!< The Loot Table Index
unsigned int RarityTableIndex; //!< The Rarity Table Index
float percent; //!< The percent that this matrix is used?
unsigned int minToDrop; //!< The minimum amount of loot from this matrix to drop
unsigned int maxToDrop; //!< The maximum amount of loot from this matrix to drop
unsigned int id; //!< The ID of the Loot Matrix
unsigned int flagID; //!< ???
UNUSED(std::string gate_version); //!< The Gate Version
};
typedef uint32_t LootMatrixIndex;
typedef std::vector<CDLootMatrix> LootMatrixEntries;
class CDLootMatrixTable : public CDTable<CDLootMatrixTable> {
public:
void LoadValuesFromDatabase();
// Gets a matrix by ID or inserts a blank one if none existed.
const LootMatrixEntries& GetMatrix(uint32_t matrixId);
private:
CDLootMatrix ReadRow(CppSQLite3Query& tableData) const;
std::unordered_map<LootMatrixIndex, LootMatrixEntries> entries;
std::vector<CDLootMatrix> entries;
public:
CDLootMatrixTable();
// Queries the table with a custom "where" clause
std::vector<CDLootMatrix> Query(std::function<bool(CDLootMatrix)> predicate);
const std::vector<CDLootMatrix>& GetEntries(void) const;
};

View File

@@ -1,15 +1,7 @@
#include "CDLootTableTable.h"
CDLootTable CDLootTableTable::ReadRow(CppSQLite3Query& tableData) const {
CDLootTable entry{};
if (tableData.eof()) return entry;
entry.itemid = tableData.getIntField("itemid", -1);
entry.MissionDrop = tableData.getIntField("MissionDrop", -1) == 1 ? true : false;
entry.sortPriority = tableData.getIntField("sortPriority", -1);
return entry;
}
void CDLootTableTable::LoadValuesFromDatabase() {
//! Constructor
CDLootTableTable::CDLootTableTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -20,6 +12,8 @@ void CDLootTableTable::LoadValuesFromDatabase() {
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
@@ -27,28 +21,32 @@ void CDLootTableTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootTable");
while (!tableData.eof()) {
CDLootTable entry;
uint32_t lootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.id = tableData.getIntField("id", -1);
entry.itemid = tableData.getIntField("itemid", -1);
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.id = tableData.getIntField("id", -1);
entry.MissionDrop = tableData.getIntField("MissionDrop", -1) == 1 ? true : false;
entry.sortPriority = tableData.getIntField("sortPriority", -1);
this->entries[lootTableIndex].push_back(ReadRow(tableData));
tableData.nextRow();
}
}
const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) {
auto itr = this->entries.find(tableId);
if (itr != this->entries.end()) {
return itr->second;
}
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootTable WHERE LootTableIndex = ?;");
query.bind(1, static_cast<int32_t>(tableId));
auto tableData = query.execQuery();
while (!tableData.eof()) {
CDLootTable entry;
this->entries[tableId].push_back(ReadRow(tableData));
this->entries.push_back(entry);
tableData.nextRow();
}
return this->entries[tableId];
tableData.finalize();
}
//! Queries the table with a custom "where" clause
std::vector<CDLootTable> CDLootTableTable::Query(std::function<bool(CDLootTable)> predicate) {
std::vector<CDLootTable> data = cpplinq::from(this->entries)
>> cpplinq::where(predicate)
>> cpplinq::to_vector();
return data;
}
//! Gets all the entries in the table
const std::vector<CDLootTable>& CDLootTableTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -6,21 +6,20 @@
struct CDLootTable {
unsigned int itemid; //!< The LOT of the item
unsigned int LootTableIndex; //!< The Loot Table Index
unsigned int id; //!< The ID
bool MissionDrop; //!< Whether or not this loot table is a mission drop
unsigned int sortPriority; //!< The sorting priority
};
typedef uint32_t LootTableIndex;
typedef std::vector<CDLootTable> LootTableEntries;
class CDLootTableTable : public CDTable<CDLootTableTable> {
private:
CDLootTable ReadRow(CppSQLite3Query& tableData) const;
std::unordered_map<LootTableIndex, LootTableEntries> entries;
std::vector<CDLootTable> entries;
public:
void LoadValuesFromDatabase();
CDLootTableTable();
// Queries the table with a custom "where" clause
const LootTableEntries& GetTable(uint32_t tableId);
std::vector<CDLootTable> Query(std::function<bool(CDLootTable)> predicate);
const std::vector<CDLootTable>& GetEntries(void) const;
};

View File

@@ -1,6 +1,7 @@
#include "CDMissionEmailTable.h"
void CDMissionEmailTable::LoadValuesFromDatabase() {
//! Constructor
CDMissionEmailTable::CDMissionEmailTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -47,7 +48,7 @@ std::vector<CDMissionEmail> CDMissionEmailTable::Query(std::function<bool(CDMiss
}
//! Gets all the entries in the table
const std::vector<CDMissionEmail>& CDMissionEmailTable::GetEntries() const {
std::vector<CDMissionEmail> CDMissionEmailTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -20,9 +20,9 @@ private:
std::vector<CDMissionEmail> entries;
public:
void LoadValuesFromDatabase();
CDMissionEmailTable();
// Queries the table with a custom "where" clause
std::vector<CDMissionEmail> Query(std::function<bool(CDMissionEmail)> predicate);
const std::vector<CDMissionEmail>& GetEntries() const;
std::vector<CDMissionEmail> GetEntries(void) const;
};

View File

@@ -1,6 +1,7 @@
#include "CDMissionNPCComponentTable.h"
void CDMissionNPCComponentTable::LoadValuesFromDatabase() {
//! Constructor
CDMissionNPCComponentTable::CDMissionNPCComponentTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -44,7 +45,7 @@ std::vector<CDMissionNPCComponent> CDMissionNPCComponentTable::Query(std::functi
}
//! Gets all the entries in the table
const std::vector<CDMissionNPCComponent>& CDMissionNPCComponentTable::GetEntries() const {
std::vector<CDMissionNPCComponent> CDMissionNPCComponentTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -16,12 +16,12 @@ private:
std::vector<CDMissionNPCComponent> entries;
public:
void LoadValuesFromDatabase();
CDMissionNPCComponentTable();
// Queries the table with a custom "where" clause
std::vector<CDMissionNPCComponent> Query(std::function<bool(CDMissionNPCComponent)> predicate);
// Gets all the entries in the table
const std::vector<CDMissionNPCComponent>& GetEntries() const;
std::vector<CDMissionNPCComponent> GetEntries(void) const;
};

View File

@@ -1,6 +1,7 @@
#include "CDMissionTasksTable.h"
void CDMissionTasksTable::LoadValuesFromDatabase() {
//! Constructor
CDMissionTasksTable::CDMissionTasksTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -55,14 +56,16 @@ std::vector<CDMissionTasks*> CDMissionTasksTable::GetByMissionID(uint32_t missio
for (auto& entry : this->entries) {
if (entry.id == missionID) {
tasks.push_back(&entry);
CDMissionTasks* task = const_cast<CDMissionTasks*>(&entry);
tasks.push_back(task);
}
}
return tasks;
}
const std::vector<CDMissionTasks>& CDMissionTasksTable::GetEntries() const {
const std::vector<CDMissionTasks>& CDMissionTasksTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -24,12 +24,12 @@ private:
std::vector<CDMissionTasks> entries;
public:
void LoadValuesFromDatabase();
CDMissionTasksTable();
// Queries the table with a custom "where" clause
std::vector<CDMissionTasks> Query(std::function<bool(CDMissionTasks)> predicate);
std::vector<CDMissionTasks*> GetByMissionID(uint32_t missionID);
const std::vector<CDMissionTasks>& GetEntries() const;
const std::vector<CDMissionTasks>& GetEntries(void) const;
};

View File

@@ -2,7 +2,8 @@
CDMissions CDMissionsTable::Default = {};
void CDMissionsTable::LoadValuesFromDatabase() {
//! Constructor
CDMissionsTable::CDMissionsTable(void) {
// First, get the size of the table
unsigned int size = 0;

View File

@@ -65,12 +65,12 @@ private:
std::vector<CDMissions> entries;
public:
void LoadValuesFromDatabase();
CDMissionsTable();
// Queries the table with a custom "where" clause
std::vector<CDMissions> Query(std::function<bool(CDMissions)> predicate);
// Gets all the entries in the table
const std::vector<CDMissions>& GetEntries() const;
const std::vector<CDMissions>& GetEntries(void) const;
const CDMissions* GetPtrByMissionID(uint32_t missionID) const;

View File

@@ -1,6 +1,7 @@
#include "CDMovementAIComponentTable.h"
void CDMovementAIComponentTable::LoadValuesFromDatabase() {
//! Constructor
CDMovementAIComponentTable::CDMovementAIComponentTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -36,6 +37,7 @@ void CDMovementAIComponentTable::LoadValuesFromDatabase() {
tableData.finalize();
}
//! Queries the table with a custom "where" clause
std::vector<CDMovementAIComponent> CDMovementAIComponentTable::Query(std::function<bool(CDMovementAIComponent)> predicate) {
std::vector<CDMovementAIComponent> data = cpplinq::from(this->entries)
@@ -45,7 +47,8 @@ std::vector<CDMovementAIComponent> CDMovementAIComponentTable::Query(std::functi
return data;
}
const std::vector<CDMovementAIComponent>& CDMovementAIComponentTable::GetEntries(void) const {
//! Gets all the entries in the table
std::vector<CDMovementAIComponent> CDMovementAIComponentTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -19,10 +19,10 @@ private:
std::vector<CDMovementAIComponent> entries;
public:
void LoadValuesFromDatabase();
CDMovementAIComponentTable();
// Queries the table with a custom "where" clause
std::vector<CDMovementAIComponent> Query(std::function<bool(CDMovementAIComponent)> predicate);
// Gets all the entries in the table
const std::vector<CDMovementAIComponent>& GetEntries() const;
std::vector<CDMovementAIComponent> GetEntries(void) const;
};

View File

@@ -1,6 +1,7 @@
#include "CDObjectSkillsTable.h"
void CDObjectSkillsTable::LoadValuesFromDatabase() {
//! Constructor
CDObjectSkillsTable::CDObjectSkillsTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -32,6 +33,7 @@ void CDObjectSkillsTable::LoadValuesFromDatabase() {
tableData.finalize();
}
//! Queries the table with a custom "where" clause
std::vector<CDObjectSkills> CDObjectSkillsTable::Query(std::function<bool(CDObjectSkills)> predicate) {
std::vector<CDObjectSkills> data = cpplinq::from(this->entries)
@@ -41,6 +43,7 @@ std::vector<CDObjectSkills> CDObjectSkillsTable::Query(std::function<bool(CDObje
return data;
}
const std::vector<CDObjectSkills>& CDObjectSkillsTable::GetEntries() const {
//! Gets all the entries in the table
std::vector<CDObjectSkills> CDObjectSkillsTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -15,12 +15,12 @@ private:
std::vector<CDObjectSkills> entries;
public:
void LoadValuesFromDatabase();
CDObjectSkillsTable();
// Queries the table with a custom "where" clause
std::vector<CDObjectSkills> Query(std::function<bool(CDObjectSkills)> predicate);
// Gets all the entries in the table
const std::vector<CDObjectSkills>& GetEntries() const;
std::vector<CDObjectSkills> GetEntries(void) const;
};

View File

@@ -1,6 +1,8 @@
#include "CDObjectsTable.h"
void CDObjectsTable::LoadValuesFromDatabase() {
//! Constructor
CDObjectsTable::CDObjectsTable(void) {
#ifdef CDCLIENT_CACHE_ALL
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Objects");
@@ -18,24 +20,25 @@ void CDObjectsTable::LoadValuesFromDatabase() {
CDObjects entry;
entry.id = tableData.getIntField("id", -1);
entry.name = tableData.getStringField("name", "");
UNUSED_COLUMN(entry.placeable = tableData.getIntField("placeable", -1);)
entry.placeable = tableData.getIntField("placeable", -1);
entry.type = tableData.getStringField("type", "");
UNUSED_COLUMN(entry.description = tableData.getStringField("description", "");)
UNUSED_COLUMN(entry.localize = tableData.getIntField("localize", -1);)
UNUSED_COLUMN(entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1);)
UNUSED_COLUMN(entry.displayName = tableData.getStringField("displayName", "");)
entry.description = tableData.getStringField("description", "");
entry.localize = tableData.getIntField("localize", -1);
entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1);
entry.displayName = tableData.getStringField("displayName", "");
entry.interactionDistance = tableData.getFloatField("interactionDistance", -1.0f);
UNUSED_COLUMN(entry.nametag = tableData.getIntField("nametag", -1);)
UNUSED_COLUMN(entry._internalNotes = tableData.getStringField("_internalNotes", "");)
UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", -1);)
UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", "");)
UNUSED_COLUMN(entry.HQ_valid = tableData.getIntField("HQ_valid", -1);)
entry.nametag = tableData.getIntField("nametag", -1);
entry._internalNotes = tableData.getStringField("_internalNotes", "");
entry.locStatus = tableData.getIntField("locStatus", -1);
entry.gate_version = tableData.getStringField("gate_version", "");
entry.HQ_valid = tableData.getIntField("HQ_valid", -1);
this->entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow();
}
tableData.finalize();
#endif
m_default.id = 0;
}
@@ -46,10 +49,12 @@ const CDObjects& CDObjectsTable::GetByID(unsigned int LOT) {
return it->second;
}
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Objects WHERE id = ?;");
query.bind(1, static_cast<int32_t>(LOT));
#ifndef CDCLIENT_CACHE_ALL
std::stringstream query;
auto tableData = query.execQuery();
query << "SELECT * FROM Objects WHERE id = " << std::to_string(LOT);
auto tableData = CDClientDatabase::ExecuteQuery(query.str());
if (tableData.eof()) {
this->entries.insert(std::make_pair(LOT, m_default));
return m_default;
@@ -83,6 +88,7 @@ const CDObjects& CDObjectsTable::GetByID(unsigned int LOT) {
if (it2 != entries.end()) {
return it2->second;
}
#endif
return m_default;
}

View File

@@ -26,7 +26,7 @@ private:
CDObjects m_default;
public:
void LoadValuesFromDatabase();
CDObjectsTable();
// Gets an entry by ID
const CDObjects& GetByID(unsigned int LOT);
};

View File

@@ -1,6 +1,7 @@
#include "CDPackageComponentTable.h"
void CDPackageComponentTable::LoadValuesFromDatabase() {
//! Constructor
CDPackageComponentTable::CDPackageComponentTable(void) {
// First, get the size of the table
unsigned int size = 0;
@@ -42,7 +43,7 @@ std::vector<CDPackageComponent> CDPackageComponentTable::Query(std::function<boo
}
//! Gets all the entries in the table
const std::vector<CDPackageComponent>& CDPackageComponentTable::GetEntries() const {
std::vector<CDPackageComponent> CDPackageComponentTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -14,9 +14,9 @@ private:
std::vector<CDPackageComponent> entries;
public:
void LoadValuesFromDatabase();
CDPackageComponentTable(void);
// Queries the table with a custom "where" clause
std::vector<CDPackageComponent> Query(std::function<bool(CDPackageComponent)> predicate);
const std::vector<CDPackageComponent>& GetEntries() const;
std::vector<CDPackageComponent> GetEntries(void) const;
};

View File

@@ -1,35 +1,46 @@
#include "CDPhysicsComponentTable.h"
void CDPhysicsComponentTable::LoadValuesFromDatabase() {
CDPhysicsComponentTable::CDPhysicsComponentTable(void) {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PhysicsComponent");
while (!tableData.eof()) {
CDPhysicsComponent entry;
entry.id = tableData.getIntField("id", -1);
entry.bStatic = tableData.getIntField("static", -1) != 0;
entry.physicsAsset = tableData.getStringField("physics_asset", "");
CDPhysicsComponent* entry = new CDPhysicsComponent();
entry->id = tableData.getIntField("id", -1);
entry->bStatic = tableData.getIntField("static", -1) != 0;
entry->physicsAsset = tableData.getStringField("physics_asset", "");
UNUSED(entry->jump = tableData.getIntField("jump", -1) != 0);
UNUSED(entry->doublejump = tableData.getIntField("doublejump", -1) != 0);
entry.speed = tableData.getFloatField("speed", -1);
entry->speed = tableData.getFloatField("speed", -1);
UNUSED(entry->rotSpeed = tableData.getFloatField("rotSpeed", -1));
entry.playerHeight = tableData.getFloatField("playerHeight");
entry.playerRadius = tableData.getFloatField("playerRadius");
entry.pcShapeType = tableData.getIntField("pcShapeType");
entry.collisionGroup = tableData.getIntField("collisionGroup");
entry->playerHeight = tableData.getFloatField("playerHeight");
entry->playerRadius = tableData.getFloatField("playerRadius");
entry->pcShapeType = tableData.getIntField("pcShapeType");
entry->collisionGroup = tableData.getIntField("collisionGroup");
UNUSED(entry->airSpeed = tableData.getFloatField("airSpeed"));
UNUSED(entry->boundaryAsset = tableData.getStringField("boundaryAsset"));
UNUSED(entry->jumpAirSpeed = tableData.getFloatField("jumpAirSpeed"));
UNUSED(entry->friction = tableData.getFloatField("friction"));
UNUSED(entry->gravityVolumeAsset = tableData.getStringField("gravityVolumeAsset"));
m_entries.insert(std::make_pair(entry.id, entry));
m_entries.insert(std::make_pair(entry->id, entry));
tableData.nextRow();
}
tableData.finalize();
}
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) {
auto itr = m_entries.find(componentID);
return itr != m_entries.end() ? &itr->second : nullptr;
CDPhysicsComponentTable::~CDPhysicsComponentTable() {
for (auto e : m_entries) {
if (e.second) delete e.second;
}
m_entries.clear();
}
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) {
for (auto e : m_entries) {
if (e.first == componentID) return e.second;
}
return nullptr;
}

View File

@@ -23,11 +23,12 @@ struct CDPhysicsComponent {
class CDPhysicsComponentTable : public CDTable<CDPhysicsComponentTable> {
public:
void LoadValuesFromDatabase();
CDPhysicsComponentTable();
~CDPhysicsComponentTable();
static const std::string GetTableName() { return "PhysicsComponent"; };
CDPhysicsComponent* GetByID(unsigned int componentID);
private:
std::map<unsigned int, CDPhysicsComponent> m_entries;
std::map<unsigned int, CDPhysicsComponent*> m_entries;
};

Some files were not shown because too many files have changed in this diff Show More