Merge remote-tracking branch 'upstream/main' into PetFixes

This commit is contained in:
jadebenn 2024-04-19 18:30:43 -05:00
commit d88b982904
85 changed files with 4008 additions and 2891 deletions

View File

@ -73,7 +73,8 @@ if(UNIX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
elseif(MSVC) elseif(MSVC)
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now # Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
add_compile_options("/wd4267" "/utf-8") # Also disable non-portable MSVC volatile behavior
add_compile_options("/wd4267" "/utf-8" "/volatile:iso")
elseif(WIN32) elseif(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS) add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif() endif()
@ -103,7 +104,7 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
make_directory(${CMAKE_BINARY_DIR}/logs) make_directory(${CMAKE_BINARY_DIR}/logs)
# Copy resource files on first build # Copy resource files on first build
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blacklist.dcf") set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf")
message(STATUS "Checking resource file integrity") message(STATUS "Checking resource file integrity")
include(Utils) include(Utils)

View File

@ -27,8 +27,8 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
ExportWordlistToDCF(filepath + ".dcf", true); ExportWordlistToDCF(filepath + ".dcf", true);
} }
if (BinaryIO::DoesFileExist("blacklist.dcf")) { if (BinaryIO::DoesFileExist("blocklist.dcf")) {
ReadWordlistDCF("blacklist.dcf", false); ReadWordlistDCF("blocklist.dcf", false);
} }
//Read player names that are ok as well: //Read player names that are ok as well:
@ -44,20 +44,20 @@ dChatFilter::~dChatFilter() {
m_DeniedWords.clear(); m_DeniedWords.clear();
} }
void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool whiteList) { void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool allowList) {
std::ifstream file(filepath); std::ifstream file(filepath);
if (file) { if (file) {
std::string line; std::string line;
while (std::getline(file, line)) { while (std::getline(file, line)) {
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
if (whiteList) m_ApprovedWords.push_back(CalculateHash(line)); if (allowList) m_ApprovedWords.push_back(CalculateHash(line));
else m_DeniedWords.push_back(CalculateHash(line)); else m_DeniedWords.push_back(CalculateHash(line));
} }
} }
} }
bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) { bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool allowList) {
std::ifstream file(filepath, std::ios::binary); std::ifstream file(filepath, std::ios::binary);
if (file) { if (file) {
fileHeader hdr; fileHeader hdr;
@ -70,13 +70,13 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) {
if (hdr.formatVersion == formatVersion) { if (hdr.formatVersion == formatVersion) {
size_t wordsToRead = 0; size_t wordsToRead = 0;
BinaryIO::BinaryRead(file, wordsToRead); BinaryIO::BinaryRead(file, wordsToRead);
if (whiteList) m_ApprovedWords.reserve(wordsToRead); if (allowList) m_ApprovedWords.reserve(wordsToRead);
else m_DeniedWords.reserve(wordsToRead); else m_DeniedWords.reserve(wordsToRead);
size_t word = 0; size_t word = 0;
for (size_t i = 0; i < wordsToRead; ++i) { for (size_t i = 0; i < wordsToRead; ++i) {
BinaryIO::BinaryRead(file, word); BinaryIO::BinaryRead(file, word);
if (whiteList) m_ApprovedWords.push_back(word); if (allowList) m_ApprovedWords.push_back(word);
else m_DeniedWords.push_back(word); else m_DeniedWords.push_back(word);
} }
@ -90,14 +90,14 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) {
return false; return false;
} }
void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool whiteList) { void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowList) {
std::ofstream file(filepath, std::ios::binary | std::ios_base::out); std::ofstream file(filepath, std::ios::binary | std::ios_base::out);
if (file) { if (file) {
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::header)); BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::header));
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::formatVersion)); BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::formatVersion));
BinaryIO::BinaryWrite(file, size_t(whiteList ? m_ApprovedWords.size() : m_DeniedWords.size())); BinaryIO::BinaryWrite(file, size_t(allowList ? m_ApprovedWords.size() : m_DeniedWords.size()));
for (size_t word : whiteList ? m_ApprovedWords : m_DeniedWords) { for (size_t word : allowList ? m_ApprovedWords : m_DeniedWords) {
BinaryIO::BinaryWrite(file, word); BinaryIO::BinaryWrite(file, word);
} }
@ -105,10 +105,10 @@ void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool whiteLis
} }
} }
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool whiteList) { std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) {
if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true. if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true.
if (message.empty()) return { }; if (message.empty()) return { };
if (!whiteList && m_DeniedWords.empty()) return { { 0, message.length() } }; if (!allowList && m_DeniedWords.empty()) return { { 0, message.length() } };
std::stringstream sMessage(message); std::stringstream sMessage(message);
std::string segment; std::string segment;
@ -126,16 +126,16 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
size_t hash = CalculateHash(segment); size_t hash = CalculateHash(segment);
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && whiteList) { if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && allowList) {
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace_back(position, originalSegment.length());
} }
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && whiteList) { if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && allowList) {
m_UserUnapprovedWordCache.push_back(hash); m_UserUnapprovedWordCache.push_back(hash);
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace_back(position, originalSegment.length());
} }
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !whiteList) { if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !allowList) {
m_UserUnapprovedWordCache.push_back(hash); m_UserUnapprovedWordCache.push_back(hash);
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace_back(position, originalSegment.length());
} }

View File

@ -21,10 +21,10 @@ public:
dChatFilter(const std::string& filepath, bool dontGenerateDCF); dChatFilter(const std::string& filepath, bool dontGenerateDCF);
~dChatFilter(); ~dChatFilter();
void ReadWordlistPlaintext(const std::string& filepath, bool whiteList); void ReadWordlistPlaintext(const std::string& filepath, bool allowList);
bool ReadWordlistDCF(const std::string& filepath, bool whiteList); bool ReadWordlistDCF(const std::string& filepath, bool allowList);
void ExportWordlistToDCF(const std::string& filepath, bool whiteList); void ExportWordlistToDCF(const std::string& filepath, bool allowList);
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool whiteList = true); std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true);
private: private:
bool m_DontGenerateDCF; bool m_DontGenerateDCF;

View File

@ -1,6 +1,6 @@
#include "ChatIgnoreList.h" #include "ChatIgnoreList.h"
#include "PlayerContainer.h" #include "PlayerContainer.h"
#include "eChatInternalMessageType.h" #include "eChatMessageType.h"
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "Logger.h"
@ -13,7 +13,7 @@
// The only thing not auto-handled is instance activities force joining the team on the server. // The only thing not auto-handled is instance activities force joining the team on the server.
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) { void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receivingPlayer); bitStream.Write(receivingPlayer);
//portion that will get routed: //portion that will get routed:

View File

@ -14,11 +14,11 @@
#include "eObjectBits.h" #include "eObjectBits.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatMessageType.h" #include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "eClientMessageType.h" #include "eClientMessageType.h"
#include "eGameMessageType.h" #include "eGameMessageType.h"
#include "StringifiedEnum.h" #include "StringifiedEnum.h"
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
#include "ChatPackets.h"
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Get from the packet which player we want to do something with: //Get from the packet which player we want to do something with:
@ -60,7 +60,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Now, we need to send the friendlist to the server they came from: //Now, we need to send the friendlist to the server they came from:
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(playerID); bitStream.Write(playerID);
//portion that will get routed: //portion that will get routed:
@ -355,6 +355,67 @@ void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) {
inStream.Read(player.gmLevel); inStream.Read(player.gmLevel);
} }
void ChatPacketHandler::HandleWho(Packet* packet) {
CINSTREAM_SKIP_HEADER;
FindPlayerRequest request;
request.Deserialize(inStream);
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
if (!sender) return;
const auto& player = Game::playerContainer.GetPlayerData(request.playerName.GetAsString());
bool online = player;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::WHO_RESPONSE);
bitStream.Write<uint8_t>(online);
bitStream.Write(player.zoneID.GetMapID());
bitStream.Write(player.zoneID.GetInstanceID());
bitStream.Write(player.zoneID.GetCloneID());
bitStream.Write(request.playerName);
SystemAddress sysAddr = sender.sysAddr;
SEND_PACKET;
}
void ChatPacketHandler::HandleShowAll(Packet* packet) {
CINSTREAM_SKIP_HEADER;
ShowAllRequest request;
request.Deserialize(inStream);
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
if (!sender) return;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::SHOW_ALL_RESPONSE);
bitStream.Write<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
bitStream.Write(Game::playerContainer.GetPlayerCount());
bitStream.Write(Game::playerContainer.GetSimCount());
bitStream.Write<uint8_t>(request.displayIndividualPlayers);
bitStream.Write<uint8_t>(request.displayZoneData);
if (request.displayZoneData || request.displayIndividualPlayers){
for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){
if (!playerData) continue;
bitStream.Write<uint8_t>(0); // structure packing
if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName));
if (request.displayZoneData) {
bitStream.Write(playerData.zoneID.GetMapID());
bitStream.Write(playerData.zoneID.GetInstanceID());
bitStream.Write(playerData.zoneID.GetCloneID());
}
}
}
SystemAddress sysAddr = sender.sysAddr;
SEND_PACKET;
}
// the structure the client uses to send this packet is shared in many chat messages // the structure the client uses to send this packet is shared in many chat messages
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages // that are sent to the server. Because of this, there are large gaps of unused data in chat messages
void ChatPacketHandler::HandleChatMessage(Packet* packet) { void ChatPacketHandler::HandleChatMessage(Packet* packet) {
@ -454,7 +515,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) { void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(routeTo.playerID); bitStream.Write(routeTo.playerID);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
@ -696,7 +757,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) { void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@ -711,7 +772,7 @@ void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerD
void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) { void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@ -738,7 +799,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool b
void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) { void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@ -763,7 +824,7 @@ void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64L
void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) { void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@ -780,7 +841,7 @@ void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i
void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) { void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@ -809,7 +870,7 @@ void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFr
void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) { void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@ -835,7 +896,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bD
void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) { void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@ -869,7 +930,7 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
[bool] - is FTP*/ [bool] - is FTP*/
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(friendData.playerID); bitStream.Write(friendData.playerID);
//portion that will get routed: //portion that will get routed:
@ -906,7 +967,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
} }
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@ -920,7 +981,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) { void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
// Portion that will get routed: // Portion that will get routed:
@ -943,7 +1004,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) { void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:

View File

@ -50,6 +50,8 @@ namespace ChatPacketHandler {
void HandleFriendResponse(Packet* packet); void HandleFriendResponse(Packet* packet);
void HandleRemoveFriend(Packet* packet); void HandleRemoveFriend(Packet* packet);
void HandleGMLevelUpdate(Packet* packet); void HandleGMLevelUpdate(Packet* packet);
void HandleWho(Packet* packet);
void HandleShowAll(Packet* packet);
void HandleChatMessage(Packet* packet); void HandleChatMessage(Packet* packet);
void HandlePrivateChatMessage(Packet* packet); void HandlePrivateChatMessage(Packet* packet);

View File

@ -17,7 +17,6 @@
#include "PlayerContainer.h" #include "PlayerContainer.h"
#include "ChatPacketHandler.h" #include "ChatPacketHandler.h"
#include "eChatMessageType.h" #include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "eWorldMessageType.h" #include "eWorldMessageType.h"
#include "ChatIgnoreList.h" #include "ChatIgnoreList.h"
#include "StringifiedEnum.h" #include "StringifiedEnum.h"
@ -182,47 +181,29 @@ int main(int argc, char** argv) {
void HandlePacket(Packet* packet) { void HandlePacket(Packet* packet) {
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
LOG("A server has disconnected, erasing their connected players from the list."); LOG("A server has disconnected, erasing their connected players from the list.");
} } else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
LOG("A server is connecting, awaiting user list."); LOG("A server is connecting, awaiting user list.");
} } else if (packet->length < 4 || packet->data[0] != ID_USER_PACKET_ENUM) return; // Nothing left to process or not the right packet type
if (packet->length < 4) return; // Nothing left to process. Need 4 bytes to continue. CINSTREAM;
inStream.SetReadOffset(BYTES_TO_BITS(1));
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) { eConnectionType connection;
switch (static_cast<eChatInternalMessageType>(packet->data[3])) { eChatMessageType chatMessageID;
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
Game::playerContainer.InsertPlayer(packet);
break;
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION: inStream.Read(connection);
Game::playerContainer.RemovePlayer(packet); if (connection != eConnectionType::CHAT) return;
break; inStream.Read(chatMessageID);
case eChatInternalMessageType::MUTE_UPDATE: switch (chatMessageID) {
case eChatMessageType::GM_MUTE:
Game::playerContainer.MuteUpdate(packet); Game::playerContainer.MuteUpdate(packet);
break; break;
case eChatInternalMessageType::CREATE_TEAM: case eChatMessageType::CREATE_TEAM:
Game::playerContainer.CreateTeamServer(packet); Game::playerContainer.CreateTeamServer(packet);
break; break;
case eChatInternalMessageType::ANNOUNCEMENT: {
//we just forward this packet to every connected server
CINSTREAM;
Game::server->Send(inStream, packet->systemAddress, true); //send to everyone except origin
break;
}
default:
LOG("Unknown CHAT_INTERNAL id: %i", int(packet->data[3]));
}
}
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
eChatMessageType chat_message_type = static_cast<eChatMessageType>(packet->data[3]);
switch (chat_message_type) {
case eChatMessageType::GET_FRIENDS_LIST: case eChatMessageType::GET_FRIENDS_LIST:
ChatPacketHandler::HandleFriendlistRequest(packet); ChatPacketHandler::HandleFriendlistRequest(packet);
break; break;
@ -296,6 +277,23 @@ void HandlePacket(Packet* packet) {
ChatPacketHandler::HandleGMLevelUpdate(packet); ChatPacketHandler::HandleGMLevelUpdate(packet);
break; break;
case eChatMessageType::LOGIN_SESSION_NOTIFY: case eChatMessageType::LOGIN_SESSION_NOTIFY:
Game::playerContainer.InsertPlayer(packet);
break;
case eChatMessageType::GM_ANNOUNCE:{
// we just forward this packet to every connected server
inStream.ResetReadPointer();
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
}
break;
case eChatMessageType::UNEXPECTED_DISCONNECT:
Game::playerContainer.RemovePlayer(packet);
break;
case eChatMessageType::WHO:
ChatPacketHandler::HandleWho(packet);
break;
case eChatMessageType::SHOW_ALL:
ChatPacketHandler::HandleShowAll(packet);
break;
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE: case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE:
case eChatMessageType::WORLD_DISCONNECT_REQUEST: case eChatMessageType::WORLD_DISCONNECT_REQUEST:
case eChatMessageType::WORLD_PROXIMITY_RESPONSE: case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
@ -308,7 +306,6 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::GUILD_KICK: case eChatMessageType::GUILD_KICK:
case eChatMessageType::GUILD_GET_STATUS: case eChatMessageType::GUILD_GET_STATUS:
case eChatMessageType::GUILD_GET_ALL: case eChatMessageType::GUILD_GET_ALL:
case eChatMessageType::SHOW_ALL:
case eChatMessageType::BLUEPRINT_MODERATED: case eChatMessageType::BLUEPRINT_MODERATED:
case eChatMessageType::BLUEPRINT_MODEL_READY: case eChatMessageType::BLUEPRINT_MODEL_READY:
case eChatMessageType::PROPERTY_READY_FOR_APPROVAL: case eChatMessageType::PROPERTY_READY_FOR_APPROVAL:
@ -323,7 +320,6 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::CSR_REQUEST: case eChatMessageType::CSR_REQUEST:
case eChatMessageType::CSR_REPLY: case eChatMessageType::CSR_REPLY:
case eChatMessageType::GM_KICK: case eChatMessageType::GM_KICK:
case eChatMessageType::GM_ANNOUNCE:
case eChatMessageType::WORLD_ROUTE_PACKET: case eChatMessageType::WORLD_ROUTE_PACKET:
case eChatMessageType::GET_ZONE_POPULATIONS: case eChatMessageType::GET_ZONE_POPULATIONS:
case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE: case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE:
@ -332,33 +328,18 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE: case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE:
case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT: case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
case eChatMessageType::UGCC_REQUEST: case eChatMessageType::UGCC_REQUEST:
case eChatMessageType::WHO:
case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE: case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
case eChatMessageType::ACHIEVEMENT_NOTIFY: case eChatMessageType::ACHIEVEMENT_NOTIFY:
case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW: case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW:
case eChatMessageType::UNEXPECTED_DISCONNECT:
case eChatMessageType::PLAYER_READY: case eChatMessageType::PLAYER_READY:
case eChatMessageType::GET_DONATION_TOTAL: case eChatMessageType::GET_DONATION_TOTAL:
case eChatMessageType::UPDATE_DONATION: case eChatMessageType::UPDATE_DONATION:
case eChatMessageType::PRG_CSR_COMMAND: case eChatMessageType::PRG_CSR_COMMAND:
case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD: case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD:
case eChatMessageType::UPDATE_FREE_TRIAL_STATUS: case eChatMessageType::UPDATE_FREE_TRIAL_STATUS:
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chat_message_type).data(), chat_message_type); LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
break; break;
default: default:
LOG("Unknown CHAT Message id: %i", chat_message_type); LOG("Unknown CHAT Message id: %i", chatMessageID);
}
}
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::WORLD) {
switch (static_cast<eWorldMessageType>(packet->data[3])) {
case eWorldMessageType::ROUTE_PACKET: {
LOG("Routing packet from world");
break;
}
default:
LOG("Unknown World id: %i", int(packet->data[3]));
}
} }
} }

View File

@ -9,9 +9,9 @@
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "Database.h" #include "Database.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "ChatPackets.h" #include "ChatPackets.h"
#include "dConfig.h" #include "dConfig.h"
#include "eChatMessageType.h"
void PlayerContainer::Initialize() { void PlayerContainer::Initialize() {
m_MaxNumberOfBestFriends = m_MaxNumberOfBestFriends =
@ -49,6 +49,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
data.sysAddr = packet->systemAddress; data.sysAddr = packet->systemAddress;
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName); m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
m_PlayerCount++;
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID()); LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
@ -87,6 +88,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
} }
} }
m_PlayerCount--;
LOG("Removed user: %llu", playerID); LOG("Removed user: %llu", playerID);
m_Players.erase(playerID); m_Players.erase(playerID);
@ -145,7 +147,7 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) { void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE);
bitStream.Write(player); bitStream.Write(player);
bitStream.Write(time); bitStream.Write(time);
@ -352,7 +354,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::TEAM_GET_STATUS);
bitStream.Write(team->teamID); bitStream.Write(team->teamID);
bitStream.Write(deleteTeam); bitStream.Write(deleteTeam);

View File

@ -71,6 +71,9 @@ public:
const PlayerData& GetPlayerData(const std::string& playerName); const PlayerData& GetPlayerData(const std::string& playerName);
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID); PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
PlayerData& GetPlayerDataMutable(const std::string& playerName); PlayerData& GetPlayerDataMutable(const std::string& playerName);
uint32_t GetPlayerCount() { return m_PlayerCount; };
uint32_t GetSimCount() { return m_SimCount; };
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; };
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members); TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
TeamData* CreateTeam(LWOOBJID leader, bool local = false); TeamData* CreateTeam(LWOOBJID leader, bool local = false);
@ -93,5 +96,7 @@ private:
std::unordered_map<LWOOBJID, std::u16string> m_Names; std::unordered_map<LWOOBJID, std::u16string> m_Names;
uint32_t m_MaxNumberOfBestFriends = 5; uint32_t m_MaxNumberOfBestFriends = 5;
uint32_t m_MaxNumberOfFriends = 50; uint32_t m_MaxNumberOfFriends = 50;
uint32_t m_PlayerCount = 0;
uint32_t m_SimCount = 0;
}; };

View File

@ -120,6 +120,8 @@ void CatchUnhandled(int sig) {
if (eptr) std::rethrow_exception(eptr); if (eptr) std::rethrow_exception(eptr);
} catch(const std::exception& e) { } catch(const std::exception& e) {
LOG("Caught exception: '%s'", e.what()); LOG("Caught exception: '%s'", e.what());
} catch (...) {
LOG("Caught unknown exception.");
} }
#ifndef INCLUDE_BACKTRACE #ifndef INCLUDE_BACKTRACE

View File

@ -8,23 +8,23 @@
#include <map> #include <map>
template <typename T> template <typename T>
inline size_t MinSize(size_t size, const std::basic_string_view<T>& string) { static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) {
if (size == size_t(-1) || size > string.size()) { if (size == SIZE_MAX || size > string.size()) {
return string.size(); return string.size();
} else { } else {
return size; return size;
} }
} }
inline bool IsLeadSurrogate(char16_t c) { inline bool IsLeadSurrogate(const char16_t c) {
return (0xD800 <= c) && (c <= 0xDBFF); return (0xD800 <= c) && (c <= 0xDBFF);
} }
inline bool IsTrailSurrogate(char16_t c) { inline bool IsTrailSurrogate(const char16_t c) {
return (0xDC00 <= c) && (c <= 0xDFFF); return (0xDC00 <= c) && (c <= 0xDFFF);
} }
inline void PushUTF8CodePoint(std::string& ret, char32_t cp) { inline void PushUTF8CodePoint(std::string& ret, const char32_t cp) {
if (cp <= 0x007F) { if (cp <= 0x007F) {
ret.push_back(static_cast<uint8_t>(cp)); ret.push_back(static_cast<uint8_t>(cp));
} else if (cp <= 0x07FF) { } else if (cp <= 0x07FF) {
@ -46,16 +46,16 @@ inline void PushUTF8CodePoint(std::string& ret, char32_t cp) {
constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD; constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD;
bool _IsSuffixChar(uint8_t c) { bool static _IsSuffixChar(const uint8_t c) {
return (c & 0xC0) == 0x80; return (c & 0xC0) == 0x80;
} }
bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) { bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
size_t rem = slice.length(); const size_t rem = slice.length();
if (slice.empty()) return false; if (slice.empty()) return false;
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front()); const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front());
if (rem > 0) { if (rem > 0) {
uint8_t first = bytes[0]; const uint8_t first = bytes[0];
if (first < 0x80) { // 1 byte character if (first < 0x80) { // 1 byte character
out = static_cast<uint32_t>(first & 0x7F); out = static_cast<uint32_t>(first & 0x7F);
slice.remove_prefix(1); slice.remove_prefix(1);
@ -64,7 +64,7 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
// middle byte, not valid at start, fall through // middle byte, not valid at start, fall through
} else if (first < 0xE0) { // two byte character } else if (first < 0xE0) { // two byte character
if (rem > 1) { if (rem > 1) {
uint8_t second = bytes[1]; const uint8_t second = bytes[1];
if (_IsSuffixChar(second)) { if (_IsSuffixChar(second)) {
out = (static_cast<uint32_t>(first & 0x1F) << 6) out = (static_cast<uint32_t>(first & 0x1F) << 6)
+ static_cast<uint32_t>(second & 0x3F); + static_cast<uint32_t>(second & 0x3F);
@ -74,8 +74,8 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
} }
} else if (first < 0xF0) { // three byte character } else if (first < 0xF0) { // three byte character
if (rem > 2) { if (rem > 2) {
uint8_t second = bytes[1]; const uint8_t second = bytes[1];
uint8_t third = bytes[2]; const uint8_t third = bytes[2];
if (_IsSuffixChar(second) && _IsSuffixChar(third)) { if (_IsSuffixChar(second) && _IsSuffixChar(third)) {
out = (static_cast<uint32_t>(first & 0x0F) << 12) out = (static_cast<uint32_t>(first & 0x0F) << 12)
+ (static_cast<uint32_t>(second & 0x3F) << 6) + (static_cast<uint32_t>(second & 0x3F) << 6)
@ -86,9 +86,9 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
} }
} else if (first < 0xF8) { // four byte character } else if (first < 0xF8) { // four byte character
if (rem > 3) { if (rem > 3) {
uint8_t second = bytes[1]; const uint8_t second = bytes[1];
uint8_t third = bytes[2]; const uint8_t third = bytes[2];
uint8_t fourth = bytes[3]; const uint8_t fourth = bytes[3];
if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) { if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) {
out = (static_cast<uint32_t>(first & 0x07) << 18) out = (static_cast<uint32_t>(first & 0x07) << 18)
+ (static_cast<uint32_t>(second & 0x3F) << 12) + (static_cast<uint32_t>(second & 0x3F) << 12)
@ -107,7 +107,7 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
} }
/// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1> /// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1>
bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) { bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t size) {
if (output.length() >= size) return false; if (output.length() >= size) return false;
if (U < 0x10000) { if (U < 0x10000) {
// If U < 0x10000, encode U as a 16-bit unsigned integer and terminate. // If U < 0x10000, encode U as a 16-bit unsigned integer and terminate.
@ -120,7 +120,7 @@ bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
// Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, // Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
// U' must be less than or equal to 0xFFFFF. That is, U' can be // U' must be less than or equal to 0xFFFFF. That is, U' can be
// represented in 20 bits. // represented in 20 bits.
uint32_t Ut = U - 0x10000; const uint32_t Ut = U - 0x10000;
// Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and // Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
// 0xDC00, respectively. These integers each have 10 bits free to // 0xDC00, respectively. These integers each have 10 bits free to
@ -141,25 +141,25 @@ bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
} else return false; } else return false;
} }
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view& string, size_t size) { std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view string, const size_t size) {
size_t newSize = MinSize(size, string); const size_t newSize = MinSize(size, string);
std::u16string output; std::u16string output;
output.reserve(newSize); output.reserve(newSize);
std::string_view iterator = string; std::string_view iterator = string;
uint32_t c; uint32_t c;
while (_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {} while (details::_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
return output; return output;
} }
//! Converts an std::string (ASCII) to UCS-2 / UTF-16 //! Converts an std::string (ASCII) to UCS-2 / UTF-16
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t size) { std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const size_t size) {
size_t newSize = MinSize(size, string); const size_t newSize = MinSize(size, string);
std::u16string ret; std::u16string ret;
ret.reserve(newSize); ret.reserve(newSize);
for (size_t i = 0; i < newSize; i++) { for (size_t i = 0; i < newSize; ++i) {
char c = string[i]; const char c = string[i];
// Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t // Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t
ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER); ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER);
} }
@ -169,18 +169,18 @@ std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t
//! Converts a (potentially-ill-formed) UTF-16 string to UTF-8 //! Converts a (potentially-ill-formed) UTF-16 string to UTF-8
//! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16> //! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16>
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view& string, size_t size) { std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const size_t size) {
size_t newSize = MinSize(size, string); const size_t newSize = MinSize(size, string);
std::string ret; std::string ret;
ret.reserve(newSize); ret.reserve(newSize);
for (size_t i = 0; i < newSize; i++) { for (size_t i = 0; i < newSize; ++i) {
char16_t u = string[i]; const char16_t u = string[i];
if (IsLeadSurrogate(u) && (i + 1) < newSize) { if (IsLeadSurrogate(u) && (i + 1) < newSize) {
char16_t next = string[i + 1]; const char16_t next = string[i + 1];
if (IsTrailSurrogate(next)) { if (IsTrailSurrogate(next)) {
i += 1; i += 1;
char32_t cp = 0x10000 const char32_t cp = 0x10000
+ ((static_cast<char32_t>(u) - 0xD800) << 10) + ((static_cast<char32_t>(u) - 0xD800) << 10)
+ (static_cast<char32_t>(next) - 0xDC00); + (static_cast<char32_t>(next) - 0xDC00);
PushUTF8CodePoint(ret, cp); PushUTF8CodePoint(ret, cp);
@ -195,40 +195,40 @@ std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view& string, size_t
return ret; return ret;
} }
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string& a, const std::string& b) { bool GeneralUtils::CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b) {
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); }); return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); });
} }
// MARK: Bits // MARK: Bits
//! Sets a specific bit in a signed 64-bit integer //! Sets a specific bit in a signed 64-bit integer
int64_t GeneralUtils::SetBit(int64_t value, uint32_t index) { int64_t GeneralUtils::SetBit(int64_t value, const uint32_t index) {
return value |= 1ULL << index; return value |= 1ULL << index;
} }
//! Clears a specific bit in a signed 64-bit integer //! Clears a specific bit in a signed 64-bit integer
int64_t GeneralUtils::ClearBit(int64_t value, uint32_t index) { int64_t GeneralUtils::ClearBit(int64_t value, const uint32_t index) {
return value &= ~(1ULL << index); return value &= ~(1ULL << index);
} }
//! Checks a specific bit in a signed 64-bit integer //! Checks a specific bit in a signed 64-bit integer
bool GeneralUtils::CheckBit(int64_t value, uint32_t index) { bool GeneralUtils::CheckBit(int64_t value, const uint32_t index) {
return value & (1ULL << index); return value & (1ULL << index);
} }
bool GeneralUtils::ReplaceInString(std::string& str, const std::string& from, const std::string& to) { bool GeneralUtils::ReplaceInString(std::string& str, const std::string_view from, const std::string_view to) {
size_t start_pos = str.find(from); const size_t start_pos = str.find(from);
if (start_pos == std::string::npos) if (start_pos == std::string::npos)
return false; return false;
str.replace(start_pos, from.length(), to); str.replace(start_pos, from.length(), to);
return true; return true;
} }
std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t delimiter) { std::vector<std::wstring> GeneralUtils::SplitString(const std::wstring_view str, const wchar_t delimiter) {
std::vector<std::wstring> vector = std::vector<std::wstring>(); std::vector<std::wstring> vector = std::vector<std::wstring>();
std::wstring current; std::wstring current;
for (const auto& c : str) { for (const wchar_t c : str) {
if (c == delimiter) { if (c == delimiter) {
vector.push_back(current); vector.push_back(current);
current = L""; current = L"";
@ -237,15 +237,15 @@ std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t d
} }
} }
vector.push_back(current); vector.push_back(std::move(current));
return vector; return vector;
} }
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string& str, char16_t delimiter) { std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string_view str, const char16_t delimiter) {
std::vector<std::u16string> vector = std::vector<std::u16string>(); std::vector<std::u16string> vector = std::vector<std::u16string>();
std::u16string current; std::u16string current;
for (const auto& c : str) { for (const char16_t c : str) {
if (c == delimiter) { if (c == delimiter) {
vector.push_back(current); vector.push_back(current);
current = u""; current = u"";
@ -254,17 +254,15 @@ std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string& str,
} }
} }
vector.push_back(current); vector.push_back(std::move(current));
return vector; return vector;
} }
std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char delimiter) { std::vector<std::string> GeneralUtils::SplitString(const std::string_view str, const char delimiter) {
std::vector<std::string> vector = std::vector<std::string>(); std::vector<std::string> vector = std::vector<std::string>();
std::string current = ""; std::string current = "";
for (size_t i = 0; i < str.length(); i++) { for (const char c : str) {
char c = str[i];
if (c == delimiter) { if (c == delimiter) {
vector.push_back(current); vector.push_back(current);
current = ""; current = "";
@ -273,8 +271,7 @@ std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char
} }
} }
vector.push_back(current); vector.push_back(std::move(current));
return vector; return vector;
} }
@ -283,7 +280,7 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
inStream.Read<uint32_t>(length); inStream.Read<uint32_t>(length);
std::u16string string; std::u16string string;
for (auto i = 0; i < length; i++) { for (uint32_t i = 0; i < length; ++i) {
uint16_t c; uint16_t c;
inStream.Read(c); inStream.Read(c);
string.push_back(c); string.push_back(c);
@ -292,35 +289,35 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
return string; return string;
} }
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string& folder) { std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string_view folder) {
// Because we dont know how large the initial number before the first _ is we need to make it a map like so. // Because we dont know how large the initial number before the first _ is we need to make it a map like so.
std::map<uint32_t, std::string> filenames{}; std::map<uint32_t, std::string> filenames{};
for (auto& t : std::filesystem::directory_iterator(folder)) { for (const auto& t : std::filesystem::directory_iterator(folder)) {
auto filename = t.path().filename().string(); auto filename = t.path().filename().string();
auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0)); const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
filenames.insert(std::make_pair(index, filename)); filenames.emplace(index, std::move(filename));
} }
// Now sort the map by the oldest migration. // Now sort the map by the oldest migration.
std::vector<std::string> sortedFiles{}; std::vector<std::string> sortedFiles{};
auto fileIterator = filenames.begin(); auto fileIterator = filenames.cbegin();
std::map<uint32_t, std::string>::iterator oldest = filenames.begin(); auto oldest = filenames.cbegin();
while (!filenames.empty()) { while (!filenames.empty()) {
if (fileIterator == filenames.end()) { if (fileIterator == filenames.cend()) {
sortedFiles.push_back(oldest->second); sortedFiles.push_back(oldest->second);
filenames.erase(oldest); filenames.erase(oldest);
fileIterator = filenames.begin(); fileIterator = filenames.cbegin();
oldest = filenames.begin(); oldest = filenames.cbegin();
continue; continue;
} }
if (oldest->first > fileIterator->first) oldest = fileIterator; if (oldest->first > fileIterator->first) oldest = fileIterator;
fileIterator++; ++fileIterator;
} }
return sortedFiles; return sortedFiles;
} }
#ifdef DARKFLAME_PLATFORM_MACOS #if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
// MacOS floating-point parse function specializations // MacOS floating-point parse function specializations
namespace GeneralUtils::details { namespace GeneralUtils::details {

View File

@ -3,17 +3,18 @@
// C++ // C++
#include <charconv> #include <charconv>
#include <cstdint> #include <cstdint>
#include <random>
#include <ctime> #include <ctime>
#include <functional>
#include <optional>
#include <random>
#include <span>
#include <stdexcept>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <optional>
#include <functional>
#include <type_traits> #include <type_traits>
#include <stdexcept>
#include "BitStream.h" #include "BitStream.h"
#include "NiPoint3.h" #include "NiPoint3.h"
#include "dPlatforms.h" #include "dPlatforms.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "Logger.h"
@ -32,29 +33,31 @@ namespace GeneralUtils {
//! Converts a plain ASCII string to a UTF-16 string //! Converts a plain ASCII string to a UTF-16 string
/*! /*!
\param string The string to convert \param string The string to convert
\param size A size to trim the string to. Default is -1 (No trimming) \param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\return An UTF-16 representation of the string \return An UTF-16 representation of the string
*/ */
std::u16string ASCIIToUTF16(const std::string_view& string, size_t size = -1); std::u16string ASCIIToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
//! Converts a UTF-8 String to a UTF-16 string //! Converts a UTF-8 String to a UTF-16 string
/*! /*!
\param string The string to convert \param string The string to convert
\param size A size to trim the string to. Default is -1 (No trimming) \param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\return An UTF-16 representation of the string \return An UTF-16 representation of the string
*/ */
std::u16string UTF8ToUTF16(const std::string_view& string, size_t size = -1); std::u16string UTF8ToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
namespace details {
//! Internal, do not use //! Internal, do not use
bool _NextUTF8Char(std::string_view& slice, uint32_t& out); bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
}
//! Converts a UTF-16 string to a UTF-8 string //! Converts a UTF-16 string to a UTF-8 string
/*! /*!
\param string The string to convert \param string The string to convert
\param size A size to trim the string to. Default is -1 (No trimming) \param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\return An UTF-8 representation of the string \return An UTF-8 representation of the string
*/ */
std::string UTF16ToWTF8(const std::u16string_view& string, size_t size = -1); std::string UTF16ToWTF8(const std::u16string_view string, const size_t size = SIZE_MAX);
/** /**
* Compares two basic strings but does so ignoring case sensitivity * Compares two basic strings but does so ignoring case sensitivity
@ -62,7 +65,7 @@ namespace GeneralUtils {
* \param b the second string to compare against the first string * \param b the second string to compare against the first string
* @return if the two strings are equal * @return if the two strings are equal
*/ */
bool CaseInsensitiveStringCompare(const std::string& a, const std::string& b); bool CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b);
// MARK: Bits // MARK: Bits
@ -70,9 +73,9 @@ namespace GeneralUtils {
//! Sets a bit on a numerical value //! Sets a bit on a numerical value
template <typename T> template <typename T>
inline void SetBit(T& value, eObjectBits bits) { inline void SetBit(T& value, const eObjectBits bits) {
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type"); static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
auto index = static_cast<size_t>(bits); const auto index = static_cast<size_t>(bits);
if (index > (sizeof(T) * 8) - 1) { if (index > (sizeof(T) * 8) - 1) {
return; return;
} }
@ -82,9 +85,9 @@ namespace GeneralUtils {
//! Clears a bit on a numerical value //! Clears a bit on a numerical value
template <typename T> template <typename T>
inline void ClearBit(T& value, eObjectBits bits) { inline void ClearBit(T& value, const eObjectBits bits) {
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type"); static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
auto index = static_cast<size_t>(bits); const auto index = static_cast<size_t>(bits);
if (index > (sizeof(T) * 8 - 1)) { if (index > (sizeof(T) * 8 - 1)) {
return; return;
} }
@ -97,14 +100,14 @@ namespace GeneralUtils {
\param value The value to set the bit for \param value The value to set the bit for
\param index The index of the bit \param index The index of the bit
*/ */
int64_t SetBit(int64_t value, uint32_t index); int64_t SetBit(int64_t value, const uint32_t index);
//! Clears a specific bit in a signed 64-bit integer //! Clears a specific bit in a signed 64-bit integer
/*! /*!
\param value The value to clear the bit from \param value The value to clear the bit from
\param index The index of the bit \param index The index of the bit
*/ */
int64_t ClearBit(int64_t value, uint32_t index); int64_t ClearBit(int64_t value, const uint32_t index);
//! Checks a specific bit in a signed 64-bit integer //! Checks a specific bit in a signed 64-bit integer
/*! /*!
@ -112,19 +115,19 @@ namespace GeneralUtils {
\param index The index of the bit \param index The index of the bit
\return Whether or not the bit is set \return Whether or not the bit is set
*/ */
bool CheckBit(int64_t value, uint32_t index); bool CheckBit(int64_t value, const uint32_t index);
bool ReplaceInString(std::string& str, const std::string& from, const std::string& to); bool ReplaceInString(std::string& str, const std::string_view from, const std::string_view to);
std::u16string ReadWString(RakNet::BitStream& inStream); std::u16string ReadWString(RakNet::BitStream& inStream);
std::vector<std::wstring> SplitString(std::wstring& str, wchar_t delimiter); std::vector<std::wstring> SplitString(const std::wstring_view str, const wchar_t delimiter);
std::vector<std::u16string> SplitString(const std::u16string& str, char16_t delimiter); std::vector<std::u16string> SplitString(const std::u16string_view str, const char16_t delimiter);
std::vector<std::string> SplitString(const std::string& str, char delimiter); std::vector<std::string> SplitString(const std::string_view str, const char delimiter);
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder); std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
// Concept constraining to enum types // Concept constraining to enum types
template <typename T> template <typename T>
@ -144,7 +147,7 @@ namespace GeneralUtils {
// If a boolean, present an alias to an intermediate integral type for parsing // If a boolean, present an alias to an intermediate integral type for parsing
template <Numeric T> requires std::same_as<T, bool> template <Numeric T> requires std::same_as<T, bool>
struct numeric_parse<T> { using type = uint32_t; }; struct numeric_parse<T> { using type = uint8_t; };
// Shorthand type alias // Shorthand type alias
template <Numeric T> template <Numeric T>
@ -156,8 +159,9 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired value if it is equivalent to the string * @returns An std::optional containing the desired value if it is equivalent to the string
*/ */
template <Numeric T> template <Numeric T>
[[nodiscard]] std::optional<T> TryParse(const std::string_view str) { [[nodiscard]] std::optional<T> TryParse(std::string_view str) {
numeric_parse_t<T> result; numeric_parse_t<T> result;
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
const char* const strEnd = str.data() + str.size(); const char* const strEnd = str.data() + str.size();
const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result); const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result);
@ -181,8 +185,10 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired value if it is equivalent to the string * @returns An std::optional containing the desired value if it is equivalent to the string
*/ */
template <std::floating_point T> template <std::floating_point T>
[[nodiscard]] std::optional<T> TryParse(const std::string_view str) noexcept [[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
try { try {
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
size_t parseNum; size_t parseNum;
const T result = details::_parse<T>(str, parseNum); const T result = details::_parse<T>(str, parseNum);
const bool isParsed = str.length() == parseNum; const bool isParsed = str.length() == parseNum;
@ -202,7 +208,7 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters * @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/ */
template <typename T> template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string& strX, const std::string& strY, const std::string& strZ) { [[nodiscard]] std::optional<NiPoint3> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
const auto x = TryParse<float>(strX); const auto x = TryParse<float>(strX);
if (!x) return std::nullopt; if (!x) return std::nullopt;
@ -214,17 +220,17 @@ namespace GeneralUtils {
} }
/** /**
* The TryParse overload for handling NiPoint3 by passingn a reference to a vector of three strings * The TryParse overload for handling NiPoint3 by passing a span of three strings
* @param str The string vector representing the X, Y, and Xcoordinates * @param str The string vector representing the X, Y, and Z coordinates
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters * @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/ */
template <typename T> template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::vector<std::string>& str) { [[nodiscard]] std::optional<NiPoint3> TryParse(const std::span<const std::string> str) {
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt; return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
} }
template <typename T> template <typename T>
std::u16string to_u16string(T value) { std::u16string to_u16string(const T value) {
return GeneralUtils::ASCIIToUTF16(std::to_string(value)); return GeneralUtils::ASCIIToUTF16(std::to_string(value));
} }
@ -243,7 +249,7 @@ namespace GeneralUtils {
\param max The maximum to generate to \param max The maximum to generate to
*/ */
template <typename T> template <typename T>
inline T GenerateRandomNumber(std::size_t min, std::size_t max) { inline T GenerateRandomNumber(const std::size_t min, const std::size_t max) {
// Make sure it is a numeric type // Make sure it is a numeric type
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type"); static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
@ -270,10 +276,10 @@ namespace GeneralUtils {
// on Windows we need to undef these or else they conflict with our numeric limits calls // on Windows we need to undef these or else they conflict with our numeric limits calls
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS // DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
#ifdef _WIN32 #ifdef _WIN32
#undef min #undef min
#undef max #undef max
#endif #endif
template <typename T> template <typename T>
inline T GenerateRandomNumber() { inline T GenerateRandomNumber() {

View File

@ -1,31 +0,0 @@
#ifndef __ECHATINTERNALMESSAGETYPE__H__
#define __ECHATINTERNALMESSAGETYPE__H__
#include <cstdint>
enum eChatInternalMessageType : uint32_t {
PLAYER_ADDED_NOTIFICATION = 0,
PLAYER_REMOVED_NOTIFICATION,
ADD_FRIEND,
ADD_BEST_FRIEND,
ADD_TO_TEAM,
ADD_BLOCK,
REMOVE_FRIEND,
REMOVE_BLOCK,
REMOVE_FROM_TEAM,
DELETE_TEAM,
REPORT,
PRIVATE_CHAT,
PRIVATE_CHAT_RESPONSE,
ANNOUNCEMENT,
MAIL_COUNT_UPDATE,
MAIL_SEND_NOTIFY,
REQUEST_USER_LIST,
FRIEND_LIST,
ROUTE_TO_PLAYER,
TEAM_UPDATE,
MUTE_UPDATE,
CREATE_TEAM,
};
#endif //!__ECHATINTERNALMESSAGETYPE__H__

View File

@ -72,7 +72,9 @@ enum class eChatMessageType :uint32_t {
UPDATE_DONATION, UPDATE_DONATION,
PRG_CSR_COMMAND, PRG_CSR_COMMAND,
HEARTBEAT_REQUEST_FROM_WORLD, HEARTBEAT_REQUEST_FROM_WORLD,
UPDATE_FREE_TRIAL_STATUS UPDATE_FREE_TRIAL_STATUS,
// CUSTOM DLU MESSAGE ID FOR INTERNAL USE
CREATE_TEAM,
}; };
#endif //!__ECHATMESSAGETYPE__H__ #endif //!__ECHATMESSAGETYPE__H__

View File

@ -5,8 +5,7 @@ enum class eConnectionType : uint16_t {
SERVER = 0, SERVER = 0,
AUTH, AUTH,
CHAT, CHAT,
CHAT_INTERNAL, WORLD = 4,
WORLD,
CLIENT, CLIENT,
MASTER MASTER
}; };

View File

@ -790,9 +790,10 @@ enum class eGameMessageType : uint16_t {
GET_MISSION_TYPE_STATES = 853, GET_MISSION_TYPE_STATES = 853,
GET_TIME_PLAYED = 854, GET_TIME_PLAYED = 854,
SET_MISSION_VIEWED = 855, SET_MISSION_VIEWED = 855,
SLASH_COMMAND_TEXT_FEEDBACK = 856, HKX_VEHICLE_LOADED = 856,
HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 857, SLASH_COMMAND_TEXT_FEEDBACK = 857,
BROADCAST_TEXT_TO_CHATBOX = 858, BROADCAST_TEXT_TO_CHATBOX = 858,
HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 859,
OPEN_PROPERTY_MANAGEMENT = 860, OPEN_PROPERTY_MANAGEMENT = 860,
OPEN_PROPERTY_VENDOR = 861, OPEN_PROPERTY_VENDOR = 861,
VOTE_ON_PROPERTY = 862, VOTE_ON_PROPERTY = 862,

View File

@ -29,8 +29,8 @@ enum class eWorldMessageType : uint32_t {
ROUTE_PACKET, // Social? ROUTE_PACKET, // Social?
POSITION_UPDATE, POSITION_UPDATE,
MAIL, MAIL,
WORD_CHECK, // Whitelist word check WORD_CHECK, // AllowList word check
STRING_CHECK, // Whitelist string check STRING_CHECK, // AllowList string check
GET_PLAYERS_IN_ZONE, GET_PLAYERS_IN_ZONE,
REQUEST_UGC_MANIFEST_INFO, REQUEST_UGC_MANIFEST_INFO,
BLUEPRINT_GET_ALL_DATA_REQUEST, BLUEPRINT_GET_ALL_DATA_REQUEST,

View File

@ -27,12 +27,9 @@ Character::Character(uint32_t id, User* parentUser) {
m_ID = id; m_ID = id;
m_ParentUser = parentUser; m_ParentUser = parentUser;
m_OurEntity = nullptr; m_OurEntity = nullptr;
m_Doc = nullptr;
} }
Character::~Character() { Character::~Character() {
if (m_Doc) delete m_Doc;
m_Doc = nullptr;
m_OurEntity = nullptr; m_OurEntity = nullptr;
m_ParentUser = nullptr; m_ParentUser = nullptr;
} }
@ -55,8 +52,6 @@ void Character::UpdateInfoFromDatabase() {
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused. m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
m_ZoneCloneID = 0; m_ZoneCloneID = 0;
m_Doc = nullptr;
//Quickly and dirtly parse the xmlData to get the info we need: //Quickly and dirtly parse the xmlData to get the info we need:
DoQuickXMLDataParse(); DoQuickXMLDataParse();
@ -70,18 +65,13 @@ void Character::UpdateInfoFromDatabase() {
} }
void Character::UpdateFromDatabase() { void Character::UpdateFromDatabase() {
if (m_Doc) delete m_Doc;
UpdateInfoFromDatabase(); UpdateInfoFromDatabase();
} }
void Character::DoQuickXMLDataParse() { void Character::DoQuickXMLDataParse() {
if (m_XMLData.size() == 0) return; if (m_XMLData.size() == 0) return;
delete m_Doc; if (m_Doc.Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
m_Doc = new tinyxml2::XMLDocument();
if (!m_Doc) return;
if (m_Doc->Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
LOG("Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID); LOG("Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID);
} else { } else {
LOG("Failed to load xmlData!"); LOG("Failed to load xmlData!");
@ -89,7 +79,7 @@ void Character::DoQuickXMLDataParse() {
return; return;
} }
tinyxml2::XMLElement* mf = m_Doc->FirstChildElement("obj")->FirstChildElement("mf"); tinyxml2::XMLElement* mf = m_Doc.FirstChildElement("obj")->FirstChildElement("mf");
if (!mf) { if (!mf) {
LOG("Failed to find mf tag!"); LOG("Failed to find mf tag!");
return; return;
@ -108,7 +98,7 @@ void Character::DoQuickXMLDataParse() {
mf->QueryAttribute("ess", &m_Eyes); mf->QueryAttribute("ess", &m_Eyes);
mf->QueryAttribute("ms", &m_Mouth); mf->QueryAttribute("ms", &m_Mouth);
tinyxml2::XMLElement* inv = m_Doc->FirstChildElement("obj")->FirstChildElement("inv"); tinyxml2::XMLElement* inv = m_Doc.FirstChildElement("obj")->FirstChildElement("inv");
if (!inv) { if (!inv) {
LOG("Char has no inv!"); LOG("Char has no inv!");
return; return;
@ -141,7 +131,7 @@ void Character::DoQuickXMLDataParse() {
} }
tinyxml2::XMLElement* character = m_Doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* character = m_Doc.FirstChildElement("obj")->FirstChildElement("char");
if (character) { if (character) {
character->QueryAttribute("cc", &m_Coins); character->QueryAttribute("cc", &m_Coins);
int32_t gm_level = 0; int32_t gm_level = 0;
@ -205,7 +195,7 @@ void Character::DoQuickXMLDataParse() {
character->QueryAttribute("lzrw", &m_OriginalRotation.w); character->QueryAttribute("lzrw", &m_OriginalRotation.w);
} }
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag"); auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
if (flags) { if (flags) {
auto* currentChild = flags->FirstChildElement(); auto* currentChild = flags->FirstChildElement();
while (currentChild) { while (currentChild) {
@ -239,12 +229,10 @@ void Character::SetBuildMode(bool buildMode) {
} }
void Character::SaveXMLToDatabase() { void Character::SaveXMLToDatabase() {
if (!m_Doc) return;
//For metrics, we'll record the time it took to save: //For metrics, we'll record the time it took to save:
auto start = std::chrono::system_clock::now(); auto start = std::chrono::system_clock::now();
tinyxml2::XMLElement* character = m_Doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* character = m_Doc.FirstChildElement("obj")->FirstChildElement("char");
if (character) { if (character) {
character->SetAttribute("gm", static_cast<uint32_t>(m_GMLevel)); character->SetAttribute("gm", static_cast<uint32_t>(m_GMLevel));
character->SetAttribute("cc", m_Coins); character->SetAttribute("cc", m_Coins);
@ -266,11 +254,11 @@ void Character::SaveXMLToDatabase() {
} }
auto emotes = character->FirstChildElement("ue"); auto emotes = character->FirstChildElement("ue");
if (!emotes) emotes = m_Doc->NewElement("ue"); if (!emotes) emotes = m_Doc.NewElement("ue");
emotes->DeleteChildren(); emotes->DeleteChildren();
for (int emoteID : m_UnlockedEmotes) { for (int emoteID : m_UnlockedEmotes) {
auto emote = m_Doc->NewElement("e"); auto emote = m_Doc.NewElement("e");
emote->SetAttribute("id", emoteID); emote->SetAttribute("id", emoteID);
emotes->LinkEndChild(emote); emotes->LinkEndChild(emote);
@ -280,15 +268,15 @@ void Character::SaveXMLToDatabase() {
} }
//Export our flags: //Export our flags:
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag"); auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
if (!flags) { if (!flags) {
flags = m_Doc->NewElement("flag"); //Create a flags tag if we don't have one flags = m_Doc.NewElement("flag"); //Create a flags tag if we don't have one
m_Doc->FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time m_Doc.FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time
} }
flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes
for (std::pair<uint32_t, uint64_t> flag : m_PlayerFlags) { for (std::pair<uint32_t, uint64_t> flag : m_PlayerFlags) {
auto* f = m_Doc->NewElement("f"); auto* f = m_Doc.NewElement("f");
f->SetAttribute("id", flag.first); f->SetAttribute("id", flag.first);
//Because of the joy that is tinyxml2, it doesn't offer a function to set a uint64 as an attribute. //Because of the joy that is tinyxml2, it doesn't offer a function to set a uint64 as an attribute.
@ -301,7 +289,7 @@ void Character::SaveXMLToDatabase() {
// Prevents the news feed from showing up on world transfers // Prevents the news feed from showing up on world transfers
if (GetPlayerFlag(ePlayerFlag::IS_NEWS_SCREEN_VISIBLE)) { if (GetPlayerFlag(ePlayerFlag::IS_NEWS_SCREEN_VISIBLE)) {
auto* s = m_Doc->NewElement("s"); auto* s = m_Doc.NewElement("s");
s->SetAttribute("si", ePlayerFlag::IS_NEWS_SCREEN_VISIBLE); s->SetAttribute("si", ePlayerFlag::IS_NEWS_SCREEN_VISIBLE);
flags->LinkEndChild(s); flags->LinkEndChild(s);
} }
@ -326,7 +314,7 @@ void Character::SaveXMLToDatabase() {
void Character::SetIsNewLogin() { void Character::SetIsNewLogin() {
// If we dont have a flag element, then we cannot have a s element as a child of flag. // If we dont have a flag element, then we cannot have a s element as a child of flag.
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag"); auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
if (!flags) return; if (!flags) return;
auto* currentChild = flags->FirstChildElement(); auto* currentChild = flags->FirstChildElement();
@ -344,7 +332,7 @@ void Character::SetIsNewLogin() {
void Character::WriteToDatabase() { void Character::WriteToDatabase() {
//Dump our xml into m_XMLData: //Dump our xml into m_XMLData:
tinyxml2::XMLPrinter printer(0, true, 0); tinyxml2::XMLPrinter printer(0, true, 0);
m_Doc->Print(&printer); m_Doc.Print(&printer);
//Finally, save to db: //Finally, save to db:
Database::Get()->UpdateCharacterXml(m_ID, printer.CStr()); Database::Get()->UpdateCharacterXml(m_ID, printer.CStr());
@ -421,15 +409,15 @@ void Character::SetRetroactiveFlags() {
void Character::SaveXmlRespawnCheckpoints() { void Character::SaveXmlRespawnCheckpoints() {
//Export our respawn points: //Export our respawn points:
auto* points = m_Doc->FirstChildElement("obj")->FirstChildElement("res"); auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res");
if (!points) { if (!points) {
points = m_Doc->NewElement("res"); points = m_Doc.NewElement("res");
m_Doc->FirstChildElement("obj")->LinkEndChild(points); m_Doc.FirstChildElement("obj")->LinkEndChild(points);
} }
points->DeleteChildren(); points->DeleteChildren();
for (const auto& point : m_WorldRespawnCheckpoints) { for (const auto& point : m_WorldRespawnCheckpoints) {
auto* r = m_Doc->NewElement("r"); auto* r = m_Doc.NewElement("r");
r->SetAttribute("w", point.first); r->SetAttribute("w", point.first);
r->SetAttribute("x", point.second.x); r->SetAttribute("x", point.second.x);
@ -443,7 +431,7 @@ void Character::SaveXmlRespawnCheckpoints() {
void Character::LoadXmlRespawnCheckpoints() { void Character::LoadXmlRespawnCheckpoints() {
m_WorldRespawnCheckpoints.clear(); m_WorldRespawnCheckpoints.clear();
auto* points = m_Doc->FirstChildElement("obj")->FirstChildElement("res"); auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res");
if (!points) { if (!points) {
return; return;
} }

View File

@ -37,7 +37,7 @@ public:
void LoadXmlRespawnCheckpoints(); void LoadXmlRespawnCheckpoints();
const std::string& GetXMLData() const { return m_XMLData; } const std::string& GetXMLData() const { return m_XMLData; }
tinyxml2::XMLDocument* GetXMLDoc() const { return m_Doc; } const tinyxml2::XMLDocument& GetXMLDoc() const { return m_Doc; }
/** /**
* Out of abundance of safety and clarity of what this saves, this is its own function. * Out of abundance of safety and clarity of what this saves, this is its own function.
@ -623,7 +623,7 @@ private:
/** /**
* The character XML belonging to this character * The character XML belonging to this character
*/ */
tinyxml2::XMLDocument* m_Doc; tinyxml2::XMLDocument m_Doc;
/** /**
* Title of an announcement this character made (reserved for GMs) * Title of an announcement this character made (reserved for GMs)

View File

@ -476,8 +476,7 @@ void Entity::Initialize() {
} }
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) {
auto* xmlDoc = m_Character ? m_Character->GetXMLDoc() : nullptr; AddComponent<InventoryComponent>();
AddComponent<InventoryComponent>(xmlDoc);
} }
// if this component exists, then we initialize it. it's value is always 0 // if this component exists, then we initialize it. it's value is always 0
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) {
@ -1244,7 +1243,7 @@ void Entity::WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType
outBitStream.Write0(); outBitStream.Write0();
} }
void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) { void Entity::UpdateXMLDoc(tinyxml2::XMLDocument& doc) {
//This function should only ever be called from within Character, meaning doc should always exist when this is called. //This function should only ever be called from within Character, meaning doc should always exist when this is called.
//Naturally, we don't include any non-player components in this update function. //Naturally, we don't include any non-player components in this update function.
@ -1635,10 +1634,8 @@ void Entity::PickupItem(const LWOOBJID& objectID) {
CDObjectSkillsTable* skillsTable = CDClientManager::GetTable<CDObjectSkillsTable>(); CDObjectSkillsTable* skillsTable = CDClientManager::GetTable<CDObjectSkillsTable>();
std::vector<CDObjectSkills> skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); }); std::vector<CDObjectSkills> skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); });
for (CDObjectSkills skill : skills) { for (CDObjectSkills skill : skills) {
CDSkillBehaviorTable* skillBehTable = CDClientManager::GetTable<CDSkillBehaviorTable>();
auto* skillComponent = GetComponent<SkillComponent>(); auto* skillComponent = GetComponent<SkillComponent>();
if (skillComponent) skillComponent->CastSkill(skill.skillID, GetObjectID(), GetObjectID()); if (skillComponent) skillComponent->CastSkill(skill.skillID, GetObjectID(), GetObjectID(), skill.castOnType, NiQuaternion(0, 0, 0, 0));
auto* missionComponent = GetComponent<MissionComponent>(); auto* missionComponent = GetComponent<MissionComponent>();

View File

@ -174,7 +174,7 @@ public:
void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType); void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType); void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
void UpdateXMLDoc(tinyxml2::XMLDocument* doc); void UpdateXMLDoc(tinyxml2::XMLDocument& doc);
void Update(float deltaTime); void Update(float deltaTime);
// Events // Events

View File

@ -26,7 +26,7 @@
#include "eCharacterCreationResponse.h" #include "eCharacterCreationResponse.h"
#include "eRenameResponse.h" #include "eRenameResponse.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatInternalMessageType.h" #include "eChatMessageType.h"
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "CheatDetection.h" #include "CheatDetection.h"
@ -83,7 +83,7 @@ void UserManager::Initialize() {
auto chatListStream = Game::assetManager->GetFile("chatplus_en_us.txt"); auto chatListStream = Game::assetManager->GetFile("chatplus_en_us.txt");
if (!chatListStream) { if (!chatListStream) {
LOG("Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str()); LOG("Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str());
throw std::runtime_error("Aborting initialization due to missing chat whitelist file."); throw std::runtime_error("Aborting initialization due to missing chat allowlist file.");
} }
while (std::getline(chatListStream, line, '\n')) { while (std::getline(chatListStream, line, '\n')) {
@ -422,7 +422,7 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
Database::Get()->DeleteCharacter(charID); Database::Get()->DeleteCharacter(charID);
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::UNEXPECTED_DISCONNECT);
bitStream.Write(objectID); bitStream.Write(objectID);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
@ -536,13 +536,13 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) { uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
try { try {
auto stmt = CDClientDatabase::CreatePreppedStmt( auto stmt = CDClientDatabase::CreatePreppedStmt(
"select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ? AND icc.decal == ?" "select obj.id as objectId from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ? AND icc.decal == ?"
); );
stmt.bind(1, "character create shirt"); stmt.bind(1, "character create shirt");
stmt.bind(2, static_cast<int>(shirtColor)); stmt.bind(2, static_cast<int>(shirtColor));
stmt.bind(3, static_cast<int>(shirtStyle)); stmt.bind(3, static_cast<int>(shirtStyle));
auto tableData = stmt.execQuery(); auto tableData = stmt.execQuery();
auto shirtLOT = tableData.getIntField(0, 4069); auto shirtLOT = tableData.getIntField("objectId", 4069);
tableData.finalize(); tableData.finalize();
return shirtLOT; return shirtLOT;
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
@ -555,12 +555,12 @@ uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
uint32_t FindCharPantsID(uint32_t pantsColor) { uint32_t FindCharPantsID(uint32_t pantsColor) {
try { try {
auto stmt = CDClientDatabase::CreatePreppedStmt( auto stmt = CDClientDatabase::CreatePreppedStmt(
"select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ?" "select obj.id as objectId from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ?"
); );
stmt.bind(1, "cc pants"); stmt.bind(1, "cc pants");
stmt.bind(2, static_cast<int>(pantsColor)); stmt.bind(2, static_cast<int>(pantsColor));
auto tableData = stmt.execQuery(); auto tableData = stmt.execQuery();
auto pantsLOT = tableData.getIntField(0, 2508); auto pantsLOT = tableData.getIntField("objectId", 2508);
tableData.finalize(); tableData.finalize();
return pantsLOT; return pantsLOT;
} catch (const std::exception& ex) { } catch (const std::exception& ex) {

View File

@ -377,10 +377,10 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
return; return;
} }
const auto name = std::string(result.getStringField(0)); const auto name = std::string(result.getStringField("effectName"));
if (type.empty()) { if (type.empty()) {
const auto typeResult = result.getStringField(1); const auto typeResult = result.getStringField("effectType");
type = GeneralUtils::ASCIIToUTF16(typeResult); type = GeneralUtils::ASCIIToUTF16(typeResult);

View File

@ -47,11 +47,11 @@ void SwitchMultipleBehavior::Load() {
auto result = query.execQuery(); auto result = query.execQuery();
while (!result.eof()) { while (!result.eof()) {
const auto behavior_id = static_cast<uint32_t>(result.getFloatField(1)); const auto behavior_id = static_cast<uint32_t>(result.getFloatField("behavior"));
auto* behavior = CreateBehavior(behavior_id); auto* behavior = CreateBehavior(behavior_id);
auto value = result.getFloatField(2); auto value = result.getFloatField("value");
this->m_behaviors.emplace_back(value, behavior); this->m_behaviors.emplace_back(value, behavior);

View File

@ -21,7 +21,7 @@
#include "eMissionTaskType.h" #include "eMissionTaskType.h"
#include "eMatchUpdate.h" #include "eMatchUpdate.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatInternalMessageType.h" #include "eChatMessageType.h"
#include "CDCurrencyTableTable.h" #include "CDCurrencyTableTable.h"
#include "CDActivityRewardsTable.h" #include "CDActivityRewardsTable.h"
@ -501,7 +501,7 @@ void ActivityInstance::StartZone() {
// only make a team if we have more than one participant // only make a team if we have more than one participant
if (participants.size() > 1) { if (participants.size() > 1) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::CREATE_TEAM);
bitStream.Write(leader->GetObjectID()); bitStream.Write(leader->GetObjectID());
bitStream.Write(m_Participants.size()); bitStream.Write(m_Participants.size());

View File

@ -45,20 +45,20 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
auto componentResult = componentQuery.execQuery(); auto componentResult = componentQuery.execQuery();
if (!componentResult.eof()) { if (!componentResult.eof()) {
if (!componentResult.fieldIsNull(0)) if (!componentResult.fieldIsNull("aggroRadius"))
m_AggroRadius = componentResult.getFloatField(0); m_AggroRadius = componentResult.getFloatField("aggroRadius");
if (!componentResult.fieldIsNull(1)) if (!componentResult.fieldIsNull("tetherSpeed"))
m_TetherSpeed = componentResult.getFloatField(1); m_TetherSpeed = componentResult.getFloatField("tetherSpeed");
if (!componentResult.fieldIsNull(2)) if (!componentResult.fieldIsNull("pursuitSpeed"))
m_PursuitSpeed = componentResult.getFloatField(2); m_PursuitSpeed = componentResult.getFloatField("pursuitSpeed");
if (!componentResult.fieldIsNull(3)) if (!componentResult.fieldIsNull("softTetherRadius"))
m_SoftTetherRadius = componentResult.getFloatField(3); m_SoftTetherRadius = componentResult.getFloatField("softTetherRadius");
if (!componentResult.fieldIsNull(4)) if (!componentResult.fieldIsNull("hardTetherRadius"))
m_HardTetherRadius = componentResult.getFloatField(4); m_HardTetherRadius = componentResult.getFloatField("hardTetherRadius");
} }
componentResult.finalize(); componentResult.finalize();
@ -82,11 +82,11 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
auto result = skillQuery.execQuery(); auto result = skillQuery.execQuery();
while (!result.eof()) { while (!result.eof()) {
const auto skillId = static_cast<uint32_t>(result.getIntField(0)); const auto skillId = static_cast<uint32_t>(result.getIntField("skillID"));
const auto abilityCooldown = static_cast<float>(result.getFloatField(1)); const auto abilityCooldown = static_cast<float>(result.getFloatField("cooldown"));
const auto behaviorId = static_cast<uint32_t>(result.getIntField(2)); const auto behaviorId = static_cast<uint32_t>(result.getIntField("behaviorID"));
auto* behavior = Behavior::CreateBehavior(behaviorId); auto* behavior = Behavior::CreateBehavior(behaviorId);
@ -150,13 +150,13 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition()); m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
//Process enter events //Process enter events
for (auto en : m_dpEntity->GetNewObjects()) { for (const auto id : m_dpEntity->GetNewObjects()) {
m_Parent->OnCollisionPhantom(en->GetObjectID()); m_Parent->OnCollisionPhantom(id);
} }
//Process exit events //Process exit events
for (auto en : m_dpEntity->GetRemovedObjects()) { for (const auto id : m_dpEntity->GetRemovedObjects()) {
m_Parent->OnCollisionLeavePhantom(en->GetObjectID()); m_Parent->OnCollisionLeavePhantom(id);
} }
// Check if we should stop the tether effect // Check if we should stop the tether effect

View File

@ -326,9 +326,9 @@ Entity* BuffComponent::GetParent() const {
return m_Parent; return m_Parent;
} }
void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void BuffComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
// Load buffs // Load buffs
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); auto* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
// Make sure we have a clean buff element. // Make sure we have a clean buff element.
auto* buffElement = dest->FirstChildElement("buff"); auto* buffElement = dest->FirstChildElement("buff");
@ -386,15 +386,15 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
} }
} }
void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void BuffComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
// Save buffs // Save buffs
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); auto* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
// Make sure we have a clean buff element. // Make sure we have a clean buff element.
auto* buffElement = dest->FirstChildElement("buff"); auto* buffElement = dest->FirstChildElement("buff");
if (buffElement == nullptr) { if (buffElement == nullptr) {
buffElement = doc->NewElement("buff"); buffElement = doc.NewElement("buff");
dest->LinkEndChild(buffElement); dest->LinkEndChild(buffElement);
} else { } else {
@ -402,7 +402,7 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
} }
for (const auto& [id, buff] : m_Buffs) { for (const auto& [id, buff] : m_Buffs) {
auto* buffEntry = doc->NewElement("b"); auto* buffEntry = doc.NewElement("b");
// TODO: change this if to if (buff.cancelOnZone || buff.cancelOnLogout) handling at some point. No current way to differentiate between zone transfer and logout. // TODO: change this if to if (buff.cancelOnZone || buff.cancelOnLogout) handling at some point. No current way to differentiate between zone transfer and logout.
if (buff.cancelOnZone) continue; if (buff.cancelOnZone) continue;
@ -450,7 +450,7 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
param.value = result.getFloatField("NumberValue"); param.value = result.getFloatField("NumberValue");
param.effectId = result.getIntField("EffectID"); param.effectId = result.getIntField("EffectID");
if (!result.fieldIsNull(3)) { if (!result.fieldIsNull("StringValue")) {
std::istringstream stream(result.getStringField("StringValue")); std::istringstream stream(result.getStringField("StringValue"));
std::string token; std::string token;

View File

@ -57,9 +57,9 @@ public:
Entity* GetParent() const; Entity* GetParent() const;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@ -186,9 +186,9 @@ void CharacterComponent::SetGMLevel(eGameMasterLevel gmlevel) {
m_GMLevel = gmlevel; m_GMLevel = gmlevel;
} }
void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void CharacterComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); auto* character = doc.FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
LOG("Failed to find char tag while loading XML!"); LOG("Failed to find char tag while loading XML!");
return; return;
@ -299,8 +299,8 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
} }
} }
void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void CharacterComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* minifig = doc->FirstChildElement("obj")->FirstChildElement("mf"); tinyxml2::XMLElement* minifig = doc.FirstChildElement("obj")->FirstChildElement("mf");
if (!minifig) { if (!minifig) {
LOG("Failed to find mf tag while updating XML!"); LOG("Failed to find mf tag while updating XML!");
return; return;
@ -320,7 +320,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
// done with minifig // done with minifig
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* character = doc.FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
LOG("Failed to find char tag while updating XML!"); LOG("Failed to find char tag while updating XML!");
return; return;
@ -338,11 +338,11 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
// Set the zone statistics of the form <zs><s/> ... <s/></zs> // Set the zone statistics of the form <zs><s/> ... <s/></zs>
auto zoneStatistics = character->FirstChildElement("zs"); auto zoneStatistics = character->FirstChildElement("zs");
if (!zoneStatistics) zoneStatistics = doc->NewElement("zs"); if (!zoneStatistics) zoneStatistics = doc.NewElement("zs");
zoneStatistics->DeleteChildren(); zoneStatistics->DeleteChildren();
for (auto pair : m_ZoneStatistics) { for (auto pair : m_ZoneStatistics) {
auto zoneStatistic = doc->NewElement("s"); auto zoneStatistic = doc.NewElement("s");
zoneStatistic->SetAttribute("map", pair.first); zoneStatistic->SetAttribute("map", pair.first);
zoneStatistic->SetAttribute("ac", pair.second.m_AchievementsCollected); zoneStatistic->SetAttribute("ac", pair.second.m_AchievementsCollected);

View File

@ -70,8 +70,8 @@ public:
CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress); CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress);
~CharacterComponent() override; ~CharacterComponent() override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@ -21,11 +21,11 @@ void Component::OnUse(Entity* originator) {
} }
void Component::UpdateXml(tinyxml2::XMLDocument* doc) { void Component::UpdateXml(tinyxml2::XMLDocument& doc) {
} }
void Component::LoadFromXml(tinyxml2::XMLDocument* doc) { void Component::LoadFromXml(const tinyxml2::XMLDocument& doc) {
} }

View File

@ -34,13 +34,13 @@ public:
* Save data from this componennt to character XML * Save data from this componennt to character XML
* @param doc the document to write data to * @param doc the document to write data to
*/ */
virtual void UpdateXml(tinyxml2::XMLDocument* doc); virtual void UpdateXml(tinyxml2::XMLDocument& doc);
/** /**
* Load base data for this component from character XML * Load base data for this component from character XML
* @param doc the document to read data from * @param doc the document to read data from
*/ */
virtual void LoadFromXml(tinyxml2::XMLDocument* doc); virtual void LoadFromXml(const tinyxml2::XMLDocument& doc);
virtual void Serialize(RakNet::BitStream& outBitStream, bool isConstruction); virtual void Serialize(RakNet::BitStream& outBitStream, bool isConstruction);

View File

@ -158,8 +158,8 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
} }
} }
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void ControllablePhysicsComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); auto* character = doc.FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
LOG("Failed to find char tag!"); LOG("Failed to find char tag!");
return; return;
@ -178,8 +178,8 @@ void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
m_DirtyPosition = true; m_DirtyPosition = true;
} }
void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* character = doc.FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
LOG("Failed to find char tag while updating XML!"); LOG("Failed to find char tag while updating XML!");
return; return;

View File

@ -28,8 +28,8 @@ public:
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
/** /**
* Sets the position of this entity, also ensures this update is serialized next tick. * Sets the position of this entity, also ensures this update is serialized next tick.

View File

@ -185,8 +185,8 @@ void DestroyableComponent::Update(float deltaTime) {
m_DamageCooldownTimer -= deltaTime; m_DamageCooldownTimer -= deltaTime;
} }
void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void DestroyableComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); auto* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
if (!dest) { if (!dest) {
LOG("Failed to find dest tag!"); LOG("Failed to find dest tag!");
return; return;
@ -207,8 +207,8 @@ void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
m_DirtyHealth = true; m_DirtyHealth = true;
} }
void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); tinyxml2::XMLElement* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
if (!dest) { if (!dest) {
LOG("Failed to find dest tag!"); LOG("Failed to find dest tag!");
return; return;
@ -389,9 +389,9 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
if (result.eof()) return; if (result.eof()) return;
if (result.fieldIsNull(0)) return; if (result.fieldIsNull("enemyList")) return;
const auto* list_string = result.getStringField(0); const auto* list_string = result.getStringField("enemyList");
std::stringstream ss(list_string); std::stringstream ss(list_string);
std::string token; std::string token;

View File

@ -26,8 +26,8 @@ public:
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
/** /**
* Initializes the component using a different LOT * Initializes the component using a different LOT

View File

@ -38,7 +38,7 @@
#include "CDObjectSkillsTable.h" #include "CDObjectSkillsTable.h"
#include "CDSkillBehaviorTable.h" #include "CDSkillBehaviorTable.h"
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) { InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
this->m_Dirty = true; this->m_Dirty = true;
this->m_Equipped = {}; this->m_Equipped = {};
this->m_Pushed = {}; this->m_Pushed = {};
@ -48,7 +48,8 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do
const auto lot = parent->GetLOT(); const auto lot = parent->GetLOT();
if (lot == 1) { if (lot == 1) {
LoadXml(document); auto* character = m_Parent->GetCharacter();
if (character) LoadXml(character->GetXMLDoc());
CheckProxyIntegrity(); CheckProxyIntegrity();
@ -472,10 +473,10 @@ bool InventoryComponent::HasSpaceForLoot(const std::unordered_map<LOT, int32_t>&
return true; return true;
} }
void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
LoadPetXml(document); LoadPetXml(document);
auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv"); auto* inventoryElement = document.FirstChildElement("obj")->FirstChildElement("inv");
if (inventoryElement == nullptr) { if (inventoryElement == nullptr) {
LOG("Failed to find 'inv' xml element!"); LOG("Failed to find 'inv' xml element!");
@ -594,10 +595,10 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) {
} }
} }
void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
UpdatePetXml(document); UpdatePetXml(document);
auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv"); auto* inventoryElement = document.FirstChildElement("obj")->FirstChildElement("inv");
if (inventoryElement == nullptr) { if (inventoryElement == nullptr) {
LOG("Failed to find 'inv' xml element!"); LOG("Failed to find 'inv' xml element!");
@ -631,7 +632,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
bags->DeleteChildren(); bags->DeleteChildren();
for (const auto* inventory : inventoriesToSave) { for (const auto* inventory : inventoriesToSave) {
auto* bag = document->NewElement("b"); auto* bag = document.NewElement("b");
bag->SetAttribute("t", inventory->GetType()); bag->SetAttribute("t", inventory->GetType());
bag->SetAttribute("m", static_cast<unsigned int>(inventory->GetSize())); bag->SetAttribute("m", static_cast<unsigned int>(inventory->GetSize()));
@ -654,14 +655,14 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
continue; continue;
} }
auto* bagElement = document->NewElement("in"); auto* bagElement = document.NewElement("in");
bagElement->SetAttribute("t", inventory->GetType()); bagElement->SetAttribute("t", inventory->GetType());
for (const auto& pair : inventory->GetItems()) { for (const auto& pair : inventory->GetItems()) {
auto* item = pair.second; auto* item = pair.second;
auto* itemElement = document->NewElement("i"); auto* itemElement = document.NewElement("i");
itemElement->SetAttribute("l", item->GetLot()); itemElement->SetAttribute("l", item->GetLot());
itemElement->SetAttribute("id", item->GetId()); itemElement->SetAttribute("id", item->GetId());
@ -680,7 +681,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
continue; continue;
} }
auto* extraInfo = document->NewElement("x"); auto* extraInfo = document.NewElement("x");
extraInfo->SetAttribute("ma", data->GetString(false).c_str()); extraInfo->SetAttribute("ma", data->GetString(false).c_str());
@ -1093,7 +1094,7 @@ void InventoryComponent::CheckItemSet(const LOT lot) {
auto result = query.execQuery(); auto result = query.execQuery();
while (!result.eof()) { while (!result.eof()) {
const auto id = result.getIntField(0); const auto id = result.getIntField("setID");
bool found = false; bool found = false;
@ -1542,8 +1543,8 @@ void InventoryComponent::PurgeProxies(Item* item) {
} }
} }
void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document) { void InventoryComponent::LoadPetXml(const tinyxml2::XMLDocument& document) {
auto* petInventoryElement = document->FirstChildElement("obj")->FirstChildElement("pet"); auto* petInventoryElement = document.FirstChildElement("obj")->FirstChildElement("pet");
if (petInventoryElement == nullptr) { if (petInventoryElement == nullptr) {
m_Pets.clear(); m_Pets.clear();
@ -1574,19 +1575,19 @@ void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document) {
} }
} }
void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document) { void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument& document) {
auto* petInventoryElement = document->FirstChildElement("obj")->FirstChildElement("pet"); auto* petInventoryElement = document.FirstChildElement("obj")->FirstChildElement("pet");
if (petInventoryElement == nullptr) { if (petInventoryElement == nullptr) {
petInventoryElement = document->NewElement("pet"); petInventoryElement = document.NewElement("pet");
document->FirstChildElement("obj")->LinkEndChild(petInventoryElement); document.FirstChildElement("obj")->LinkEndChild(petInventoryElement);
} }
petInventoryElement->DeleteChildren(); petInventoryElement->DeleteChildren();
for (const auto& pet : m_Pets) { for (const auto& pet : m_Pets) {
auto* petElement = document->NewElement("p"); auto* petElement = document.NewElement("p");
petElement->SetAttribute("id", pet.first); petElement->SetAttribute("id", pet.first);
petElement->SetAttribute("l", pet.second.lot); petElement->SetAttribute("l", pet.second.lot);

View File

@ -38,12 +38,12 @@ enum class eItemType : int32_t;
class InventoryComponent final : public Component { class InventoryComponent final : public Component {
public: public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY; static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr); InventoryComponent(Entity* parent);
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void LoadXml(tinyxml2::XMLDocument* document); void LoadXml(const tinyxml2::XMLDocument& document);
void UpdateXml(tinyxml2::XMLDocument* document) override; void UpdateXml(tinyxml2::XMLDocument& document) override;
/** /**
* Returns an inventory of the specified type, if it exists * Returns an inventory of the specified type, if it exists
@ -470,13 +470,13 @@ private:
* Saves all the pet information stored in inventory items to the database * Saves all the pet information stored in inventory items to the database
* @param document the xml doc to save to * @param document the xml doc to save to
*/ */
void LoadPetXml(tinyxml2::XMLDocument* document); void LoadPetXml(const tinyxml2::XMLDocument& document);
/** /**
* Loads all the pet information from an xml doc into items * Loads all the pet information from an xml doc into items
* @param document the xml doc to load from * @param document the xml doc to load from
*/ */
void UpdatePetXml(tinyxml2::XMLDocument* document); void UpdatePetXml(tinyxml2::XMLDocument& document);
}; };
#endif #endif

View File

@ -13,8 +13,8 @@ LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component
m_CharacterVersion = eCharacterVersion::LIVE; m_CharacterVersion = eCharacterVersion::LIVE;
} }
void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); tinyxml2::XMLElement* level = doc.FirstChildElement("obj")->FirstChildElement("lvl");
if (!level) { if (!level) {
LOG("Failed to find lvl tag while updating XML!"); LOG("Failed to find lvl tag while updating XML!");
return; return;
@ -24,8 +24,8 @@ void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
level->SetAttribute("cv", static_cast<uint32_t>(m_CharacterVersion)); level->SetAttribute("cv", static_cast<uint32_t>(m_CharacterVersion));
} }
void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void LevelProgressionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); auto* level = doc.FirstChildElement("obj")->FirstChildElement("lvl");
if (!level) { if (!level) {
LOG("Failed to find lvl tag while loading XML!"); LOG("Failed to find lvl tag while loading XML!");
return; return;

View File

@ -27,13 +27,13 @@ public:
* Save data from this componennt to character XML * Save data from this componennt to character XML
* @param doc the document to write data to * @param doc the document to write data to
*/ */
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
/** /**
* Load base data for this component from character XML * Load base data for this component from character XML
* @param doc the document to read data from * @param doc the document to read data from
*/ */
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
/** /**
* Gets the current level of the entity * Gets the current level of the entity

View File

@ -466,8 +466,8 @@ bool MissionComponent::RequiresItem(const LOT lot) {
return false; return false;
} }
if (!result.fieldIsNull(0)) { if (!result.fieldIsNull("type")) {
const auto type = std::string(result.getStringField(0)); const auto type = std::string(result.getStringField("type"));
result.finalize(); result.finalize();
@ -504,10 +504,8 @@ bool MissionComponent::RequiresItem(const LOT lot) {
} }
void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
if (doc == nullptr) return; auto* mis = doc.FirstChildElement("obj")->FirstChildElement("mis");
auto* mis = doc->FirstChildElement("obj")->FirstChildElement("mis");
if (mis == nullptr) return; if (mis == nullptr) return;
@ -523,7 +521,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* mission = new Mission(this, missionId); auto* mission = new Mission(this, missionId);
mission->LoadFromXml(doneM); mission->LoadFromXml(*doneM);
doneM = doneM->NextSiblingElement(); doneM = doneM->NextSiblingElement();
@ -540,7 +538,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* mission = new Mission(this, missionId); auto* mission = new Mission(this, missionId);
mission->LoadFromXml(currentM); mission->LoadFromXml(*currentM);
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) { if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
mission->SetUniqueMissionOrderID(missionOrder); mission->SetUniqueMissionOrderID(missionOrder);
@ -554,25 +552,23 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
} }
void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void MissionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
if (doc == nullptr) return;
auto shouldInsertMis = false; auto shouldInsertMis = false;
auto* obj = doc->FirstChildElement("obj"); auto* obj = doc.FirstChildElement("obj");
auto* mis = obj->FirstChildElement("mis"); auto* mis = obj->FirstChildElement("mis");
if (mis == nullptr) { if (mis == nullptr) {
mis = doc->NewElement("mis"); mis = doc.NewElement("mis");
shouldInsertMis = true; shouldInsertMis = true;
} }
mis->DeleteChildren(); mis->DeleteChildren();
auto* done = doc->NewElement("done"); auto* done = doc.NewElement("done");
auto* cur = doc->NewElement("cur"); auto* cur = doc.NewElement("cur");
for (const auto& pair : m_Missions) { for (const auto& pair : m_Missions) {
auto* mission = pair.second; auto* mission = pair.second;
@ -580,10 +576,10 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
if (mission) { if (mission) {
const auto complete = mission->IsComplete(); const auto complete = mission->IsComplete();
auto* m = doc->NewElement("m"); auto* m = doc.NewElement("m");
if (complete) { if (complete) {
mission->UpdateXml(m); mission->UpdateXml(*m);
done->LinkEndChild(m); done->LinkEndChild(m);
@ -591,7 +587,7 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
} }
if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID()); if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID());
mission->UpdateXml(m); mission->UpdateXml(*m);
cur->LinkEndChild(m); cur->LinkEndChild(m);
} }

View File

@ -31,8 +31,8 @@ public:
explicit MissionComponent(Entity* parent); explicit MissionComponent(Entity* parent);
~MissionComponent() override; ~MissionComponent() override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
/** /**
* Returns all the missions for this entity, mapped by mission ID * Returns all the missions for this entity, mapped by mission ID

View File

@ -215,20 +215,20 @@ void PetComponent::OnUse(Entity* originator) {
return; return;
} }
if (result.fieldIsNull(0)) { if (result.fieldIsNull("ValidPiecesLXF")) {
result.finalize(); result.finalize();
return; return;
} }
buildFile = std::string(result.getStringField(0)); buildFile = std::string(result.getStringField("ValidPiecesLXF"));
PetPuzzleData data; PetPuzzleData data;
data.buildFile = buildFile; data.buildFile = buildFile;
data.puzzleModelLot = result.getIntField(1); data.puzzleModelLot = result.getIntField("PuzzleModelLot");
data.timeLimit = result.getFloatField(2); data.timeLimit = result.getFloatField("Timelimit");
data.numValidPieces = result.getIntField(3); data.numValidPieces = result.getIntField("NumValidPieces");
data.imaginationCost = result.getIntField(4); data.imaginationCost = result.getIntField("imagCostPerBuild");
if (data.timeLimit <= 0) data.timeLimit = 60; if (data.timeLimit <= 0) data.timeLimit = 60;
imaginationCost = data.imaginationCost; imaginationCost = data.imaginationCost;

View File

@ -323,14 +323,13 @@ void PhantomPhysicsComponent::Update(float deltaTime) {
if (!m_dpEntity) return; if (!m_dpEntity) return;
//Process enter events //Process enter events
for (auto en : m_dpEntity->GetNewObjects()) { for (const auto id : m_dpEntity->GetNewObjects()) {
if (!en) continue; ApplyCollisionEffect(id, m_EffectType, m_DirectionalMultiplier);
ApplyCollisionEffect(en->GetObjectID(), m_EffectType, m_DirectionalMultiplier); m_Parent->OnCollisionPhantom(id);
m_Parent->OnCollisionPhantom(en->GetObjectID());
//If we are a respawn volume, inform the client: //If we are a respawn volume, inform the client:
if (m_IsRespawnVolume) { if (m_IsRespawnVolume) {
auto entity = Game::entityManager->GetEntity(en->GetObjectID()); auto* const entity = Game::entityManager->GetEntity(id);
if (entity) { if (entity) {
GameMessages::SendPlayerReachedRespawnCheckpoint(entity, m_RespawnPos, m_RespawnRot); GameMessages::SendPlayerReachedRespawnCheckpoint(entity, m_RespawnPos, m_RespawnRot);
@ -341,10 +340,9 @@ void PhantomPhysicsComponent::Update(float deltaTime) {
} }
//Process exit events //Process exit events
for (auto en : m_dpEntity->GetRemovedObjects()) { for (const auto id : m_dpEntity->GetRemovedObjects()) {
if (!en) continue; ApplyCollisionEffect(id, m_EffectType, 1.0f);
ApplyCollisionEffect(en->GetObjectID(), m_EffectType, 1.0f); m_Parent->OnCollisionLeavePhantom(id);
m_Parent->OnCollisionLeavePhantom(en->GetObjectID());
} }
} }

View File

@ -18,8 +18,8 @@ PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId)
// Should a result not exist for this default to attached visible // Should a result not exist for this default to attached visible
if (!result.eof()) { if (!result.eof()) {
m_PossessionType = static_cast<ePossessionType>(result.getIntField(0, 1)); // Default to Attached Visible m_PossessionType = static_cast<ePossessionType>(result.getIntField("possessionType", 1)); // Default to Attached Visible
m_DepossessOnHit = static_cast<bool>(result.getIntField(1, 0)); m_DepossessOnHit = static_cast<bool>(result.getIntField("depossessOnHit", 0));
} else { } else {
m_PossessionType = ePossessionType::ATTACHED_VISIBLE; m_PossessionType = ePossessionType::ATTACHED_VISIBLE;
m_DepossessOnHit = false; m_DepossessOnHit = false;

View File

@ -49,11 +49,11 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo
auto result = query.execQuery(); auto result = query.execQuery();
if (result.eof() || result.fieldIsNull(0)) { if (result.eof() || result.fieldIsNull("id")) {
return; return;
} }
templateId = result.getIntField(0); templateId = result.getIntField("id");
auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId); auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId);
@ -105,7 +105,7 @@ std::vector<NiPoint3> PropertyManagementComponent::GetPaths() const {
std::vector<float> points; std::vector<float> points;
std::istringstream stream(result.getStringField(0)); std::istringstream stream(result.getStringField("path"));
std::string token; std::string token;
while (std::getline(stream, token, ' ')) { while (std::getline(stream, token, ' ')) {
@ -352,16 +352,11 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
auto* spawner = Game::zoneManager->GetSpawner(spawnerId); auto* spawner = Game::zoneManager->GetSpawner(spawnerId);
auto ldfModelBehavior = new LDFData<LWOOBJID>(u"modelBehaviors", 0); info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
auto userModelID = new LDFData<LWOOBJID>(u"userModelID", info.spawnerID); info.nodes[0]->config.push_back(new LDFData<LWOOBJID>(u"userModelID", info.spawnerID));
auto modelType = new LDFData<int>(u"modelType", 2); info.nodes[0]->config.push_back(new LDFData<int>(u"modelType", 2));
auto propertyObjectID = new LDFData<bool>(u"propertyObjectID", true); info.nodes[0]->config.push_back(new LDFData<bool>(u"propertyObjectID", true));
auto componentWhitelist = new LDFData<int>(u"componentWhitelist", 1); info.nodes[0]->config.push_back(new LDFData<int>(u"componentWhitelist", 1));
info.nodes[0]->config.push_back(componentWhitelist);
info.nodes[0]->config.push_back(ldfModelBehavior);
info.nodes[0]->config.push_back(modelType);
info.nodes[0]->config.push_back(propertyObjectID);
info.nodes[0]->config.push_back(userModelID);
auto* model = spawner->Spawn(); auto* model = spawner->Spawn();
@ -585,29 +580,17 @@ void PropertyManagementComponent::Load() {
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER); GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT); GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
LDFBaseData* ldfBlueprintID = new LDFData<LWOOBJID>(u"blueprintid", blueprintID); settings.push_back(new LDFData<LWOOBJID>(u"blueprintid", blueprintID));
LDFBaseData* componentWhitelist = new LDFData<int>(u"componentWhitelist", 1); settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
LDFBaseData* modelType = new LDFData<int>(u"modelType", 2); settings.push_back(new LDFData<int>(u"modelType", 2));
LDFBaseData* propertyObjectID = new LDFData<bool>(u"propertyObjectID", true); settings.push_back(new LDFData<bool>(u"propertyObjectID", true));
LDFBaseData* userModelID = new LDFData<LWOOBJID>(u"userModelID", databaseModel.id); settings.push_back(new LDFData<LWOOBJID>(u"userModelID", databaseModel.id));
settings.push_back(ldfBlueprintID);
settings.push_back(componentWhitelist);
settings.push_back(modelType);
settings.push_back(propertyObjectID);
settings.push_back(userModelID);
} else { } else {
auto modelType = new LDFData<int>(u"modelType", 2); settings.push_back(new LDFData<int>(u"modelType", 2));
auto userModelID = new LDFData<LWOOBJID>(u"userModelID", databaseModel.id); settings.push_back(new LDFData<LWOOBJID>(u"userModelID", databaseModel.id));
auto ldfModelBehavior = new LDFData<LWOOBJID>(u"modelBehaviors", 0); settings.push_back(new LDFData<LWOOBJID>(u"modelBehaviors", 0));
auto propertyObjectID = new LDFData<bool>(u"propertyObjectID", true); settings.push_back(new LDFData<bool>(u"propertyObjectID", true));
auto componentWhitelist = new LDFData<int>(u"componentWhitelist", 1); settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
settings.push_back(componentWhitelist);
settings.push_back(ldfModelBehavior);
settings.push_back(modelType);
settings.push_back(propertyObjectID);
settings.push_back(userModelID);
} }
node->config = settings; node->config = settings;

View File

@ -5,7 +5,7 @@
#include "EntityManager.h" #include "EntityManager.h"
#include "SimplePhysicsComponent.h" #include "SimplePhysicsComponent.h"
const std::map<LWOOBJID, dpEntity*> ProximityMonitorComponent::m_EmptyObjectMap = {}; const std::unordered_set<LWOOBJID> ProximityMonitorComponent::m_EmptyObjectSet = {};
ProximityMonitorComponent::ProximityMonitorComponent(Entity* parent, int radiusSmall, int radiusLarge) : Component(parent) { ProximityMonitorComponent::ProximityMonitorComponent(Entity* parent, int radiusSmall, int radiusLarge) : Component(parent) {
if (radiusSmall != -1 && radiusLarge != -1) { if (radiusSmall != -1 && radiusLarge != -1) {
@ -38,26 +38,26 @@ void ProximityMonitorComponent::SetProximityRadius(dpEntity* entity, const std::
m_ProximitiesData.insert(std::make_pair(name, entity)); m_ProximitiesData.insert(std::make_pair(name, entity));
} }
const std::map<LWOOBJID, dpEntity*>& ProximityMonitorComponent::GetProximityObjects(const std::string& name) { const std::unordered_set<LWOOBJID>& ProximityMonitorComponent::GetProximityObjects(const std::string& name) {
const auto& iter = m_ProximitiesData.find(name); const auto iter = m_ProximitiesData.find(name);
if (iter == m_ProximitiesData.end()) { if (iter == m_ProximitiesData.cend()) {
return m_EmptyObjectMap; return m_EmptyObjectSet;
} }
return iter->second->GetCurrentlyCollidingObjects(); return iter->second->GetCurrentlyCollidingObjects();
} }
bool ProximityMonitorComponent::IsInProximity(const std::string& name, LWOOBJID objectID) { bool ProximityMonitorComponent::IsInProximity(const std::string& name, LWOOBJID objectID) {
const auto& iter = m_ProximitiesData.find(name); const auto iter = m_ProximitiesData.find(name);
if (iter == m_ProximitiesData.end()) { if (iter == m_ProximitiesData.cend()) {
return false; return false;
} }
const auto& collitions = iter->second->GetCurrentlyCollidingObjects(); const auto& collisions = iter->second->GetCurrentlyCollidingObjects();
return collitions.find(objectID) != collitions.end(); return collisions.contains(objectID);
} }
void ProximityMonitorComponent::Update(float deltaTime) { void ProximityMonitorComponent::Update(float deltaTime) {
@ -66,13 +66,13 @@ void ProximityMonitorComponent::Update(float deltaTime) {
prox.second->SetPosition(m_Parent->GetPosition()); prox.second->SetPosition(m_Parent->GetPosition());
//Process enter events //Process enter events
for (auto* en : prox.second->GetNewObjects()) { for (const auto id : prox.second->GetNewObjects()) {
m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "ENTER"); m_Parent->OnCollisionProximity(id, prox.first, "ENTER");
} }
//Process exit events //Process exit events
for (auto* en : prox.second->GetRemovedObjects()) { for (const auto id : prox.second->GetRemovedObjects()) {
m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "LEAVE"); m_Parent->OnCollisionProximity(id, prox.first, "LEAVE");
} }
} }
} }

View File

@ -6,6 +6,8 @@
#ifndef PROXIMITYMONITORCOMPONENT_H #ifndef PROXIMITYMONITORCOMPONENT_H
#define PROXIMITYMONITORCOMPONENT_H #define PROXIMITYMONITORCOMPONENT_H
#include <unordered_set>
#include "BitStream.h" #include "BitStream.h"
#include "Entity.h" #include "Entity.h"
#include "dpWorld.h" #include "dpWorld.h"
@ -42,9 +44,9 @@ public:
/** /**
* Returns the last of entities that are used to check proximity, given a name * Returns the last of entities that are used to check proximity, given a name
* @param name the proximity name to retrieve physics objects for * @param name the proximity name to retrieve physics objects for
* @return a map of physics entities for this name, indexed by object ID * @return a set of physics entity object IDs for this name
*/ */
const std::map<LWOOBJID, dpEntity*>& GetProximityObjects(const std::string& name); const std::unordered_set<LWOOBJID>& GetProximityObjects(const std::string& name);
/** /**
* Checks if the passed object is in proximity of the named proximity sensor * Checks if the passed object is in proximity of the named proximity sensor
@ -70,7 +72,7 @@ private:
/** /**
* Default value for the proximity data * Default value for the proximity data
*/ */
static const std::map<LWOOBJID, dpEntity*> m_EmptyObjectMap; static const std::unordered_set<LWOOBJID> m_EmptyObjectSet;
}; };
#endif // PROXIMITYMONITORCOMPONENT_H #endif // PROXIMITYMONITORCOMPONENT_H

View File

@ -117,7 +117,7 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e
auto result = query.execQuery(); auto result = query.execQuery();
if (result.eof() || result.fieldIsNull(0)) { if (result.eof() || result.fieldIsNull("animation_length")) {
result.finalize(); result.finalize();
m_DurationCache[effectId] = 0; m_DurationCache[effectId] = 0;
@ -127,7 +127,7 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e
return; return;
} }
effect.time = static_cast<float>(result.getFloatField(0)); effect.time = static_cast<float>(result.getFloatField("animation_length"));
result.finalize(); result.finalize();

View File

@ -27,12 +27,12 @@ RocketLaunchpadControlComponent::RocketLaunchpadControlComponent(Entity* parent,
auto result = query.execQuery(); auto result = query.execQuery();
if (!result.eof() && !result.fieldIsNull(0)) { if (!result.eof() && !result.fieldIsNull("targetZone")) {
m_TargetZone = result.getIntField(0); m_TargetZone = result.getIntField("targetZone");
m_DefaultZone = result.getIntField(1); m_DefaultZone = result.getIntField("defaultZoneID");
m_TargetScene = result.getStringField(2); m_TargetScene = result.getStringField("targetScene");
m_AltPrecondition = new PreconditionExpression(result.getStringField(3)); m_AltPrecondition = new PreconditionExpression(result.getStringField("altLandingPrecondition"));
m_AltLandingScene = result.getStringField(4); m_AltLandingScene = result.getStringField("altLandingSpawnPointName");
} }
result.finalize(); result.finalize();

View File

@ -99,7 +99,7 @@ void SkillComponent::SyncPlayerProjectile(const LWOOBJID projectileId, RakNet::B
return; return;
} }
const auto behavior_id = static_cast<uint32_t>(result.getIntField(0)); const auto behavior_id = static_cast<uint32_t>(result.getIntField("behaviorID"));
result.finalize(); result.finalize();
@ -227,7 +227,7 @@ void SkillComponent::RegisterCalculatedProjectile(const LWOOBJID projectileId, B
this->m_managedProjectiles.push_back(entry); this->m_managedProjectiles.push_back(entry);
} }
bool SkillComponent::CastSkill(const uint32_t skillId, LWOOBJID target, const LWOOBJID optionalOriginatorID) { bool SkillComponent::CastSkill(const uint32_t skillId, LWOOBJID target, const LWOOBJID optionalOriginatorID, const int32_t castType, const NiQuaternion rotationOverride) {
uint32_t behaviorId = -1; uint32_t behaviorId = -1;
// try to find it via the cache // try to find it via the cache
const auto& pair = m_skillBehaviorCache.find(skillId); const auto& pair = m_skillBehaviorCache.find(skillId);
@ -247,11 +247,19 @@ bool SkillComponent::CastSkill(const uint32_t skillId, LWOOBJID target, const LW
return false; return false;
} }
return CalculateBehavior(skillId, behaviorId, target, false, false, optionalOriginatorID).success; return CalculateBehavior(skillId, behaviorId, target, false, false, optionalOriginatorID, castType, rotationOverride).success;
} }
SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, const uint32_t behaviorId, const LWOOBJID target, const bool ignoreTarget, const bool clientInitalized, const LWOOBJID originatorOverride) { SkillExecutionResult SkillComponent::CalculateBehavior(
const uint32_t skillId,
const uint32_t behaviorId,
const LWOOBJID target,
const bool ignoreTarget,
const bool clientInitalized,
const LWOOBJID originatorOverride,
const int32_t castType,
const NiQuaternion rotationOverride) {
RakNet::BitStream bitStream{}; RakNet::BitStream bitStream{};
auto* behavior = Behavior::CreateBehavior(behaviorId); auto* behavior = Behavior::CreateBehavior(behaviorId);
@ -283,7 +291,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, c
// Echo start skill // Echo start skill
EchoStartSkill start; EchoStartSkill start;
start.iCastType = 0; start.iCastType = castType;
start.skillID = skillId; start.skillID = skillId;
start.uiSkillHandle = context->skillUId; start.uiSkillHandle = context->skillUId;
start.optionalOriginatorID = context->originator; start.optionalOriginatorID = context->originator;
@ -294,6 +302,10 @@ SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, c
if (originator != nullptr) { if (originator != nullptr) {
start.originatorRot = originator->GetRotation(); start.originatorRot = originator->GetRotation();
} }
if (rotationOverride != NiQuaternionConstant::IDENTITY) {
start.originatorRot = rotationOverride;
}
//start.optionalTargetID = target; //start.optionalTargetID = target;
start.sBitStream.assign(reinterpret_cast<char*>(bitStream.GetData()), bitStream.GetNumberOfBytesUsed()); start.sBitStream.assign(reinterpret_cast<char*>(bitStream.GetData()), bitStream.GetNumberOfBytesUsed());
@ -413,7 +425,7 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry)
return; return;
} }
const auto behaviorId = static_cast<uint32_t>(result.getIntField(0)); const auto behaviorId = static_cast<uint32_t>(result.getIntField("behaviorID"));
result.finalize(); result.finalize();
@ -464,7 +476,7 @@ void SkillComponent::HandleUnCast(const uint32_t behaviorId, const LWOOBJID targ
behavior->UnCast(&context, { target }); behavior->UnCast(&context, { target });
} }
SkillComponent::SkillComponent(Entity* parent): Component(parent) { SkillComponent::SkillComponent(Entity* parent) : Component(parent) {
this->m_skillUid = 0; this->m_skillUid = 0;
} }

View File

@ -127,7 +127,7 @@ public:
* @param optionalOriginatorID change the originator of the skill * @param optionalOriginatorID change the originator of the skill
* @return if the case succeeded * @return if the case succeeded
*/ */
bool CastSkill(const uint32_t skillId, LWOOBJID target = LWOOBJID_EMPTY, const LWOOBJID optionalOriginatorID = LWOOBJID_EMPTY); bool CastSkill(const uint32_t skillId, LWOOBJID target = LWOOBJID_EMPTY, const LWOOBJID optionalOriginatorID = LWOOBJID_EMPTY, const int32_t castType = 0, const NiQuaternion rotationOverride = NiQuaternionConstant::IDENTITY);
/** /**
* Initializes a server-side skill calculation. * Initializes a server-side skill calculation.
@ -139,7 +139,7 @@ public:
* @param originatorOverride an override for the originator of the skill calculation * @param originatorOverride an override for the originator of the skill calculation
* @return the result of the skill calculation * @return the result of the skill calculation
*/ */
SkillExecutionResult CalculateBehavior(uint32_t skillId, uint32_t behaviorId, LWOOBJID target, bool ignoreTarget = false, bool clientInitalized = false, LWOOBJID originatorOverride = LWOOBJID_EMPTY); SkillExecutionResult CalculateBehavior(uint32_t skillId, uint32_t behaviorId, LWOOBJID target, bool ignoreTarget = false, bool clientInitalized = false, LWOOBJID originatorOverride = LWOOBJID_EMPTY, const int32_t castType = 0, const NiQuaternion rotationOverride = NiQuaternionConstant::IDENTITY);
/** /**
* Register a server-side projectile. * Register a server-side projectile.

View File

@ -2676,17 +2676,11 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream& inStream, Entity* ent
info.spawnerID = entity->GetObjectID(); info.spawnerID = entity->GetObjectID();
info.spawnerNodeID = 0; info.spawnerNodeID = 0;
LDFBaseData* ldfBlueprintID = new LDFData<LWOOBJID>(u"blueprintid", blueprintID); info.settings.push_back(new LDFData<LWOOBJID>(u"blueprintid", blueprintID));
LDFBaseData* componentWhitelist = new LDFData<int>(u"componentWhitelist", 1); info.settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
LDFBaseData* modelType = new LDFData<int>(u"modelType", 2); info.settings.push_back(new LDFData<int>(u"modelType", 2));
LDFBaseData* propertyObjectID = new LDFData<bool>(u"propertyObjectID", true); info.settings.push_back(new LDFData<bool>(u"propertyObjectID", true));
LDFBaseData* userModelID = new LDFData<LWOOBJID>(u"userModelID", newIDL); info.settings.push_back(new LDFData<LWOOBJID>(u"userModelID", newIDL));
info.settings.push_back(ldfBlueprintID);
info.settings.push_back(componentWhitelist);
info.settings.push_back(modelType);
info.settings.push_back(propertyObjectID);
info.settings.push_back(userModelID);
Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr);
if (newEntity) { if (newEntity) {
@ -5336,7 +5330,7 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream& inStream, En
bool iLootTypeSourceIsDefault = false; bool iLootTypeSourceIsDefault = false;
LWOOBJID iLootTypeSource = LWOOBJID_EMPTY; LWOOBJID iLootTypeSource = LWOOBJID_EMPTY;
bool iObjIDIsDefault = false; bool iObjIDIsDefault = false;
LWOOBJID iObjID; LWOOBJID iObjID = LWOOBJID_EMPTY;
bool iObjTemplateIsDefault = false; bool iObjTemplateIsDefault = false;
LOT iObjTemplate = LOT_NULL; LOT iObjTemplate = LOT_NULL;
bool iRequestingObjIDIsDefault = false; bool iRequestingObjIDIsDefault = false;
@ -6243,3 +6237,15 @@ void GameMessages::HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Ent
if (!characterComponent) return; if (!characterComponent) return;
characterComponent->SetCurrentInteracting(LWOOBJID_EMPTY); characterComponent->SetCurrentInteracting(LWOOBJID_EMPTY);
} }
void GameMessages::SendSlashCommandFeedbackText(Entity* entity, std::u16string text) {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(entity->GetObjectID());
bitStream.Write(eGameMessageType::SLASH_COMMAND_TEXT_FEEDBACK);
bitStream.Write<uint32_t>(text.size());
bitStream.Write(text);
auto sysAddr = entity->GetSystemAddress();
SEND_PACKET;
}

View File

@ -685,6 +685,8 @@ namespace GameMessages {
void HandleRemoveDonationItem(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); void HandleRemoveDonationItem(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr);
void HandleConfirmDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity); void HandleConfirmDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity);
void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity); void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity);
void SendSlashCommandFeedbackText(Entity* entity, std::u16string text);
}; };
#endif // GAMEMESSAGES_H #endif // GAMEMESSAGES_H

View File

@ -405,18 +405,18 @@ void Item::DisassembleModel(uint32_t numToDismantle) {
auto result = query.execQuery(); auto result = query.execQuery();
if (result.eof() || result.fieldIsNull(0)) { if (result.eof() || result.fieldIsNull("render_asset")) {
return; return;
} }
std::string renderAsset = std::string(result.getStringField(0)); std::string renderAsset = std::string(result.getStringField("render_asset"));
// normalize path slashes // normalize path slashes
for (auto& c : renderAsset) { for (auto& c : renderAsset) {
if (c == '\\') c = '/'; if (c == '\\') c = '/';
} }
std::string lxfmlFolderName = std::string(result.getStringField(1)); std::string lxfmlFolderName = std::string(result.getStringField("LXFMLFolder"));
if (!lxfmlFolderName.empty()) lxfmlFolderName.insert(0, "/"); if (!lxfmlFolderName.empty()) lxfmlFolderName.insert(0, "/");
std::vector<std::string> renderAssetSplit = GeneralUtils::SplitString(renderAsset, '/'); std::vector<std::string> renderAssetSplit = GeneralUtils::SplitString(renderAsset, '/');

View File

@ -8,10 +8,13 @@
#include "MissionComponent.h" #include "MissionComponent.h"
#include "eMissionTaskType.h" #include "eMissionTaskType.h"
#include <algorithm> #include <algorithm>
#include <array>
#include "CDSkillBehaviorTable.h" #include "CDSkillBehaviorTable.h"
ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) { ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) {
using namespace std::string_view_literals;
this->m_ID = id; this->m_ID = id;
this->m_InventoryComponent = inventoryComponent; this->m_InventoryComponent = inventoryComponent;
@ -27,14 +30,16 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) {
return; return;
} }
for (auto i = 0; i < 5; ++i) { constexpr std::array rowNames = { "skillSetWith2"sv, "skillSetWith3"sv, "skillSetWith4"sv, "skillSetWith5"sv, "skillSetWith6"sv };
if (result.fieldIsNull(i)) { for (auto i = 0; i < rowNames.size(); ++i) {
const auto rowName = rowNames[i];
if (result.fieldIsNull(rowName.data())) {
continue; continue;
} }
auto skillQuery = CDClientDatabase::CreatePreppedStmt( auto skillQuery = CDClientDatabase::CreatePreppedStmt(
"SELECT SkillID FROM ItemSetSkills WHERE SkillSetID = ?;"); "SELECT SkillID FROM ItemSetSkills WHERE SkillSetID = ?;");
skillQuery.bind(1, result.getIntField(i)); skillQuery.bind(1, result.getIntField(rowName.data()));
auto skillResult = skillQuery.execQuery(); auto skillResult = skillQuery.execQuery();
@ -43,13 +48,13 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) {
} }
while (!skillResult.eof()) { while (!skillResult.eof()) {
if (skillResult.fieldIsNull(0)) { if (skillResult.fieldIsNull("SkillID")) {
skillResult.nextRow(); skillResult.nextRow();
continue; continue;
} }
const auto skillId = skillResult.getIntField(0); const auto skillId = skillResult.getIntField("SkillID");
switch (i) { switch (i) {
case 0: case 0:
@ -75,7 +80,7 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) {
} }
} }
std::string ids = result.getStringField(5); std::string ids = result.getStringField("itemIDs");
ids.erase(std::remove_if(ids.begin(), ids.end(), ::isspace), ids.end()); ids.erase(std::remove_if(ids.begin(), ids.end(), ::isspace), ids.end());

View File

@ -65,24 +65,24 @@ Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) {
} }
} }
void Mission::LoadFromXml(tinyxml2::XMLElement* element) { void Mission::LoadFromXml(const tinyxml2::XMLElement& element) {
// Start custom XML // Start custom XML
if (element->Attribute("state") != nullptr) { if (element.Attribute("state") != nullptr) {
m_State = static_cast<eMissionState>(std::stoul(element->Attribute("state"))); m_State = static_cast<eMissionState>(std::stoul(element.Attribute("state")));
} }
// End custom XML // End custom XML
if (element->Attribute("cct") != nullptr) { if (element.Attribute("cct") != nullptr) {
m_Completions = std::stoul(element->Attribute("cct")); m_Completions = std::stoul(element.Attribute("cct"));
m_Timestamp = std::stoul(element->Attribute("cts")); m_Timestamp = std::stoul(element.Attribute("cts"));
if (IsComplete()) { if (IsComplete()) {
return; return;
} }
} }
auto* task = element->FirstChildElement(); auto* task = element.FirstChildElement();
auto index = 0U; auto index = 0U;
@ -132,19 +132,19 @@ void Mission::LoadFromXml(tinyxml2::XMLElement* element) {
} }
} }
void Mission::UpdateXml(tinyxml2::XMLElement* element) { void Mission::UpdateXml(tinyxml2::XMLElement& element) {
// Start custom XML // Start custom XML
element->SetAttribute("state", static_cast<unsigned int>(m_State)); element.SetAttribute("state", static_cast<unsigned int>(m_State));
// End custom XML // End custom XML
element->DeleteChildren(); element.DeleteChildren();
element->SetAttribute("id", static_cast<unsigned int>(info.id)); element.SetAttribute("id", static_cast<unsigned int>(info.id));
if (m_Completions > 0) { if (m_Completions > 0) {
element->SetAttribute("cct", static_cast<unsigned int>(m_Completions)); element.SetAttribute("cct", static_cast<unsigned int>(m_Completions));
element->SetAttribute("cts", static_cast<unsigned int>(m_Timestamp)); element.SetAttribute("cts", static_cast<unsigned int>(m_Timestamp));
if (IsComplete()) { if (IsComplete()) {
return; return;
@ -155,27 +155,27 @@ void Mission::UpdateXml(tinyxml2::XMLElement* element) {
if (task->GetType() == eMissionTaskType::COLLECTION || if (task->GetType() == eMissionTaskType::COLLECTION ||
task->GetType() == eMissionTaskType::VISIT_PROPERTY) { task->GetType() == eMissionTaskType::VISIT_PROPERTY) {
auto* child = element->GetDocument()->NewElement("sv"); auto* child = element.GetDocument()->NewElement("sv");
child->SetAttribute("v", static_cast<unsigned int>(task->GetProgress())); child->SetAttribute("v", static_cast<unsigned int>(task->GetProgress()));
element->LinkEndChild(child); element.LinkEndChild(child);
for (auto unique : task->GetUnique()) { for (auto unique : task->GetUnique()) {
auto* uniqueElement = element->GetDocument()->NewElement("sv"); auto* uniqueElement = element.GetDocument()->NewElement("sv");
uniqueElement->SetAttribute("v", static_cast<unsigned int>(unique)); uniqueElement->SetAttribute("v", static_cast<unsigned int>(unique));
element->LinkEndChild(uniqueElement); element.LinkEndChild(uniqueElement);
} }
break; break;
} }
auto* child = element->GetDocument()->NewElement("sv"); auto* child = element.GetDocument()->NewElement("sv");
child->SetAttribute("v", static_cast<unsigned int>(task->GetProgress())); child->SetAttribute("v", static_cast<unsigned int>(task->GetProgress()));
element->LinkEndChild(child); element.LinkEndChild(child);
} }
} }

View File

@ -28,8 +28,8 @@ public:
Mission(MissionComponent* missionComponent, uint32_t missionId); Mission(MissionComponent* missionComponent, uint32_t missionId);
~Mission(); ~Mission();
void LoadFromXml(tinyxml2::XMLElement* element); void LoadFromXml(const tinyxml2::XMLElement& element);
void UpdateXml(tinyxml2::XMLElement* element); void UpdateXml(tinyxml2::XMLElement& element);
/** /**
* Returns the ID of this mission * Returns the ID of this mission

View File

@ -29,15 +29,14 @@ const BrickList& BrickDatabase::GetBricks(const LxfmlPath& lxfmlPath) {
return emptyCache; return emptyCache;
} }
auto* doc = new tinyxml2::XMLDocument(); tinyxml2::XMLDocument doc;
if (doc->Parse(data.str().c_str(), data.str().size()) != 0) { if (doc.Parse(data.str().c_str(), data.str().size()) != 0) {
delete doc;
return emptyCache; return emptyCache;
} }
BrickList parts; BrickList parts;
auto* lxfml = doc->FirstChildElement("LXFML"); auto* lxfml = doc.FirstChildElement("LXFML");
auto* bricks = lxfml->FirstChildElement("Bricks"); auto* bricks = lxfml->FirstChildElement("Bricks");
std::string searchTerm = "Brick"; std::string searchTerm = "Brick";
@ -86,7 +85,5 @@ const BrickList& BrickDatabase::GetBricks(const LxfmlPath& lxfmlPath) {
m_Cache[lxfmlPath] = parts; m_Cache[lxfmlPath] = parts;
delete doc;
return m_Cache[lxfmlPath]; return m_Cache[lxfmlPath];
} }

View File

@ -8,9 +8,15 @@ set(DGAME_DUTILITIES_SOURCES "BrickDatabase.cpp"
"SlashCommandHandler.cpp" "SlashCommandHandler.cpp"
"VanityUtilities.cpp") "VanityUtilities.cpp")
add_subdirectory(SlashCommands)
foreach(file ${DGAME_DUTILITIES_SLASHCOMMANDS})
set(DGAME_DUTILITIES_SOURCES ${DGAME_DUTILITIES_SOURCES} "SlashCommands/${file}")
endforeach()
add_library(dUtilities OBJECT ${DGAME_DUTILITIES_SOURCES}) add_library(dUtilities OBJECT ${DGAME_DUTILITIES_SOURCES})
target_precompile_headers(dUtilities REUSE_FROM dGameBase) target_precompile_headers(dUtilities REUSE_FROM dGameBase)
target_include_directories(dUtilities PUBLIC "." target_include_directories(dUtilities PUBLIC "." "SlashCommands"
PRIVATE PRIVATE
"${PROJECT_SOURCE_DIR}/dGame/dComponents" "${PROJECT_SOURCE_DIR}/dGame/dComponents"
"${PROJECT_SOURCE_DIR}/dGame/dInventory" # transitive via PossessableComponent.h "${PROJECT_SOURCE_DIR}/dGame/dInventory" # transitive via PossessableComponent.h

View File

@ -33,10 +33,10 @@ Precondition::Precondition(const uint32_t condition) {
return; return;
} }
this->type = static_cast<PreconditionType>(result.fieldIsNull(0) ? 0 : result.getIntField(0)); this->type = static_cast<PreconditionType>(result.fieldIsNull("type") ? 0 : result.getIntField("type"));
if (!result.fieldIsNull(1)) { if (!result.fieldIsNull("targetLOT")) {
std::istringstream stream(result.getStringField(1)); std::istringstream stream(result.getStringField("targetLOT"));
std::string token; std::string token;
while (std::getline(stream, token, ',')) { while (std::getline(stream, token, ',')) {
@ -45,7 +45,7 @@ Precondition::Precondition(const uint32_t condition) {
} }
} }
this->count = result.fieldIsNull(2) ? 1 : result.getIntField(2); this->count = result.fieldIsNull("targetCount") ? 1 : result.getIntField("targetCount");
result.finalize(); result.finalize();
} }

File diff suppressed because it is too large Load Diff

View File

@ -7,13 +7,28 @@
#define SLASHCOMMANDHANDLER_H #define SLASHCOMMANDHANDLER_H
#include "RakNetTypes.h" #include "RakNetTypes.h"
#include "eGameMasterLevel.h"
#include <string> #include <string>
class Entity; class Entity;
struct Command {
std::string help;
std::string info;
std::vector<std::string> aliases;
std::function<void(Entity*, const SystemAddress&,const std::string)> handle;
eGameMasterLevel requiredLevel = eGameMasterLevel::OPERATOR;
};
namespace SlashCommandHandler { namespace SlashCommandHandler {
void HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr); void HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr);
void SendAnnouncement(const std::string& title, const std::string& message); void SendAnnouncement(const std::string& title, const std::string& message);
void RegisterCommand(Command info);
void Startup();
}; };
namespace GMZeroCommands {
void Help(Entity* entity, const SystemAddress& sysAddr, const std::string args);
}
#endif // SLASHCOMMANDHANDLER_H #endif // SLASHCOMMANDHANDLER_H

View File

@ -0,0 +1,6 @@
set(DGAME_DUTILITIES_SLASHCOMMANDS
"DEVGMCommands.cpp"
"GMGreaterThanZeroCommands.cpp"
"GMZeroCommands.cpp"
PARENT_SCOPE
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
namespace DEVGMCommands {
void SetGMLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ToggleNameplate(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ToggleSkipCinematics(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Kill(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Metrics(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Announce(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetAnnTitle(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetAnnMsg(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ShutdownUniverse(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetMinifig(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void TestMap(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ReportProxPhys(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SpawnPhysicsVerts(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ActivateSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void AddMission(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Boost(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Unboost(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Buff(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void BuffMe(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void BuffMed(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ClearFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void CompleteMission(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void CreatePrivate(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void DebugUi(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Dismount(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ReloadConfig(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ForceSave(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Freecam(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void FreeMoney(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void GetNavmeshHeight(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void GiveUScore(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void GmAddItem(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Inspect(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ListSpawns(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void LocRow(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Lookup(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void PlayAnimation(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void PlayEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void PlayLvlFx(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void PlayRebuildFx(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Pos(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void RefillStats(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Reforge(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ResetMission(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Rot(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void RunMacro(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetControlScheme(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetCurrency(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetInventorySize(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetUiState(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Spawn(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SpawnGroup(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SpeedBoost(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void StartCelebration(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void StopEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Toggle(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void TpAll(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void TriggerSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void UnlockEmote(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetSkillSlot(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void AddFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void GetFactions(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetRewardCode(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Crash(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void RollLoot(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args);
}

View File

@ -0,0 +1,325 @@
#include "GMGreaterThanZeroCommands.h"
// Classes
#include "Character.h"
#include "ChatPackets.h"
#include "dServer.h"
#include "PlayerManager.h"
#include "User.h"
// Database
#include "Database.h"
// Components
#include "DestroyableComponent.h"
#include "PropertyManagementComponent.h"
// Enums
#include "eChatMessageType.h"
#include "eServerDisconnectIdentifiers.h"
#include "eObjectBits.h"
namespace GMGreaterThanZeroCommands {
void Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
if (splitArgs.size() == 1) {
auto* player = PlayerManager::GetPlayer(splitArgs[0]);
std::u16string username = GeneralUtils::UTF8ToUTF16(splitArgs[0]);
if (player == nullptr) {
ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + username);
return;
}
Game::server->Disconnect(player->GetSystemAddress(), eServerDisconnectIdentifiers::KICK);
ChatPackets::SendSystemMessage(sysAddr, u"Kicked: " + username);
} else {
ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /kick <username>");
}
}
void Ban(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
if (splitArgs.size() == 1) {
auto* player = PlayerManager::GetPlayer(splitArgs[0]);
uint32_t accountId = 0;
if (player == nullptr) {
auto characterInfo = Database::Get()->GetCharacterInfo(splitArgs[0]);
if (characterInfo) {
accountId = characterInfo->accountId;
}
if (accountId == 0) {
ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(splitArgs[0]));
return;
}
} else {
auto* character = player->GetCharacter();
auto* user = character != nullptr ? character->GetParentUser() : nullptr;
if (user) accountId = user->GetAccountID();
}
if (accountId != 0) Database::Get()->UpdateAccountBan(accountId, true);
if (player != nullptr) {
Game::server->Disconnect(player->GetSystemAddress(), eServerDisconnectIdentifiers::FREE_TRIAL_EXPIRED);
}
ChatPackets::SendSystemMessage(sysAddr, u"Banned: " + GeneralUtils::ASCIIToUTF16(splitArgs[0]));
} else {
ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /ban <username>");
}
}
void MailItem(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
if (splitArgs.size() < 2) return;
const auto& playerName = splitArgs[0];
auto playerInfo = Database::Get()->GetCharacterInfo(playerName);
uint32_t receiverID = 0;
if (!playerInfo) {
ChatPackets::SendSystemMessage(sysAddr, u"Failed to find that player");
return;
}
receiverID = playerInfo->id;
const auto lot = GeneralUtils::TryParse<LOT>(splitArgs.at(1));
if (!lot) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid item lot.");
return;
}
IMail::MailInfo mailInsert;
mailInsert.senderId = entity->GetObjectID();
mailInsert.senderUsername = "Darkflame Universe";
mailInsert.receiverId = receiverID;
mailInsert.recipient = playerName;
mailInsert.subject = "Lost item";
mailInsert.body = "This is a replacement item for one you lost.";
mailInsert.itemID = LWOOBJID_EMPTY;
mailInsert.itemLOT = lot.value();
mailInsert.itemSubkey = LWOOBJID_EMPTY;
mailInsert.itemCount = 1;
Database::Get()->InsertNewMail(mailInsert);
ChatPackets::SendSystemMessage(sysAddr, u"Mail sent");
}
void ApproveProperty(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
if (PropertyManagementComponent::Instance() != nullptr) {
PropertyManagementComponent::Instance()->UpdateApprovedStatus(true);
}
}
void Mute(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
if (splitArgs.size() >= 1) {
auto* player = PlayerManager::GetPlayer(splitArgs[0]);
uint32_t accountId = 0;
LWOOBJID characterId = 0;
if (player == nullptr) {
auto characterInfo = Database::Get()->GetCharacterInfo(splitArgs[0]);
if (characterInfo) {
accountId = characterInfo->accountId;
characterId = characterInfo->id;
GeneralUtils::SetBit(characterId, eObjectBits::CHARACTER);
GeneralUtils::SetBit(characterId, eObjectBits::PERSISTENT);
}
if (accountId == 0) {
ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(splitArgs[0]));
return;
}
} else {
auto* character = player->GetCharacter();
auto* user = character != nullptr ? character->GetParentUser() : nullptr;
if (user) accountId = user->GetAccountID();
characterId = player->GetObjectID();
}
time_t expire = 1; // Default to indefinate mute
if (splitArgs.size() >= 2) {
const auto days = GeneralUtils::TryParse<uint32_t>(splitArgs[1]);
if (!days) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid days.");
return;
}
std::optional<uint32_t> hours;
if (splitArgs.size() >= 3) {
hours = GeneralUtils::TryParse<uint32_t>(splitArgs[2]);
if (!hours) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid hours.");
return;
}
}
expire = time(NULL);
expire += 24 * 60 * 60 * days.value();
expire += 60 * 60 * hours.value_or(0);
}
if (accountId != 0) Database::Get()->UpdateAccountUnmuteTime(accountId, expire);
char buffer[32] = "brought up for review.\0";
if (expire != 1) {
std::tm* ptm = std::localtime(&expire);
// Format: Mo, 15.06.2009 20:20:00
std::strftime(buffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm);
}
const auto timeStr = GeneralUtils::ASCIIToUTF16(std::string(buffer));
ChatPackets::SendSystemMessage(sysAddr, u"Muted: " + GeneralUtils::UTF8ToUTF16(splitArgs[0]) + u" until " + timeStr);
//Notify chat about it
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE);
bitStream.Write(characterId);
bitStream.Write(expire);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
} else {
ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /mute <username> <days (optional)> <hours (optional)>");
}
}
void Fly(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
auto* character = entity->GetCharacter();
if (character) {
bool isFlying = character->GetIsFlying();
if (isFlying) {
GameMessages::SendSetJetPackMode(entity, false);
character->SetIsFlying(false);
} else {
float speedScale = 1.0f;
if (splitArgs.size() >= 1) {
const auto tempScaleStore = GeneralUtils::TryParse<float>(splitArgs.at(0));
if (tempScaleStore) {
speedScale = tempScaleStore.value();
} else {
ChatPackets::SendSystemMessage(sysAddr, u"Failed to parse speed scale argument.");
}
}
float airSpeed = 20 * speedScale;
float maxAirSpeed = 30 * speedScale;
float verticalVelocity = 1.5 * speedScale;
GameMessages::SendSetJetPackMode(entity, true, true, false, 167, airSpeed, maxAirSpeed, verticalVelocity);
character->SetIsFlying(true);
}
}
}
void AttackImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
if (splitArgs.empty()) return;
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
const auto state = GeneralUtils::TryParse<int32_t>(splitArgs[0]);
if (!state) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid state.");
return;
}
if (destroyableComponent) destroyableComponent->SetIsImmune(state.value());
}
void GmImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
if (splitArgs.empty()) return;
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
const auto state = GeneralUtils::TryParse<int32_t>(splitArgs[0]);
if (!state) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid state.");
return;
}
if (destroyableComponent) destroyableComponent->SetIsGMImmune(state.value());
}
void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
GameMessages::SendToggleGMInvis(entity->GetObjectID(), true, UNASSIGNED_SYSTEM_ADDRESS);
}
void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(args), UNASSIGNED_SYSTEM_ADDRESS);
}
void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
std::string name = entity->GetCharacter()->GetName() + " - " + args;
GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS);
}
void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
bool displayZoneData = true;
bool displayIndividualPlayers = true;
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
if (!splitArgs.empty() && !splitArgs.at(0).empty()) displayZoneData = splitArgs.at(0) == "1";
if (splitArgs.size() > 1) displayIndividualPlayers = splitArgs.at(1) == "1";
ShowAllRequest request {
.requestor = entity->GetObjectID(),
.displayZoneData = displayZoneData,
.displayIndividualPlayers = displayIndividualPlayers
};
CBITSTREAM;
request.Serialize(bitStream);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
}
void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
if (args.empty()) {
GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given");
return;
}
FindPlayerRequest request {
.requestor = entity->GetObjectID(),
.playerName = LUWString(args)
};
CBITSTREAM;
request.Serialize(bitStream);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
}
}

View File

@ -0,0 +1,15 @@
namespace GMGreaterThanZeroCommands {
void Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void MailItem(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Ban(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ApproveProperty(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Mute(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Fly(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void AttackImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void GmImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args);
}

View File

@ -0,0 +1,228 @@
#include "GMZeroCommands.h"
// Classes
#include "Amf3.h"
#include "BinaryPathFinder.h"
#include "ChatPackets.h"
#include "dServer.h"
#include "dZoneManager.h"
#include "Mail.h"
#include "PlayerManager.h"
#include "SlashCommandHandler.h"
#include "VanityUtilities.h"
#include "WorldPackets.h"
#include "ZoneInstanceManager.h"
// Components
#include "BuffComponent.h"
#include "CharacterComponent.h"
#include "DestroyableComponent.h"
#include "ScriptedActivityComponent.h"
#include "SkillComponent.h"
// Emuns
#include "eGameMasterLevel.h"
namespace GMZeroCommands {
void Pvp(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
auto* character = entity->GetComponent<CharacterComponent>();
if (character == nullptr) {
LOG("Failed to find character component!");
return;
}
character->SetPvpEnabled(!character->GetPvpEnabled());
Game::entityManager->SerializeEntity(entity);
std::stringstream message;
message << character->GetName() << " changed their PVP flag to " << std::to_string(character->GetPvpEnabled()) << "!";
ChatPackets::SendSystemMessage(UNASSIGNED_SYSTEM_ADDRESS, GeneralUtils::UTF8ToUTF16(message.str()), true);
}
void Who(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
ChatPackets::SendSystemMessage(
sysAddr,
u"Players in this instance: (" + GeneralUtils::to_u16string(PlayerManager::GetAllPlayers().size()) + u")"
);
for (auto* player : PlayerManager::GetAllPlayers()) {
const auto& name = player->GetCharacter()->GetName();
ChatPackets::SendSystemMessage(
sysAddr,
GeneralUtils::UTF8ToUTF16(player == entity ? name + " (you)" : name)
);
}
}
void Ping(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
if (!args.empty() && args.starts_with("-l")) {
std::stringstream message;
message << "Your latest ping: " << std::to_string(Game::server->GetLatestPing(sysAddr)) << "ms";
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(message.str()));
} else {
std::stringstream message;
message << "Your average ping: " << std::to_string(Game::server->GetPing(sysAddr)) << "ms";
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(message.str()));
}
}
void FixStats(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
// Reset skill component and buff component
auto* skillComponent = entity->GetComponent<SkillComponent>();
auto* buffComponent = entity->GetComponent<BuffComponent>();
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
// If any of the components are nullptr, return
if (skillComponent == nullptr || buffComponent == nullptr || destroyableComponent == nullptr) {
return;
}
// Reset skill component
skillComponent->Reset();
// Reset buff component
buffComponent->Reset();
// Fix the destroyable component
destroyableComponent->FixStats();
}
void Credits(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto& customText = VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/CREDITS.md").string());
{
AMFArrayValue args;
args.Insert("state", "Story");
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", args);
}
entity->AddCallbackTimer(0.5f, [customText, entity]() {
AMFArrayValue args;
args.Insert("visible", true);
args.Insert("text", customText);
LOG("Sending %s", customText.c_str());
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "ToggleStoryBox", args);
});
}
void Info(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto& customText = VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/INFO.md").string());
{
AMFArrayValue args;
args.Insert("state", "Story");
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", args);
}
entity->AddCallbackTimer(0.5f, [customText, entity]() {
AMFArrayValue args;
args.Insert("visible", true);
args.Insert("text", customText);
LOG("Sending %s", customText.c_str());
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "ToggleStoryBox", args);
});
}
void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto currentZone = Game::zoneManager->GetZone()->GetZoneID().GetMapID();
LWOMAPID newZone = 0;
if (currentZone == 1001 || currentZone % 100 == 0) {
ChatPackets::SendSystemMessage(sysAddr, u"You are not in an instanced zone.");
return;
} else {
newZone = (currentZone / 100) * 100;
}
// If new zone would be inaccessible, then default to Avant Gardens.
if (!Game::zoneManager->CheckIfAccessibleZone(newZone)) newZone = 1100;
ChatPackets::SendSystemMessage(sysAddr, u"Leaving zone...");
const auto objid = entity->GetObjectID();
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, newZone, 0, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
auto* entity = Game::entityManager->GetEntity(objid);
if (entity == nullptr) {
return;
}
const auto sysAddr = entity->GetSystemAddress();
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", entity->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
if (entity->GetCharacter()) {
entity->GetCharacter()->SetZoneID(zoneID);
entity->GetCharacter()->SetZoneInstance(zoneInstance);
entity->GetCharacter()->SetZoneClone(zoneClone);
}
entity->GetCharacter()->SaveXMLToDatabase();
WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift);
});
}
void Join(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
auto splitArgs = GeneralUtils::SplitString(args, ' ');
if (splitArgs.empty()) return;
ChatPackets::SendSystemMessage(sysAddr, u"Requesting private map...");
const auto& password = splitArgs[0];
ZoneInstanceManager::Instance()->RequestPrivateZone(Game::server, false, password, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
if (entity->GetCharacter()) {
entity->GetCharacter()->SetZoneID(zoneID);
entity->GetCharacter()->SetZoneInstance(zoneInstance);
entity->GetCharacter()->SetZoneClone(zoneClone);
}
entity->GetCharacter()->SaveXMLToDatabase();
WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift);
});
}
void Die(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
entity->Smash(entity->GetObjectID());
}
void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
ScriptedActivityComponent* scriptedActivityComponent = Game::zoneManager->GetZoneControlObject()->GetComponent<ScriptedActivityComponent>();
if (scriptedActivityComponent) { // check if user is in activity world and if so, they can't resurrect
ChatPackets::SendSystemMessage(sysAddr, u"You cannot resurrect in an activity world.");
return;
}
GameMessages::SendResurrect(entity);
}
void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
Mail::HandleNotificationRequest(entity->GetSystemAddress(), entity->GetObjectID());
}
void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto zoneId = Game::zoneManager->GetZone()->GetZoneID();
ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID())));
}
};

View File

@ -0,0 +1,15 @@
namespace GMZeroCommands {
void Help(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Credits(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Info(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Die(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Ping(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Pvp(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Who(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void FixStats(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Join(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args);
void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args);
}

View File

@ -12,6 +12,30 @@
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatMessageType.h" #include "eChatMessageType.h"
void ShowAllRequest::Serialize(RakNet::BitStream& bitStream) {
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::SHOW_ALL);
bitStream.Write(this->requestor);
bitStream.Write(this->displayZoneData);
bitStream.Write(this->displayIndividualPlayers);
}
void ShowAllRequest::Deserialize(RakNet::BitStream& inStream) {
inStream.Read(this->requestor);
inStream.Read(this->displayZoneData);
inStream.Read(this->displayIndividualPlayers);
}
void FindPlayerRequest::Serialize(RakNet::BitStream& bitStream) {
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WHO);
bitStream.Write(this->requestor);
bitStream.Write(this->playerName);
}
void FindPlayerRequest::Deserialize(RakNet::BitStream& inStream) {
inStream.Read(this->requestor);
inStream.Read(this->playerName);
}
void ChatPackets::SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message) { void ChatPackets::SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GENERAL_CHAT_MESSAGE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GENERAL_CHAT_MESSAGE);

View File

@ -11,6 +11,21 @@ struct SystemAddress;
#include <string> #include <string>
#include "dCommonVars.h" #include "dCommonVars.h"
struct ShowAllRequest{
LWOOBJID requestor = LWOOBJID_EMPTY;
bool displayZoneData = true;
bool displayIndividualPlayers = true;
void Serialize(RakNet::BitStream& bitStream);
void Deserialize(RakNet::BitStream& inStream);
};
struct FindPlayerRequest{
LWOOBJID requestor = LWOOBJID_EMPTY;
LUWString playerName;
void Serialize(RakNet::BitStream& bitStream);
void Deserialize(RakNet::BitStream& inStream);
};
namespace ChatPackets { namespace ChatPackets {
void SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message); void SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message);
void SendSystemMessage(const SystemAddress& sysAddr, const std::u16string& message, bool broadcast = false); void SendSystemMessage(const SystemAddress& sysAddr, const std::u16string& message, bool broadcast = false);

View File

@ -12,6 +12,15 @@
#include <iostream> #include <iostream>
void HTTPMonitorInfo::Serialize(RakNet::BitStream &bitStream) const {
bitStream.Write(port);
bitStream.Write<uint8_t>(openWeb);
bitStream.Write<uint8_t>(supportsSum);
bitStream.Write<uint8_t>(supportsDetail);
bitStream.Write<uint8_t>(supportsWho);
bitStream.Write<uint8_t>(supportsObjects);
}
void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone) { void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone) {
RakNet::BitStream bitStream; RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::LOAD_STATIC_ZONE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::LOAD_STATIC_ZONE);
@ -160,3 +169,18 @@ void WorldPackets::SendGMLevelChange(const SystemAddress& sysAddr, bool success,
SEND_PACKET; SEND_PACKET;
} }
void WorldPackets::SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::HTTP_MONITOR_INFO_RESPONSE);
info.Serialize(bitStream);
SEND_PACKET;
}
void WorldPackets::SendDebugOuput(const SystemAddress& sysAddr, const std::string& data){
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::DEBUG_OUTPUT);
bitStream.Write<uint32_t>(data.size());
bitStream.Write(data);
SEND_PACKET;
}

View File

@ -10,6 +10,19 @@ struct SystemAddress;
enum class eGameMasterLevel : uint8_t; enum class eGameMasterLevel : uint8_t;
enum class eCharacterCreationResponse : uint8_t; enum class eCharacterCreationResponse : uint8_t;
enum class eRenameResponse : uint8_t; enum class eRenameResponse : uint8_t;
namespace RakNet {
class BitStream;
};
struct HTTPMonitorInfo {
uint16_t port = 80;
bool openWeb = false;
bool supportsSum = false;
bool supportsDetail = false;
bool supportsWho = false;
bool supportsObjects = false;
void Serialize(RakNet::BitStream &bitstream) const;
};
namespace WorldPackets { namespace WorldPackets {
void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone); void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone);
@ -21,6 +34,8 @@ namespace WorldPackets {
void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm);
void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector<std::pair<uint8_t, uint8_t>> unacceptedItems); void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector<std::pair<uint8_t, uint8_t>> unacceptedItems);
void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel); void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel);
void SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info);
void SendDebugOuput(const SystemAddress& sysAddr, const std::string& data);
} }
#endif // WORLDPACKETS_H #endif // WORLDPACKETS_H

View File

@ -3,8 +3,6 @@
#include "dpShapeBox.h" #include "dpShapeBox.h"
#include "dpGrid.h" #include "dpGrid.h"
#include <iostream>
dpEntity::dpEntity(const LWOOBJID& objectID, dpShapeType shapeType, bool isStatic) { dpEntity::dpEntity(const LWOOBJID& objectID, dpShapeType shapeType, bool isStatic) {
m_ObjectID = objectID; m_ObjectID = objectID;
m_IsStatic = isStatic; m_IsStatic = isStatic;
@ -76,16 +74,17 @@ void dpEntity::CheckCollision(dpEntity* other) {
return; return;
} }
bool wasFound = m_CurrentlyCollidingObjects.contains(other->GetObjectID()); const auto objId = other->GetObjectID();
const auto objItr = m_CurrentlyCollidingObjects.find(objId);
bool isColliding = m_CollisionShape->IsColliding(other->GetShape()); const bool wasFound = objItr != m_CurrentlyCollidingObjects.cend();
const bool isColliding = m_CollisionShape->IsColliding(other->GetShape());
if (isColliding && !wasFound) { if (isColliding && !wasFound) {
m_CurrentlyCollidingObjects.emplace(other->GetObjectID(), other); m_CurrentlyCollidingObjects.emplace(objId);
m_NewObjects.push_back(other); m_NewObjects.push_back(objId);
} else if (!isColliding && wasFound) { } else if (!isColliding && wasFound) {
m_CurrentlyCollidingObjects.erase(other->GetObjectID()); m_CurrentlyCollidingObjects.erase(objItr);
m_RemovedObjects.push_back(other); m_RemovedObjects.push_back(objId);
} }
} }

View File

@ -2,7 +2,8 @@
#include "NiPoint3.h" #include "NiPoint3.h"
#include "NiQuaternion.h" #include "NiQuaternion.h"
#include <vector> #include <vector>
#include <map> #include <unordered_set>
#include <span>
#include "dCommonVars.h" #include "dCommonVars.h"
#include "dpCommon.h" #include "dpCommon.h"
@ -49,9 +50,9 @@ public:
bool GetSleeping() const { return m_Sleeping; } bool GetSleeping() const { return m_Sleeping; }
void SetSleeping(bool value) { m_Sleeping = value; } void SetSleeping(bool value) { m_Sleeping = value; }
const std::vector<dpEntity*>& GetNewObjects() const { return m_NewObjects; } std::span<const LWOOBJID> GetNewObjects() const { return m_NewObjects; }
const std::vector<dpEntity*>& GetRemovedObjects() const { return m_RemovedObjects; } std::span<const LWOOBJID> GetRemovedObjects() const { return m_RemovedObjects; }
const std::map<LWOOBJID, dpEntity*>& GetCurrentlyCollidingObjects() const { return m_CurrentlyCollidingObjects; } const std::unordered_set<LWOOBJID>& GetCurrentlyCollidingObjects() const { return m_CurrentlyCollidingObjects; }
void PreUpdate() { m_NewObjects.clear(); m_RemovedObjects.clear(); } void PreUpdate() { m_NewObjects.clear(); m_RemovedObjects.clear(); }
@ -80,7 +81,7 @@ private:
bool m_IsGargantuan = false; bool m_IsGargantuan = false;
std::vector<dpEntity*> m_NewObjects; std::vector<LWOOBJID> m_NewObjects;
std::vector<dpEntity*> m_RemovedObjects; std::vector<LWOOBJID> m_RemovedObjects;
std::map<LWOOBJID, dpEntity*> m_CurrentlyCollidingObjects; std::unordered_set<LWOOBJID> m_CurrentlyCollidingObjects;
}; };

View File

@ -21,7 +21,7 @@ void WanderingVendor::OnProximityUpdate(Entity* self, Entity* entering, std::str
const auto proxObjs = proximityMonitorComponent->GetProximityObjects("playermonitor"); const auto proxObjs = proximityMonitorComponent->GetProximityObjects("playermonitor");
bool foundPlayer = false; bool foundPlayer = false;
for (const auto id : proxObjs | std::views::keys) { for (const auto id : proxObjs) {
auto* entity = Game::entityManager->GetEntity(id); auto* entity = Game::entityManager->GetEntity(id);
if (entity && entity->IsPlayer()) { if (entity && entity->IsPlayer()) {
foundPlayer = true; foundPlayer = true;

View File

@ -17,7 +17,9 @@ void TokenConsoleServer::OnUse(Entity* self, Entity* user) {
inv->RemoveItem(6194, bricksToTake); inv->RemoveItem(6194, bricksToTake);
//play sound //play sound
GameMessages::SendPlayNDAudioEmitter(self, user->GetSystemAddress(), "947d0d52-c7f8-4516-8dee-e1593a7fd1d1"); if (self->HasVar(u"sound1")) {
GameMessages::SendPlayNDAudioEmitter(self, user->GetSystemAddress(), self->GetVarAsString(u"sound1"));
}
//figure out which faction the player belongs to: //figure out which faction the player belongs to:
auto character = user->GetCharacter(); auto character = user->GetCharacter();

View File

@ -39,7 +39,7 @@ void NsTokenConsoleServer::OnUse(Entity* self, Entity* user) {
const auto useSound = self->GetVar<std::string>(u"sound1"); const auto useSound = self->GetVar<std::string>(u"sound1");
if (!useSound.empty()) { if (!useSound.empty()) {
GameMessages::SendPlayNDAudioEmitter(self, UNASSIGNED_SYSTEM_ADDRESS, useSound); GameMessages::SendPlayNDAudioEmitter(self, user->GetSystemAddress(), useSound);
} }
// Player must be in faction to interact with this entity. // Player must be in faction to interact with this entity.

View File

@ -23,13 +23,13 @@ void AgBusDoor::OnProximityUpdate(Entity* self, Entity* entering, std::string na
m_Counter = 0; m_Counter = 0;
m_OuterCounter = 0; m_OuterCounter = 0;
for (const auto& pair : proximityMonitorComponent->GetProximityObjects("busDoor")) { for (const auto id : proximityMonitorComponent->GetProximityObjects("busDoor")) {
auto* entity = Game::entityManager->GetEntity(pair.first); const auto* const entity = Game::entityManager->GetEntity(id);
if (entity != nullptr && entity->IsPlayer()) m_Counter++; if (entity != nullptr && entity->IsPlayer()) m_Counter++;
} }
for (const auto& pair : proximityMonitorComponent->GetProximityObjects("busDoorOuter")) { for (const auto id : proximityMonitorComponent->GetProximityObjects("busDoorOuter")) {
auto* entity = Game::entityManager->GetEntity(pair.first); const auto* const entity = Game::entityManager->GetEntity(id);
if (entity != nullptr && entity->IsPlayer()) m_OuterCounter++; if (entity != nullptr && entity->IsPlayer()) m_OuterCounter++;
} }

View File

@ -66,7 +66,7 @@
#include "eObjectBits.h" #include "eObjectBits.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eServerMessageType.h" #include "eServerMessageType.h"
#include "eChatInternalMessageType.h" #include "eChatMessageType.h"
#include "eWorldMessageType.h" #include "eWorldMessageType.h"
#include "eMasterMessageType.h" #include "eMasterMessageType.h"
#include "eGameMessageType.h" #include "eGameMessageType.h"
@ -79,6 +79,7 @@
#include "PositionUpdate.h" #include "PositionUpdate.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "eLoginResponse.h" #include "eLoginResponse.h"
#include "SlashCommandHandler.h"
namespace Game { namespace Game {
Logger* logger = nullptr; Logger* logger = nullptr;
@ -313,6 +314,9 @@ int main(int argc, char** argv) {
uint32_t sqlPingTime = 10 * 60 * currentFramerate; // 10 minutes in frames uint32_t sqlPingTime = 10 * 60 * currentFramerate; // 10 minutes in frames
uint32_t emptyShutdownTime = (cloneID == 0 ? 30 : 5) * 60 * currentFramerate; // 30 minutes for main worlds, 5 for all others. uint32_t emptyShutdownTime = (cloneID == 0 ? 30 : 5) * 60 * currentFramerate; // 30 minutes for main worlds, 5 for all others.
// Register slash commands if not in zone 0
if (zoneID != 0) SlashCommandHandler::Startup();
Game::logger->Flush(); // once immediately before the main loop Game::logger->Flush(); // once immediately before the main loop
while (true) { while (true) {
Metrics::StartMeasurement(MetricVariable::Frame); Metrics::StartMeasurement(MetricVariable::Frame);
@ -541,9 +545,9 @@ void HandlePacketChat(Packet* packet) {
} }
if (packet->data[0] == ID_USER_PACKET_ENUM) { if (packet->data[0] == ID_USER_PACKET_ENUM) {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) { if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
switch (static_cast<eChatInternalMessageType>(packet->data[3])) { switch (static_cast<eChatMessageType>(packet->data[3])) {
case eChatInternalMessageType::ROUTE_TO_PLAYER: { case eChatMessageType::WORLD_ROUTE_PACKET: {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
LWOOBJID playerID; LWOOBJID playerID;
inStream.Read(playerID); inStream.Read(playerID);
@ -560,11 +564,10 @@ void HandlePacketChat(Packet* packet) {
} }
SEND_PACKET; //send routed packet to player SEND_PACKET; //send routed packet to player
break; break;
} }
case eChatInternalMessageType::ANNOUNCEMENT: { case eChatMessageType::GM_ANNOUNCE: {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
std::string title; std::string title;
@ -597,7 +600,7 @@ void HandlePacketChat(Packet* packet) {
break; break;
} }
case eChatInternalMessageType::MUTE_UPDATE: { case eChatMessageType::GM_MUTE: {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
LWOOBJID playerId; LWOOBJID playerId;
time_t expire = 0; time_t expire = 0;
@ -616,7 +619,7 @@ void HandlePacketChat(Packet* packet) {
break; break;
} }
case eChatInternalMessageType::TEAM_UPDATE: { case eChatMessageType::TEAM_GET_STATUS: {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
LWOOBJID teamID = 0; LWOOBJID teamID = 0;
@ -650,9 +653,8 @@ void HandlePacketChat(Packet* packet) {
break; break;
} }
default: default:
LOG("Received an unknown chat internal: %i", int(packet->data[3])); LOG("Received an unknown chat: %i", int(packet->data[3]));
} }
} }
} }
@ -817,7 +819,7 @@ void HandlePacket(Packet* packet) {
{ {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::UNEXPECTED_DISCONNECT);
bitStream.Write(user->GetLoggedInChar()); bitStream.Write(user->GetLoggedInChar());
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
} }
@ -986,7 +988,7 @@ void HandlePacket(Packet* packet) {
// This means we swapped characters and we need to remove the previous player from the container. // This means we swapped characters and we need to remove the previous player from the container.
if (static_cast<uint32_t>(lastCharacter) != playerID) { if (static_cast<uint32_t>(lastCharacter) != playerID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::UNEXPECTED_DISCONNECT);
bitStream.Write(lastCharacter); bitStream.Write(lastCharacter);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
} }
@ -1132,7 +1134,7 @@ void HandlePacket(Packet* packet) {
const auto& playerName = character->GetName(); const auto& playerName = character->GetName();
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::LOGIN_SESSION_NOTIFY);
bitStream.Write(player->GetObjectID()); bitStream.Write(player->GetObjectID());
bitStream.Write<uint32_t>(playerName.size()); bitStream.Write<uint32_t>(playerName.size());
for (size_t i = 0; i < playerName.size(); i++) { for (size_t i = 0; i < playerName.size(); i++) {

View File

@ -15,12 +15,12 @@ TEST_F(EncodingTest, TestEncodingHello) {
originalWord = "Hello World!"; originalWord = "Hello World!";
originalWordSv = originalWord; originalWordSv = originalWord;
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'H'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'H');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'e'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'e');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'l'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'l');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'l'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'l');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'o'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 'o');
EXPECT_EQ(GeneralUtils::_NextUTF8Char(originalWordSv, out), true); EXPECT_EQ(GeneralUtils::details::_NextUTF8Char(originalWordSv, out), true);
EXPECT_EQ(GeneralUtils::UTF8ToUTF16("Hello World!"), u"Hello World!"); EXPECT_EQ(GeneralUtils::UTF8ToUTF16("Hello World!"), u"Hello World!");
}; };
@ -29,15 +29,15 @@ TEST_F(EncodingTest, TestEncodingUmlaut) {
originalWord = reinterpret_cast<const char*>(u8"Frühling"); originalWord = reinterpret_cast<const char*>(u8"Frühling");
originalWordSv = originalWord; originalWordSv = originalWord;
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'F'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'F');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'r'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'r');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'ü'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'ü');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'h'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'h');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'l'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'l');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'i'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'i');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'n'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'n');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'g'); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'g');
EXPECT_EQ(GeneralUtils::_NextUTF8Char(originalWordSv, out), false); EXPECT_EQ(GeneralUtils::details::_NextUTF8Char(originalWordSv, out), false);
EXPECT_EQ(GeneralUtils::UTF8ToUTF16("Frühling"), u"Frühling"); EXPECT_EQ(GeneralUtils::UTF8ToUTF16("Frühling"), u"Frühling");
}; };
@ -46,10 +46,10 @@ TEST_F(EncodingTest, TestEncodingChinese) {
originalWord = "中文字"; originalWord = "中文字";
originalWordSv = originalWord; originalWordSv = originalWord;
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U''); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U''); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'');
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U''); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'');
EXPECT_EQ(GeneralUtils::_NextUTF8Char(originalWordSv, out), false); EXPECT_EQ(GeneralUtils::details::_NextUTF8Char(originalWordSv, out), false);
EXPECT_EQ(GeneralUtils::UTF8ToUTF16("中文字"), u"中文字"); EXPECT_EQ(GeneralUtils::UTF8ToUTF16("中文字"), u"中文字");
}; };
@ -58,11 +58,11 @@ TEST_F(EncodingTest, TestEncodingEmoji) {
originalWord = "👨‍⚖️"; originalWord = "👨‍⚖️";
originalWordSv = originalWord; originalWordSv = originalWord;
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 0x1F468); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 0x1F468);
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 0x200D); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 0x200D);
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 0x2696); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 0x2696);
GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 0xFE0F); GeneralUtils::details::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, 0xFE0F);
EXPECT_EQ(GeneralUtils::_NextUTF8Char(originalWordSv, out), false); EXPECT_EQ(GeneralUtils::details::_NextUTF8Char(originalWordSv, out), false);
EXPECT_EQ(GeneralUtils::UTF8ToUTF16("👨‍⚖️"), u"👨‍⚖️"); EXPECT_EQ(GeneralUtils::UTF8ToUTF16("👨‍⚖️"), u"👨‍⚖️");
}; };

View File

@ -121,7 +121,7 @@ TEST(MagicEnumTest, eGameMessageTypeTest) {
namespace { namespace {
template <typename T> template <typename T>
void AssertEnumArraySorted(const T& eArray) { void AssertEnumArraySorted(const T& eArray) {
for (int i = 0; i < eArray->size(); ++i) { for (int i = 0; i < eArray->size() - 1; ++i) {
const auto entryCurr = eArray->at(i).first; const auto entryCurr = eArray->at(i).first;
LOG_EARRAY(eArray, i, entryCurr); LOG_EARRAY(eArray, i, entryCurr);
const auto entryNext = eArray->at(++i).first; const auto entryNext = eArray->at(++i).first;

View File

@ -36,10 +36,11 @@
#include "sqlite3.h" #include "sqlite3.h"
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <exception>
#define CPPSQLITE_ERROR 1000 #define CPPSQLITE_ERROR 1000
class CppSQLite3Exception class CppSQLite3Exception : public std::exception
{ {
public: public:
@ -55,6 +56,8 @@ public:
const char* errorMessage() { return mpszErrMess; } const char* errorMessage() { return mpszErrMess; }
const char* what() const noexcept override { return mpszErrMess; }
static const char* errorCodeAsString(int nErrCode); static const char* errorCodeAsString(int nErrCode);
private: private: