diff --git a/CMakeLists.txt b/CMakeLists.txt index 00c12e28..cd657772 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -314,6 +314,7 @@ add_subdirectory(dGame) add_subdirectory(dZoneManager) add_subdirectory(dNavigation) add_subdirectory(dPhysics) +add_subdirectory(dServer) # Create a list of common libraries shared between all binaries set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp" "magic_enum") diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index c2cfbfcc..5593f0e1 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -25,6 +25,9 @@ #include "eAuthMessageType.h" #include "Game.h" +#include "Server.h" + + namespace Game { Logger* logger = nullptr; dServer* server = nullptr; @@ -33,7 +36,6 @@ namespace Game { std::mt19937 randomEngine; } -Logger* SetupLogger(); void HandlePacket(Packet* packet); int main(int argc, char** argv) { @@ -46,14 +48,11 @@ int main(int argc, char** argv) { std::signal(SIGINT, Game::OnSignal); std::signal(SIGTERM, Game::OnSignal); - //Create all the objects we need to run our service: - Game::logger = SetupLogger(); - if (!Game::logger) return EXIT_FAILURE; - - //Read our config: Game::config = new dConfig("authconfig.ini"); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); + + //Create all the objects we need to run our service: + Server::SetupLogger("AuthServer"); + if (!Game::logger) return EXIT_FAILURE; LOG("Starting Auth server..."); LOG("Version: %s", PROJECT_VERSION); @@ -162,18 +161,6 @@ int main(int argc, char** argv) { return EXIT_SUCCESS; } -Logger* SetupLogger() { - std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/AuthServer_" + std::to_string(time(nullptr)) + ".log")).string(); - bool logToConsole = false; - bool logDebugStatements = false; -#ifdef _DEBUG - logToConsole = true; - logDebugStatements = true; -#endif - - return new Logger(logPath, logToConsole, logDebugStatements); -} - void HandlePacket(Packet* packet) { if (packet->length < 4) return; diff --git a/dAuthServer/CMakeLists.txt b/dAuthServer/CMakeLists.txt index ebcbccad..7dcbf041 100644 --- a/dAuthServer/CMakeLists.txt +++ b/dAuthServer/CMakeLists.txt @@ -1,3 +1,7 @@ add_executable(AuthServer "AuthServer.cpp") -target_link_libraries(AuthServer ${COMMON_LIBRARIES}) + +target_link_libraries(AuthServer ${COMMON_LIBRARIES} dServer) + +target_include_directories(AuthServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer) + add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") diff --git a/dChatServer/CMakeLists.txt b/dChatServer/CMakeLists.txt index b6a3efb8..cc4cee1f 100644 --- a/dChatServer/CMakeLists.txt +++ b/dChatServer/CMakeLists.txt @@ -6,7 +6,9 @@ set(DCHATSERVER_SOURCES add_executable(ChatServer "ChatServer.cpp") add_library(dChatServer ${DCHATSERVER_SOURCES}) +target_include_directories(dChatServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer) add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter) -target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer) +target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer) + diff --git a/dChatServer/ChatIgnoreList.cpp b/dChatServer/ChatIgnoreList.cpp index 94d1ac7a..3b5103f0 100644 --- a/dChatServer/ChatIgnoreList.cpp +++ b/dChatServer/ChatIgnoreList.cpp @@ -26,37 +26,36 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) { LWOOBJID playerId; inStream.Read(playerId); - auto* receiver = Game::playerContainer.GetPlayerData(playerId); + auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId); if (!receiver) { LOG("Tried to get ignore list, but player %llu not found in container", playerId); return; } - if (!receiver->ignoredPlayers.empty()) { - LOG_DEBUG("Player %llu already has an ignore list", playerId); - return; - } + if (!receiver.ignoredPlayers.empty()) { + LOG_DEBUG("Player %llu already has an ignore list, but is requesting it again.", playerId); + } else { + auto ignoreList = Database::Get()->GetIgnoreList(static_cast(playerId)); + if (ignoreList.empty()) { + LOG_DEBUG("Player %llu has no ignores", playerId); + return; + } - auto ignoreList = Database::Get()->GetIgnoreList(static_cast(playerId)); - if (ignoreList.empty()) { - LOG_DEBUG("Player %llu has no ignores", playerId); - return; - } - - for (auto& ignoredPlayer : ignoreList) { - receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayer.id, ignoredPlayer.name }); - GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::CHARACTER); - GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::PERSISTENT); + for (auto& ignoredPlayer : ignoreList) { + receiver.ignoredPlayers.emplace_back(ignoredPlayer.name, ignoredPlayer.id); + GeneralUtils::SetBit(receiver.ignoredPlayers.back().playerId, eObjectBits::CHARACTER); + GeneralUtils::SetBit(receiver.ignoredPlayers.back().playerId, eObjectBits::PERSISTENT); + } } CBITSTREAM; - WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::GET_IGNORE); + WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::GET_IGNORE); bitStream.Write(false); // Probably is Is Free Trial, but we don't care about that bitStream.Write(0); // literally spacing due to struct alignment - bitStream.Write(receiver->ignoredPlayers.size()); - for (const auto& ignoredPlayer : receiver->ignoredPlayers) { + bitStream.Write(receiver.ignoredPlayers.size()); + for (const auto& ignoredPlayer : receiver.ignoredPlayers) { bitStream.Write(ignoredPlayer.playerId); bitStream.Write(LUWString(ignoredPlayer.playerName, 36)); } @@ -69,14 +68,14 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { LWOOBJID playerId; inStream.Read(playerId); - auto* receiver = Game::playerContainer.GetPlayerData(playerId); + auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId); if (!receiver) { LOG("Tried to get ignore list, but player %llu not found in container", playerId); return; } constexpr int32_t MAX_IGNORES = 32; - if (receiver->ignoredPlayers.size() > MAX_IGNORES) { + if (receiver.ignoredPlayers.size() > MAX_IGNORES) { LOG_DEBUG("Player %llu has too many ignores", playerId); return; } @@ -88,21 +87,21 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { std::string toIgnoreStr = toIgnoreName.GetAsString(); CBITSTREAM; - WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::ADD_IGNORE); + WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::ADD_IGNORE); // Check if the player exists LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY; - if (toIgnoreStr == receiver->playerName || toIgnoreStr.find("[GM]") == 0) { + if (toIgnoreStr == receiver.playerName || toIgnoreStr.find("[GM]") == 0) { LOG_DEBUG("Player %llu tried to ignore themselves", playerId); bitStream.Write(ChatIgnoreList::AddResponse::GENERAL_ERROR); - } else if (std::count(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), toIgnoreStr) > 0) { + } else if (std::count(receiver.ignoredPlayers.begin(), receiver.ignoredPlayers.end(), toIgnoreStr) > 0) { LOG_DEBUG("Player %llu is already ignoring %s", playerId, toIgnoreStr.c_str()); bitStream.Write(ChatIgnoreList::AddResponse::ALREADY_IGNORED); } else { // Get the playerId falling back to query if not online - auto* playerData = Game::playerContainer.GetPlayerData(toIgnoreStr); + const auto& playerData = Game::playerContainer.GetPlayerData(toIgnoreStr); if (!playerData) { // Fall back to query auto player = Database::Get()->GetCharacterInfo(toIgnoreStr); @@ -112,7 +111,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { ignoredPlayerId = player->id; } } else { - ignoredPlayerId = playerData->playerID; + ignoredPlayerId = playerData.playerID; } if (ignoredPlayerId != LWOOBJID_EMPTY) { @@ -120,7 +119,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER); GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::PERSISTENT); - receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayerId, toIgnoreStr }); + receiver.ignoredPlayers.emplace_back(toIgnoreStr, ignoredPlayerId); LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str()); bitStream.Write(ChatIgnoreList::AddResponse::SUCCESS); @@ -141,7 +140,7 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) { LWOOBJID playerId; inStream.Read(playerId); - auto* receiver = Game::playerContainer.GetPlayerData(playerId); + auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId); if (!receiver) { LOG("Tried to get ignore list, but player %llu not found in container", playerId); return; @@ -153,17 +152,17 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) { inStream.Read(removedIgnoreName); std::string removedIgnoreStr = removedIgnoreName.GetAsString(); - auto toRemove = std::remove(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), removedIgnoreStr); - if (toRemove == receiver->ignoredPlayers.end()) { + auto toRemove = std::remove(receiver.ignoredPlayers.begin(), receiver.ignoredPlayers.end(), removedIgnoreStr); + if (toRemove == receiver.ignoredPlayers.end()) { LOG_DEBUG("Player %llu is not ignoring %s", playerId, removedIgnoreStr.c_str()); return; } Database::Get()->RemoveIgnore(static_cast(playerId), static_cast(toRemove->playerId)); - receiver->ignoredPlayers.erase(toRemove, receiver->ignoredPlayers.end()); + receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end()); CBITSTREAM; - WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::REMOVE_IGNORE); + WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::REMOVE_IGNORE); bitStream.Write(0); LUWString playerNameSend(removedIgnoreStr, 33); diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index d1f0fc74..4bdddd68 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -25,7 +25,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { LWOOBJID playerID = 0; inStream.Read(playerID); - auto player = Game::playerContainer.GetPlayerData(playerID); + auto& player = Game::playerContainer.GetPlayerDataMutable(playerID); if (!player) return; auto friendsList = Database::Get()->GetFriendsList(playerID); @@ -37,15 +37,15 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER); fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs - if (fd.isBestFriend) player->countOfBestFriends += 1; + if (fd.isBestFriend) player.countOfBestFriends += 1; fd.friendName = friendData.friendName; //Now check if they're online: - auto fr = Game::playerContainer.GetPlayerData(fd.friendID); + const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID); if (fr) { fd.isOnline = true; - fd.zoneID = fr->zoneID; + fd.zoneID = fr.zoneID; //Since this friend is online, we need to update them on the fact that we've just logged in: SendFriendUpdate(fr, player, 1, fd.isBestFriend); @@ -54,7 +54,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { fd.zoneID = LWOZONEID(); } - player->friends.push_back(fd); + player.friends.push_back(fd); } //Now, we need to send the friendlist to the server they came from: @@ -66,13 +66,13 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE); bitStream.Write(0); bitStream.Write(1); //Length of packet -- just writing one as it doesn't matter, client skips it. - bitStream.Write(player->friends.size()); + bitStream.Write(player.friends.size()); - for (auto& data : player->friends) { + for (const auto& data : player.friends) { data.Serialize(bitStream); } - SystemAddress sysAddr = player->sysAddr; + SystemAddress sysAddr = player.sysAddr; SEND_PACKET; } @@ -95,51 +95,49 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { char isBestFriendRequest{}; inStream.Read(isBestFriendRequest); - auto requestor = Game::playerContainer.GetPlayerData(requestorPlayerID); + auto& requestor = Game::playerContainer.GetPlayerDataMutable(requestorPlayerID); if (!requestor) { LOG("No requestor player %llu sent to %s found.", requestorPlayerID, playerName.c_str()); return; } - if (requestor->playerName == playerName) { + if (requestor.playerName == playerName) { SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN); return; }; - std::unique_ptr requestee(Game::playerContainer.GetPlayerData(playerName)); + + auto& requestee = Game::playerContainer.GetPlayerDataMutable(playerName); // Check if player is online first if (isBestFriendRequest && !requestee) { - for (auto friendDataCandidate : requestor->friends) { - if (friendDataCandidate.friendName == playerName) { - requestee.reset(new PlayerData()); - // Setup the needed info since you can add a best friend offline. - requestee->playerID = friendDataCandidate.friendID; - requestee->playerName = friendDataCandidate.friendName; - requestee->zoneID = LWOZONEID(); + for (auto& friendDataCandidate : requestor.friends) { + if (friendDataCandidate.friendName != playerName) continue; + // Setup the needed info since you can add a best friend offline. + requestee.playerID = friendDataCandidate.friendID; + requestee.playerName = friendDataCandidate.friendName; + requestee.zoneID = LWOZONEID(); - FriendData requesteeFriendData{}; - requesteeFriendData.friendID = requestor->playerID; - requesteeFriendData.friendName = requestor->playerName; - requesteeFriendData.isFTP = false; - requesteeFriendData.isOnline = false; - requesteeFriendData.zoneID = requestor->zoneID; - requestee->friends.push_back(requesteeFriendData); - requestee->sysAddr = UNASSIGNED_SYSTEM_ADDRESS; - break; - } + FriendData requesteeFriendData{}; + requesteeFriendData.friendID = requestor.playerID; + requesteeFriendData.friendName = requestor.playerName; + requesteeFriendData.isFTP = false; + requesteeFriendData.isOnline = false; + requesteeFriendData.zoneID = requestor.zoneID; + requestee.friends.push_back(requesteeFriendData); + requestee.sysAddr = UNASSIGNED_SYSTEM_ADDRESS; + break; } } // If at this point we dont have a target, then they arent online and we cant send the request. // Send the response code that corresponds to what the error is. if (!requestee) { - requestee.reset(new PlayerData()); - requestee->playerName = playerName; + requestee.playerName = playerName; auto responseType = Database::Get()->GetCharacterInfo(playerName) ? eAddFriendResponseType::NOTONLINE : eAddFriendResponseType::INVALIDCHARACTER; - SendFriendResponse(requestor, requestee.get(), responseType); + SendFriendResponse(requestor, requestee, responseType); return; } @@ -147,7 +145,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { uint8_t oldBestFriendStatus{}; uint8_t bestFriendStatus{}; - auto bestFriendInfo = Database::Get()->GetBestFriendStatus(requestorPlayerID, requestee->playerID); + auto bestFriendInfo = Database::Get()->GetBestFriendStatus(requestorPlayerID, requestee.playerID); if (bestFriendInfo) { // Get the IDs LWOOBJID queryPlayerID = bestFriendInfo->playerCharacterId; @@ -174,51 +172,48 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { // Only do updates if there was a change in the bff status. if (oldBestFriendStatus != bestFriendStatus) { auto maxBestFriends = Game::playerContainer.GetMaxNumberOfBestFriends(); - if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) { - if (requestee->countOfBestFriends >= maxBestFriends) { - SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false); + if (requestee.countOfBestFriends >= maxBestFriends || requestor.countOfBestFriends >= maxBestFriends) { + if (requestee.countOfBestFriends >= maxBestFriends) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::THEIRFRIENDLISTFULL, false); } - if (requestor->countOfBestFriends >= maxBestFriends) { - SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false); + if (requestor.countOfBestFriends >= maxBestFriends) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::YOURFRIENDSLISTFULL, false); } } else { // Then update the database with this new info. - Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee->playerID, bestFriendStatus); + Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee.playerID, bestFriendStatus); // Sent the best friend update here if the value is 3 if (bestFriendStatus == 3U) { - requestee->countOfBestFriends += 1; - requestor->countOfBestFriends += 1; - if (requestee->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee.get(), requestor, eAddFriendResponseType::ACCEPTED, false, true); - if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::ACCEPTED, false, true); - for (auto& friendData : requestor->friends) { - if (friendData.friendID == requestee->playerID) { + requestee.countOfBestFriends += 1; + requestor.countOfBestFriends += 1; + if (requestee.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true); + if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true); + for (auto& friendData : requestor.friends) { + if (friendData.friendID == requestee.playerID) { friendData.isBestFriend = true; } } - for (auto& friendData : requestee->friends) { - if (friendData.friendID == requestor->playerID) { + for (auto& friendData : requestee.friends) { + if (friendData.friendID == requestor.playerID) { friendData.isBestFriend = true; } } } } } else { - if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true); + if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true); } } else { auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends(); - if (requestee->friends.size() >= maxFriends) { - SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false); - } else if (requestor->friends.size() >= maxFriends) { - SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false); + if (requestee.friends.size() >= maxFriends) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::THEIRFRIENDLISTFULL, false); + } else if (requestor.friends.size() >= maxFriends) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::YOURFRIENDSLISTFULL, false); } else { // Do not send this if we are requesting to be a best friend. - SendFriendRequest(requestee.get(), requestor); + SendFriendRequest(requestee, requestor); } } - - // If the player is actually a player and not a ghost one defined above, release it from being deleted. - if (requestee->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) requestee.release(); } void ChatPacketHandler::HandleFriendResponse(Packet* packet) { @@ -230,8 +225,8 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { std::string friendName = PacketUtils::ReadString(0x15, packet, true); //Now to try and find both of these: - auto requestor = Game::playerContainer.GetPlayerData(playerID); - auto requestee = Game::playerContainer.GetPlayerData(friendName); + auto& requestor = Game::playerContainer.GetPlayerDataMutable(playerID); + auto& requestee = Game::playerContainer.GetPlayerDataMutable(friendName); if (!requestor || !requestee) return; eAddFriendResponseType serverResponseCode{}; @@ -254,8 +249,8 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { // Now that we have handled the base cases, we need to check the other cases. if (serverResponseCode == eAddFriendResponseType::ACCEPTED) { - for (auto friendData : requestor->friends) { - if (friendData.friendID == requestee->playerID) { + for (const auto& friendData : requestor.friends) { + if (friendData.friendID == requestee.playerID) { serverResponseCode = eAddFriendResponseType::ALREADYFRIEND; if (friendData.isBestFriend) { isAlreadyBestFriends = 1U; @@ -267,25 +262,23 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { // This message is NOT sent for best friends and is handled differently for those requests. if (serverResponseCode == eAddFriendResponseType::ACCEPTED) { // Add the each player to the others friend list. - FriendData requestorData; - requestorData.zoneID = requestor->zoneID; - requestorData.friendID = requestor->playerID; - requestorData.friendName = requestor->playerName; + auto& requestorData = requestee.friends.emplace_back(); + requestorData.zoneID = requestor.zoneID; + requestorData.friendID = requestor.playerID; + requestorData.friendName = requestor.playerName; requestorData.isBestFriend = false; requestorData.isFTP = false; requestorData.isOnline = true; - requestee->friends.push_back(requestorData); - FriendData requesteeData; - requesteeData.zoneID = requestee->zoneID; - requesteeData.friendID = requestee->playerID; - requesteeData.friendName = requestee->playerName; + auto& requesteeData = requestor.friends.emplace_back(); + requesteeData.zoneID = requestee.zoneID; + requesteeData.friendID = requestee.playerID; + requesteeData.friendName = requestee.playerName; requesteeData.isBestFriend = false; requesteeData.isFTP = false; requesteeData.isOnline = true; - requestor->friends.push_back(requesteeData); - Database::Get()->AddFriend(requestor->playerID, requestee->playerID); + Database::Get()->AddFriend(requestor.playerID, requestee.playerID); } if (serverResponseCode != eAddFriendResponseType::DECLINED) SendFriendResponse(requestor, requestee, serverResponseCode, isAlreadyBestFriends); @@ -313,26 +306,27 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { Database::Get()->RemoveFriend(playerID, friendID); //Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended: - auto goonA = Game::playerContainer.GetPlayerData(playerID); + auto& goonA = Game::playerContainer.GetPlayerDataMutable(playerID); if (goonA) { // Remove the friend from our list of friends - for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) { - if ((*friendData).friendID == friendID) { - if ((*friendData).isBestFriend) --goonA->countOfBestFriends; - goonA->friends.erase(friendData); + for (auto friendData = goonA.friends.cbegin(); friendData != goonA.friends.cend(); friendData++) { + if (friendData->friendID == friendID) { + if (friendData->isBestFriend) --goonA.countOfBestFriends; + goonA.friends.erase(friendData); break; } } SendRemoveFriend(goonA, friendName, true); } - auto goonB = Game::playerContainer.GetPlayerData(friendID); + auto& goonB = Game::playerContainer.GetPlayerDataMutable(friendID); if (!goonB) return; + // Do it again for other person - for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) { - if ((*friendData).friendID == playerID) { - if ((*friendData).isBestFriend) --goonB->countOfBestFriends; - goonB->friends.erase(friendData); + for (auto friendData = goonB.friends.cbegin(); friendData != goonB.friends.cend(); friendData++) { + if (friendData->friendID == playerID) { + if (friendData->isBestFriend) --goonB.countOfBestFriends; + goonB.friends.erase(friendData); break; } } @@ -346,13 +340,11 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) { LWOOBJID playerID = LWOOBJID_EMPTY; inStream.Read(playerID); - auto* sender = Game::playerContainer.GetPlayerData(playerID); + const auto& sender = Game::playerContainer.GetPlayerData(playerID); - if (sender == nullptr) return; + if (!sender) return; - if (Game::playerContainer.GetIsMuted(sender)) return; - - const auto senderName = std::string(sender->playerName.c_str()); + if (sender.GetIsMuted()) return; inStream.SetReadOffset(0x14 * 8); @@ -361,7 +353,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) { std::string message = PacketUtils::ReadString(0x66, packet, true, 512); - LOG("Got a message from (%s) [%d]: %s", senderName.c_str(), channel, message.c_str()); + LOG("Got a message from (%s) [%d]: %s", sender.playerName.c_str(), channel, message.c_str()); if (channel != 8) return; @@ -370,30 +362,28 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) { if (team == nullptr) return; for (const auto memberId : team->memberIDs) { - auto* otherMember = Game::playerContainer.GetPlayerData(memberId); + const auto& otherMember = Game::playerContainer.GetPlayerData(memberId); - if (otherMember == nullptr) return; - - const auto otherName = std::string(otherMember->playerName.c_str()); + if (!otherMember) return; CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(otherMember->playerID); + bitStream.Write(otherMember.playerID); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(otherMember->playerID); + bitStream.Write(otherMember.playerID); bitStream.Write(8); bitStream.Write(69); - bitStream.Write(LUWString(senderName)); - bitStream.Write(sender->playerID); + bitStream.Write(LUWString(sender.playerName)); + bitStream.Write(sender.playerID); bitStream.Write(0); bitStream.Write(0); //not mythran nametag - bitStream.Write(LUWString(otherName)); + bitStream.Write(LUWString(otherMember.playerName)); bitStream.Write(0); //not mythran for receiver bitStream.Write(0); //teams? bitStream.Write(LUWString(message, 512)); - SystemAddress sysAddr = otherMember->sysAddr; + SystemAddress sysAddr = otherMember.sysAddr; SEND_PACKET; } } @@ -404,35 +394,32 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { std::string message = PacketUtils::ReadString(0xAA, packet, true, 512); //Get the bois: - auto goonA = Game::playerContainer.GetPlayerData(senderID); - auto goonB = Game::playerContainer.GetPlayerData(receiverName); + const auto& goonA = Game::playerContainer.GetPlayerData(senderID); + const auto& goonB = Game::playerContainer.GetPlayerData(receiverName); if (!goonA || !goonB) return; - if (Game::playerContainer.GetIsMuted(goonA)) return; - - std::string goonAName = goonA->playerName.c_str(); - std::string goonBName = goonB->playerName.c_str(); + if (goonA.GetIsMuted()) return; //To the sender: { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(goonA->playerID); + bitStream.Write(goonA.playerID); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(goonA->playerID); + bitStream.Write(goonA.playerID); bitStream.Write(7); bitStream.Write(69); - bitStream.Write(LUWString(goonAName)); - bitStream.Write(goonA->playerID); + bitStream.Write(LUWString(goonA.playerName)); + bitStream.Write(goonA.playerID); bitStream.Write(0); bitStream.Write(0); //not mythran nametag - bitStream.Write(LUWString(goonBName)); + bitStream.Write(LUWString(goonB.playerName)); bitStream.Write(0); //not mythran for receiver bitStream.Write(0); //success bitStream.Write(LUWString(message, 512)); - SystemAddress sysAddr = goonA->sysAddr; + SystemAddress sysAddr = goonA.sysAddr; SEND_PACKET; } @@ -440,22 +427,22 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(goonB->playerID); + bitStream.Write(goonB.playerID); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(goonA->playerID); + bitStream.Write(goonA.playerID); bitStream.Write(7); bitStream.Write(69); - bitStream.Write(LUWString(goonAName)); - bitStream.Write(goonA->playerID); + bitStream.Write(LUWString(goonA.playerName)); + bitStream.Write(goonA.playerID); bitStream.Write(0); bitStream.Write(0); //not mythran nametag - bitStream.Write(LUWString(goonBName)); + bitStream.Write(LUWString(goonB.playerName)); bitStream.Write(0); //not mythran for receiver bitStream.Write(3); //new whisper bitStream.Write(LUWString(message, 512)); - SystemAddress sysAddr = goonB->sysAddr; + SystemAddress sysAddr = goonB.sysAddr; SEND_PACKET; } } @@ -466,11 +453,9 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) { inStream.Read(playerID); std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true); - auto* player = Game::playerContainer.GetPlayerData(playerID); + const auto& player = Game::playerContainer.GetPlayerData(playerID); - if (player == nullptr) { - return; - } + if (!player) return; auto* team = Game::playerContainer.GetTeam(playerID); @@ -478,13 +463,11 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) { team = Game::playerContainer.CreateTeam(playerID); } - auto* other = Game::playerContainer.GetPlayerData(invitedPlayer); + const auto& other = Game::playerContainer.GetPlayerData(invitedPlayer); - if (other == nullptr) { - return; - } + if (!other) return; - if (Game::playerContainer.GetTeam(other->playerID) != nullptr) { + if (Game::playerContainer.GetTeam(other.playerID) != nullptr) { return; } @@ -558,12 +541,12 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet) { LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.c_str()); - auto* kicked = Game::playerContainer.GetPlayerData(kickedPlayer); + const auto& kicked = Game::playerContainer.GetPlayerData(kickedPlayer); LWOOBJID kickedId = LWOOBJID_EMPTY; - if (kicked != nullptr) { - kickedId = kicked->playerID; + if (kicked) { + kickedId = kicked.playerID; } else { kickedId = Game::playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer)); } @@ -588,16 +571,16 @@ void ChatPacketHandler::HandleTeamPromote(Packet* packet) { LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.c_str()); - auto* promoted = Game::playerContainer.GetPlayerData(promotedPlayer); + const auto& promoted = Game::playerContainer.GetPlayerData(promotedPlayer); - if (promoted == nullptr) return; + if (!promoted) return; auto* team = Game::playerContainer.GetTeam(playerID); if (team != nullptr) { if (team->leaderID != playerID) return; - Game::playerContainer.PromoteMember(team, promoted->playerID); + Game::playerContainer.PromoteMember(team, promoted.playerID); } } @@ -630,10 +613,10 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) { inStream.Read(playerID); auto* team = Game::playerContainer.GetTeam(playerID); - auto* data = Game::playerContainer.GetPlayerData(playerID); + const auto& data = Game::playerContainer.GetPlayerData(playerID); - if (team != nullptr && data != nullptr) { - if (team->local && data->zoneID.GetMapID() != team->zoneId.GetMapID() && data->zoneID.GetCloneID() != team->zoneId.GetCloneID()) { + if (team != nullptr && data) { + if (team->local && data.zoneID.GetMapID() != team->zoneId.GetMapID() && data.zoneID.GetCloneID() != team->zoneId.GetCloneID()) { Game::playerContainer.RemoveMember(team, playerID, false, false, true, true); return; @@ -653,49 +636,49 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) { Game::playerContainer.TeamStatusUpdate(team); - const auto leaderName = GeneralUtils::UTF8ToUTF16(data->playerName); + const auto leaderName = GeneralUtils::UTF8ToUTF16(data.playerName); for (const auto memberId : team->memberIDs) { - auto* otherMember = Game::playerContainer.GetPlayerData(memberId); + const auto& otherMember = Game::playerContainer.GetPlayerData(memberId); if (memberId == playerID) continue; const auto memberName = Game::playerContainer.GetName(memberId); - if (otherMember != nullptr) { - ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID); + if (otherMember) { + ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data.playerID, data.zoneID); } - ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0)); + ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0)); } Game::playerContainer.UpdateTeamsOnWorld(team, false); } } -void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender) { +void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE); - bitStream.Write(LUWString(sender->playerName.c_str())); - bitStream.Write(sender->playerID); + bitStream.Write(LUWString(sender.playerName.c_str())); + bitStream.Write(sender.playerID); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamInviteConfirm(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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_INVITE_CONFIRM); bitStream.Write(bLeaderIsFreeTrial); @@ -710,19 +693,19 @@ void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeader bitStream.Write(character); } - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamStatus(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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_GET_STATUS_RESPONSE); bitStream.Write(i64LeaderID); @@ -735,36 +718,36 @@ void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderI bitStream.Write(character); } - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID) { +void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_SET_LEADER); bitStream.Write(i64PlayerID); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamAddPlayer(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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_ADD_PLAYER); bitStream.Write(bIsFreeTrial); @@ -776,24 +759,24 @@ void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTria bitStream.Write(character); } bitStream.Write1(); - if (receiver->zoneID.GetCloneID() == zoneID.GetCloneID()) { + if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) { zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0); } bitStream.Write(zoneID); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamRemovePlayer(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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_REMOVE_PLAYER); bitStream.Write(bDisband); @@ -807,32 +790,32 @@ void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband bitStream.Write(character); } - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) { +void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_SET_OFF_WORLD_FLAG); bitStream.Write(i64PlayerID); - if (receiver->zoneID.GetCloneID() == zoneID.GetCloneID()) { + if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) { zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0); } bitStream.Write(zoneID); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend) { +void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend) { /*chat notification is displayed if log in / out and friend is updated in friends list [u8] - update type Update types @@ -848,38 +831,36 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(friendData->playerID); + bitStream.Write(friendData.playerID); //portion that will get routed: BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY); bitStream.Write(notifyType); - std::string playerName = playerData->playerName.c_str(); + std::string playerName = playerData.playerName.c_str(); bitStream.Write(LUWString(playerName)); - bitStream.Write(playerData->zoneID.GetMapID()); - bitStream.Write(playerData->zoneID.GetInstanceID()); + bitStream.Write(playerData.zoneID.GetMapID()); + bitStream.Write(playerData.zoneID.GetInstanceID()); - if (playerData->zoneID.GetCloneID() == friendData->zoneID.GetCloneID()) { + if (playerData.zoneID.GetCloneID() == friendData.zoneID.GetCloneID()) { bitStream.Write(0); } else { - bitStream.Write(playerData->zoneID.GetCloneID()); + bitStream.Write(playerData.zoneID.GetCloneID()); } bitStream.Write(isBestFriend); //isBFF bitStream.Write(0); //isFTP - SystemAddress sysAddr = friendData->sysAddr; + SystemAddress sysAddr = friendData.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* sender) { - if (!receiver || !sender) return; - +void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const PlayerData& sender) { //Make sure people aren't requesting people that they're already friends with: - for (auto fr : receiver->friends) { - if (fr.friendID == sender->playerID) { + for (const auto& fr : receiver.friends) { + if (fr.friendID == sender.playerID) { SendFriendResponse(sender, receiver, eAddFriendResponseType::ALREADYFRIEND, fr.isBestFriend); return; //we have this player as a friend, yeet this function so it doesn't send another request. } @@ -887,54 +868,50 @@ void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* send CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST); - bitStream.Write(LUWString(sender->playerName.c_str())); + bitStream.Write(LUWString(sender.playerName)); bitStream.Write(0); // This is a BFF flag however this is unused in live and does not have an implementation client side. - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendFriendResponse(PlayerData* receiver, PlayerData* sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) { - if (!receiver || !sender) return; - +void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); // Portion that will get routed: BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE); bitStream.Write(responseCode); // For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver. - bitStream.Write(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender->sysAddr != UNASSIGNED_SYSTEM_ADDRESS); + bitStream.Write(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.sysAddr != UNASSIGNED_SYSTEM_ADDRESS); // Then write the player name - bitStream.Write(LUWString(sender->playerName.c_str())); + bitStream.Write(LUWString(sender.playerName)); // Then if this is an acceptance code, write the following extra info. if (responseCode == eAddFriendResponseType::ACCEPTED) { - bitStream.Write(sender->playerID); - bitStream.Write(sender->zoneID); + bitStream.Write(sender.playerID); + bitStream.Write(sender.zoneID); bitStream.Write(isBestFriendRequest); //isBFF bitStream.Write(0); //isFTP } - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful) { - if (!receiver) return; - +void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE); bitStream.Write(isSuccessful); //isOnline bitStream.Write(LUWString(personToRemove)); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } diff --git a/dChatServer/ChatPacketHandler.h b/dChatServer/ChatPacketHandler.h index f2d83502..6c9c2de6 100644 --- a/dChatServer/ChatPacketHandler.h +++ b/dChatServer/ChatPacketHandler.h @@ -4,6 +4,7 @@ #include "BitStream.h" struct PlayerData; + enum class eAddFriendResponseType : uint8_t; namespace ChatPacketHandler { @@ -23,18 +24,18 @@ namespace ChatPacketHandler { void HandleTeamLootOption(Packet* packet); void HandleTeamStatusRequest(Packet* packet); - void SendTeamInvite(PlayerData* receiver, PlayerData* sender); - void SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName); - void SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName); - void SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID); - void SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID); - void SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName); - void SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID); + void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender); + void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName); + void SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName); + void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID); + void SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID); + void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName); + void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID); //FriendData is the player we're SENDING this stuff to. Player is the friend that changed state. - void SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend); + void SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend); - void SendFriendRequest(PlayerData* receiver, PlayerData* sender); - void SendFriendResponse(PlayerData* receiver, PlayerData* sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U); - void SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful); + void SendFriendRequest(const PlayerData& receiver, const PlayerData& sender); + void SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U); + void SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful); }; diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index 8e1d3adf..8ab66d73 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -22,6 +22,7 @@ #include "ChatIgnoreList.h" #include "Game.h" +#include "Server.h" //RakNet includes: #include "RakNetDefines.h" @@ -38,7 +39,6 @@ namespace Game { PlayerContainer playerContainer; } -Logger* SetupLogger(); void HandlePacket(Packet* packet); int main(int argc, char** argv) { @@ -51,14 +51,13 @@ int main(int argc, char** argv) { std::signal(SIGINT, Game::OnSignal); std::signal(SIGTERM, Game::OnSignal); + Game::config = new dConfig("chatconfig.ini"); + //Create all the objects we need to run our service: - Game::logger = SetupLogger(); + Server::SetupLogger("ChatServer"); if (!Game::logger) return EXIT_FAILURE; //Read our config: - Game::config = new dConfig("chatconfig.ini"); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); LOG("Starting Chat server..."); LOG("Version: %s", PROJECT_VERSION); @@ -182,18 +181,6 @@ int main(int argc, char** argv) { return EXIT_SUCCESS; } -Logger* SetupLogger() { - std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/ChatServer_" + std::to_string(time(nullptr)) + ".log")).string(); - bool logToConsole = false; - bool logDebugStatements = false; -#ifdef _DEBUG - logToConsole = true; - logDebugStatements = true; -#endif - - return new Logger(logPath, logToConsole, logDebugStatements); -} - void HandlePacket(Packet* packet) { 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."); diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index f309ea0a..242ccad1 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -28,27 +28,32 @@ TeamData::TeamData() { void PlayerContainer::InsertPlayer(Packet* packet) { CINSTREAM_SKIP_HEADER; - PlayerData* data = new PlayerData(); - inStream.Read(data->playerID); + LWOOBJID playerId; + if (!inStream.Read(playerId)) { + LOG("Failed to read player ID"); + return; + } + + auto& data = m_Players[playerId]; + data.playerID = playerId; uint32_t len; inStream.Read(len); for (int i = 0; i < len; i++) { char character; inStream.Read(character); - data->playerName += character; + data.playerName += character; } - inStream.Read(data->zoneID); - inStream.Read(data->muteExpire); - data->sysAddr = packet->systemAddress; + inStream.Read(data.zoneID); + inStream.Read(data.muteExpire); + data.sysAddr = packet->systemAddress; - m_Names[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName); + m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName); - m_Players.insert(std::make_pair(data->playerID, data)); - LOG("Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID()); + LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID()); - Database::Get()->UpdateActivityLog(data->playerID, eActivityType::PlayerLoggedIn, data->zoneID.GetMapID()); + Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID()); } void PlayerContainer::RemovePlayer(Packet* packet) { @@ -57,26 +62,27 @@ void PlayerContainer::RemovePlayer(Packet* packet) { inStream.Read(playerID); //Before they get kicked, we need to also send a message to their friends saying that they disconnected. - std::unique_ptr player(this->GetPlayerData(playerID)); + const auto& player = GetPlayerData(playerID); - if (player == nullptr) { + if (!player) { + LOG("Failed to find user: %llu", playerID); return; } - for (auto& fr : player->friends) { - auto fd = this->GetPlayerData(fr.friendID); - if (fd) ChatPacketHandler::SendFriendUpdate(fd, player.get(), 0, fr.isBestFriend); + for (const auto& fr : player.friends) { + const auto& fd = this->GetPlayerData(fr.friendID); + if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend); } auto* team = GetTeam(playerID); if (team != nullptr) { - const auto memberName = GeneralUtils::UTF8ToUTF16(std::string(player->playerName.c_str())); + const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName); for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 }); } @@ -85,7 +91,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) { LOG("Removed user: %llu", playerID); m_Players.erase(playerID); - Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player->zoneID.GetMapID()); + Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player.zoneID.GetMapID()); } void PlayerContainer::MuteUpdate(Packet* packet) { @@ -95,15 +101,15 @@ void PlayerContainer::MuteUpdate(Packet* packet) { time_t expire = 0; inStream.Read(expire); - auto* player = this->GetPlayerData(playerID); + auto& player = this->GetPlayerDataMutable(playerID); - if (player == nullptr) { + if (!player) { LOG("Failed to find user: %llu", playerID); return; } - player->muteExpire = expire; + player.muteExpire = expire; BroadcastMuteUpdate(playerID, expire); } @@ -201,11 +207,11 @@ TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) { } void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { - if (team->memberIDs.size() >= 4){ + if (team->memberIDs.size() >= 4) { LOG("Tried to add player to team that already had 4 players"); - auto* player = GetPlayerData(playerID); + const auto& player = GetPlayerData(playerID); if (!player) return; - ChatPackets::SendSystemMessage(player->sysAddr, u"The teams is full! You have not been added to a team!"); + ChatPackets::SendSystemMessage(player.sysAddr, u"The teams is full! You have not been added to a team!"); return; } @@ -215,18 +221,18 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { team->memberIDs.push_back(playerID); - auto* leader = GetPlayerData(team->leaderID); - auto* member = GetPlayerData(playerID); + const auto& leader = GetPlayerData(team->leaderID); + const auto& member = GetPlayerData(playerID); - if (leader == nullptr || member == nullptr) return; + if (!leader || !member) return; - const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName); - const auto memberName = GeneralUtils::UTF8ToUTF16(member->playerName); + const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName); + const auto memberName = GeneralUtils::UTF8ToUTF16(member.playerName); - ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName); + ChatPacketHandler::SendTeamInviteConfirm(member, false, leader.playerID, leader.zoneID, team->lootFlag, 0, 0, leaderName); if (!team->local) { - ChatPacketHandler::SendTeamSetLeader(member, leader->playerID); + ChatPacketHandler::SendTeamSetLeader(member, leader.playerID); } else { ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY); } @@ -234,16 +240,16 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { UpdateTeamsOnWorld(team, false); for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); if (otherMember == member) continue; const auto otherMemberName = GetName(memberId); - ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0)); + ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0)); - if (otherMember != nullptr) { - ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member->playerID, memberName, member->zoneID); + if (otherMember) { + ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID); } } } @@ -253,9 +259,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba if (index == team->memberIDs.end()) return; - auto* member = GetPlayerData(playerID); + const auto& member = GetPlayerData(playerID); - if (member != nullptr && !silent) { + if (member && !silent) { ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY); } @@ -266,9 +272,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba continue; } - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName); } @@ -290,9 +296,9 @@ void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) { team->leaderID = newLeader; for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader); } @@ -304,14 +310,14 @@ void PlayerContainer::DisbandTeam(TeamData* team) { if (index == mTeams.end()) return; for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; - const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember->playerName); + const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember.playerName); ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY); - ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember->playerID, memberName); + ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember.playerID, memberName); } UpdateTeamsOnWorld(team, true); @@ -326,19 +332,19 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) { if (index == mTeams.end()) return; - auto* leader = GetPlayerData(team->leaderID); + const auto& leader = GetPlayerData(team->leaderID); - if (leader == nullptr) return; + if (!leader) return; - const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName); + const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName); for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; if (!team->local) { - ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader->zoneID, team->lootFlag, 0, leaderName); + ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader.zoneID, team->lootFlag, 0, leaderName); } } @@ -364,23 +370,42 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { } std::u16string PlayerContainer::GetName(LWOOBJID playerID) { - const auto& pair = m_Names.find(playerID); + const auto iter = m_Names.find(playerID); - if (pair == m_Names.end()) return u""; + if (iter == m_Names.end()) return u""; - return pair->second; + return iter->second; } LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) { - for (const auto& pair : m_Names) { - if (pair.second == playerName) { - return pair.first; + LWOOBJID toReturn = LWOOBJID_EMPTY; + + for (const auto& [id, name] : m_Names) { + if (name == playerName) { + toReturn = id; + break; } } - return LWOOBJID_EMPTY; + return toReturn; } -bool PlayerContainer::GetIsMuted(PlayerData* data) { - return data->muteExpire == 1 || data->muteExpire > time(NULL); +PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) { + return m_Players[playerID]; +} + +PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) { + for (auto& [id, player] : m_Players) { + if (!player) continue; + if (player.playerName == playerName) return player; + } + return m_Players[LWOOBJID_EMPTY]; +} + +const PlayerData& PlayerContainer::GetPlayerData(const LWOOBJID& playerID) { + return GetPlayerDataMutable(playerID); +} + +const PlayerData& PlayerContainer::GetPlayerData(const std::string& playerName) { + return GetPlayerDataMutable(playerName); } diff --git a/dChatServer/PlayerContainer.h b/dChatServer/PlayerContainer.h index 51bff6c1..3cacc62d 100644 --- a/dChatServer/PlayerContainer.h +++ b/dChatServer/PlayerContainer.h @@ -8,6 +8,7 @@ #include struct IgnoreData { + IgnoreData(const std::string& name, const LWOOBJID& id) : playerName(name), playerId(id) {} inline bool operator==(const std::string& other) const noexcept { return playerName == other; } @@ -16,19 +17,31 @@ struct IgnoreData { return playerId == other; } - LWOOBJID playerId; + LWOOBJID playerId = LWOOBJID_EMPTY; std::string playerName; }; struct PlayerData { - LWOOBJID playerID; + operator bool() const noexcept { + return playerID != LWOOBJID_EMPTY; + } + + bool operator==(const PlayerData& other) const noexcept { + return playerID == other.playerID; + } + + bool GetIsMuted() const { + return muteExpire == 1 || muteExpire > time(NULL); + } + + SystemAddress sysAddr{}; + LWOZONEID zoneID{}; + LWOOBJID playerID = LWOOBJID_EMPTY; + time_t muteExpire = 0; + uint8_t countOfBestFriends = 0; std::string playerName; - SystemAddress sysAddr; - LWOZONEID zoneID; std::vector friends; std::vector ignoredPlayers; - time_t muteExpire; - uint8_t countOfBestFriends = 0; }; struct TeamData { @@ -52,22 +65,10 @@ public: void CreateTeamServer(Packet* packet); void BroadcastMuteUpdate(LWOOBJID player, time_t time); - PlayerData* GetPlayerData(const LWOOBJID& playerID) { - auto it = m_Players.find(playerID); - if (it != m_Players.end()) return it->second; - return nullptr; - } - - PlayerData* GetPlayerData(const std::string& playerName) { - for (auto player : m_Players) { - if (player.second) { - std::string pn = player.second->playerName.c_str(); - if (pn == playerName) return player.second; - } - } - - return nullptr; - } + const PlayerData& GetPlayerData(const LWOOBJID& playerID); + const PlayerData& GetPlayerData(const std::string& playerName); + PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID); + PlayerData& GetPlayerDataMutable(const std::string& playerName); TeamData* CreateLocalTeam(std::vector members); TeamData* CreateTeam(LWOOBJID leader, bool local = false); @@ -80,15 +81,12 @@ public: void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam); std::u16string GetName(LWOOBJID playerID); LWOOBJID GetId(const std::u16string& playerName); - bool GetIsMuted(PlayerData* data); uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; } uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; } - std::map& GetAllPlayerData() { return m_Players; } - private: LWOOBJID m_TeamIDCounter = 0; - std::map m_Players; + std::map m_Players; std::vector mTeams; std::unordered_map m_Names; uint32_t m_MaxNumberOfBestFriends = 5; diff --git a/dCommon/dEnums/dCommonVars.h b/dCommon/dEnums/dCommonVars.h index 513e9c8f..f4f8fdfb 100644 --- a/dCommon/dEnums/dCommonVars.h +++ b/dCommon/dEnums/dCommonVars.h @@ -130,7 +130,7 @@ public: LWOOBJID friendID; std::string friendName; - void Serialize(RakNet::BitStream& bitStream) { + void Serialize(RakNet::BitStream& bitStream) const { bitStream.Write(isOnline); bitStream.Write(isBestFriend); bitStream.Write(isFTP); diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 0ffd99dc..4d1ec733 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -348,17 +348,6 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit }); } - if (chatCommand == "resetmission") { - uint32_t missionId; - if (!GeneralUtils::TryParse(args[0], missionId)) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission ID."); - return; - } - auto* missionComponent = entity->GetComponent(); - if (!missionComponent) return; - missionComponent->ResetMission(missionId); - } - if (user->GetMaxGMLevel() == eGameMasterLevel::CIVILIAN || entity->GetGMLevel() >= eGameMasterLevel::CIVILIAN) { if (chatCommand == "die") { entity->Smash(entity->GetObjectID()); @@ -388,6 +377,18 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (entity->GetGMLevel() == eGameMasterLevel::CIVILIAN) return; } + if (chatCommand == "resetmission" && args.size() >= 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { + uint32_t missionId; + if (!GeneralUtils::TryParse(args[0], missionId)) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission ID."); + return; + } + + auto* missionComponent = entity->GetComponent(); + if (!missionComponent) return; + missionComponent->ResetMission(missionId); + } + // Log command to database Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), chatCommand); @@ -704,33 +705,6 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit entity->GetCharacter()->SetPlayerFlag(flagId, false); } - if (chatCommand == "resetmission" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { - if (args.size() == 0) return; - - uint32_t missionID; - - if (!GeneralUtils::TryParse(args[0], missionID)) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission id."); - return; - } - - auto* comp = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); - - if (comp == nullptr) { - return; - } - - auto* mission = comp->GetMission(missionID); - - if (mission == nullptr) { - return; - } - - mission->SetMissionState(eMissionState::ACTIVE); - - return; - } - // Pet status utility if (chatCommand == "setpetstatus" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { if (args.size() == 0) { diff --git a/dMasterServer/CMakeLists.txt b/dMasterServer/CMakeLists.txt index 108cddc2..32a7b1ec 100644 --- a/dMasterServer/CMakeLists.txt +++ b/dMasterServer/CMakeLists.txt @@ -9,7 +9,8 @@ add_executable(MasterServer "MasterServer.cpp") add_compile_definitions(MasterServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") target_link_libraries(dMasterServer ${COMMON_LIBRARIES}) -target_link_libraries(MasterServer ${COMMON_LIBRARIES} dMasterServer) +target_link_libraries(MasterServer ${COMMON_LIBRARIES} dMasterServer dServer) +target_include_directories(dMasterServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer) if(WIN32) add_dependencies(MasterServer WorldServer AuthServer ChatServer) diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index e5997b51..a5691e49 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -40,6 +40,7 @@ #include "FdbToSqlite.h" #include "BitStreamUtils.h" #include "Start.h" +#include "Server.h" namespace Game { Logger* logger = nullptr; @@ -55,7 +56,6 @@ namespace Game { bool shutdownSequenceStarted = false; int ShutdownSequence(int32_t signal = -1); int32_t FinalizeShutdown(int32_t signal = -1); -Logger* SetupLogger(); void HandlePacket(Packet* packet); std::map activeSessions; SystemAddress authServerMasterPeerSysAddr; @@ -77,8 +77,10 @@ int main(int argc, char** argv) { std::signal(SIGINT, Game::OnSignal); std::signal(SIGTERM, Game::OnSignal); + Game::config = new dConfig("masterconfig.ini"); + //Create all the objects we need to run our service: - Game::logger = SetupLogger(); + Server::SetupLogger("MasterServer"); if (!Game::logger) return EXIT_FAILURE; if (!dConfig::Exists("authconfig.ini")) LOG("Could not find authconfig.ini, using default settings"); @@ -87,9 +89,6 @@ int main(int argc, char** argv) { if (!dConfig::Exists("sharedconfig.ini")) LOG("Could not find sharedconfig.ini, using default settings"); if (!dConfig::Exists("worldconfig.ini")) LOG("Could not find worldconfig.ini, using default settings"); - Game::config = new dConfig("masterconfig.ini"); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); uint32_t clientNetVersion = 171022; const auto clientNetVersionString = Game::config->GetValue("client_net_version"); @@ -395,19 +394,6 @@ int main(int argc, char** argv) { return ShutdownSequence(EXIT_SUCCESS); } -Logger* SetupLogger() { - std::string logPath = - (BinaryPathFinder::GetBinaryDir() / ("logs/MasterServer_" + std::to_string(time(nullptr)) + ".log")).string(); - bool logToConsole = false; - bool logDebugStatements = false; -#ifdef _DEBUG - logToConsole = true; - logDebugStatements = true; -#endif - - return new Logger(logPath, logToConsole, logDebugStatements); -} - void HandlePacket(Packet* packet) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) { LOG("A server has disconnected"); diff --git a/dServer/CMakeLists.txt b/dServer/CMakeLists.txt new file mode 100644 index 00000000..356e55b7 --- /dev/null +++ b/dServer/CMakeLists.txt @@ -0,0 +1,6 @@ +set(DSERVER_SOURCES + "Server.cpp") + +add_library(dServer STATIC ${DSERVER_SOURCES}) + +target_include_directories(dServer PUBLIC ".") diff --git a/dServer/Server.cpp b/dServer/Server.cpp new file mode 100644 index 00000000..cd801a3b --- /dev/null +++ b/dServer/Server.cpp @@ -0,0 +1,29 @@ +#include "Server.h" + +#include "BinaryPathFinder.h" +#include "Game.h" +#include "Logger.h" +#include "dConfig.h" + +void Server::SetupLogger(const std::string_view serviceName) { + if (Game::logger) { + LOG("A logger has already been setup, skipping."); + return; + } + + const auto logsDir = BinaryPathFinder::GetBinaryDir() / "logs"; + + if (!std::filesystem::exists(logsDir)) std::filesystem::create_directory(logsDir); + + std::string logPath = (logsDir / serviceName).string() + "_" + std::to_string(time(nullptr)) + ".log"; + bool logToConsole = false; + bool logDebugStatements = false; +#ifdef _DEBUG + logToConsole = true; + logDebugStatements = true; +#endif + Game::logger = new Logger(logPath, logToConsole, logDebugStatements); + + Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); + Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); +} diff --git a/dServer/Server.h b/dServer/Server.h new file mode 100644 index 00000000..4e9f2543 --- /dev/null +++ b/dServer/Server.h @@ -0,0 +1,10 @@ +#ifndef __SERVER__H__ +#define __SERVER__H__ + +#include + +namespace Server { + void SetupLogger(const std::string_view serviceName); +}; + +#endif //!__SERVER__H__ diff --git a/dWorldServer/CMakeLists.txt b/dWorldServer/CMakeLists.txt index 336a60f9..41cba0e2 100644 --- a/dWorldServer/CMakeLists.txt +++ b/dWorldServer/CMakeLists.txt @@ -7,5 +7,6 @@ add_executable(WorldServer "WorldServer.cpp") add_compile_definitions(WorldServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") target_link_libraries(dWorldServer ${COMMON_LIBRARIES}) -target_link_libraries(WorldServer ${COMMON_LIBRARIES} dChatFilter dGame dZoneManager dPhysics Detour Recast tinyxml2 dWorldServer dNavigation) +target_link_libraries(WorldServer ${COMMON_LIBRARIES} dChatFilter dGame dZoneManager dPhysics Detour Recast tinyxml2 dWorldServer dNavigation dServer) +target_include_directories(WorldServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer) diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index f911f804..d65596b6 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -77,6 +77,7 @@ #include "CheatDetection.h" #include "eGameMasterLevel.h" #include "StringifiedEnum.h" +#include "Server.h" namespace Game { Logger* logger = nullptr; @@ -102,8 +103,8 @@ void WorldShutdownProcess(uint32_t zoneId); void FinalizeShutdown(); void SendShutdownMessageToMaster(); -Logger* SetupLogger(uint32_t zoneID, uint32_t instanceID); void HandlePacketChat(Packet* packet); +void HandleMasterPacket(Packet* packet); void HandlePacket(Packet* packet); struct tempSessionInfo { @@ -143,14 +144,11 @@ int main(int argc, char** argv) { if (argument == "-port") ourPort = atoi(argv[i + 1]); } - //Create all the objects we need to run our service: - Game::logger = SetupLogger(zoneID, instanceID); - if (!Game::logger) return EXIT_FAILURE; - - //Read our config: Game::config = new dConfig("worldconfig.ini"); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); + + //Create all the objects we need to run our service: + Server::SetupLogger("WorldServer_" + std::to_string(zoneID) + "_" + std::to_string(instanceID)); + if (!Game::logger) return EXIT_FAILURE; LOG("Starting World server..."); LOG("Version: %s", Game::projectVersion.c_str()); @@ -412,7 +410,7 @@ int main(int argc, char** argv) { //Check for packets here: packet = Game::server->ReceiveFromMaster(); if (packet) { //We can get messages not handle-able by the dServer class, so handle them if we returned anything. - HandlePacket(packet); + HandleMasterPacket(packet); Game::server->DeallocateMasterPacket(packet); } @@ -529,18 +527,6 @@ int main(int argc, char** argv) { return EXIT_SUCCESS; } -Logger* SetupLogger(uint32_t zoneID, uint32_t instanceID) { - std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/WorldServer_" + std::to_string(zoneID) + "_" + std::to_string(instanceID) + "_" + std::to_string(time(nullptr)) + ".log")).string(); - bool logToConsole = false; - bool logDebugStatements = false; -#ifdef _DEBUG - logToConsole = true; - logDebugStatements = true; -#endif - - return new Logger(logPath, logToConsole, logDebugStatements); -} - void HandlePacketChat(Packet* packet) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { LOG("Lost our connection to chat, zone(%i), instance(%i)", Game::server->GetZoneID(), Game::server->GetInstanceID()); @@ -672,6 +658,138 @@ void HandlePacketChat(Packet* packet) { } } +void HandleMasterPacket(Packet* packet) { + + if (static_cast(packet->data[1]) != eConnectionType::MASTER || packet->length < 4) return; + switch (static_cast(packet->data[3])) { + case eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE: { + uint64_t requestID = PacketUtils::ReadU64(8, packet); + uint32_t objectID = PacketUtils::ReadU32(16, packet); + ObjectIDManager::HandleRequestPersistentIDResponse(requestID, objectID); + break; + } + + case eMasterMessageType::SESSION_KEY_RESPONSE: { + //Read our session key and to which user it belongs: + RakNet::BitStream inStream(packet->data, packet->length, false); + uint64_t header = inStream.Read(header); + uint32_t sessionKey = 0; + std::string username; + + inStream.Read(sessionKey); + username = PacketUtils::ReadString(12, packet, false); + + //Find them: + auto it = m_PendingUsers.find(username); + if (it == m_PendingUsers.end()) return; + + //Convert our key: + std::string userHash = std::to_string(sessionKey); + userHash = md5(userHash); + + //Verify it: + if (userHash != it->second.hash) { + LOG("SOMEONE IS TRYING TO HACK? SESSION KEY MISMATCH: ours: %s != master: %s", userHash.c_str(), it->second.hash.c_str()); + Game::server->Disconnect(it->second.sysAddr, eServerDisconnectIdentifiers::INVALID_SESSION_KEY); + return; + } else { + LOG("User %s authenticated with correct key.", username.c_str()); + + UserManager::Instance()->DeleteUser(packet->systemAddress); + + //Create our user and send them in: + UserManager::Instance()->CreateUser(it->second.sysAddr, username, userHash); + + auto zone = Game::zoneManager->GetZone(); + if (zone) { + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + + if (zone->GetZoneID().GetMapID() == 1100) { + auto pos = zone->GetSpawnPos(); + x = pos.x; + y = pos.y; + z = pos.z; + } + + WorldPackets::SendLoadStaticZone(it->second.sysAddr, x, y, z, zone->GetChecksum()); + } + + if (Game::server->GetZoneID() == 0) { + //Since doing this reroute breaks the client's request, we have to call this manually. + UserManager::Instance()->RequestCharacterList(it->second.sysAddr); + } + + m_PendingUsers.erase(username); + + //Notify master: + { + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PLAYER_ADDED); + bitStream.Write((LWOMAPID)Game::server->GetZoneID()); + bitStream.Write((LWOINSTANCEID)instanceID); + Game::server->SendToMaster(&bitStream); + } + } + + break; + } + case eMasterMessageType::AFFIRM_TRANSFER_REQUEST: { + const uint64_t requestID = PacketUtils::ReadU64(8, packet); + + LOG("Got affirmation request of transfer %llu", requestID); + + CBITSTREAM; + + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::AFFIRM_TRANSFER_RESPONSE); + bitStream.Write(requestID); + Game::server->SendToMaster(&bitStream); + + break; + } + + case eMasterMessageType::SHUTDOWN: { + Game::lastSignal = -1; + LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID()); + break; + } + + case eMasterMessageType::NEW_SESSION_ALERT: { + RakNet::BitStream inStream(packet->data, packet->length, false); + uint64_t header = inStream.Read(header); + uint32_t sessionKey = inStream.Read(sessionKey); + + std::string username; + + uint32_t len; + inStream.Read(len); + + for (uint32_t i = 0; i < len; i++) { + char character; inStream.Read(character); + username += character; + } + + //Find them: + User* user = UserManager::Instance()->GetUser(username.c_str()); + if (!user) { + LOG("Got new session alert for user %s, but they're not logged in.", username.c_str()); + return; + } + + //Check the key: + if (sessionKey != std::atoi(user->GetSessionKey().c_str())) { + LOG("Got new session alert for user %s, but the session key is invalid.", username.c_str()); + Game::server->Disconnect(user->GetSystemAddress(), eServerDisconnectIdentifiers::INVALID_SESSION_KEY); + return; + } + break; + } + default: + LOG("Unknown packet ID from master %i", int(packet->data[3])); + } +} + void HandlePacket(Packet* packet) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { auto user = UserManager::Instance()->GetUser(packet->systemAddress); @@ -730,145 +848,6 @@ void HandlePacket(Packet* packet) { } } - if (static_cast(packet->data[1]) == eConnectionType::MASTER) { - switch (static_cast(packet->data[3])) { - case eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE: { - uint64_t requestID = PacketUtils::ReadU64(8, packet); - uint32_t objectID = PacketUtils::ReadU32(16, packet); - ObjectIDManager::HandleRequestPersistentIDResponse(requestID, objectID); - break; - } - - case eMasterMessageType::REQUEST_ZONE_TRANSFER_RESPONSE: { - uint64_t requestID = PacketUtils::ReadU64(8, packet); - ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(requestID, packet); - break; - } - - case eMasterMessageType::SESSION_KEY_RESPONSE: { - //Read our session key and to which user it belongs: - RakNet::BitStream inStream(packet->data, packet->length, false); - uint64_t header = inStream.Read(header); - uint32_t sessionKey = 0; - std::string username; - - inStream.Read(sessionKey); - username = PacketUtils::ReadString(12, packet, false); - - //Find them: - auto it = m_PendingUsers.find(username); - if (it == m_PendingUsers.end()) return; - - //Convert our key: - std::string userHash = std::to_string(sessionKey); - userHash = md5(userHash); - - //Verify it: - if (userHash != it->second.hash) { - LOG("SOMEONE IS TRYING TO HACK? SESSION KEY MISMATCH: ours: %s != master: %s", userHash.c_str(), it->second.hash.c_str()); - Game::server->Disconnect(it->second.sysAddr, eServerDisconnectIdentifiers::INVALID_SESSION_KEY); - return; - } else { - LOG("User %s authenticated with correct key.", username.c_str()); - - UserManager::Instance()->DeleteUser(packet->systemAddress); - - //Create our user and send them in: - UserManager::Instance()->CreateUser(it->second.sysAddr, username, userHash); - - auto zone = Game::zoneManager->GetZone(); - if (zone) { - float x = 0.0f; - float y = 0.0f; - float z = 0.0f; - - if (zone->GetZoneID().GetMapID() == 1100) { - auto pos = zone->GetSpawnPos(); - x = pos.x; - y = pos.y; - z = pos.z; - } - - WorldPackets::SendLoadStaticZone(it->second.sysAddr, x, y, z, zone->GetChecksum()); - } - - if (Game::server->GetZoneID() == 0) { - //Since doing this reroute breaks the client's request, we have to call this manually. - UserManager::Instance()->RequestCharacterList(it->second.sysAddr); - } - - m_PendingUsers.erase(username); - - //Notify master: - { - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PLAYER_ADDED); - bitStream.Write((LWOMAPID)Game::server->GetZoneID()); - bitStream.Write((LWOINSTANCEID)instanceID); - Game::server->SendToMaster(&bitStream); - } - } - - break; - } - case eMasterMessageType::AFFIRM_TRANSFER_REQUEST: { - const uint64_t requestID = PacketUtils::ReadU64(8, packet); - - LOG("Got affirmation request of transfer %llu", requestID); - - CBITSTREAM; - - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::AFFIRM_TRANSFER_RESPONSE); - bitStream.Write(requestID); - Game::server->SendToMaster(&bitStream); - - break; - } - - case eMasterMessageType::SHUTDOWN: { - Game::lastSignal = -1; - LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID()); - break; - } - - case eMasterMessageType::NEW_SESSION_ALERT: { - RakNet::BitStream inStream(packet->data, packet->length, false); - uint64_t header = inStream.Read(header); - uint32_t sessionKey = inStream.Read(sessionKey); - - std::string username; - - uint32_t len; - inStream.Read(len); - - for (uint32_t i = 0; i < len; i++) { - char character; inStream.Read(character); - username += character; - } - - //Find them: - User* user = UserManager::Instance()->GetUser(username.c_str()); - if (!user) { - LOG("Got new session alert for user %s, but they're not logged in.", username.c_str()); - return; - } - - //Check the key: - if (sessionKey != std::atoi(user->GetSessionKey().c_str())) { - LOG("Got new session alert for user %s, but the session key is invalid.", username.c_str()); - Game::server->Disconnect(user->GetSystemAddress(), eServerDisconnectIdentifiers::INVALID_SESSION_KEY); - return; - } - break; - } - - default: - LOG("Unknown packet ID from master %i", int(packet->data[3])); - } - - return; - } - if (static_cast(packet->data[1]) != eConnectionType::WORLD) return; switch (static_cast(packet->data[3])) { diff --git a/docs/Commands.md b/docs/Commands.md index 1312ede0..d485847f 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -1,3 +1,4 @@ + # In-game commands * All commands are prefixed by `/` and typed in the in-game chat window. Some commands require elevated gmlevel privileges. Operands within `<>` are required, operands within `()` are not. @@ -5,34 +6,34 @@ |Command|Usage|Description|Admin Level Requirement| |--- |--- |--- |--- | -|credits|`/credits`|Displays the names of the people behind Darkflame Universe.|| -|die|`/die`|Smashes the player.|| -|info|`/info`|Displays server info to the user, including where to find the server's source code.|| -|instanceinfo|`/instanceinfo`|Displays in the chat the current zone, clone, and instance id.|| +|credits|`/credits`|Displays the names of the people behind Darkflame Universe.|0| +|die|`/die`|Smashes the player.|0| +|info|`/info`|Displays server info to the user, including where to find the server's source code.|0| +|instanceinfo|`/instanceinfo`|Displays in the chat the current zone, clone, and instance id.|0| |ping|`/ping (-l)`|Displays in chat your average ping. If the `-l` flag is used, the latest ping is displayed.|| -|pvp|`/pvp`|Toggle your PVP flag.|| -|resurrect|`/resurrect`|Resurrects the player.|| -|requestmailcount|`/requestmailcount`|Sends notification with number of unread messages in the player's mailbox.|| -|who|`/who`|Displays in chat all players on the instance.|| -|togglenameplate|`/togglenameplate`|Turns the nameplate above your head that is visible to other players off and on.|8 or if `allow_nameplate_off` is set to exactly `1` in the settings| -|toggleskipcinematics|`/toggleskipcinematics`|Skips mission and world load related cinematics.|8 or if `allow_players_to_skip_cinematics` is set to exactly `1` in the settings then 0| +|pvp|`/pvp`|Toggle your PVP flag.|0| +|resurrect|`/resurrect`|Resurrects the player.|0| +|requestmailcount|`/requestmailcount`|Sends notification with number of unread messages in the player's mailbox.|0| +|who|`/who`|Displays in chat all players on the instance.|0| +|togglenameplate|`/togglenameplate`|Turns the nameplate above your head that is visible to other players off and on.|8 unless `allow_nameplate_off` is set to exactly `1` in the settings then admin level requirement is 0| +|toggleskipcinematics|`/toggleskipcinematics`|Skips mission and world load related cinematics.|8 unless `allow_players_to_skip_cinematics` is set to exactly `1` in the settings then admin level requirement is 0| ## Moderation Commands |Command|Usage|Description|Admin Level Requirement| |--- |--- |--- |--- | -|gmlevel|`/gmlevel `|Within the authorized range of levels for the current account, changes the character's game master level to the specified value. This is required to use certain commands. Aliases: `/setgmlevel`, `/makegm`.|| +|gmlevel|`/gmlevel `|Within the authorized range of levels for the current account, changes the character's game master level to the specified value. This is required to use certain commands. Aliases: `/setgmlevel`, `/makegm`.|Account GM level greater than 0| |kick|`/kick `|Kicks the player off the server.|2| |mailitem|`/mailitem `|Mails an item to the given player. The mailed item has predetermined content. The sender name is set to "Darkflame Universe." The title of the message is "Lost item." The body of the message is "This is a replacement item for one you lost."|3| |ban|`/ban `|Bans a user from the server.|4| |approveproperty|`/approveproperty`|Approves the property the player is currently visiting.|5| |mute|`/mute (days) (hours)`|Mute player for the given amount of time. If no time is given, the mute is indefinite.|6| +|fly|`/fly `|This toggles your flying state with an optional parameter for the speed scale.|6| |attackimmune|`/attackimmune `|Sets the character's immunity to basic attacks state, where value can be one of "1", to make yourself immune to basic attack damage, or "0" to undo.|8| |gmimmune|`/gmimmunve `|Sets the character's GMImmune state, where value can be one of "1", to make yourself immune to damage, or "0" to undo.|8| |gminvis|`/gminvis`|Toggles invisibility for the character, though it's currently a bit buggy. Requires nonzero GM Level for the character, but the account must have a GM level of 8.|8| |setname|`/setname `|Sets a temporary name for your player. The name resets when you log out.|8| |title|`/title `|Temporarily appends your player's name with " - <title>". This resets when you log out.|8| -|fly|`/fly <speed>`|This toggles your flying state with an optional parameter for the speed scale.|4| ## Server Operation Commands @@ -51,14 +52,14 @@ These commands are primarily for development and testing. The usage of many of t |Command|Usage|Description|Admin Level Requirement| |--- |--- |--- |--- | -|fix-stats|`/fix-stats`|Resets skills, buffs, and destroyables.|| -|join|`/join <password>`|Joins a private zone with given password.|| -|leave-zone|`/leave-zone`|If you are in an instanced zone, transfers you to the closest main world. For example, if you are in an instance of Avant Gardens Survival or the Spider Queen Battle, you are sent to Avant Gardens. If you are in the Battle of Nimbus Station, you are sent to Nimbus Station.|| +|fix-stats|`/fix-stats`|Resets skills, buffs, and destroyables.|0| +|join|`/join <password>`|Joins a private zone with given password.|0| +|leave-zone|`/leave-zone` or <br> `/leavezone`|If you are in an instanced zone, transfers you to the closest main world. For example, if you are in an instance of Avant Gardens Survival or the Spider Queen Battle, you are sent to Avant Gardens. If you are in the Battle of Nimbus Station, you are sent to Nimbus Station.|0| |setminifig|`/setminifig <body part> <minifig item id>`|Alters your player's minifig. Body part can be one of "Eyebrows", "Eyes", "HairColor", "HairStyle", "Pants", "LeftHand", "Mouth", "RightHand", "Shirt", or "Hands". Changing minifig parts could break the character so this command is limited to GMs.|1| |testmap|`/testmap <zone> (force) (clone-id)`|Transfers you to the given zone by id and clone id. Add "force" to skip checking if the zone is accessible (this can softlock your character, though, if you e.g. try to teleport to Frostburgh).|1| |reportproxphys|`/reportproxphys`|Prints to console the position and radius of proximity sensors.|6| |spawnphysicsverts|`/spawnphysicsverts`|Spawns a 1x1 brick at all vertices of phantom physics objects.|6| -|teleport|`/teleport <x> (y) <z>`|Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z). Alias: `/tele`.|6| +|teleport|`/teleport <x> (y) <z>` or <br> `/tele <x> (y) <z>`|Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z). Alias: `/tele`.|6| |activatespawner|`/activatespawner <spawner name>`|Activates spawner by name.|8| |addmission|`/addmission <mission id>`|Accepts the mission, adding it to your journal.|8| |boost|`/boost (time)`|Adds a passive boost action if you are in a vehicle. If time is given it will end after that amount of time|8| @@ -96,9 +97,10 @@ These commands are primarily for development and testing. The usage of many of t |setcontrolscheme|`/setcontrolscheme <scheme number>`|Sets the character control scheme to the specified number.|8| |setcurrency|`/setcurrency <coins>`|Sets your coins.|8| |setflag|`/setflag (value) <flag id>`|Sets the given inventory or health flag to the given value, where value can be one of "on" or "off". If no value is given, by default this adds the flag to your character (equivalent of calling `/setflag on <flag id>`).|8| -|setinventorysize|`/setinventorysize <size> (inventory)`|Sets your inventory size to the given size. If `inventory` is provided, the number or string will be used to set that inventory to the requested size. Alias: `/setinvsize`|8| +|setinventorysize|`/setinventorysize <size> (inventory)` or <br> `/setinvsize <size> (inventory)`|Sets your inventory size to the given size. If `inventory` is provided, the number or string will be used to set that inventory to the requested size. Alias: `/setinvsize`|8| |setuistate|`/setuistate <ui state>`|Changes UI state.|8| |spawn|`/spawn <id>`|Spawns an object at your location by id.|8| +|spawngroup|`/spawngroup <id> <amount> <radius>`|Spawns `<amount>` of object `<id>` within the given `<radius>` from your location|8| |speedboost|`/speedboost <amount>`|Sets the speed multiplier to the given amount. `/speedboost 1.5` will set the speed multiplier to 1.5x the normal speed.|8| |startcelebration|`/startcelebration <id>`|Starts a celebration effect on your character.|8| |stopeffect|`/stopeffect <effect id>`|Stops the given effect.|8| @@ -107,14 +109,15 @@ These commands are primarily for development and testing. The usage of many of t |triggerspawner|`/triggerspawner <spawner name>`|Triggers spawner by name.|8| |unlock-emote|`/unlock-emote <emote id>`|Unlocks for your character the emote of the given id.|8| |Set Level|`/setlevel <requested_level> (username)`|Sets the using entities level to the requested level. Takes an optional parameter of an in-game players username to set the level of.|8| -|crash|`/crash`|Crashes the server.|9| -|rollloot|`/rollloot <loot matrix index> <item id> <amount>`|Given a `loot matrix index`, look for `item id` in that matrix `amount` times and print to the chat box statistics of rolling that loot matrix.|9| -|castskill|`/castskill <skill id>`|Casts the skill as the player|9| |setskillslot|`/setskillslot <slot> <skill id>`||8| |setfaction|`/setfaction <faction id>`|Clears the users current factions and sets it|8| |addfaction|`/addfaction <faction id>`|Add the faction to the users list of factions|8| |getfactions|`/getfactions`|Shows the player's factions|8| |setrewardcode|`/setrewardcode <code>`|Sets the rewardcode for the account you are logged into if it's a valid rewardcode, See cdclient table `RewardCodes`|8| +|crash|`/crash`|Crashes the server.|9| +|rollloot|`/rollloot <loot matrix index> <item id> <amount>`|Given a `loot matrix index`, look for `item id` in that matrix `amount` times and print to the chat box statistics of rolling that loot matrix.|9| +|castskill|`/castskill <skill id>`|Casts the skill as the player|9| + ## Detailed `/inspect` Usage `/inspect <component> (-m <waypoint> | -a <animation> | -s | -p | -f (faction) | -t)` diff --git a/tests/dCommonTests/dEnumsTests/MagicEnumTests.cpp b/tests/dCommonTests/dEnumsTests/MagicEnumTests.cpp index 2ad92914..0ca2e2ea 100644 --- a/tests/dCommonTests/dEnumsTests/MagicEnumTests.cpp +++ b/tests/dCommonTests/dEnumsTests/MagicEnumTests.cpp @@ -116,27 +116,30 @@ TEST(MagicEnumTest, eGameMessageTypeTest) { delete Game::logger; } -#define ASSERT_EARRAY_SORTED(EARRAY_VAR)\ - for (int i = 0; i < EARRAY_VAR->size(); i++) {\ - const auto entryCurr = EARRAY_VAR->at(i).first;\ - LOG_EARRAY(EARRAY_VAR, i, entryCurr);\ - const auto entryNext = EARRAY_VAR->at(++i).first;\ - LOG_EARRAY(EARRAY_VAR, i, entryNext);\ - ASSERT_TRUE(entryCurr < entryNext);\ - };\ +#define LOG_EARRAY(EARRAY_VAR, INDICE, ENTRY) LOG(#EARRAY_VAR"[%i] = %i, %s", INDICE, ENTRY, magic_enum::enum_name(ENTRY).data()); -#define LOG_EARRAY(EARRAY_VAR, INDICE, ENTRY)\ - LOG(#EARRAY_VAR"[%i] = %i, %s", INDICE, ENTRY, magic_enum::enum_name(ENTRY).data()); +namespace { + template <typename T> + void AssertEnumArraySorted(const T& eArray) { + for (int i = 0; i < eArray->size(); ++i) { + const auto entryCurr = eArray->at(i).first; + LOG_EARRAY(eArray, i, entryCurr); + const auto entryNext = eArray->at(++i).first; + LOG_EARRAY(eArray, i, entryNext); + ASSERT_TRUE(entryCurr < entryNext); + } + } +} // Test that the magic enum arrays are pre-sorted TEST(MagicEnumTest, ArraysAreSorted) { Game::logger = new Logger("./MagicEnumTest_ArraysAreSorted.log", true, true); constexpr auto wmArray = &magic_enum::enum_entries<eWorldMessageType>(); - ASSERT_EARRAY_SORTED(wmArray); + AssertEnumArraySorted(wmArray); constexpr auto gmArray = &magic_enum::enum_entries<eGameMessageType>(); - ASSERT_EARRAY_SORTED(gmArray); + AssertEnumArraySorted(gmArray); delete Game::logger; }