diff --git a/.env.example b/.env.example index 462fb17f..5e84184c 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,7 @@ # Full path to the LEGO Universe client -CLIENT_PATH=/Users/someuser/LEGO Universe -# Can improve build time -BUILD_THREADS=1 +CLIENT_PATH=./client # Updates NET_VERSION in CMakeVariables.txt -BUILD_VERSION=171022 +NET_VERSION=171022 # make sure this is a long random string # grab a "SHA 256-bit Key" from here: https://keygen.io/ ACCOUNT_MANAGER_SECRET= @@ -12,6 +10,5 @@ EXTERNAL_IP=localhost # Database values # Be careful with special characters here. It is more safe to use normal characters and/or numbers. MARIADB_USER=darkflame -MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME -MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME +MARIADB_PASSWORD= MARIADB_DATABASE=darkflame diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 8a81def7..ed7c0c7b 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -13,7 +13,7 @@ jobs: continue-on-error: true strategy: matrix: - os: [ windows-2022, ubuntu-20.04, macos-11 ] + os: [ windows-2022, ubuntu-22.04, macos-11 ] steps: - uses: actions/checkout@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index b42ddc6c..21c9105e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,7 +51,7 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE) # Disabled no-register # Disabled unknown pragmas because Linux doesn't understand Windows pragmas. if(UNIX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -fPIC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wuninitialized -fPIC") add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0) if(NOT APPLE) @@ -249,7 +249,9 @@ endforeach() # Add linking directories: # link_directories(${PROJECT_BINARY_DIR}) - +if (UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") +endif() file( GLOB HEADERS_DZONEMANAGER LIST_DIRECTORIES false @@ -281,6 +283,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" "MariaDB::ConnCpp" "magic_enum") diff --git a/CMakePresets.json b/CMakePresets.json index f8170755..77b8b242 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -14,7 +14,7 @@ "generator": "Unix Makefiles" }, { - "name": "ci-ubuntu-20.04", + "name": "ci-ubuntu-22.04", "displayName": "CI configure step for Ubuntu", "description": "Same as default, Used in GitHub actions workflow", "inherits": "default" @@ -67,8 +67,8 @@ "jobs": 2 }, { - "name": "ci-ubuntu-20.04", - "configurePreset": "ci-ubuntu-20.04", + "name": "ci-ubuntu-22.04", + "configurePreset": "ci-ubuntu-22.04", "displayName": "Linux CI Build", "description": "This preset is used by the CI build on linux", "jobs": 2 @@ -83,8 +83,8 @@ ], "testPresets": [ { - "name": "ci-ubuntu-20.04", - "configurePreset": "ci-ubuntu-20.04", + "name": "ci-ubuntu-22.04", + "configurePreset": "ci-ubuntu-22.04", "displayName": "CI Tests on Linux", "description": "Runs all tests on a linux configuration", "execution": { diff --git a/README.md b/README.md index 72bacc95..1d998511 100644 --- a/README.md +++ b/README.md @@ -348,12 +348,40 @@ certutil -hashfile SHA1 Known good *SHA1* checksum of the Darkflame Universe client: - `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed) + # Docker -## Standalone +The Darkflame Server is automatically built and published as a Docker Container / [OCI](https://opencontainers.org/) Image to the GitHub Container Registry at: +[`ghcr.io/darkflameuniverse/darkflameserver`](https://github.com/DarkflameUniverse/DarkflameServer/pkgs/container/darkflameserver). -For standalone deployment, you can use the image provided via Github's Container Repository: -`ghcr.io/darkflameuniverse/darkflameserver` +## Compose + +You can use the `docker-compose` tool to [setup a MariaDB database](#database-setup), run the Darkflame Server and manage it with [Nexus Dashboard](https://github.com/DarkflameUniverse/NexusDashboard) all +at once. For that: + +- [Install Docker Desktop](https://docs.docker.com/get-docker/) +- Open the directory that contains your LU Client + - If the `legouniverse.exe` is in a subfolder called `client`, you're good to go. There may also be a folder `versions`. + - Otherwise, create a new `client` folder and move the exe and everything else (e.g. `res` and `locale`) in there. This is necessary to work around a bug in the client that will prevent that you to log back in after getting disconnected. +- Download the [docker-compose.yml](docker-compose.yml) file and place it next to `client`. +- Download the [.env.example](.env.example) file and place it next to `client` with the file name `.env` + - You may get warnings that this name starts with a dot, acknowledge those, this is intentional. Depending on your operating system, you may need to activate showing hidden files (e.g. Ctrl-H in Gnome on Linux) and/or file extensions ("File name extensions" in the "View" tab on Windows). + - Update the `ACCOUNT_MANAGER_SECRET` and `MARIADB_PASSWORD` with strong random passwords. + - Use a password generator like + - Avoid `:` and `@` characters + - Once the database user is created, changing the password will not update it, so the server will just fail to connect. + - Set `EXTERNAL_IP` to your LAN IP or public IP if you want to host the game for friends & family +- Open a terminal in the folder with the `docker-compose.yml` and `client` +- Run `docker compose up -d` + - This might require `sudo` on Linux, and a recent version of [docker compose](https://docs.docker.com/compose/install/) +- Run `docker exec -it dlu-darkflameserver-1 /app/MasterServer -a` and follow the instructions to create the initial admin account +- Open to access Nexus Dashboard with the admin account to create normal users +- Set `AUTHSERVERIP=0:localhost` in `client/boot.cfg` + - Replace `localhost` with the value of `EXTERNAL_IP` if you changed that earlier. + - Also make sure `UGCUSE3DSERVICES=7:` is set to `0` +- Launch `legouniverse.exe` + +## Standalone This assumes that you have a database deployed to your host or in another docker container. @@ -376,14 +404,6 @@ You will need to replace the `/path/to/`'s to reflect the paths on your host. Any config option in the `.ini`'s can be overridden with environmental variables: Ex: `log_to_console=1` from `shared_config.ini` would be overidden like `-e LOG_TO_CONSOLE=0` -## Compose - -See the [compose](docker-compose.yml) file in the root of the repo. - -This compose file is for a full deployment: MariaDB, DarkflameServer, and Nexus Dashboard. - -All of the environmental options are listed in the compose file so the can be pass through, or you can edit the compose file to suit your specific needs - # Development Documentation This is a Work in Progress, but below are some quick links to documentaion for systems and structs in the server [Networked message structs](https://lcdruniverse.org/lu_packets/lu_packets/index.html) 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..d77eeeed 100644 --- a/dChatServer/ChatIgnoreList.cpp +++ b/dChatServer/ChatIgnoreList.cpp @@ -2,7 +2,6 @@ #include "PlayerContainer.h" #include "eChatInternalMessageType.h" #include "BitStreamUtils.h" -#include "PacketUtils.h" #include "Game.h" #include "Logger.h" #include "eObjectBits.h" @@ -26,37 +25,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,40 +67,40 @@ 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; } inStream.IgnoreBytes(4); // ignore some garbage zeros idk - LUWString toIgnoreName(33); + LUWString toIgnoreName; inStream.Read(toIgnoreName); 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 +110,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { ignoredPlayerId = player->id; } } else { - ignoredPlayerId = playerData->playerID; + ignoredPlayerId = playerData.playerID; } if (ignoredPlayerId != LWOOBJID_EMPTY) { @@ -120,7 +118,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 +139,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; @@ -149,21 +147,21 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) { inStream.IgnoreBytes(4); // ignore some garbage zeros idk - LUWString removedIgnoreName(33); + LUWString removedIgnoreName; 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..5e2e58d7 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -2,7 +2,6 @@ #include "PlayerContainer.h" #include "Database.h" #include -#include "PacketUtils.h" #include "BitStreamUtils.h" #include "Game.h" #include "dServer.h" @@ -18,6 +17,8 @@ #include "eChatInternalMessageType.h" #include "eClientMessageType.h" #include "eGameMessageType.h" +#include "StringifiedEnum.h" +#include "eGameMasterLevel.h" void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { //Get from the packet which player we want to do something with: @@ -25,7 +26,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 +38,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 +55,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,80 +67,81 @@ 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; } void ChatPacketHandler::HandleFriendRequest(Packet* packet) { CINSTREAM_SKIP_HEADER; + LWOOBJID requestorPlayerID; - inStream.Read(requestorPlayerID); - uint32_t spacing{}; - inStream.Read(spacing); - std::string playerName = ""; - uint16_t character; - bool noMoreLettersInName = false; - - for (uint32_t j = 0; j < 33; j++) { - inStream.Read(character); - if (character == '\0') noMoreLettersInName = true; - if (!noMoreLettersInName) playerName.push_back(static_cast(character)); - } - + LUWString LUplayerName; char isBestFriendRequest{}; + + inStream.Read(requestorPlayerID); + inStream.IgnoreBytes(4); + inStream.Read(LUplayerName); inStream.Read(isBestFriendRequest); - auto requestor = Game::playerContainer.GetPlayerData(requestorPlayerID); + auto playerName = LUplayerName.GetAsString(); + + 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) { - SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN); + // you cannot friend yourself + if (requestor.playerName == playerName) { + SendFriendResponse(requestor, requestor, eAddFriendResponseType::GENERALERROR); 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; + } + + // Prevent GM friend spam + // If the player we are trying to be friends with is not a civilian and we are a civilian, abort the process + if (requestee.gmLevel > eGameMasterLevel::CIVILIAN && requestor.gmLevel == eGameMasterLevel::CIVILIAN ) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::MYTHRAN); return; } @@ -147,7 +149,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,64 +176,65 @@ 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) { CINSTREAM_SKIP_HEADER; - LWOOBJID playerID; - inStream.Read(playerID); - eAddFriendResponseCode clientResponseCode = static_cast(packet->data[0x14]); - std::string friendName = PacketUtils::ReadString(0x15, packet, true); + LWOOBJID playerID; + eAddFriendResponseCode clientResponseCode; + LUWString friendName; + + inStream.Read(playerID); + inStream.IgnoreBytes(4); + inStream.Read(clientResponseCode); + inStream.Read(friendName); //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.GetAsString()); if (!requestor || !requestee) return; eAddFriendResponseType serverResponseCode{}; @@ -254,8 +257,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 +270,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); @@ -295,8 +296,11 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { CINSTREAM_SKIP_HEADER; LWOOBJID playerID; + LUWString LUFriendName; inStream.Read(playerID); - std::string friendName = PacketUtils::ReadString(0x14, packet, true); + inStream.IgnoreBytes(4); + inStream.Read(LUFriendName); + auto friendName = LUFriendName.GetAsString(); //we'll have to query the db here to find the user, since you can delete them while they're offline. //First, we need to find their ID: @@ -313,26 +317,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; } } @@ -341,150 +346,160 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { SendRemoveFriend(goonB, goonAName, true); } -void ChatPacketHandler::HandleChatMessage(Packet* packet) { - CINSTREAM_SKIP_HEADER; - LWOOBJID playerID = LWOOBJID_EMPTY; - inStream.Read(playerID); - - auto* sender = Game::playerContainer.GetPlayerData(playerID); - - if (sender == nullptr) return; - - if (Game::playerContainer.GetIsMuted(sender)) return; - - const auto senderName = std::string(sender->playerName.c_str()); - - inStream.SetReadOffset(0x14 * 8); - - uint8_t channel = 0; - inStream.Read(channel); - - std::string message = PacketUtils::ReadString(0x66, packet, true, 512); - - LOG("Got a message from (%s) [%d]: %s", senderName.c_str(), channel, message.c_str()); - - if (channel != 8) return; - - auto* team = Game::playerContainer.GetTeam(playerID); - - if (team == nullptr) return; - - for (const auto memberId : team->memberIDs) { - auto* otherMember = Game::playerContainer.GetPlayerData(memberId); - - if (otherMember == nullptr) return; - - const auto otherName = std::string(otherMember->playerName.c_str()); - - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(otherMember->playerID); - - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(otherMember->playerID); - bitStream.Write(8); - bitStream.Write(69); - bitStream.Write(LUWString(senderName)); - bitStream.Write(sender->playerID); - bitStream.Write(0); - bitStream.Write(0); //not mythran nametag - bitStream.Write(LUWString(otherName)); - bitStream.Write(0); //not mythran for receiver - bitStream.Write(0); //teams? - bitStream.Write(LUWString(message, 512)); - - SystemAddress sysAddr = otherMember->sysAddr; - SEND_PACKET; - } -} - -void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { - LWOOBJID senderID = PacketUtils::ReadS64(0x08, packet); - std::string receiverName = PacketUtils::ReadString(0x66, packet, true); - std::string message = PacketUtils::ReadString(0xAA, packet, true, 512); - - //Get the bois: - auto goonA = Game::playerContainer.GetPlayerData(senderID); - 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(); - - //To the sender: - { - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(goonA->playerID); - - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(goonA->playerID); - bitStream.Write(7); - bitStream.Write(69); - bitStream.Write(LUWString(goonAName)); - bitStream.Write(goonA->playerID); - bitStream.Write(0); - bitStream.Write(0); //not mythran nametag - bitStream.Write(LUWString(goonBName)); - bitStream.Write(0); //not mythran for receiver - bitStream.Write(0); //success - bitStream.Write(LUWString(message, 512)); - - SystemAddress sysAddr = goonA->sysAddr; - SEND_PACKET; - } - - //To the receiver: - { - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(goonB->playerID); - - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(goonA->playerID); - bitStream.Write(7); - bitStream.Write(69); - bitStream.Write(LUWString(goonAName)); - bitStream.Write(goonA->playerID); - bitStream.Write(0); - bitStream.Write(0); //not mythran nametag - bitStream.Write(LUWString(goonBName)); - bitStream.Write(0); //not mythran for receiver - bitStream.Write(3); //new whisper - bitStream.Write(LUWString(message, 512)); - - SystemAddress sysAddr = goonB->sysAddr; - SEND_PACKET; - } -} - -void ChatPacketHandler::HandleTeamInvite(Packet* packet) { +void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) { CINSTREAM_SKIP_HEADER; LWOOBJID playerID; inStream.Read(playerID); - std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true); + auto& player = Game::playerContainer.GetPlayerData(playerID); + if (!player) return; + inStream.Read(player.gmLevel); +} - auto* player = Game::playerContainer.GetPlayerData(playerID); +// the structure the client uses to send this packet is shared in many chat messages +// that are sent to the server. Because of this, there are large gaps of unused data in chat messages +void ChatPacketHandler::HandleChatMessage(Packet* packet) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerID; + inStream.Read(playerID); - if (player == nullptr) { + const auto& sender = Game::playerContainer.GetPlayerData(playerID); + if (!sender || sender.GetIsMuted()) return; + + eChatChannel channel; + uint32_t size; + + inStream.IgnoreBytes(4); + inStream.Read(channel); + inStream.Read(size); + inStream.IgnoreBytes(77); + + LUWString message(size); + inStream.Read(message); + + LOG("Got a message from (%s) via [%s]: %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str()); + + switch (channel) { + case eChatChannel::TEAM: { + auto* team = Game::playerContainer.GetTeam(playerID); + if (team == nullptr) return; + + for (const auto memberId : team->memberIDs) { + const auto& otherMember = Game::playerContainer.GetPlayerData(memberId); + if (!otherMember) return; + SendPrivateChatMessage(sender, otherMember, otherMember, message, eChatChannel::TEAM, eChatMessageResponseCode::SENT); + } + break; + } + default: + LOG("Unhandled Chat channel [%s]", StringifiedEnum::ToString(channel).data()); + break; + } +} + +// the structure the client uses to send this packet is shared in many chat messages +// that are sent to the server. Because of this, there are large gaps of unused data in chat messages +void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerID; + inStream.Read(playerID); + + const auto& sender = Game::playerContainer.GetPlayerData(playerID); + if (!sender || sender.GetIsMuted()) return; + + eChatChannel channel; + uint32_t size; + LUWString LUReceiverName; + + inStream.IgnoreBytes(4); + inStream.Read(channel); + if (channel != eChatChannel::PRIVATE_CHAT) LOG("WARNING: Received Private chat with the wrong channel!"); + + inStream.Read(size); + inStream.IgnoreBytes(77); + + inStream.Read(LUReceiverName); + auto receiverName = LUReceiverName.GetAsString(); + inStream.IgnoreBytes(2); + + LUWString message(size); + inStream.Read(message); + + LOG("Got a message from (%s) via [%s]: %s to %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str(), receiverName.c_str()); + + const auto& receiver = Game::playerContainer.GetPlayerData(receiverName); + if (!receiver) { + PlayerData otherPlayer; + otherPlayer.playerName = receiverName; + auto responseType = Database::Get()->GetCharacterInfo(receiverName) + ? eChatMessageResponseCode::NOTONLINE + : eChatMessageResponseCode::GENERALERROR; + + SendPrivateChatMessage(sender, otherPlayer, sender, message, eChatChannel::GENERAL, responseType); return; } + // Check to see if they are friends + // only freinds can whispr each other + for (const auto& fr : receiver.friends) { + if (fr.friendID == sender.playerID) { + //To the sender: + SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::PRIVATE_CHAT, eChatMessageResponseCode::SENT); + //To the receiver: + SendPrivateChatMessage(sender, receiver, receiver, message, eChatChannel::PRIVATE_CHAT, eChatMessageResponseCode::RECEIVEDNEWWHISPER); + return; + } + } + SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::GENERAL, eChatMessageResponseCode::NOTFRIENDS); +} + +void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) { + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(routeTo.playerID); + + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); + bitStream.Write(sender.playerID); + bitStream.Write(channel); + bitStream.Write(0); // not used + bitStream.Write(LUWString(sender.playerName)); + bitStream.Write(sender.playerID); + bitStream.Write(0); // sourceID + bitStream.Write(sender.gmLevel); + bitStream.Write(LUWString(receiver.playerName)); + bitStream.Write(receiver.gmLevel); + bitStream.Write(responseCode); + bitStream.Write(message); + + SystemAddress sysAddr = routeTo.sysAddr; + SEND_PACKET; +} + + +void ChatPacketHandler::HandleTeamInvite(Packet* packet) { + CINSTREAM_SKIP_HEADER; + + LWOOBJID playerID; + LUWString invitedPlayer; + + inStream.Read(playerID); + inStream.IgnoreBytes(4); + inStream.Read(invitedPlayer); + + const auto& player = Game::playerContainer.GetPlayerData(playerID); + + if (!player) return; + auto* team = Game::playerContainer.GetTeam(playerID); if (team == nullptr) { team = Game::playerContainer.CreateTeam(playerID); } - auto* other = Game::playerContainer.GetPlayerData(invitedPlayer); + const auto& other = Game::playerContainer.GetPlayerData(invitedPlayer.GetAsString()); - if (other == nullptr) { - return; - } + if (!other) return; - if (Game::playerContainer.GetTeam(other->playerID) != nullptr) { + if (Game::playerContainer.GetTeam(other.playerID) != nullptr) { return; } @@ -497,7 +512,7 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) { SendTeamInvite(other, player); - LOG("Got team invite: %llu -> %s", playerID, invitedPlayer.c_str()); + LOG("Got team invite: %llu -> %s", playerID, invitedPlayer.GetAsString().c_str()); } void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) { @@ -551,21 +566,25 @@ void ChatPacketHandler::HandleTeamLeave(Packet* packet) { void ChatPacketHandler::HandleTeamKick(Packet* packet) { CINSTREAM_SKIP_HEADER; + LWOOBJID playerID = LWOOBJID_EMPTY; + LUWString kickedPlayer; + inStream.Read(playerID); + inStream.IgnoreBytes(4); + inStream.Read(kickedPlayer); - std::string kickedPlayer = PacketUtils::ReadString(0x14, packet, true); - LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.c_str()); + LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.GetAsString().c_str()); - auto* kicked = Game::playerContainer.GetPlayerData(kickedPlayer); + const auto& kicked = Game::playerContainer.GetPlayerData(kickedPlayer.GetAsString()); LWOOBJID kickedId = LWOOBJID_EMPTY; - if (kicked != nullptr) { - kickedId = kicked->playerID; + if (kicked) { + kickedId = kicked.playerID; } else { - kickedId = Game::playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer)); + kickedId = Game::playerContainer.GetId(kickedPlayer.string); } if (kickedId == LWOOBJID_EMPTY) return; @@ -581,23 +600,26 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet) { void ChatPacketHandler::HandleTeamPromote(Packet* packet) { CINSTREAM_SKIP_HEADER; + LWOOBJID playerID = LWOOBJID_EMPTY; + LUWString promotedPlayer; + inStream.Read(playerID); + inStream.IgnoreBytes(4); + inStream.Read(promotedPlayer); - std::string promotedPlayer = PacketUtils::ReadString(0x14, packet, true); + LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.GetAsString().c_str()); - LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.c_str()); + const auto& promoted = Game::playerContainer.GetPlayerData(promotedPlayer.GetAsString()); - 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 +652,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 +675,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 +732,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 +757,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 +798,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 +829,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 +870,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 +907,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..847fc899 100644 --- a/dChatServer/ChatPacketHandler.h +++ b/dChatServer/ChatPacketHandler.h @@ -4,16 +4,56 @@ #include "BitStream.h" struct PlayerData; + enum class eAddFriendResponseType : uint8_t; +enum class eChatChannel : uint8_t { + SYSTEMNOTIFY = 0, + SYSTEMWARNING, + SYSTEMERROR, + BROADCAST, + LOCAL, + LOCALNOANIM, + EMOTE, + PRIVATE_CHAT, + TEAM, + TEAMLOCAL, + GUILD, + GUILDNOTIFY, + PROPERTY, + ADMIN, + COMBATDAMAGE, + COMBATHEALING, + COMBATLOOT, + COMBATEXP, + COMBATDEATH, + GENERAL, + TRADE, + LFG, + USER +}; + + +enum class eChatMessageResponseCode : uint8_t { + SENT = 0, + NOTONLINE, + GENERALERROR, + RECEIVEDNEWWHISPER, + NOTFRIENDS, + SENDERFREETRIAL, + RECEIVERFREETRIAL, +}; + namespace ChatPacketHandler { void HandleFriendlistRequest(Packet* packet); void HandleFriendRequest(Packet* packet); void HandleFriendResponse(Packet* packet); void HandleRemoveFriend(Packet* packet); + void HandleGMLevelUpdate(Packet* packet); void HandleChatMessage(Packet* packet); void HandlePrivateChatMessage(Packet* packet); + void SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode); void HandleTeamInvite(Packet* packet); void HandleTeamInviteResponse(Packet* packet); @@ -23,18 +63,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..d04cbd01 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -20,8 +20,10 @@ #include "eChatInternalMessageType.h" #include "eWorldMessageType.h" #include "ChatIgnoreList.h" +#include "StringifiedEnum.h" #include "Game.h" +#include "Server.h" //RakNet includes: #include "RakNetDefines.h" @@ -38,7 +40,6 @@ namespace Game { PlayerContainer playerContainer; } -Logger* SetupLogger(); void HandlePacket(Packet* packet); int main(int argc, char** argv) { @@ -51,14 +52,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 +182,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."); @@ -236,7 +224,8 @@ void HandlePacket(Packet* packet) { } if (static_cast(packet->data[1]) == eConnectionType::CHAT) { - switch (static_cast(packet->data[3])) { + eChatMessageType chat_message_type = static_cast(packet->data[3]); + switch (chat_message_type) { case eChatMessageType::GET_FRIENDS_LIST: ChatPacketHandler::HandleFriendlistRequest(packet); break; @@ -306,9 +295,61 @@ void HandlePacket(Packet* packet) { case eChatMessageType::TEAM_SET_LOOT: ChatPacketHandler::HandleTeamLootOption(packet); break; - + case eChatMessageType::GMLEVEL_UPDATE: + ChatPacketHandler::HandleGMLevelUpdate(packet); + break; + case eChatMessageType::LOGIN_SESSION_NOTIFY: + case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE: + case eChatMessageType::WORLD_DISCONNECT_REQUEST: + case eChatMessageType::WORLD_PROXIMITY_RESPONSE: + case eChatMessageType::WORLD_PARCEL_RESPONSE: + case eChatMessageType::TEAM_MISSED_INVITE_CHECK: + case eChatMessageType::GUILD_CREATE: + case eChatMessageType::GUILD_INVITE: + case eChatMessageType::GUILD_INVITE_RESPONSE: + case eChatMessageType::GUILD_LEAVE: + case eChatMessageType::GUILD_KICK: + case eChatMessageType::GUILD_GET_STATUS: + case eChatMessageType::GUILD_GET_ALL: + case eChatMessageType::SHOW_ALL: + case eChatMessageType::BLUEPRINT_MODERATED: + case eChatMessageType::BLUEPRINT_MODEL_READY: + case eChatMessageType::PROPERTY_READY_FOR_APPROVAL: + case eChatMessageType::PROPERTY_MODERATION_CHANGED: + case eChatMessageType::PROPERTY_BUILDMODE_CHANGED: + case eChatMessageType::PROPERTY_BUILDMODE_CHANGED_REPORT: + case eChatMessageType::MAIL: + case eChatMessageType::WORLD_INSTANCE_LOCATION_REQUEST: + case eChatMessageType::REPUTATION_UPDATE: + case eChatMessageType::SEND_CANNED_TEXT: + case eChatMessageType::CHARACTER_NAME_CHANGE_REQUEST: + case eChatMessageType::CSR_REQUEST: + case eChatMessageType::CSR_REPLY: + case eChatMessageType::GM_KICK: + case eChatMessageType::GM_ANNOUNCE: + case eChatMessageType::WORLD_ROUTE_PACKET: + case eChatMessageType::GET_ZONE_POPULATIONS: + case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE: + case eChatMessageType::MATCH_REQUEST: + case eChatMessageType::UGCMANIFEST_REPORT_MISSING_FILE: + case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE: + case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT: + case eChatMessageType::UGCC_REQUEST: + case eChatMessageType::WHO: + case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE: + case eChatMessageType::ACHIEVEMENT_NOTIFY: + case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW: + case eChatMessageType::UNEXPECTED_DISCONNECT: + case eChatMessageType::PLAYER_READY: + case eChatMessageType::GET_DONATION_TOTAL: + case eChatMessageType::UPDATE_DONATION: + case eChatMessageType::PRG_CSR_COMMAND: + case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD: + case eChatMessageType::UPDATE_FREE_TRIAL_STATUS: + LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chat_message_type).data(), chat_message_type); + break; default: - LOG("Unknown CHAT id: %i", int(packet->data[3])); + LOG("Unknown CHAT Message id: %i", chat_message_type); } } diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index f309ea0a..95bafd8b 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -10,6 +10,7 @@ #include "Database.h" #include "eConnectionType.h" #include "eChatInternalMessageType.h" +#include "eGameMasterLevel.h" #include "ChatPackets.h" #include "dConfig.h" @@ -22,33 +23,43 @@ PlayerContainer::~PlayerContainer() { m_Players.clear(); } +PlayerData::PlayerData() { + gmLevel = eGameMasterLevel::CIVILIAN; +} + TeamData::TeamData() { lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1; } 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); + inStream.Read(data.gmLevel); + 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 +68,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 +97,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 +107,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 +213,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 +227,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 +246,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 +265,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 +278,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 +302,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 +316,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 +338,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 +376,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..f34b1e54 100644 --- a/dChatServer/PlayerContainer.h +++ b/dChatServer/PlayerContainer.h @@ -7,7 +7,10 @@ #include "dServer.h" #include +enum class eGameMasterLevel : uint8_t; + 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 +19,34 @@ struct IgnoreData { return playerId == other; } - LWOOBJID playerId; + LWOOBJID playerId = LWOOBJID_EMPTY; std::string playerName; }; struct PlayerData { - LWOOBJID playerID; + PlayerData(); + 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; + eGameMasterLevel gmLevel; + bool isFTP = false; }; struct TeamData { @@ -52,22 +70,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 +86,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/CMakeLists.txt b/dCommon/CMakeLists.txt index 419462de..07d42d00 100644 --- a/dCommon/CMakeLists.txt +++ b/dCommon/CMakeLists.txt @@ -20,6 +20,12 @@ set(DCOMMON_SOURCES "FdbToSqlite.cpp" ) +# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible. +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97185 +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_source_files_properties("FdbToSqlite.cpp" PROPERTIES COMPILE_FLAGS "-Wno-stringop-overflow") +endif() + add_subdirectory(dClient) foreach(file ${DCOMMON_DCLIENT_SOURCES}) diff --git a/dCommon/PositionUpdate.h b/dCommon/PositionUpdate.h new file mode 100644 index 00000000..1b84e371 --- /dev/null +++ b/dCommon/PositionUpdate.h @@ -0,0 +1,50 @@ +#ifndef __POSITIONUPDATE__H__ +#define __POSITIONUPDATE__H__ + +#include "NiPoint3.h" +#include "NiQuaternion.h" + + +struct RemoteInputInfo { + RemoteInputInfo() { + m_RemoteInputX = 0; + m_RemoteInputY = 0; + m_IsPowersliding = false; + m_IsModified = false; + } + + void operator=(const RemoteInputInfo& other) { + m_RemoteInputX = other.m_RemoteInputX; + m_RemoteInputY = other.m_RemoteInputY; + m_IsPowersliding = other.m_IsPowersliding; + m_IsModified = other.m_IsModified; + } + + bool operator==(const RemoteInputInfo& other) { + return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified; + } + + float m_RemoteInputX; + float m_RemoteInputY; + bool m_IsPowersliding; + bool m_IsModified; +}; + +struct LocalSpaceInfo { + LWOOBJID objectId = LWOOBJID_EMPTY; + NiPoint3 position = NiPoint3::ZERO; + NiPoint3 linearVelocity = NiPoint3::ZERO; +}; + +struct PositionUpdate { + NiPoint3 position = NiPoint3::ZERO; + NiQuaternion rotation = NiQuaternion::IDENTITY; + bool onGround = false; + bool onRail = false; + NiPoint3 velocity = NiPoint3::ZERO; + NiPoint3 angularVelocity = NiPoint3::ZERO; + LocalSpaceInfo localSpaceInfo; + RemoteInputInfo remoteInputInfo; +}; + +#endif //!__POSITIONUPDATE__H__ diff --git a/dCommon/dEnums/StringifiedEnum.h b/dCommon/dEnums/StringifiedEnum.h index 16eafdca..1816d705 100644 --- a/dCommon/dEnums/StringifiedEnum.h +++ b/dCommon/dEnums/StringifiedEnum.h @@ -9,15 +9,15 @@ namespace StringifiedEnum { const std::string_view ToString(const T e) { static_assert(std::is_enum_v, "Not an enum"); // Check type - constexpr auto sv = &magic_enum::enum_entries(); + constexpr auto& sv = magic_enum::enum_entries(); const auto it = std::lower_bound( - sv->begin(), sv->end(), e, + sv.begin(), sv.end(), e, [&](const std::pair& lhs, const T rhs) { return lhs.first < rhs; } ); std::string_view output; - if (it != sv->end() && it->first == e) { + if (it != sv.end() && it->first == e) { output = it->second; } else { output = "UNKNOWN"; 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/dCommon/dEnums/eGameMessageType.h b/dCommon/dEnums/eGameMessageType.h index 14673fac..e3fc22b6 100644 --- a/dCommon/dEnums/eGameMessageType.h +++ b/dCommon/dEnums/eGameMessageType.h @@ -629,7 +629,7 @@ enum class eGameMessageType : uint16_t { GET_INSTRUCTION_COUNT = 676, GET_IS_NPC = 677, ACTIVATE_BUBBLE_BUFF = 678, - DECTIVATE_BUBBLE_BUFF = 679, // thanks netdevil + DECTIVATE_BUBBLE_BUFF = 679, // This is spelled wrong in the client, so we misspell it here. EXHIBIT_VOTE = 680, ADD_PET_TO_PLAYER = 681, REMOVE_PET_FROM_PLAYER = 682, diff --git a/dCommon/dEnums/ePetAbilityType.h b/dCommon/dEnums/ePetAbilityType.h new file mode 100644 index 00000000..0cc6d6bd --- /dev/null +++ b/dCommon/dEnums/ePetAbilityType.h @@ -0,0 +1,13 @@ +#ifndef __EPETABILITYTYPE__H__ +#define __EPETABILITYTYPE__H__ + +#include + +enum class ePetAbilityType : uint32_t { + Invalid, + GoToObject, + JumpOnObject, + DigAtPosition +}; + +#endif //!__EPETABILITYTYPE__H__ diff --git a/dDatabase/CDClientDatabase/CDClientDatabase.cpp b/dDatabase/CDClientDatabase/CDClientDatabase.cpp index 4c2df1d2..886030a1 100644 --- a/dDatabase/CDClientDatabase/CDClientDatabase.cpp +++ b/dDatabase/CDClientDatabase/CDClientDatabase.cpp @@ -4,9 +4,13 @@ // Static Variables static CppSQLite3DB* conn = new CppSQLite3DB(); +// Status Variables +bool CDClientDatabase::isConnected = false; + //! Opens a connection with the CDClient void CDClientDatabase::Connect(const std::string& filename) { conn->open(filename.c_str()); + isConnected = true; } //! Queries the CDClient diff --git a/dDatabase/CDClientDatabase/CDClientDatabase.h b/dDatabase/CDClientDatabase/CDClientDatabase.h index 7f42918d..fa58906c 100644 --- a/dDatabase/CDClientDatabase/CDClientDatabase.h +++ b/dDatabase/CDClientDatabase/CDClientDatabase.h @@ -15,6 +15,10 @@ //! The CDClient Database namespace namespace CDClientDatabase { + /** + * Boolean defining the connection status of CDClient + */ + extern bool isConnected; //! Opens a connection with the CDClient /*! diff --git a/dDatabase/CDClientDatabase/CDClientManager.cpp b/dDatabase/CDClientDatabase/CDClientManager.cpp index 74c5c975..f270d849 100644 --- a/dDatabase/CDClientDatabase/CDClientManager.cpp +++ b/dDatabase/CDClientDatabase/CDClientManager.cpp @@ -3,6 +3,7 @@ #include "CDAnimationsTable.h" #include "CDBehaviorParameterTable.h" #include "CDBehaviorTemplateTable.h" +#include "CDClientDatabase.h" #include "CDComponentsRegistryTable.h" #include "CDCurrencyTableTable.h" #include "CDDestructibleComponentTable.h" @@ -39,6 +40,8 @@ #include "CDRailActivatorComponent.h" #include "CDRewardCodesTable.h" +#include + #ifndef CDCLIENT_CACHE_ALL // Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory. // A vanilla CDClient takes about 46MB of memory + the regular world data. @@ -51,7 +54,16 @@ #define CDCLIENT_DONT_CACHE_TABLE(x) #endif -CDClientManager::CDClientManager() { +class CDClientConnectionException : public std::exception { +public: + virtual const char* what() const throw() { + return "CDClientDatabase is not connected!"; + } +}; + +void CDClientManager::LoadValuesFromDatabase() { + if (!CDClientDatabase::isConnected) throw CDClientConnectionException(); + CDActivityRewardsTable::Instance().LoadValuesFromDatabase(); CDActivitiesTable::Instance().LoadValuesFromDatabase(); CDCLIENT_DONT_CACHE_TABLE(CDAnimationsTable::Instance().LoadValuesFromDatabase()); @@ -79,6 +91,7 @@ CDClientManager::CDClientManager() { CDCLIENT_DONT_CACHE_TABLE(CDObjectsTable::Instance().LoadValuesFromDatabase()); CDPhysicsComponentTable::Instance().LoadValuesFromDatabase(); CDPackageComponentTable::Instance().LoadValuesFromDatabase(); + CDPetComponentTable::Instance().LoadValuesFromDatabase(); CDProximityMonitorComponentTable::Instance().LoadValuesFromDatabase(); CDPropertyEntranceComponentTable::Instance().LoadValuesFromDatabase(); CDPropertyTemplateTable::Instance().LoadValuesFromDatabase(); @@ -92,3 +105,9 @@ CDClientManager::CDClientManager() { CDVendorComponentTable::Instance().LoadValuesFromDatabase(); CDZoneTableTable::Instance().LoadValuesFromDatabase(); } + +void CDClientManager::LoadValuesFromDefaults() { + LOG("Loading default CDClient tables!"); + + CDPetComponentTable::Instance().LoadValuesFromDefaults(); +} diff --git a/dDatabase/CDClientDatabase/CDClientManager.h b/dDatabase/CDClientDatabase/CDClientManager.h index 74069ff4..ae628a36 100644 --- a/dDatabase/CDClientDatabase/CDClientManager.h +++ b/dDatabase/CDClientDatabase/CDClientManager.h @@ -11,7 +11,10 @@ */ class CDClientManager : public Singleton { public: - CDClientManager(); + CDClientManager() = default; + + void LoadValuesFromDatabase(); + void LoadValuesFromDefaults(); /** * Fetch a table from CDClient diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp index c967f724..998c4095 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp @@ -2,7 +2,7 @@ void CDActivitiesTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Activities"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h index 2e39d595..75fc602e 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h @@ -4,22 +4,22 @@ #include "CDTable.h" struct CDActivities { - unsigned int ActivityID; - unsigned int locStatus; - unsigned int instanceMapID; - unsigned int minTeams; - unsigned int maxTeams; - unsigned int minTeamSize; - unsigned int maxTeamSize; - unsigned int waitTime; - unsigned int startDelay; + uint32_t ActivityID; + uint32_t locStatus; + uint32_t instanceMapID; + uint32_t minTeams; + uint32_t maxTeams; + uint32_t minTeamSize; + uint32_t maxTeamSize; + uint32_t waitTime; + uint32_t startDelay; bool requiresUniqueData; - unsigned int leaderboardType; + uint32_t leaderboardType; bool localize; - int optionalCostLOT; - int optionalCostCount; + int32_t optionalCostLOT; + int32_t optionalCostCount; bool showUIRewards; - unsigned int CommunityActivityFlagID; + uint32_t CommunityActivityFlagID; std::string gate_version; bool noTeamLootOnDeath; float optionalPercentage; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp index a2434d19..7795a177 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp @@ -3,7 +3,7 @@ void CDActivityRewardsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ActivityRewards"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h index a177a3c0..40ab17e7 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h @@ -4,12 +4,12 @@ #include "CDTable.h" struct CDActivityRewards { - unsigned int objectTemplate; //!< The object template (?) - unsigned int ActivityRewardIndex; //!< The activity reward index - int activityRating; //!< The activity rating - unsigned int LootMatrixIndex; //!< The loot matrix index - unsigned int CurrencyIndex; //!< The currency index - unsigned int ChallengeRating; //!< The challenge rating + uint32_t objectTemplate; //!< The object template (?) + uint32_t ActivityRewardIndex; //!< The activity reward index + int32_t activityRating; //!< The activity rating + uint32_t LootMatrixIndex; //!< The loot matrix index + uint32_t CurrencyIndex; //!< The currency index + uint32_t ChallengeRating; //!< The challenge rating std::string description; //!< The description }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h index 494d5cde..1b6280ca 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h @@ -4,13 +4,13 @@ #include struct CDAnimation { - // unsigned int animationGroupID; + // uint32_t animationGroupID; // std::string animation_type; // The above two are a pair to represent a primary key in the map. std::string animation_name; //!< The animation name float chance_to_play; //!< The chance to play the animation - UNUSED_COLUMN(unsigned int min_loops;) //!< The minimum number of loops - UNUSED_COLUMN(unsigned int max_loops;) //!< The maximum number of loops + UNUSED_COLUMN(uint32_t min_loops;) //!< The minimum number of loops + UNUSED_COLUMN(uint32_t max_loops;) //!< The maximum number of loops float animation_length; //!< The animation length UNUSED_COLUMN(bool hideEquip;) //!< Whether or not to hide the equip UNUSED_COLUMN(bool ignoreUpperBody;) //!< Whether or not to ignore the upper body diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp index c343ef85..a67398a9 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp @@ -3,7 +3,7 @@ void CDBehaviorTemplateTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM BehaviorTemplate"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h index 49ce11f2..cbc494a2 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h @@ -6,9 +6,9 @@ #include struct CDBehaviorTemplate { - unsigned int behaviorID; //!< The Behavior ID - unsigned int templateID; //!< The Template ID (LOT) - unsigned int effectID; //!< The Effect ID attached + uint32_t behaviorID; //!< The Behavior ID + uint32_t templateID; //!< The Template ID (LOT) + uint32_t effectID; //!< The Effect ID attached std::unordered_set::iterator effectHandle; //!< The effect handle }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp index 9be75f0b..abc917b5 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp @@ -3,7 +3,7 @@ void CDBrickIDTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM BrickIDTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h index 68c0b1b6..1a9f9a94 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h @@ -10,8 +10,8 @@ //! BrickIDTable Entry Struct struct CDBrickIDTable { - unsigned int NDObjectID; - unsigned int LEGOBrickID; + uint32_t NDObjectID; + uint32_t LEGOBrickID; }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h index fce4f6aa..45da7614 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h @@ -7,9 +7,9 @@ enum class eReplicaComponentType : uint32_t; struct CDComponentsRegistry { - unsigned int id; //!< The LOT is used as the ID + uint32_t id; //!< The LOT is used as the ID eReplicaComponentType component_type; //!< See ComponentTypes enum for values - unsigned int component_id; //!< The ID used within the component's table (0 may either mean it's non-networked, or that the ID is actually 0 + uint32_t component_id; //!< The ID used within the component's table (0 may either mean it's non-networked, or that the ID is actually 0 }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp index 58c517f2..deb2b168 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp @@ -4,7 +4,7 @@ void CDCurrencyTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM CurrencyTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h index fd70a968..9c68c277 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h @@ -10,11 +10,11 @@ //! CurrencyTable Struct struct CDCurrencyTable { - unsigned int currencyIndex; //!< The Currency Index - unsigned int npcminlevel; //!< The minimum level of the npc - unsigned int minvalue; //!< The minimum currency - unsigned int maxvalue; //!< The maximum currency - unsigned int id; //!< The ID of the currency index + uint32_t currencyIndex; //!< The Currency Index + uint32_t npcminlevel; //!< The minimum level of the npc + uint32_t minvalue; //!< The minimum currency + uint32_t maxvalue; //!< The maximum currency + uint32_t id; //!< The ID of the currency index }; //! CurrencyTable table diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp index f92aec94..4939a50e 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp @@ -2,7 +2,7 @@ void CDDestructibleComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM DestructibleComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h index fb6ee4cd..0871d9ea 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h @@ -4,20 +4,20 @@ #include "CDTable.h" struct CDDestructibleComponent { - unsigned int id; //!< The component ID from the ComponentsRegistry Table - int faction; //!< The Faction ID of the object + uint32_t id; //!< The component ID from the ComponentsRegistry Table + int32_t faction; //!< The Faction ID of the object std::string factionList; //!< A list of the faction IDs - int life; //!< The amount of life of the object - unsigned int imagination; //!< The amount of imagination of the object - int LootMatrixIndex; //!< The Loot Matrix Index - int CurrencyIndex; //!< The Currency Index - unsigned int level; //!< ??? + int32_t life; //!< The amount of life of the object + uint32_t imagination; //!< The amount of imagination of the object + int32_t LootMatrixIndex; //!< The Loot Matrix Index + int32_t CurrencyIndex; //!< The Currency Index + uint32_t level; //!< ??? float armor; //!< The amount of armor of the object - unsigned int death_behavior; //!< The behavior ID of the death behavior + uint32_t death_behavior; //!< The behavior ID of the death behavior bool isnpc; //!< Whether or not the object is an NPC - unsigned int attack_priority; //!< ??? + uint32_t attack_priority; //!< ??? bool isSmashable; //!< Whether or not the object is smashable - int difficultyLevel; //!< ??? + int32_t difficultyLevel; //!< ??? }; class CDDestructibleComponentTable : public CDTable { diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp index 77aa226c..a7f39705 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp @@ -20,7 +20,7 @@ void CDEmoteTableTable::LoadValuesFromDatabase() { tableData.finalize(); } -CDEmoteTable* CDEmoteTableTable::GetEmote(int id) { +CDEmoteTable* CDEmoteTableTable::GetEmote(int32_t id) { auto itr = entries.find(id); return itr != entries.end() ? &itr->second : nullptr; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h index a49d1c45..360cfc38 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h @@ -16,11 +16,11 @@ struct CDEmoteTable { gateVersion = ""; } - int ID; + int32_t ID; std::string animationName; std::string iconFilename; - int locState; - int channel; + int32_t locState; + int32_t channel; bool locked; bool localize; std::string gateVersion; @@ -33,5 +33,5 @@ private: public: void LoadValuesFromDatabase(); // Returns an emote by ID - CDEmoteTable* GetEmote(int id); + CDEmoteTable* GetEmote(int32_t id); }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp index 9b133155..1a146bf1 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp @@ -3,7 +3,7 @@ void CDFeatureGatingTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM FeatureGating"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp index ffc8fee6..1a21a899 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp @@ -3,7 +3,7 @@ void CDInventoryComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM InventoryComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h index 26d47ffe..1f5a525d 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h @@ -4,9 +4,9 @@ #include "CDTable.h" struct CDInventoryComponent { - unsigned int id; //!< The component ID for this object - unsigned int itemid; //!< The LOT of the object - unsigned int count; //!< The count of the items the object has + uint32_t id; //!< The component ID for this object + uint32_t itemid; //!< The LOT of the object + uint32_t count; //!< The count of the items the object has bool equip; //!< Whether or not to equip the item }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp index 5d6722f9..9f7609e9 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp @@ -5,7 +5,7 @@ CDItemComponent CDItemComponentTable::Default = {}; void CDItemComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ItemComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -69,7 +69,7 @@ void CDItemComponentTable::LoadValuesFromDatabase() { tableData.finalize(); } -const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) { +const CDItemComponent& CDItemComponentTable::GetItemComponentByID(uint32_t skillID) { const auto& it = this->entries.find(skillID); if (it != this->entries.end()) { return it->second; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h index 685e5acd..014c9801 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h @@ -5,60 +5,60 @@ #include "dCommonVars.h" struct CDItemComponent { - unsigned int id; //!< The Component ID + uint32_t id; //!< The Component ID std::string equipLocation; //!< The equip location - unsigned int baseValue; //!< The monetary base value of the item + uint32_t baseValue; //!< The monetary base value of the item bool isKitPiece; //!< Whether or not the item belongs to a kit - unsigned int rarity; //!< The rarity of the item - unsigned int itemType; //!< The item type + uint32_t rarity; //!< The rarity of the item + uint32_t itemType; //!< The item type int64_t itemInfo; //!< The item info bool inLootTable; //!< Whether or not the item is in a loot table bool inVendor; //!< Whether or not the item is in a vendor inventory bool isUnique; //!< ??? bool isBOP; //!< ??? bool isBOE; //!< ??? - unsigned int reqFlagID; //!< User must have completed this flag to get the item - unsigned int reqSpecialtyID; //!< ??? - unsigned int reqSpecRank; //!< ??? - unsigned int reqAchievementID; //!< The required achievement must be completed - unsigned int stackSize; //!< The stack size of the item - unsigned int color1; //!< Something to do with item color... - unsigned int decal; //!< The decal of the item - unsigned int offsetGroupID; //!< Something to do with group IDs - unsigned int buildTypes; //!< Something to do with building + uint32_t reqFlagID; //!< User must have completed this flag to get the item + uint32_t reqSpecialtyID; //!< ??? + uint32_t reqSpecRank; //!< ??? + uint32_t reqAchievementID; //!< The required achievement must be completed + uint32_t stackSize; //!< The stack size of the item + uint32_t color1; //!< Something to do with item color... + uint32_t decal; //!< The decal of the item + uint32_t offsetGroupID; //!< Something to do with group IDs + uint32_t buildTypes; //!< Something to do with building std::string reqPrecondition; //!< The required precondition - unsigned int animationFlag; //!< The Animation Flag - unsigned int equipEffects; //!< The effect played when the item is equipped + uint32_t animationFlag; //!< The Animation Flag + uint32_t equipEffects; //!< The effect played when the item is equipped bool readyForQA; //!< ??? - unsigned int itemRating; //!< ??? + uint32_t itemRating; //!< ??? bool isTwoHanded; //!< Whether or not the item is double handed - unsigned int minNumRequired; //!< Maybe the minimum number required for a mission, or to own this object? - unsigned int delResIndex; //!< ??? - unsigned int currencyLOT; //!< ??? - unsigned int altCurrencyCost; //!< ??? + uint32_t minNumRequired; //!< Maybe the minimum number required for a mission, or to own this object? + uint32_t delResIndex; //!< ??? + uint32_t currencyLOT; //!< ??? + uint32_t altCurrencyCost; //!< ??? std::string subItems; //!< A comma seperate string of sub items (maybe for multi-itemed things like faction test gear set) UNUSED(std::string audioEventUse); //!< ??? bool noEquipAnimation; //!< Whether or not there is an equip animation - unsigned int commendationLOT; //!< The commendation LOT - unsigned int commendationCost; //!< The commendation cost + uint32_t commendationLOT; //!< The commendation LOT + uint32_t commendationCost; //!< The commendation cost UNUSED(std::string audioEquipMetaEventSet); //!< ??? std::string currencyCosts; //!< Used for crafting UNUSED(std::string ingredientInfo); //!< Unused - unsigned int locStatus; //!< ??? - unsigned int forgeType; //!< Forge Type + uint32_t locStatus; //!< ??? + uint32_t forgeType; //!< Forge Type float SellMultiplier; //!< Something to do with early vendors perhaps (but replaced) }; class CDItemComponentTable : public CDTable { private: - std::map entries; + std::map entries; public: void LoadValuesFromDatabase(); static std::map ParseCraftingCurrencies(const CDItemComponent& itemComponent); // Gets an entry by ID - const CDItemComponent& GetItemComponentByID(unsigned int skillID); + const CDItemComponent& GetItemComponentByID(uint32_t skillID); static CDItemComponent Default; }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp index 6fb1689e..0376bad4 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp @@ -3,7 +3,7 @@ void CDItemSetSkillsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ItemSetSkills"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -44,7 +44,7 @@ const std::vector& CDItemSetSkillsTable::GetEntries() const { return this->entries; } -std::vector CDItemSetSkillsTable::GetBySkillID(unsigned int SkillSetID) { +std::vector CDItemSetSkillsTable::GetBySkillID(uint32_t SkillSetID) { std::vector toReturn; for (CDItemSetSkills entry : this->entries) { diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h index 07321a7f..ee5fda05 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h @@ -4,9 +4,9 @@ #include "CDTable.h" struct CDItemSetSkills { - unsigned int SkillSetID; //!< The skill set ID - unsigned int SkillID; //!< The skill ID - unsigned int SkillCastType; //!< The skill cast type + uint32_t SkillSetID; //!< The skill set ID + uint32_t SkillID; //!< The skill ID + uint32_t SkillCastType; //!< The skill cast type }; class CDItemSetSkillsTable : public CDTable { @@ -20,5 +20,5 @@ public: const std::vector& GetEntries() const; - std::vector GetBySkillID(unsigned int SkillSetID); + std::vector GetBySkillID(uint32_t SkillSetID); }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp index de70e180..f3859ae2 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp @@ -3,7 +3,7 @@ void CDItemSetsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ItemSets"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h index a3a738b1..77cc0c35 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h @@ -4,20 +4,20 @@ #include "CDTable.h" struct CDItemSets { - unsigned int setID; //!< The item set ID - unsigned int locStatus; //!< The loc status + uint32_t setID; //!< The item set ID + uint32_t locStatus; //!< The loc status std::string itemIDs; //!< THe item IDs - unsigned int kitType; //!< The item kit type - unsigned int kitRank; //!< The item kit rank - unsigned int kitImage; //!< The item kit image - unsigned int skillSetWith2; //!< The skill set with 2 - unsigned int skillSetWith3; //!< The skill set with 3 - unsigned int skillSetWith4; //!< The skill set with 4 - unsigned int skillSetWith5; //!< The skill set with 5 - unsigned int skillSetWith6; //!< The skill set with 6 + uint32_t kitType; //!< The item kit type + uint32_t kitRank; //!< The item kit rank + uint32_t kitImage; //!< The item kit image + uint32_t skillSetWith2; //!< The skill set with 2 + uint32_t skillSetWith3; //!< The skill set with 3 + uint32_t skillSetWith4; //!< The skill set with 4 + uint32_t skillSetWith5; //!< The skill set with 5 + uint32_t skillSetWith6; //!< The skill set with 6 bool localize; //!< Whether or localize std::string gate_version; //!< The gate version - unsigned int kitID; //!< The kit ID + uint32_t kitID; //!< The kit ID float priority; //!< The priority }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp index d0a6ca93..930f3a85 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp @@ -3,7 +3,7 @@ void CDLevelProgressionLookupTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LevelProgressionLookup"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h index 6b68bd0d..fa1bb4cc 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h @@ -4,8 +4,8 @@ #include "CDTable.h" struct CDLevelProgressionLookup { - unsigned int id; //!< The Level ID - unsigned int requiredUScore; //!< The required LEGO Score + uint32_t id; //!< The Level ID + uint32_t requiredUScore; //!< The required LEGO Score std::string BehaviorEffect; //!< The behavior effect attached to this }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp index 4755c116..36bebb69 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp @@ -16,7 +16,7 @@ CDLootMatrix CDLootMatrixTable::ReadRow(CppSQLite3Query& tableData) const { void CDLootMatrixTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootMatrix"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h index 551583f6..c7157e07 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h @@ -4,12 +4,12 @@ #include "CDTable.h" struct CDLootMatrix { - unsigned int LootTableIndex; //!< The Loot Table Index - unsigned int RarityTableIndex; //!< The Rarity Table Index + uint32_t LootTableIndex; //!< The Loot Table Index + uint32_t RarityTableIndex; //!< The Rarity Table Index float percent; //!< The percent that this matrix is used? - unsigned int minToDrop; //!< The minimum amount of loot from this matrix to drop - unsigned int maxToDrop; //!< The maximum amount of loot from this matrix to drop - unsigned int flagID; //!< ??? + uint32_t minToDrop; //!< The minimum amount of loot from this matrix to drop + uint32_t maxToDrop; //!< The maximum amount of loot from this matrix to drop + uint32_t flagID; //!< ??? UNUSED(std::string gate_version); //!< The Gate Version }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp index 9ee875df..a90f8108 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp @@ -40,7 +40,7 @@ CDLootTable CDLootTableTable::ReadRow(CppSQLite3Query& tableData) const { void CDLootTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h index b8ac2066..e432d621 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h @@ -4,10 +4,10 @@ #include "CDTable.h" struct CDLootTable { - unsigned int itemid; //!< The LOT of the item - unsigned int LootTableIndex; //!< The Loot Table Index + uint32_t itemid; //!< The LOT of the item + uint32_t LootTableIndex; //!< The Loot Table Index bool MissionDrop; //!< Whether or not this loot table is a mission drop - unsigned int sortPriority; //!< The sorting priority + uint32_t sortPriority; //!< The sorting priority }; typedef uint32_t LootTableIndex; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp index 0babef13..c7e884c2 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp @@ -3,7 +3,7 @@ void CDMissionEmailTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM MissionEmail"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h index 954da78e..06c15e71 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h @@ -4,13 +4,13 @@ #include "CDTable.h" struct CDMissionEmail { - unsigned int ID; - unsigned int messageType; - unsigned int notificationGroup; - unsigned int missionID; - unsigned int attachmentLOT; + uint32_t ID; + uint32_t messageType; + uint32_t notificationGroup; + uint32_t missionID; + uint32_t attachmentLOT; bool localize; - unsigned int locStatus; + uint32_t locStatus; std::string gate_version; }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp index 59ea40b7..87d2bd61 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp @@ -3,7 +3,7 @@ void CDMissionNPCComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM MissionNPCComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h index 2b2b3303..8c4b790d 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h @@ -4,8 +4,8 @@ #include "CDTable.h" struct CDMissionNPCComponent { - unsigned int id; //!< The ID - unsigned int missionID; //!< The Mission ID + uint32_t id; //!< The ID + uint32_t missionID; //!< The Mission ID bool offersMission; //!< Whether or not this NPC offers a mission bool acceptsMission; //!< Whether or not this NPC accepts a mission std::string gate_version; //!< The gate version diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp index 9795ea8f..b2cb9e21 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp @@ -3,7 +3,7 @@ void CDMissionTasksTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM MissionTasks"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h index 0b169db8..420dbfbe 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h @@ -4,17 +4,17 @@ #include "CDTable.h" struct CDMissionTasks { - unsigned int id; //!< The Mission ID that the task belongs to - UNUSED(unsigned int locStatus); //!< ??? - unsigned int taskType; //!< The task type - unsigned int target; //!< The mission target + uint32_t id; //!< The Mission ID that the task belongs to + UNUSED(uint32_t locStatus); //!< ??? + uint32_t taskType; //!< The task type + uint32_t target; //!< The mission target std::string targetGroup; //!< The mission target group - int targetValue; //!< The target value + int32_t targetValue; //!< The target value std::string taskParam1; //!< The task param 1 UNUSED(std::string largeTaskIcon); //!< ??? - UNUSED(unsigned int IconID); //!< ??? - unsigned int uid; //!< ??? - UNUSED(unsigned int largeTaskIconID); //!< ??? + UNUSED(uint32_t IconID); //!< ??? + uint32_t uid; //!< ??? + UNUSED(uint32_t largeTaskIconID); //!< ??? UNUSED(bool localize); //!< Whether or not the task should be localized UNUSED(std::string gate_version); //!< ??? }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp index 37f0c81c..bc9eb76c 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp @@ -5,7 +5,7 @@ CDMissions CDMissionsTable::Default = {}; void CDMissionsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Missions"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h index c8ddc2a3..de4b21c3 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h @@ -6,58 +6,58 @@ #include struct CDMissions { - int id; //!< The Mission ID + int32_t id; //!< The Mission ID std::string defined_type; //!< The type of mission std::string defined_subtype; //!< The subtype of the mission - int UISortOrder; //!< The UI Sort Order for the mission - int offer_objectID; //!< The LOT of the mission giver - int target_objectID; //!< The LOT of the mission's target + int32_t UISortOrder; //!< The UI Sort Order for the mission + int32_t offer_objectID; //!< The LOT of the mission giver + int32_t target_objectID; //!< The LOT of the mission's target int64_t reward_currency; //!< The amount of currency to reward the player - int LegoScore; //!< The amount of LEGO Score to reward the player + int32_t LegoScore; //!< The amount of LEGO Score to reward the player int64_t reward_reputation; //!< The reputation to award the player bool isChoiceReward; //!< Whether or not the user has the option to choose their loot - int reward_item1; //!< The first rewarded item - int reward_item1_count; //!< The count of the first item to be rewarded - int reward_item2; //!< The second rewarded item - int reward_item2_count; //!< The count of the second item to be rewarded - int reward_item3; //!< The third rewarded item - int reward_item3_count; //!< The count of the third item to be rewarded - int reward_item4; //!< The fourth rewarded item - int reward_item4_count; //!< The count of the fourth item to be rewarded - int reward_emote; //!< The first emote to be rewarded - int reward_emote2; //!< The second emote to be rewarded - int reward_emote3; //!< The third emote to be rewarded - int reward_emote4; //!< The fourth emote to be rewarded - int reward_maximagination; //!< The amount of max imagination to reward - int reward_maxhealth; //!< The amount of max health to reward - int reward_maxinventory; //!< The amount of max inventory to reward - int reward_maxmodel; //!< ??? - int reward_maxwidget; //!< ??? - int reward_maxwallet; //!< ??? + int32_t reward_item1; //!< The first rewarded item + int32_t reward_item1_count; //!< The count of the first item to be rewarded + int32_t reward_item2; //!< The second rewarded item + int32_t reward_item2_count; //!< The count of the second item to be rewarded + int32_t reward_item3; //!< The third rewarded item + int32_t reward_item3_count; //!< The count of the third item to be rewarded + int32_t reward_item4; //!< The fourth rewarded item + int32_t reward_item4_count; //!< The count of the fourth item to be rewarded + int32_t reward_emote; //!< The first emote to be rewarded + int32_t reward_emote2; //!< The second emote to be rewarded + int32_t reward_emote3; //!< The third emote to be rewarded + int32_t reward_emote4; //!< The fourth emote to be rewarded + int32_t reward_maximagination; //!< The amount of max imagination to reward + int32_t reward_maxhealth; //!< The amount of max health to reward + int32_t reward_maxinventory; //!< The amount of max inventory to reward + int32_t reward_maxmodel; //!< ??? + int32_t reward_maxwidget; //!< ??? + int32_t reward_maxwallet; //!< ??? bool repeatable; //!< Whether or not this mission can be repeated (for instance, is it a daily mission) int64_t reward_currency_repeatable; //!< The repeatable reward - int reward_item1_repeatable; //!< The first rewarded item - int reward_item1_repeat_count; //!< The count of the first item to be rewarded - int reward_item2_repeatable; //!< The second rewarded item - int reward_item2_repeat_count; //!< The count of the second item to be rewarded - int reward_item3_repeatable; //!< The third rewarded item - int reward_item3_repeat_count; //!< The count of the third item to be rewarded - int reward_item4_repeatable; //!< The fourth rewarded item - int reward_item4_repeat_count; //!< The count of the fourth item to be rewarded - int time_limit; //!< The time limit of the mission + int32_t reward_item1_repeatable; //!< The first rewarded item + int32_t reward_item1_repeat_count; //!< The count of the first item to be rewarded + int32_t reward_item2_repeatable; //!< The second rewarded item + int32_t reward_item2_repeat_count; //!< The count of the second item to be rewarded + int32_t reward_item3_repeatable; //!< The third rewarded item + int32_t reward_item3_repeat_count; //!< The count of the third item to be rewarded + int32_t reward_item4_repeatable; //!< The fourth rewarded item + int32_t reward_item4_repeat_count; //!< The count of the fourth item to be rewarded + int32_t time_limit; //!< The time limit of the mission bool isMission; //!< Maybe to differentiate between missions and achievements? - int missionIconID; //!< The mission icon ID + int32_t missionIconID; //!< The mission icon ID std::string prereqMissionID; //!< A '|' seperated list of prerequisite missions bool localize; //!< Whether or not to localize the mission bool inMOTD; //!< In Match of the Day(?) int64_t cooldownTime; //!< The mission cooldown time bool isRandom; //!< ??? std::string randomPool; //!< ??? - int UIPrereqID; //!< ??? + int32_t UIPrereqID; //!< ??? UNUSED(std::string gate_version); //!< The gate version UNUSED(std::string HUDStates); //!< ??? - UNUSED(int locStatus); //!< ??? - int reward_bankinventory; //!< The amount of bank space this mission rewards + UNUSED(int32_t locStatus); //!< ??? + int32_t reward_bankinventory; //!< The amount of bank space this mission rewards }; class CDMissionsTable : public CDTable { diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp index 35b46416..be1c3d96 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp @@ -3,7 +3,7 @@ void CDMovementAIComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM MovementAIComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h index b40694bd..8b415f3a 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h @@ -4,7 +4,7 @@ #include "CDTable.h" struct CDMovementAIComponent { - unsigned int id; + uint32_t id; std::string MovementType; float WanderChance; float WanderDelayMin; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp index 2439622c..958c6cc8 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp @@ -3,7 +3,7 @@ void CDObjectSkillsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ObjectSkills"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h index bd9929e2..0b88fb6f 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h @@ -4,10 +4,10 @@ #include "CDTable.h" struct CDObjectSkills { - unsigned int objectTemplate; //!< The LOT of the item - unsigned int skillID; //!< The Skill ID of the object - unsigned int castOnType; //!< ??? - unsigned int AICombatWeight; //!< ??? + uint32_t objectTemplate; //!< The LOT of the item + uint32_t skillID; //!< The Skill ID of the object + uint32_t castOnType; //!< ??? + uint32_t AICombatWeight; //!< ??? }; class CDObjectSkillsTable : public CDTable { diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp index d3094b68..3282e14c 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp @@ -2,7 +2,7 @@ void CDObjectsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Objects"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -40,7 +40,7 @@ void CDObjectsTable::LoadValuesFromDatabase() { m_default.id = 0; } -const CDObjects& CDObjectsTable::GetByID(unsigned int LOT) { +const CDObjects& CDObjectsTable::GetByID(uint32_t LOT) { const auto& it = this->entries.find(LOT); if (it != this->entries.end()) { return it->second; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h index 3a776684..2ef47727 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h @@ -4,30 +4,30 @@ #include "CDTable.h" struct CDObjects { - unsigned int id; //!< The LOT of the object + uint32_t id; //!< The LOT of the object std::string name; //!< The internal name of the object - UNUSED(unsigned int placeable); //!< Whether or not the object is placable + UNUSED(uint32_t placeable); //!< Whether or not the object is placable std::string type; //!< The object type UNUSED(std::string description); //!< An internal description of the object - UNUSED(unsigned int localize); //!< Whether or not the object should localize - UNUSED(unsigned int npcTemplateID); //!< Something related to NPCs... + UNUSED(uint32_t localize); //!< Whether or not the object should localize + UNUSED(uint32_t npcTemplateID); //!< Something related to NPCs... UNUSED(std::string displayName); //!< The display name of the object float interactionDistance; //!< The interaction distance of the object - UNUSED(unsigned int nametag); //!< ??? + UNUSED(uint32_t nametag); //!< ??? UNUSED(std::string _internalNotes); //!< Some internal notes (rarely used) - UNUSED(unsigned int locStatus); //!< ??? + UNUSED(uint32_t locStatus); //!< ??? UNUSED(std::string gate_version); //!< The gate version for the object - UNUSED(unsigned int HQ_valid); //!< Probably used for the Nexus HQ database on LEGOUniverse.com + UNUSED(uint32_t HQ_valid); //!< Probably used for the Nexus HQ database on LEGOUniverse.com }; class CDObjectsTable : public CDTable { private: - std::map entries; + std::map entries; CDObjects m_default; public: void LoadValuesFromDatabase(); // Gets an entry by ID - const CDObjects& GetByID(unsigned int LOT); + const CDObjects& GetByID(uint32_t LOT); }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp index 8b955162..8038c779 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp @@ -3,7 +3,7 @@ void CDPackageComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM PackageComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h index 7ee58761..cc8b0636 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h @@ -4,9 +4,9 @@ #include "CDTable.h" struct CDPackageComponent { - unsigned int id; - unsigned int LootMatrixIndex; - unsigned int packageType; + uint32_t id; + uint32_t LootMatrixIndex; + uint32_t packageType; }; class CDPackageComponentTable : public CDTable { diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp new file mode 100644 index 00000000..c3dd5d50 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp @@ -0,0 +1,61 @@ +#include "CDPetComponentTable.h" + +namespace { + // Default entries for fallback + CDPetComponent defaultEntry{ + .id = 0, + UNUSED_ENTRY(.minTameUpdateTime = 60.0f,) + UNUSED_ENTRY(.maxTameUpdateTime = 300.0f,) + UNUSED_ENTRY(.percentTameChance = 101.0f,) + UNUSED_ENTRY(.tameability = 100.0f,) + UNUSED_ENTRY(.elementType = 1,) + .walkSpeed = 2.5f, + .runSpeed = 5.0f, + .sprintSpeed = 10.0f, + UNUSED_ENTRY(.idleTimeMin = 60.0f,) + UNUSED_ENTRY(.idleTimeMax = 300.0f,) + UNUSED_ENTRY(.petForm = 0,) + .imaginationDrainRate = 60.0f, + UNUSED_ENTRY(.AudioMetaEventSet = "",) + UNUSED_ENTRY(.buffIDs = "",) + }; +} + +void CDPetComponentTable::LoadValuesFromDatabase() { + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PetComponent"); + while (!tableData.eof()) { + const uint32_t componentID = tableData.getIntField("id", defaultEntry.id); + + auto& entry = m_Entries[componentID]; + entry.id = componentID; + UNUSED_COLUMN(entry.minTameUpdateTime = tableData.getFloatField("minTameUpdateTime", defaultEntry.minTameUpdateTime)); + UNUSED_COLUMN(entry.maxTameUpdateTime = tableData.getFloatField("maxTameUpdateTime", defaultEntry.maxTameUpdateTime)); + UNUSED_COLUMN(entry.percentTameChance = tableData.getFloatField("percentTameChance", defaultEntry.percentTameChance)); + UNUSED_COLUMN(entry.tameability = tableData.getFloatField("tamability", defaultEntry.tameability)); // Mispelled as "tamability" in CDClient + UNUSED_COLUMN(entry.elementType = tableData.getIntField("elementType", defaultEntry.elementType)); + entry.walkSpeed = static_cast(tableData.getFloatField("walkSpeed", defaultEntry.walkSpeed)); + entry.runSpeed = static_cast(tableData.getFloatField("runSpeed", defaultEntry.runSpeed)); + entry.sprintSpeed = static_cast(tableData.getFloatField("sprintSpeed", defaultEntry.sprintSpeed)); + UNUSED_COLUMN(entry.idleTimeMin = tableData.getFloatField("idleTimeMin", defaultEntry.idleTimeMin)); + UNUSED_COLUMN(entry.idleTimeMax = tableData.getFloatField("idleTimeMax", defaultEntry.idleTimeMax)); + UNUSED_COLUMN(entry.petForm = tableData.getIntField("petForm", defaultEntry.petForm)); + entry.imaginationDrainRate = static_cast(tableData.getFloatField("imaginationDrainRate", defaultEntry.imaginationDrainRate)); + UNUSED_COLUMN(entry.AudioMetaEventSet = tableData.getStringField("AudioMetaEventSet", defaultEntry.AudioMetaEventSet)); + UNUSED_COLUMN(entry.buffIDs = tableData.getStringField("buffIDs", defaultEntry.buffIDs)); + + tableData.nextRow(); + } +} + +void CDPetComponentTable::LoadValuesFromDefaults() { + m_Entries.insert(std::make_pair(defaultEntry.id, defaultEntry)); +} + +CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) { + auto itr = m_Entries.find(componentID); + if (itr == m_Entries.end()) { + LOG("Unable to load pet component (ID %i) values from database! Using default values instead.", componentID); + return defaultEntry; + } + return itr->second; +} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h new file mode 100644 index 00000000..fa54e457 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h @@ -0,0 +1,45 @@ +#pragma once +#include "CDTable.h" +#include +#include + +struct CDPetComponent { + uint32_t id; + UNUSED_COLUMN(float minTameUpdateTime;) + UNUSED_COLUMN(float maxTameUpdateTime;) + UNUSED_COLUMN(float percentTameChance;) + UNUSED_COLUMN(float tameability;) // Mispelled as "tamability" in CDClient + UNUSED_COLUMN(uint32_t elementType;) + float walkSpeed; + float runSpeed; + float sprintSpeed; + UNUSED_COLUMN(float idleTimeMin;) + UNUSED_COLUMN(float idleTimeMax;) + UNUSED_COLUMN(uint32_t petForm;) + float imaginationDrainRate; + UNUSED_COLUMN(std::string AudioMetaEventSet;) + UNUSED_COLUMN(std::string buffIDs;) +}; + +class CDPetComponentTable : public CDTable { +public: + + /** + * Load values from the CD client database + */ + void LoadValuesFromDatabase(); + + /** + * Load the default values into memory instead of attempting to connect to the CD client database + */ + void LoadValuesFromDefaults(); + + /** + * Gets the pet component table corresponding to the pet component ID + * @returns A reference to the corresponding table, or the default if one could not be found + */ + CDPetComponent& GetByID(const uint32_t componentID); + +private: + std::map m_Entries; +}; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp index e17be4df..ebc5327b 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp @@ -28,7 +28,7 @@ void CDPhysicsComponentTable::LoadValuesFromDatabase() { tableData.finalize(); } -CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) { +CDPhysicsComponent* CDPhysicsComponentTable::GetByID(uint32_t componentID) { auto itr = m_entries.find(componentID); return itr != m_entries.end() ? &itr->second : nullptr; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h index 49f3b4c3..5ed33cc9 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h @@ -3,7 +3,7 @@ #include struct CDPhysicsComponent { - int id; + int32_t id; bool bStatic; std::string physicsAsset; UNUSED(bool jump); @@ -12,8 +12,8 @@ struct CDPhysicsComponent { UNUSED(float rotSpeed); float playerHeight; float playerRadius; - int pcShapeType; - int collisionGroup; + int32_t pcShapeType; + int32_t collisionGroup; UNUSED(float airSpeed); UNUSED(std::string boundaryAsset); UNUSED(float jumpAirSpeed); @@ -26,8 +26,8 @@ public: void LoadValuesFromDatabase(); static const std::string GetTableName() { return "PhysicsComponent"; }; - CDPhysicsComponent* GetByID(unsigned int componentID); + CDPhysicsComponent* GetByID(uint32_t componentID); private: - std::map m_entries; + std::map m_entries; }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp index ae0abac8..6edd00b2 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp @@ -3,7 +3,7 @@ void CDProximityMonitorComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ProximityMonitorComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h index a50dd37e..861c900e 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h @@ -4,7 +4,7 @@ #include "CDTable.h" struct CDProximityMonitorComponent { - unsigned int id; + uint32_t id; std::string Proximities; bool LoadOnClient; bool LoadOnServer; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp index aa451ae3..6f086e34 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp @@ -3,7 +3,7 @@ void CDRarityTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RarityTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h index e2053da7..1248350b 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h @@ -5,7 +5,7 @@ struct CDRarityTable { float randmax; - unsigned int rarity; + uint32_t rarity; }; typedef std::vector RarityTable; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp index f5706a28..30534936 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp @@ -3,7 +3,7 @@ void CDRebuildComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RebuildComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h index fc78e904..aed71905 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h @@ -4,15 +4,15 @@ #include "CDTable.h" struct CDRebuildComponent { - unsigned int id; //!< The component Id + uint32_t id; //!< The component Id float reset_time; //!< The reset time float complete_time; //!< The complete time - unsigned int take_imagination; //!< The amount of imagination it costs + uint32_t take_imagination; //!< The amount of imagination it costs bool interruptible; //!< Whether or not the rebuild is interruptible bool self_activator; //!< Whether or not the rebuild is a rebuild activator itself std::string custom_modules; //!< The custom modules - unsigned int activityID; //!< The activity ID - unsigned int post_imagination_cost; //!< The post imagination cost + uint32_t activityID; //!< The activity ID + uint32_t post_imagination_cost; //!< The post imagination cost float time_before_smash; //!< The time before smash }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp index 2bda73f4..4dab9ee9 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp @@ -3,7 +3,7 @@ void CDRewardCodesTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RewardCodes"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp index 1a922199..a2fe0514 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp @@ -3,7 +3,7 @@ void CDScriptComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ScriptComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -28,8 +28,8 @@ void CDScriptComponentTable::LoadValuesFromDatabase() { tableData.finalize(); } -const CDScriptComponent& CDScriptComponentTable::GetByID(unsigned int id) { - std::map::iterator it = this->entries.find(id); +const CDScriptComponent& CDScriptComponentTable::GetByID(uint32_t id) { + std::map::iterator it = this->entries.find(id); if (it != this->entries.end()) { return it->second; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h index d2b7e7ae..56296776 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h @@ -4,19 +4,19 @@ #include "CDTable.h" struct CDScriptComponent { - unsigned int id; //!< The component ID + uint32_t id; //!< The component ID std::string script_name; //!< The script name std::string client_script_name; //!< The client script name }; class CDScriptComponentTable : public CDTable { private: - std::map entries; + std::map entries; CDScriptComponent m_ToReturnWhenNoneFound; public: void LoadValuesFromDatabase(); // Gets an entry by scriptID - const CDScriptComponent& GetByID(unsigned int id); + const CDScriptComponent& GetByID(uint32_t id); }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp index 8ffbb5ce..51ed7de3 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp @@ -4,7 +4,7 @@ void CDSkillBehaviorTable::LoadValuesFromDatabase() { m_empty = CDSkillBehavior(); // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM SkillBehavior"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -49,8 +49,8 @@ void CDSkillBehaviorTable::LoadValuesFromDatabase() { tableData.finalize(); } -const CDSkillBehavior& CDSkillBehaviorTable::GetSkillByID(unsigned int skillID) { - std::map::iterator it = this->entries.find(skillID); +const CDSkillBehavior& CDSkillBehaviorTable::GetSkillByID(uint32_t skillID) { + std::map::iterator it = this->entries.find(skillID); if (it != this->entries.end()) { return it->second; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h index 5b1081cd..0c970be6 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h @@ -4,36 +4,36 @@ #include "CDTable.h" struct CDSkillBehavior { - unsigned int skillID; //!< The Skill ID of the skill - UNUSED(unsigned int locStatus); //!< ?? - unsigned int behaviorID; //!< The Behavior ID of the skill - unsigned int imaginationcost; //!< The imagination cost of the skill - unsigned int cooldowngroup; //!< The cooldown group ID of the skill + uint32_t skillID; //!< The Skill ID of the skill + UNUSED(uint32_t locStatus); //!< ?? + uint32_t behaviorID; //!< The Behavior ID of the skill + uint32_t imaginationcost; //!< The imagination cost of the skill + uint32_t cooldowngroup; //!< The cooldown group ID of the skill float cooldown; //!< The cooldown time of the skill UNUSED(bool isNpcEditor); //!< ??? - UNUSED(unsigned int skillIcon); //!< The Skill Icon ID + UNUSED(uint32_t skillIcon); //!< The Skill Icon ID UNUSED(std::string oomSkillID); //!< ??? - UNUSED(unsigned int oomBehaviorEffectID); //!< ??? - UNUSED(unsigned int castTypeDesc); //!< The cast type description(?) - UNUSED(unsigned int imBonusUI); //!< The imagination bonus of the skill - UNUSED(nsigned int lifeBonusUI); //!< The life bonus of the skill - UNUSED(unsigned int armorBonusUI); //!< The armor bonus of the skill - UNUSED(unsigned int damageUI); //!< ??? + UNUSED(uint32_t oomBehaviorEffectID); //!< ??? + UNUSED(uint32_t castTypeDesc); //!< The cast type description(?) + UNUSED(uint32_t imBonusUI); //!< The imagination bonus of the skill + UNUSED(nint32_t lifeBonusUI); //!< The life bonus of the skill + UNUSED(uint32_t armorBonusUI); //!< The armor bonus of the skill + UNUSED(uint32_t damageUI); //!< ??? UNUSED(bool hideIcon); //!< Whether or not to show the icon UNUSED(bool localize); //!< ??? UNUSED(std::string gate_version); //!< ??? - UNUSED(unsigned int cancelType); //!< The cancel type (?) + UNUSED(uint32_t cancelType); //!< The cancel type (?) }; class CDSkillBehaviorTable : public CDTable { private: - std::map entries; + std::map entries; CDSkillBehavior m_empty; public: void LoadValuesFromDatabase(); // Gets an entry by skillID - const CDSkillBehavior& GetSkillByID(unsigned int skillID); + const CDSkillBehavior& GetSkillByID(uint32_t skillID); }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h index 0a8f29ad..ab965127 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h @@ -23,6 +23,9 @@ // Enable this to skip some unused columns in some tables #define UNUSED_COLUMN(v) +// Use this to skip unused defaults for unused entries in some tables +#define UNUSED_ENTRY(v, x) + #pragma warning (disable : 4244) //Disable double to float conversion warnings #pragma warning (disable : 4715) //Disable "not all control paths return a value" diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp index 0f963b04..990d0b32 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp @@ -3,7 +3,7 @@ void CDVendorComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM VendorComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h index 29854d49..133ce78f 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h @@ -4,11 +4,11 @@ #include "CDTable.h" struct CDVendorComponent { - unsigned int id; //!< The Component ID + uint32_t id; //!< The Component ID float buyScalar; //!< Buy Scalar (what does that mean?) float sellScalar; //!< Sell Scalar (what does that mean?) float refreshTimeSeconds; //!< The refresh time - unsigned int LootMatrixIndex; //!< LootMatrixIndex of the vendor's items + uint32_t LootMatrixIndex; //!< LootMatrixIndex of the vendor's items }; class CDVendorComponentTable : public CDTable { diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp index 2793ccb9..b599c37f 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp @@ -3,7 +3,7 @@ void CDZoneTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ZoneTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -53,7 +53,7 @@ void CDZoneTableTable::LoadValuesFromDatabase() { } //! Queries the table with a zoneID to find. -const CDZoneTable* CDZoneTableTable::Query(unsigned int zoneID) { +const CDZoneTable* CDZoneTableTable::Query(uint32_t zoneID) { const auto& iter = m_Entries.find(zoneID); if (iter != m_Entries.end()) { diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h index c3f51aa6..5f5970ae 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h @@ -4,14 +4,14 @@ #include "CDTable.h" struct CDZoneTable { - unsigned int zoneID; //!< The Zone ID of the object - unsigned int locStatus; //!< The Locale Status(?) + uint32_t zoneID; //!< The Zone ID of the object + uint32_t locStatus; //!< The Locale Status(?) std::string zoneName; //!< The name of the zone - unsigned int scriptID; //!< The Script ID of the zone (ScriptsTable) + uint32_t scriptID; //!< The Script ID of the zone (ScriptsTable) float ghostdistance_min; //!< The minimum ghosting distance float ghostdistance; //!< The ghosting distance - unsigned int population_soft_cap; //!< The "soft cap" on the world population - unsigned int population_hard_cap; //!< The "hard cap" on the world population + uint32_t population_soft_cap; //!< The "soft cap" on the world population + uint32_t population_hard_cap; //!< The "hard cap" on the world population UNUSED(std::string DisplayDescription); //!< The display description of the world UNUSED(std::string mapFolder); //!< ??? float smashableMinDistance; //!< The minimum smashable distance? @@ -19,9 +19,9 @@ struct CDZoneTable { UNUSED(std::string mixerProgram); //!< ??? UNUSED(std::string clientPhysicsFramerate); //!< The client physics framerate std::string serverPhysicsFramerate; //!< The server physics framerate - unsigned int zoneControlTemplate; //!< The Zone Control template - unsigned int widthInChunks; //!< The width of the world in chunks - unsigned int heightInChunks; //!< The height of the world in chunks + uint32_t zoneControlTemplate; //!< The Zone Control template + uint32_t widthInChunks; //!< The width of the world in chunks + uint32_t heightInChunks; //!< The height of the world in chunks bool petsAllowed; //!< Whether or not pets are allowed in the world bool localize; //!< Whether or not the world should be localized float fZoneWeight; //!< ??? @@ -35,11 +35,11 @@ struct CDZoneTable { class CDZoneTableTable : public CDTable { private: - std::map m_Entries; + std::map m_Entries; public: void LoadValuesFromDatabase(); // Queries the table with a zoneID to find. - const CDZoneTable* Query(unsigned int zoneID); + const CDZoneTable* Query(uint32_t zoneID); }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt index b2551efa..af401db2 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt +++ b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt @@ -23,6 +23,7 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp" "CDMovementAIComponentTable.cpp" "CDObjectSkillsTable.cpp" "CDObjectsTable.cpp" + "CDPetComponentTable.cpp" "CDPackageComponentTable.cpp" "CDPhysicsComponentTable.cpp" "CDPropertyEntranceComponentTable.cpp" diff --git a/dGame/CMakeLists.txt b/dGame/CMakeLists.txt index 76af2e7e..b13f52a6 100644 --- a/dGame/CMakeLists.txt +++ b/dGame/CMakeLists.txt @@ -3,6 +3,7 @@ set(DGAME_SOURCES "Character.cpp" "EntityManager.cpp" "LeaderboardManager.cpp" "Player.cpp" + "PlayerManager.cpp" "TeamManager.cpp" "TradingManager.cpp" "User.cpp" diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index aaa01e97..8b72a80c 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -3,7 +3,6 @@ #include "CDClientManager.h" #include "Game.h" #include "Logger.h" -#include "PacketUtils.h" #include #include "CDDestructibleComponentTable.h" #include "CDClientDatabase.h" @@ -25,6 +24,8 @@ #include "eMissionTaskType.h" #include "eTriggerEventType.h" #include "eObjectBits.h" +#include "PositionUpdate.h" +#include "eChatMessageType.h" //Component includes: #include "Component.h" @@ -80,6 +81,7 @@ #include "RacingStatsComponent.h" #include "CollectibleComponent.h" #include "ItemComponent.h" +#include "GhostComponent.h" // Table includes #include "CDComponentsRegistryTable.h" @@ -436,6 +438,8 @@ void Entity::Initialize() { AddComponent(); AddComponent(m_Character)->LoadFromXml(m_Character->GetXMLDoc()); + + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) { @@ -855,9 +859,20 @@ void Entity::SetGMLevel(eGameMasterLevel value) { } CharacterComponent* character = GetComponent(); - if (character) character->SetGMLevel(value); + if (!character) return; + character->SetGMLevel(value); GameMessages::SendGMLevelBroadcast(m_ObjectID, value); + + // Update the chat server of our GM Level + { + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GMLEVEL_UPDATE); + bitStream.Write(m_ObjectID); + bitStream.Write(m_GMLevel); + + Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); + } } void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType) { @@ -1222,39 +1237,56 @@ void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) { void Entity::Update(const float deltaTime) { uint32_t timerPosition; - timerPosition = 0; - while (timerPosition < m_Timers.size()) { - m_Timers[timerPosition]->Update(deltaTime); - if (m_Timers[timerPosition]->GetTime() <= 0) { - const auto timerName = m_Timers[timerPosition]->GetName(); - - delete m_Timers[timerPosition]; + for (timerPosition = 0; timerPosition < m_Timers.size();) { + auto& timer = m_Timers[timerPosition]; + timer.Update(deltaTime); + // If the timer is expired, erase it and dont increment the position because the next timer will be at the same position. + // Before: [0, 1, 2, 3, ..., n] + // timerPosition ^ + // After: [0, 1, 3, ..., n] + // timerPosition ^ + if (timer.GetTime() <= 0) { + // Remove the timer from the list of timers first so that scripts and events can remove timers without causing iterator invalidation + auto timerName = timer.GetName(); m_Timers.erase(m_Timers.begin() + timerPosition); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { script->OnTimerDone(this, timerName); } + TriggerEvent(eTriggerEventType::TIMER_DONE, this); + } else { + // If the timer isnt expired, go to the next timer. + timerPosition++; + } + } + + for (timerPosition = 0; timerPosition < m_CallbackTimers.size(); ) { + // If the timer is expired, erase it and dont increment the position because the next timer will be at the same position. + // Before: [0, 1, 2, 3, ..., n] + // timerPosition ^ + // After: [0, 1, 3, ..., n] + // timerPosition ^ + auto& callbackTimer = m_CallbackTimers[timerPosition]; + callbackTimer.Update(deltaTime); + if (callbackTimer.GetTime() <= 0) { + // Remove the timer from the list of timers first so that callbacks can remove timers without causing iterator invalidation + auto callback = callbackTimer.GetCallback(); + m_CallbackTimers.erase(m_CallbackTimers.begin() + timerPosition); + callback(); } else { timerPosition++; } } - for (int i = 0; i < m_CallbackTimers.size(); i++) { - m_CallbackTimers[i]->Update(deltaTime); - if (m_CallbackTimers[i]->GetTime() <= 0) { - m_CallbackTimers[i]->GetCallback()(); - delete m_CallbackTimers[i]; - m_CallbackTimers.erase(m_CallbackTimers.begin() + i); - } + // Add pending timers to the list of timers so they start next tick. + if (!m_PendingTimers.empty()) { + m_Timers.insert(m_Timers.end(), m_PendingTimers.begin(), m_PendingTimers.end()); + m_PendingTimers.clear(); } - // Add pending timers to the list of timers so they start next tick. - if (m_PendingTimers.size() > 0) { - for (auto namedTimer : m_PendingTimers) { - m_Timers.push_back(namedTimer); - } - m_PendingTimers.clear(); + if (!m_PendingCallbackTimers.empty()) { + m_CallbackTimers.insert(m_CallbackTimers.end(), m_PendingCallbackTimers.begin(), m_PendingCallbackTimers.end()); + m_PendingCallbackTimers.clear(); } if (IsSleeping()) { @@ -1692,31 +1724,20 @@ void Entity::RemoveParent() { } void Entity::AddTimer(std::string name, float time) { - EntityTimer* timer = new EntityTimer(name, time); - m_PendingTimers.push_back(timer); + m_PendingTimers.emplace_back(name, time); } void Entity::AddCallbackTimer(float time, std::function callback) { - EntityCallbackTimer* timer = new EntityCallbackTimer(time, callback); - m_CallbackTimers.push_back(timer); + m_PendingCallbackTimers.emplace_back(time, callback); } bool Entity::HasTimer(const std::string& name) { - for (auto* timer : m_Timers) { - if (timer->GetName() == name) { - return true; - } - } - - return false; + return std::find(m_Timers.begin(), m_Timers.end(), name) != m_Timers.end(); } void Entity::CancelCallbackTimers() { - for (auto* callback : m_CallbackTimers) { - delete callback; - } - m_CallbackTimers.clear(); + m_PendingCallbackTimers.clear(); } void Entity::ScheduleKillAfterUpdate(Entity* murderer) { @@ -1728,8 +1749,8 @@ void Entity::ScheduleKillAfterUpdate(Entity* murderer) { void Entity::CancelTimer(const std::string& name) { for (int i = 0; i < m_Timers.size(); i++) { - if (m_Timers[i]->GetName() == name) { - delete m_Timers[i]; + auto& timer = m_Timers[i]; + if (timer == name) { m_Timers.erase(m_Timers.begin() + i); return; } @@ -1737,21 +1758,10 @@ void Entity::CancelTimer(const std::string& name) { } void Entity::CancelAllTimers() { - /*for (auto timer : m_Timers) { - if (timer) delete timer; - }*/ - - for (auto* timer : m_Timers) { - delete timer; - } - m_Timers.clear(); - - for (auto* callBackTimer : m_CallbackTimers) { - delete callBackTimer; - } - + m_PendingTimers.clear(); m_CallbackTimers.clear(); + m_PendingCallbackTimers.clear(); } bool Entity::IsPlayer() const { @@ -1884,7 +1894,7 @@ const NiQuaternion& Entity::GetRotation() const { return NiQuaternion::IDENTITY; } -void Entity::SetPosition(NiPoint3 position) { +void Entity::SetPosition(const NiPoint3& position) { auto* controllable = GetComponent(); if (controllable != nullptr) { @@ -1912,7 +1922,7 @@ void Entity::SetPosition(NiPoint3 position) { Game::entityManager->SerializeEntity(this); } -void Entity::SetRotation(NiQuaternion rotation) { +void Entity::SetRotation(const NiQuaternion& rotation) { auto* controllable = GetComponent(); if (controllable != nullptr) { @@ -2061,3 +2071,75 @@ uint8_t Entity::GetCollectibleID() const { auto* collectible = GetComponent(); return collectible ? collectible->GetCollectibleId() : 0; } + +void Entity::ProcessPositionUpdate(PositionUpdate& update) { + if (!IsPlayer()) return; + auto* controllablePhysicsComponent = GetComponent(); + if (!controllablePhysicsComponent) return; + + auto* possessorComponent = GetComponent(); + bool updateChar = true; + + if (possessorComponent) { + auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); + + if (possassableEntity) { + auto* possessableComponent = possassableEntity->GetComponent(); + + // While possessing something, only update char if we are attached to the thing we are possessing + updateChar = possessableComponent && possessableComponent->GetPossessionType() == ePossessionType::ATTACHED_VISIBLE; + + auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent(); + if (havokVehiclePhysicsComponent) { + havokVehiclePhysicsComponent->SetPosition(update.position); + havokVehiclePhysicsComponent->SetRotation(update.rotation); + havokVehiclePhysicsComponent->SetIsOnGround(update.onGround); + havokVehiclePhysicsComponent->SetIsOnRail(update.onRail); + havokVehiclePhysicsComponent->SetVelocity(update.velocity); + havokVehiclePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3::ZERO); + havokVehiclePhysicsComponent->SetAngularVelocity(update.angularVelocity); + havokVehiclePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::ZERO); + havokVehiclePhysicsComponent->SetRemoteInputInfo(update.remoteInputInfo); + } else { + // Need to get the mount's controllable physics + auto* possessedControllablePhysicsComponent = possassableEntity->GetComponent(); + if (!possessedControllablePhysicsComponent) return; + possessedControllablePhysicsComponent->SetPosition(update.position); + possessedControllablePhysicsComponent->SetRotation(update.rotation); + possessedControllablePhysicsComponent->SetIsOnGround(update.onGround); + possessedControllablePhysicsComponent->SetIsOnRail(update.onRail); + possessedControllablePhysicsComponent->SetVelocity(update.velocity); + possessedControllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3::ZERO); + possessedControllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); + possessedControllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::ZERO); + } + Game::entityManager->SerializeEntity(possassableEntity); + } + } + + if (!updateChar) { + update.velocity = NiPoint3::ZERO; + update.angularVelocity = NiPoint3::ZERO; + } + + // Handle statistics + auto* characterComponent = GetComponent(); + if (characterComponent) { + characterComponent->TrackPositionUpdate(update.position); + } + + controllablePhysicsComponent->SetPosition(update.position); + controllablePhysicsComponent->SetRotation(update.rotation); + controllablePhysicsComponent->SetIsOnGround(update.onGround); + controllablePhysicsComponent->SetIsOnRail(update.onRail); + controllablePhysicsComponent->SetVelocity(update.velocity); + controllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3::ZERO); + controllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); + controllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::ZERO); + + auto* ghostComponent = GetComponent(); + if (ghostComponent) ghostComponent->SetGhostReferencePoint(update.position); + Game::entityManager->QueueGhostUpdate(GetObjectID()); + + if (updateChar) Game::entityManager->SerializeEntity(this); +} diff --git a/dGame/Entity.h b/dGame/Entity.h index 77d77b16..36621d5c 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -31,6 +31,7 @@ class Component; class Item; class Character; class EntityCallbackTimer; +class PositionUpdate; enum class eTriggerEventType; enum class eGameMasterLevel : uint8_t; enum class eReplicaComponentType : uint32_t; @@ -105,7 +106,7 @@ public: virtual User* GetParentUser() const; - virtual SystemAddress GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; }; + virtual const SystemAddress& GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; }; /** * Setters @@ -123,13 +124,13 @@ public: void SetNetworkId(uint16_t id); - void SetPosition(NiPoint3 position); + void SetPosition(const NiPoint3& position); - void SetRotation(NiQuaternion rotation); + void SetRotation(const NiQuaternion& rotation); - virtual void SetRespawnPos(NiPoint3 position) {} + virtual void SetRespawnPos(const NiPoint3& position) {} - virtual void SetRespawnRot(NiQuaternion rotation) {} + virtual void SetRespawnRot(const NiQuaternion& rotation) {} virtual void SetSystemAddress(const SystemAddress& value) {}; @@ -160,6 +161,8 @@ public: void AddChild(Entity* child); void RemoveChild(Entity* child); void RemoveParent(); + + // Adds a timer to start next frame with the given name and time. void AddTimer(std::string name, float time); void AddCallbackTimer(float time, std::function callback); bool HasTimer(const std::string& name); @@ -226,8 +229,8 @@ public: void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr); void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; } - virtual NiPoint3 GetRespawnPosition() const { return NiPoint3::ZERO; } - virtual NiQuaternion GetRespawnRotation() const { return NiQuaternion::IDENTITY; } + virtual const NiPoint3& GetRespawnPosition() const { return NiPoint3::ZERO; } + virtual const NiQuaternion& GetRespawnRotation() const { return NiQuaternion::IDENTITY; } void Sleep(); void Wake(); @@ -294,6 +297,8 @@ public: Entity* GetScheduledKiller() { return m_ScheduleKiller; } + void ProcessPositionUpdate(PositionUpdate& update); + protected: LWOOBJID m_ObjectID; @@ -324,9 +329,10 @@ protected: std::vector> m_PhantomCollisionCallbacks; std::unordered_map m_Components; - std::vector m_Timers; - std::vector m_PendingTimers; - std::vector m_CallbackTimers; + std::vector m_Timers; + std::vector m_PendingTimers; + std::vector m_CallbackTimers; + std::vector m_PendingCallbackTimers; bool m_ShouldDestroyAfterUpdate = false; diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp index a098dbcf..10655d25 100644 --- a/dGame/EntityManager.cpp +++ b/dGame/EntityManager.cpp @@ -11,7 +11,6 @@ #include "SkillComponent.h" #include "SwitchComponent.h" #include "UserManager.h" -#include "PacketUtils.h" #include "Metrics.hpp" #include "dZoneManager.h" #include "MissionComponent.h" @@ -24,6 +23,8 @@ #include "eGameMasterLevel.h" #include "eReplicaComponentType.h" #include "eReplicaPacketType.h" +#include "PlayerManager.h" +#include "GhostComponent.h" // Configure which zones have ghosting disabled, mostly small worlds. std::vector EntityManager::m_GhostingExcludedZones = { @@ -188,8 +189,9 @@ void EntityManager::SerializeEntities() { entity->WriteComponents(&stream, eReplicaPacketType::SERIALIZATION); if (entity->GetIsGhostingCandidate()) { - for (auto* player : Player::GetAllPlayers()) { - if (player->IsObserved(toSerialize)) { + for (auto* player : PlayerManager::GetAllPlayers()) { + auto* ghostComponent = player->GetComponent(); + if (ghostComponent && ghostComponent->IsObserved(toSerialize)) { Game::server->Send(&stream, player->GetSystemAddress(), false); } } @@ -377,11 +379,12 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr if (skipChecks) { Game::server->Send(&stream, UNASSIGNED_SYSTEM_ADDRESS, true); } else { - for (auto* player : Player::GetAllPlayers()) { + for (auto* player : PlayerManager::GetAllPlayers()) { if (player->GetPlayerReadyForUpdates()) { Game::server->Send(&stream, player->GetSystemAddress(), false); } else { - player->AddLimboConstruction(entity->GetObjectID()); + auto* ghostComponent = player->GetComponent(); + if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID()); } } } @@ -389,8 +392,6 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr Game::server->Send(&stream, sysAddr, false); } - // PacketUtils::SavePacket("[24]_"+std::to_string(entity->GetObjectID()) + "_" + std::to_string(m_SerializationCounter) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed()); - if (entity->IsPlayer()) { if (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) { GameMessages::SendToggleGMInvis(entity->GetObjectID(), true, sysAddr); @@ -408,7 +409,7 @@ void EntityManager::ConstructAllEntities(const SystemAddress& sysAddr) { } } - UpdateGhosting(Player::GetPlayer(sysAddr)); + UpdateGhosting(PlayerManager::GetPlayer(sysAddr)); } void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr) { @@ -421,9 +422,10 @@ void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr) Game::server->Send(&stream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); - for (auto* player : Player::GetAllPlayers()) { + for (auto* player : PlayerManager::GetAllPlayers()) { if (!player->GetPlayerReadyForUpdates()) { - player->RemoveLimboConstruction(entity->GetObjectID()); + auto* ghostComponent = player->GetComponent(); + if (ghostComponent) ghostComponent->RemoveLimboConstruction(entity->GetObjectID()); } } } @@ -434,8 +436,6 @@ void EntityManager::SerializeEntity(Entity* entity) { if (std::find(m_EntitiesToSerialize.begin(), m_EntitiesToSerialize.end(), entity->GetObjectID()) == m_EntitiesToSerialize.end()) { m_EntitiesToSerialize.push_back(entity->GetObjectID()); } - - //PacketUtils::SavePacket(std::to_string(m_SerializationCounter) + "_[27]_"+std::to_string(entity->GetObjectID()) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed()); } void EntityManager::DestructAllEntities(const SystemAddress& sysAddr) { @@ -470,7 +470,7 @@ void EntityManager::QueueGhostUpdate(LWOOBJID playerID) { void EntityManager::UpdateGhosting() { for (const auto playerID : m_PlayersToUpdateGhosting) { - auto* player = Player::GetPlayer(playerID); + auto* player = PlayerManager::GetPlayer(playerID); if (player == nullptr) { continue; @@ -488,13 +488,14 @@ void EntityManager::UpdateGhosting(Player* player) { } auto* missionComponent = player->GetComponent(); + auto* ghostComponent = player->GetComponent(); - if (missionComponent == nullptr) { + if (missionComponent == nullptr || !ghostComponent) { return; } - const auto& referencePoint = player->GetGhostReferencePoint(); - const auto isOverride = player->GetGhostOverride(); + const auto& referencePoint = ghostComponent->GetGhostReferencePoint(); + const auto isOverride = ghostComponent->GetGhostOverride(); for (auto* entity : m_EntitiesToGhost) { const auto isAudioEmitter = entity->GetLOT() == 6368; @@ -503,7 +504,7 @@ void EntityManager::UpdateGhosting(Player* player) { const int32_t id = entity->GetObjectID(); - const auto observed = player->IsObserved(id); + const auto observed = ghostComponent->IsObserved(id); const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint); @@ -515,7 +516,7 @@ void EntityManager::UpdateGhosting(Player* player) { } if (observed && distance > ghostingDistanceMax && !isOverride) { - player->GhostEntity(id); + ghostComponent->GhostEntity(id); DestructEntity(entity, player->GetSystemAddress()); @@ -532,7 +533,7 @@ void EntityManager::UpdateGhosting(Player* player) { } } - player->ObserveEntity(id); + ghostComponent->ObserveEntity(id); ConstructEntity(entity, player->GetSystemAddress()); @@ -553,23 +554,26 @@ void EntityManager::CheckGhosting(Entity* entity) { const auto isAudioEmitter = entity->GetLOT() == 6368; - for (auto* player : Player::GetAllPlayers()) { - const auto& entityPoint = player->GetGhostReferencePoint(); + for (auto* player : PlayerManager::GetAllPlayers()) { + auto* ghostComponent = player->GetComponent(); + if (!ghostComponent) continue; + + const auto& entityPoint = ghostComponent->GetGhostReferencePoint(); const int32_t id = entity->GetObjectID(); - const auto observed = player->IsObserved(id); + const auto observed = ghostComponent->IsObserved(id); const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint); if (observed && distance > ghostingDistanceMax) { - player->GhostEntity(id); + ghostComponent->GhostEntity(id); DestructEntity(entity, player->GetSystemAddress()); entity->SetObservers(entity->GetObservers() - 1); } else if (!observed && ghostingDistanceMin > distance) { - player->ObserveEntity(id); + ghostComponent->ObserveEntity(id); ConstructEntity(entity, player->GetSystemAddress()); diff --git a/dGame/LeaderboardManager.h b/dGame/LeaderboardManager.h index e2ce3f97..89537ba0 100644 --- a/dGame/LeaderboardManager.h +++ b/dGame/LeaderboardManager.h @@ -6,7 +6,6 @@ #include #include -#include "Singleton.h" #include "dCommonVars.h" #include "LDFFormat.h" diff --git a/dGame/Player.cpp b/dGame/Player.cpp index abbbf059..8f414b43 100644 --- a/dGame/Player.cpp +++ b/dGame/Player.cpp @@ -3,22 +3,33 @@ #include #include "Character.h" -#include "Database.h" -#include "MissionComponent.h" #include "UserManager.h" #include "EntityManager.h" +#include "Game.h" #include "Logger.h" -#include "ZoneInstanceManager.h" -#include "WorldPackets.h" #include "dZoneManager.h" -#include "CharacterComponent.h" -#include "Mail.h" #include "User.h" #include "CppScripts.h" #include "Loot.h" #include "eReplicaComponentType.h" +#include "PlayerManager.h" -std::vector Player::m_Players = {}; +void Player::SetRespawnPos(const NiPoint3& position) { + if (!m_Character) return; + + m_respawnPos = position; + + m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position); + +} + +void Player::SetRespawnRot(const NiQuaternion& rotation) { + m_respawnRot = rotation; +} + +void Player::SetSystemAddress(const SystemAddress& value) { + m_SystemAddress = value; +} Player::Player(const LWOOBJID& objectID, const EntityInfo info, User* user, Entity* parentEntity) : Entity(objectID, info, parentEntity) { m_ParentUser = user; @@ -26,263 +37,19 @@ Player::Player(const LWOOBJID& objectID, const EntityInfo info, User* user, Enti m_ParentUser->SetLoggedInChar(objectID); m_GMLevel = m_Character->GetGMLevel(); m_SystemAddress = m_ParentUser->GetSystemAddress(); - m_DroppedLoot = {}; m_DroppedCoins = 0; - m_GhostReferencePoint = NiPoint3::ZERO; - m_GhostOverridePoint = NiPoint3::ZERO; - m_GhostOverride = false; - m_ObservedEntitiesLength = 256; - m_ObservedEntitiesUsed = 0; - m_ObservedEntities.resize(m_ObservedEntitiesLength); - m_Character->SetEntity(this); - const auto& iter = std::find(m_Players.begin(), m_Players.end(), this); - - if (iter != m_Players.end()) { - return; - } - - m_Players.push_back(this); -} - -User* Player::GetParentUser() const { - return m_ParentUser; -} - -SystemAddress Player::GetSystemAddress() const { - return m_SystemAddress; -} - -void Player::SetSystemAddress(const SystemAddress& value) { - m_SystemAddress = value; -} - -void Player::SetRespawnPos(const NiPoint3 position) { - if (!m_Character) return; - - m_respawnPos = position; - - m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position); -} - -void Player::SetRespawnRot(const NiQuaternion rotation) { - m_respawnRot = rotation; -} - -NiPoint3 Player::GetRespawnPosition() const { - return m_respawnPos; -} - -NiQuaternion Player::GetRespawnRotation() const { - return m_respawnRot; -} - -void Player::SendMail(const LWOOBJID sender, const std::string& senderName, const std::string& subject, const std::string& body, LOT attachment, uint16_t attachmentCount) const { - Mail::SendMail(sender, senderName, this, subject, body, attachment, attachmentCount); -} - -void Player::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) { - const auto objid = GetObjectID(); - - ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneId, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { - auto* entity = Game::entityManager->GetEntity(objid); - - if (entity == nullptr) { - return; - } - - const auto sysAddr = entity->GetSystemAddress(); - - auto* character = entity->GetCharacter(); - auto* characterComponent = entity->GetComponent(); - - if (character != nullptr && characterComponent != nullptr) { - character->SetZoneID(zoneID); - character->SetZoneInstance(zoneInstance); - character->SetZoneClone(zoneClone); - - characterComponent->SetLastRocketConfig(u""); - - character->SaveXMLToDatabase(); - } - - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); - - Game::entityManager->DestructEntity(entity); - return; - }); -} - -void Player::AddLimboConstruction(LWOOBJID objectId) { - const auto& iter = std::find(m_LimboConstructions.begin(), m_LimboConstructions.end(), objectId); - - if (iter != m_LimboConstructions.end()) { - return; - } - - m_LimboConstructions.push_back(objectId); -} - -void Player::RemoveLimboConstruction(LWOOBJID objectId) { - const auto& iter = std::find(m_LimboConstructions.begin(), m_LimboConstructions.end(), objectId); - - if (iter == m_LimboConstructions.end()) { - return; - } - - m_LimboConstructions.erase(iter); -} - -void Player::ConstructLimboEntities() { - for (const auto objectId : m_LimboConstructions) { - auto* entity = Game::entityManager->GetEntity(objectId); - - if (entity == nullptr) { - continue; - } - - Game::entityManager->ConstructEntity(entity, m_SystemAddress); - } - - m_LimboConstructions.clear(); -} - -std::map& Player::GetDroppedLoot() { - return m_DroppedLoot; -} - -const NiPoint3& Player::GetGhostReferencePoint() const { - return m_GhostOverride ? m_GhostOverridePoint : m_GhostReferencePoint; -} - -const NiPoint3& Player::GetOriginGhostReferencePoint() const { - return m_GhostReferencePoint; -} - -void Player::SetGhostReferencePoint(const NiPoint3& value) { - m_GhostReferencePoint = value; -} - -void Player::SetGhostOverridePoint(const NiPoint3& value) { - m_GhostOverridePoint = value; -} - -const NiPoint3& Player::GetGhostOverridePoint() const { - return m_GhostOverridePoint; -} - -void Player::SetGhostOverride(bool value) { - m_GhostOverride = value; -} - -bool Player::GetGhostOverride() const { - return m_GhostOverride; -} - -void Player::ObserveEntity(int32_t id) { - for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) { - if (m_ObservedEntities[i] == 0 || m_ObservedEntities[i] == id) { - m_ObservedEntities[i] = id; - - return; - } - } - - const auto index = m_ObservedEntitiesUsed++; - - if (m_ObservedEntitiesUsed > m_ObservedEntitiesLength) { - m_ObservedEntities.resize(m_ObservedEntitiesLength + m_ObservedEntitiesLength); - - m_ObservedEntitiesLength = m_ObservedEntitiesLength + m_ObservedEntitiesLength; - } - - m_ObservedEntities[index] = id; -} - -bool Player::IsObserved(int32_t id) { - for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) { - if (m_ObservedEntities[i] == id) { - return true; - } - } - - return false; -} - -void Player::GhostEntity(int32_t id) { - for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) { - if (m_ObservedEntities[i] == id) { - m_ObservedEntities[i] = 0; - } - } -} - -Player* Player::GetPlayer(const SystemAddress& sysAddr) { - auto* entity = UserManager::Instance()->GetUser(sysAddr)->GetLastUsedChar()->GetEntity(); - - return static_cast(entity); -} - -Player* Player::GetPlayer(const std::string& name) { - const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER); - - for (auto* character : characters) { - if (!character->IsPlayer()) continue; - - if (GeneralUtils::CaseInsensitiveStringCompare(name, character->GetCharacter()->GetName())) { - return dynamic_cast(character); - } - } - - return nullptr; -} - -Player* Player::GetPlayer(LWOOBJID playerID) { - for (auto* player : m_Players) { - if (player->GetObjectID() == playerID) { - return player; - } - } - - return nullptr; -} - -const std::vector& Player::GetAllPlayers() { - return m_Players; -} - -uint64_t Player::GetDroppedCoins() { - return m_DroppedCoins; -} - -void Player::SetDroppedCoins(uint64_t value) { - m_DroppedCoins = value; + PlayerManager::AddPlayer(this); } Player::~Player() { LOG("Deleted player"); - - for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) { - const auto id = m_ObservedEntities[i]; - - if (id == 0) { - continue; - } - - auto* entity = Game::entityManager->GetGhostCandidate(id); - - if (entity != nullptr) { - entity->SetObservers(entity->GetObservers() - 1); - } - } - - m_LimboConstructions.clear(); - - const auto& iter = std::find(m_Players.begin(), m_Players.end(), this); - - if (iter == m_Players.end()) { + + // Make sure the player exists first. Remove afterwards to prevent the OnPlayerExist functions from not being able to find the player. + if (!PlayerManager::RemovePlayer(this)) { + LOG("Unable to find player to remove from manager."); return; } @@ -301,6 +68,4 @@ Player::~Player() { } } } - - m_Players.erase(iter); } diff --git a/dGame/Player.h b/dGame/Player.h index 287ee613..dd8efd9c 100644 --- a/dGame/Player.h +++ b/dGame/Player.h @@ -18,92 +18,34 @@ public: * Getters */ - User* GetParentUser() const override; + User* GetParentUser() const override { return m_ParentUser; }; - SystemAddress GetSystemAddress() const override; + const SystemAddress& GetSystemAddress() const override { return m_SystemAddress; }; - NiPoint3 GetRespawnPosition() const override; + const NiPoint3& GetRespawnPosition() const override { return m_respawnPos; }; - NiQuaternion GetRespawnRotation() const override; + const NiQuaternion& GetRespawnRotation() const override { return m_respawnRot; }; - const NiPoint3& GetGhostReferencePoint() const; + std::map& GetDroppedLoot() { return m_DroppedLoot; }; - const NiPoint3& GetOriginGhostReferencePoint() const; - - const NiPoint3& GetGhostOverridePoint() const; - - bool GetGhostOverride() const; - - std::map& GetDroppedLoot(); - - uint64_t GetDroppedCoins(); + uint64_t GetDroppedCoins() const { return m_DroppedCoins; }; /** * Setters */ + void SetDroppedCoins(const uint64_t value) { m_DroppedCoins = value; }; + void SetSystemAddress(const SystemAddress& value) override; - void SetRespawnPos(NiPoint3 position) override; + void SetRespawnPos(const NiPoint3& position) override; - void SetRespawnRot(NiQuaternion rotation) override; - - void SetGhostReferencePoint(const NiPoint3& value); - - void SetGhostOverridePoint(const NiPoint3& value); - - void SetGhostOverride(bool value); - - void SetDroppedCoins(uint64_t value); - - /** - * Wrapper for sending an in-game mail. - * - * @param sender id of the sender. LWOOBJID_EMPTY for system mail - * @param senderName name of the sender. Max 32 characters. - * @param subject mail subject. Max 50 characters. - * @param body mail body. Max 400 characters. - * @param attachment LOT of the attached item. LOT_NULL if no attachment. - * @param attachmentCount stack size for attachment. - */ - void SendMail(LWOOBJID sender, const std::string& senderName, const std::string& subject, const std::string& body, LOT attachment, uint16_t attachmentCount) const; - - /** - * Wrapper for transfering the player to another instance. - * - * @param zoneId zoneID for the new instance. - * @param cloneId cloneID for the new instance. - */ - void SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId = 0); + void SetRespawnRot(const NiQuaternion& rotation) override; /** * Ghosting */ - void AddLimboConstruction(LWOOBJID objectId); - - void RemoveLimboConstruction(LWOOBJID objectId); - - void ConstructLimboEntities(); - - void ObserveEntity(int32_t id); - - bool IsObserved(int32_t id); - - void GhostEntity(int32_t id); - - /** - * Static methods - */ - - static Player* GetPlayer(const SystemAddress& sysAddr); - - static Player* GetPlayer(const std::string& name); - - static Player* GetPlayer(LWOOBJID playerID); - - static const std::vector& GetAllPlayers(); - ~Player() override; private: SystemAddress m_SystemAddress; @@ -114,23 +56,7 @@ private: User* m_ParentUser; - NiPoint3 m_GhostReferencePoint; - - NiPoint3 m_GhostOverridePoint; - - bool m_GhostOverride; - - std::vector m_ObservedEntities; - - int32_t m_ObservedEntitiesLength; - - int32_t m_ObservedEntitiesUsed; - - std::vector m_LimboConstructions; - std::map m_DroppedLoot; uint64_t m_DroppedCoins; - - static std::vector m_Players; }; diff --git a/dGame/PlayerManager.cpp b/dGame/PlayerManager.cpp new file mode 100644 index 00000000..e3017f05 --- /dev/null +++ b/dGame/PlayerManager.cpp @@ -0,0 +1,68 @@ +#include "PlayerManager.h" + +#include "Character.h" +#include "Player.h" +#include "User.h" +#include "UserManager.h" +#include "eReplicaComponentType.h" + +namespace { + std::vector m_Players; +}; + +const std::vector& PlayerManager::GetAllPlayers() { + return m_Players; +} + +void PlayerManager::AddPlayer(Player* player) { + const auto& iter = std::find(m_Players.begin(), m_Players.end(), player); + + if (iter == m_Players.end()) { + m_Players.push_back(player); + } +} + +bool PlayerManager::RemovePlayer(Player* player) { + const auto iter = std::find(m_Players.begin(), m_Players.end(), player); + + const bool toReturn = iter != m_Players.end(); + if (toReturn) { + m_Players.erase(iter); + } + + return toReturn; +} + +Player* PlayerManager::GetPlayer(const SystemAddress& sysAddr) { + auto* entity = UserManager::Instance()->GetUser(sysAddr)->GetLastUsedChar()->GetEntity(); + + return static_cast(entity); +} + +Player* PlayerManager::GetPlayer(const std::string& name) { + const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER); + + Player* player = nullptr; + for (auto* character : characters) { + if (!character->IsPlayer()) continue; + + if (GeneralUtils::CaseInsensitiveStringCompare(name, character->GetCharacter()->GetName())) { + player = dynamic_cast(character); + break; + } + } + + return player; +} + +Player* PlayerManager::GetPlayer(LWOOBJID playerID) { + Player* playerToReturn = nullptr; + for (auto* player : m_Players) { + if (player->GetObjectID() == playerID) { + playerToReturn = player; + break; + } + } + + return playerToReturn; +} diff --git a/dGame/PlayerManager.h b/dGame/PlayerManager.h new file mode 100644 index 00000000..bb54f83b --- /dev/null +++ b/dGame/PlayerManager.h @@ -0,0 +1,25 @@ +#ifndef __PLAYERMANAGER__H__ +#define __PLAYERMANAGER__H__ + +#include "dCommonVars.h" + +#include + +class Player; +struct SystemAddress; + +namespace PlayerManager { + void AddPlayer(Player* player); + + bool RemovePlayer(Player* player); + + Player* GetPlayer(const SystemAddress& sysAddr); + + Player* GetPlayer(const std::string& name); + + Player* GetPlayer(LWOOBJID playerID); + + const std::vector& GetAllPlayers(); +}; + +#endif //!__PLAYERMANAGER__H__ diff --git a/dGame/UserManager.cpp b/dGame/UserManager.cpp index 736339a4..fc582108 100644 --- a/dGame/UserManager.cpp +++ b/dGame/UserManager.cpp @@ -11,7 +11,6 @@ #include "WorldPackets.h" #include "Character.h" #include "BitStream.h" -#include "PacketUtils.h" #include "ObjectIDManager.h" #include "Logger.h" #include "GeneralUtils.h" @@ -216,31 +215,92 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) { chars.push_back(character); } - WorldPackets::SendCharacterList(sysAddr, u); + RakNet::BitStream bitStream; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_LIST_RESPONSE); + + std::vector characters = u->GetCharacters(); + bitStream.Write(characters.size()); + bitStream.Write(0); //TODO: Pick the most recent played index. character index in front, just picking 0 + + for (uint32_t i = 0; i < characters.size(); ++i) { + bitStream.Write(characters[i]->GetObjectID()); + bitStream.Write(0); + + bitStream.Write(LUWString(characters[i]->GetName())); + bitStream.Write(LUWString(characters[i]->GetUnapprovedName())); + + bitStream.Write(characters[i]->GetNameRejected()); + bitStream.Write(false); + + bitStream.Write(LUString("", 10)); + + bitStream.Write(characters[i]->GetShirtColor()); + bitStream.Write(characters[i]->GetShirtStyle()); + bitStream.Write(characters[i]->GetPantsColor()); + bitStream.Write(characters[i]->GetHairStyle()); + bitStream.Write(characters[i]->GetHairColor()); + bitStream.Write(characters[i]->GetLeftHand()); + bitStream.Write(characters[i]->GetRightHand()); + bitStream.Write(characters[i]->GetEyebrows()); + bitStream.Write(characters[i]->GetEyes()); + bitStream.Write(characters[i]->GetMouth()); + bitStream.Write(0); + + bitStream.Write(characters[i]->GetZoneID()); + bitStream.Write(characters[i]->GetZoneInstance()); + bitStream.Write(characters[i]->GetZoneClone()); + + bitStream.Write(characters[i]->GetLastLogin()); + + const auto& equippedItems = characters[i]->GetEquippedItems(); + bitStream.Write(equippedItems.size()); + + for (uint32_t j = 0; j < equippedItems.size(); ++j) { + bitStream.Write(equippedItems[j]); + } + } + + SEND_PACKET; } void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) { User* u = GetUser(sysAddr); if (!u) return; + + LUWString LUWStringName; + uint32_t firstNameIndex; + uint32_t middleNameIndex; + uint32_t lastNameIndex; + uint32_t shirtColor; + uint32_t shirtStyle; + uint32_t pantsColor; + uint32_t hairStyle; + uint32_t hairColor; + uint32_t lh; + uint32_t rh; + uint32_t eyebrows; + uint32_t eyes; + uint32_t mouth; - std::string name = PacketUtils::ReadString(8, packet, true); + CINSTREAM_SKIP_HEADER; + inStream.Read(LUWStringName); + inStream.Read(firstNameIndex); + inStream.Read(middleNameIndex); + inStream.Read(lastNameIndex); + inStream.IgnoreBytes(9); + inStream.Read(shirtColor); + inStream.Read(shirtStyle); + inStream.Read(pantsColor); + inStream.Read(hairStyle); + inStream.Read(hairColor); + inStream.Read(lh); + inStream.Read(rh); + inStream.Read(eyebrows); + inStream.Read(eyes); + inStream.Read(mouth); - uint32_t firstNameIndex = PacketUtils::ReadU32(74, packet); - uint32_t middleNameIndex = PacketUtils::ReadU32(78, packet); - uint32_t lastNameIndex = PacketUtils::ReadU32(82, packet); + const auto name = LUWStringName.GetAsString(); std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex); - - uint32_t shirtColor = PacketUtils::ReadU32(95, packet); - uint32_t shirtStyle = PacketUtils::ReadU32(99, packet); - uint32_t pantsColor = PacketUtils::ReadU32(103, packet); - uint32_t hairStyle = PacketUtils::ReadU32(107, packet); - uint32_t hairColor = PacketUtils::ReadU32(111, packet); - uint32_t lh = PacketUtils::ReadU32(115, packet); - uint32_t rh = PacketUtils::ReadU32(119, packet); - uint32_t eyebrows = PacketUtils::ReadU32(123, packet); - uint32_t eyes = PacketUtils::ReadU32(127, packet); - uint32_t mouth = PacketUtils::ReadU32(131, packet); - LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle); LOT pantsLOT = FindCharPantsID(pantsColor); @@ -322,7 +382,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS); UserManager::RequestCharacterList(sysAddr); - }); + }); } void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) { @@ -332,7 +392,9 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) return; } - LWOOBJID objectID = PacketUtils::ReadS64(8, packet); + CINSTREAM_SKIP_HEADER; + LWOOBJID objectID; + inStream.Read(objectID); uint32_t charID = static_cast(objectID); LOG("Received char delete req for ID: %llu (%u)", objectID, charID); @@ -366,14 +428,18 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) return; } - LWOOBJID objectID = PacketUtils::ReadS64(8, packet); + CINSTREAM_SKIP_HEADER; + LWOOBJID objectID; + inStream.Read(objectID); GeneralUtils::ClearBit(objectID, eObjectBits::CHARACTER); GeneralUtils::ClearBit(objectID, eObjectBits::PERSISTENT); uint32_t charID = static_cast(objectID); LOG("Received char rename request for ID: %llu (%u)", objectID, charID); - std::string newName = PacketUtils::ReadString(16, packet, true); + LUWString LUWStringName; + inStream.Read(LUWStringName); + const auto newName = LUWStringName.GetAsString(); Character* character = nullptr; diff --git a/dGame/dComponents/ActivityComponent.cpp b/dGame/dComponents/ActivityComponent.cpp index 7ea7500b..aa6a4604 100644 --- a/dGame/dComponents/ActivityComponent.cpp +++ b/dGame/dComponents/ActivityComponent.cpp @@ -30,40 +30,36 @@ #include "LeaderboardManager.h" ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Component(parent) { - if (activityID > 0) m_ActivityID = activityID; - else m_ActivityID = parent->GetVar(u"activityID"); - CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); - std::vector activities = activitiesTable->Query([this](CDActivities entry) {return (entry.ActivityID == m_ActivityID); }); + /* + * This is precisely what the client does functionally + * Use the component id as the default activity id and load its data from the database + * if activityID is specified and if that column exists in the activities table, update the activity info with that data. + */ - for (CDActivities activity : activities) { - m_ActivityInfo = activity; - if (static_cast(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") { - m_ActivityInfo.minTeamSize = 1; - m_ActivityInfo.minTeams = 1; - } - if (m_ActivityInfo.instanceMapID == -1) { - const auto& transferOverride = parent->GetVarAsString(u"transferZoneID"); - if (!transferOverride.empty()) { - GeneralUtils::TryParse(transferOverride, m_ActivityInfo.instanceMapID); - } - } + m_ActivityID = activityID; + LoadActivityData(activityID); + if (m_Parent->HasVar(u"activityID")) { + m_ActivityID = parent->GetVar(u"activityID"); + LoadActivityData(m_ActivityID); } auto* destroyableComponent = m_Parent->GetComponent(); if (destroyableComponent) { - // check for LMIs and set the loot LMIs + // First lookup the loot matrix id for this component id. CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable(); std::vector activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); }); uint32_t startingLMI = 0; + // If we have one, set the starting loot matrix id to that. if (activityRewards.size() > 0) { startingLMI = activityRewards[0].LootMatrixIndex; } if (startingLMI > 0) { - // now time for bodge :) + // We may have more than 1 loot matrix index to use depending ont the size of the team that is looting the activity. + // So this logic will get the rest of the loot matrix indices for this activity. std::vector objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); }); for (const auto& item : objectTemplateActivities) { @@ -74,6 +70,25 @@ ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Compo } } } +void ActivityComponent::LoadActivityData(const int32_t activityId) { + CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); + std::vector activities = activitiesTable->Query([activityId](CDActivities entry) {return (entry.ActivityID == activityId); }); + + bool soloRacing = Game::config->GetValue("solo_racing") == "1"; + for (CDActivities activity : activities) { + m_ActivityInfo = activity; + if (static_cast(activity.leaderboardType) == Leaderboard::Type::Racing && soloRacing) { + m_ActivityInfo.minTeamSize = 1; + m_ActivityInfo.minTeams = 1; + } + if (m_ActivityInfo.instanceMapID == -1) { + const auto& transferOverride = m_Parent->GetVarAsString(u"transferZoneID"); + if (!transferOverride.empty()) { + GeneralUtils::TryParse(transferOverride, m_ActivityInfo.instanceMapID); + } + } + } +} void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_DirtyActivityInfo); diff --git a/dGame/dComponents/ActivityComponent.h b/dGame/dComponents/ActivityComponent.h index 96dbd5fb..de63b343 100644 --- a/dGame/dComponents/ActivityComponent.h +++ b/dGame/dComponents/ActivityComponent.h @@ -152,6 +152,8 @@ class ActivityComponent : public Component { public: ActivityComponent(Entity* parent, int32_t activityID); + void LoadActivityData(const int32_t activityId); + void Update(float deltaTime) override; void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index 4e969ced..11dff20f 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -25,6 +25,7 @@ #include "Metrics.hpp" #include "CDComponentsRegistryTable.h" #include "CDPhysicsComponentTable.h" +#include "dNavMesh.h" BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) { m_Target = LWOOBJID_EMPTY; @@ -34,7 +35,6 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): m_MovementAI = nullptr; m_Disabled = false; m_SkillEntries = {}; - m_MovementAI = nullptr; m_SoftTimer = 5.0f; //Grab the aggro information from BaseCombatAI: @@ -129,17 +129,17 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): m_dpEntity->SetPosition(m_Parent->GetPosition()); m_dpEntityEnemy->SetPosition(m_Parent->GetPosition()); - dpWorld::Instance().AddEntity(m_dpEntity); - dpWorld::Instance().AddEntity(m_dpEntityEnemy); + dpWorld::AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntityEnemy); } BaseCombatAIComponent::~BaseCombatAIComponent() { if (m_dpEntity) - dpWorld::Instance().RemoveEntity(m_dpEntity); + dpWorld::RemoveEntity(m_dpEntity); if (m_dpEntityEnemy) - dpWorld::Instance().RemoveEntity(m_dpEntityEnemy); + dpWorld::RemoveEntity(m_dpEntityEnemy); } void BaseCombatAIComponent::Update(const float deltaTime) { @@ -654,8 +654,8 @@ void BaseCombatAIComponent::Wander() { auto destination = m_StartPosition + delta; - if (dpWorld::Instance().IsLoaded()) { - destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination); + if (dpWorld::IsLoaded()) { + destination.y = dpWorld::GetNavMesh()->GetHeightAtPoint(destination); } if (Vector3::DistanceSquared(destination, m_MovementAI->GetParent()->GetPosition()) < 2 * 2) { diff --git a/dGame/dComponents/BaseCombatAIComponent.h b/dGame/dComponents/BaseCombatAIComponent.h index 8ae04611..f00910e7 100644 --- a/dGame/dComponents/BaseCombatAIComponent.h +++ b/dGame/dComponents/BaseCombatAIComponent.h @@ -45,9 +45,9 @@ struct AiSkillEntry /** * Handles the AI of entities, making them wander, tether and attack their enemies */ -class BaseCombatAIComponent : public Component { +class BaseCombatAIComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI; BaseCombatAIComponent(Entity* parentEntity, uint32_t id); ~BaseCombatAIComponent() override; diff --git a/dGame/dComponents/BouncerComponent.h b/dGame/dComponents/BouncerComponent.h index cb3d8df3..b41881c6 100644 --- a/dGame/dComponents/BouncerComponent.h +++ b/dGame/dComponents/BouncerComponent.h @@ -10,9 +10,9 @@ /** * Attached to bouncer entities, allowing other entities to bounce off of it */ -class BouncerComponent : public Component { +class BouncerComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER; BouncerComponent(Entity* parentEntity); ~BouncerComponent() override; diff --git a/dGame/dComponents/BuffComponent.h b/dGame/dComponents/BuffComponent.h index 7f7b44d8..18aa7a42 100644 --- a/dGame/dComponents/BuffComponent.h +++ b/dGame/dComponents/BuffComponent.h @@ -47,9 +47,9 @@ struct Buff { /** * Allows for the application of buffs to the parent entity, altering health, armor and imagination. */ -class BuffComponent : public Component { +class BuffComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUFF; explicit BuffComponent(Entity* parent); diff --git a/dGame/dComponents/BuildBorderComponent.h b/dGame/dComponents/BuildBorderComponent.h index 985c0388..a59ac363 100644 --- a/dGame/dComponents/BuildBorderComponent.h +++ b/dGame/dComponents/BuildBorderComponent.h @@ -14,9 +14,9 @@ /** * Component for the build border, allowing the user to start building when interacting with it */ -class BuildBorderComponent : public Component { +class BuildBorderComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER; BuildBorderComponent(Entity* parent); ~BuildBorderComponent() override; diff --git a/dGame/dComponents/CMakeLists.txt b/dGame/dComponents/CMakeLists.txt index c9f0177c..e0e76208 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -10,6 +10,7 @@ set(DGAME_DCOMPONENTS_SOURCES "ControllablePhysicsComponent.cpp" "DestroyableComponent.cpp" "DonationVendorComponent.cpp" + "GhostComponent.cpp" "InventoryComponent.cpp" "ItemComponent.cpp" "LevelProgressionComponent.cpp" diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index 702ab70f..70eec0cf 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -20,6 +20,8 @@ #include "Database.h" #include "CDRewardCodesTable.h" #include "Mail.h" +#include "ZoneInstanceManager.h" +#include "WorldPackets.h" #include CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) { @@ -763,12 +765,12 @@ void CharacterComponent::AwardClaimCodes() { if (!m_Parent) return; auto* user = m_Parent->GetParentUser(); if (!user) return; - + auto rewardCodes = Database::Get()->GetRewardCodesByAccountID(user->GetAccountID()); if (rewardCodes.empty()) return; auto* cdrewardCodes = CDClientManager::Instance().GetTable(); - for (auto const rewardCode: rewardCodes){ + for (auto const rewardCode : rewardCodes) { LOG_DEBUG("Processing RewardCode %i", rewardCode); const uint32_t rewardCodeIndex = rewardCode >> 6; const uint32_t bitIndex = rewardCode % 64; @@ -786,3 +788,32 @@ void CharacterComponent::AwardClaimCodes() { Mail::SendMail(LWOOBJID_EMPTY, "%[MAIL_SYSTEM_NOTIFICATION]", m_Parent, subject.str(), body.str(), attachmentLOT, 1); } } + +void CharacterComponent::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) const { + const auto objid = m_Parent->GetObjectID(); + + ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneId, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { + auto* entity = Game::entityManager->GetEntity(objid); + + if (!entity) return; + + const auto sysAddr = entity->GetSystemAddress(); + + auto* character = entity->GetCharacter(); + auto* characterComponent = entity->GetComponent(); + + if (character && characterComponent) { + character->SetZoneID(zoneID); + character->SetZoneInstance(zoneInstance); + character->SetZoneClone(zoneClone); + + characterComponent->SetLastRocketConfig(u""); + + character->SaveXMLToDatabase(); + } + + WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + + Game::entityManager->DestructEntity(entity); + }); +} diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index 158e011e..a44d359d 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -61,9 +61,9 @@ enum StatisticID { /** * Represents a character, including their rockets and stats */ -class CharacterComponent : public Component { +class CharacterComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER; CharacterComponent(Entity* parent, Character* character); ~CharacterComponent() override; @@ -281,6 +281,14 @@ public: LWOOBJID GetCurrentInteracting() {return m_CurrentInteracting;}; + /** + * Sends a player to another zone with an optional clone ID + * + * @param zoneId zoneID for the new instance. + * @param cloneId cloneID for the new instance. + */ + void SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId = 0) const; + /** * Character info regarding this character, including clothing styles, etc. */ diff --git a/dGame/dComponents/CollectibleComponent.h b/dGame/dComponents/CollectibleComponent.h index 3ff71c6f..10a23293 100644 --- a/dGame/dComponents/CollectibleComponent.h +++ b/dGame/dComponents/CollectibleComponent.h @@ -4,9 +4,9 @@ #include "Component.h" #include "eReplicaComponentType.h" -class CollectibleComponent : public Component { +class CollectibleComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE; CollectibleComponent(Entity* parentEntity, int32_t collectibleId) : Component(parentEntity), m_CollectibleId(collectibleId) {} int16_t GetCollectibleId() const { return m_CollectibleId; } diff --git a/dGame/dComponents/Component.h b/dGame/dComponents/Component.h index d1ad0860..70f30f76 100644 --- a/dGame/dComponents/Component.h +++ b/dGame/dComponents/Component.h @@ -7,8 +7,7 @@ class Entity; /** * Component base class, provides methods for game loop updates, usage events and loading and saving to XML. */ -class Component -{ +class Component { public: Component(Entity* parent); virtual ~Component(); diff --git a/dGame/dComponents/ControllablePhysicsComponent.cpp b/dGame/dComponents/ControllablePhysicsComponent.cpp index dd981f66..be5227a0 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.cpp +++ b/dGame/dComponents/ControllablePhysicsComponent.cpp @@ -57,13 +57,13 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Phy float radius = 1.5f; m_dpEntity = new dpEntity(m_Parent->GetObjectID(), radius, false); m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } } ControllablePhysicsComponent::~ControllablePhysicsComponent() { if (m_dpEntity) { - dpWorld::Instance().RemoveEntity(m_dpEntity); + dpWorld::RemoveEntity(m_dpEntity); } } diff --git a/dGame/dComponents/ControllablePhysicsComponent.h b/dGame/dComponents/ControllablePhysicsComponent.h index e5c3f890..e850cfeb 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.h +++ b/dGame/dComponents/ControllablePhysicsComponent.h @@ -21,7 +21,7 @@ enum class eStateChangeType : uint32_t; */ class ControllablePhysicsComponent : public PhysicsComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS; ControllablePhysicsComponent(Entity* entity); ~ControllablePhysicsComponent() override; diff --git a/dGame/dComponents/DestroyableComponent.h b/dGame/dComponents/DestroyableComponent.h index b81ab9f3..1f45b43e 100644 --- a/dGame/dComponents/DestroyableComponent.h +++ b/dGame/dComponents/DestroyableComponent.h @@ -17,9 +17,9 @@ enum class eStateChangeType : uint32_t; * Represents the stats of an entity, for example its health, imagination and armor. Also handles factions, which * indicate which enemies this entity has. */ -class DestroyableComponent : public Component { +class DestroyableComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE; DestroyableComponent(Entity* parentEntity); ~DestroyableComponent() override; diff --git a/dGame/dComponents/DonationVendorComponent.h b/dGame/dComponents/DonationVendorComponent.h index d1743118..7eb60849 100644 --- a/dGame/dComponents/DonationVendorComponent.h +++ b/dGame/dComponents/DonationVendorComponent.h @@ -8,7 +8,7 @@ class Entity; class DonationVendorComponent final : public VendorComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR; DonationVendorComponent(Entity* parent); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; uint32_t GetActivityID() {return m_ActivityId;}; diff --git a/dGame/dComponents/GhostComponent.cpp b/dGame/dComponents/GhostComponent.cpp new file mode 100644 index 00000000..2978c912 --- /dev/null +++ b/dGame/dComponents/GhostComponent.cpp @@ -0,0 +1,57 @@ +#include "GhostComponent.h" + +GhostComponent::GhostComponent(Entity* parent) : Component(parent) { + m_GhostReferencePoint = NiPoint3::ZERO; + m_GhostOverridePoint = NiPoint3::ZERO; + m_GhostOverride = false; +} + +GhostComponent::~GhostComponent() { + for (auto& observedEntity : m_ObservedEntities) { + if (observedEntity == 0) continue; + + auto* entity = Game::entityManager->GetGhostCandidate(observedEntity); + if (!entity) continue; + + entity->SetObservers(entity->GetObservers() - 1); + } +} + +void GhostComponent::SetGhostReferencePoint(const NiPoint3& value) { + m_GhostReferencePoint = value; +} + +void GhostComponent::SetGhostOverridePoint(const NiPoint3& value) { + m_GhostOverridePoint = value; +} + +void GhostComponent::AddLimboConstruction(LWOOBJID objectId) { + m_LimboConstructions.insert(objectId); +} + +void GhostComponent::RemoveLimboConstruction(LWOOBJID objectId) { + m_LimboConstructions.erase(objectId); +} + +void GhostComponent::ConstructLimboEntities() { + for (const auto& objectId : m_LimboConstructions) { + auto* entity = Game::entityManager->GetEntity(objectId); + if (!entity) continue; + + Game::entityManager->ConstructEntity(entity, m_Parent->GetSystemAddress()); + } + + m_LimboConstructions.clear(); +} + +void GhostComponent::ObserveEntity(int32_t id) { + m_ObservedEntities.insert(id); +} + +bool GhostComponent::IsObserved(int32_t id) { + return m_ObservedEntities.contains(id); +} + +void GhostComponent::GhostEntity(int32_t id) { + m_ObservedEntities.erase(id); +} diff --git a/dGame/dComponents/GhostComponent.h b/dGame/dComponents/GhostComponent.h new file mode 100644 index 00000000..bc4f158d --- /dev/null +++ b/dGame/dComponents/GhostComponent.h @@ -0,0 +1,54 @@ +#ifndef __GHOSTCOMPONENT__H__ +#define __GHOSTCOMPONENT__H__ + +#include "Component.h" +#include "eReplicaComponentType.h" +#include + +class NiPoint3; + +class GhostComponent final : public Component { +public: + static inline const eReplicaComponentType ComponentType = eReplicaComponentType::GHOST; + GhostComponent(Entity* parent); + ~GhostComponent() override; + + void SetGhostOverride(bool value) { m_GhostOverride = value; }; + + const NiPoint3& GetGhostReferencePoint() const { return m_GhostOverride ? m_GhostOverridePoint : m_GhostReferencePoint; }; + + const NiPoint3& GetOriginGhostReferencePoint() const { return m_GhostReferencePoint; }; + + const NiPoint3& GetGhostOverridePoint() const { return m_GhostOverridePoint; }; + + bool GetGhostOverride() const { return m_GhostOverride; }; + + void SetGhostReferencePoint(const NiPoint3& value); + + void SetGhostOverridePoint(const NiPoint3& value); + + void AddLimboConstruction(const LWOOBJID objectId); + + void RemoveLimboConstruction(const LWOOBJID objectId); + + void ConstructLimboEntities(); + + void ObserveEntity(const int32_t id); + + bool IsObserved(const int32_t id); + + void GhostEntity(const int32_t id); + +private: + NiPoint3 m_GhostReferencePoint; + + NiPoint3 m_GhostOverridePoint; + + std::unordered_set m_ObservedEntities; + + std::unordered_set m_LimboConstructions; + + bool m_GhostOverride; +}; + +#endif //!__GHOSTCOMPONENT__H__ diff --git a/dGame/dComponents/HavokVehiclePhysicsComponent.h b/dGame/dComponents/HavokVehiclePhysicsComponent.h index 2d04e0ac..85a0e279 100644 --- a/dGame/dComponents/HavokVehiclePhysicsComponent.h +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.h @@ -4,38 +4,14 @@ #include "Entity.h" #include "PhysicsComponent.h" #include "eReplicaComponentType.h" - -struct RemoteInputInfo { - RemoteInputInfo() { - m_RemoteInputX = 0; - m_RemoteInputY = 0; - m_IsPowersliding = false; - m_IsModified = false; - } - - void operator=(const RemoteInputInfo& other) { - m_RemoteInputX = other.m_RemoteInputX; - m_RemoteInputY = other.m_RemoteInputY; - m_IsPowersliding = other.m_IsPowersliding; - m_IsModified = other.m_IsModified; - } - - bool operator==(const RemoteInputInfo& other) { - return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified; - } - - float m_RemoteInputX; - float m_RemoteInputY; - bool m_IsPowersliding; - bool m_IsModified; -}; +#include "PositionUpdate.h" /** * Physics component for vehicles. */ class HavokVehiclePhysicsComponent : public PhysicsComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS; HavokVehiclePhysicsComponent(Entity* parentEntity); diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 92b5171a..23d5c163 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -31,6 +31,7 @@ #include "eMissionTaskType.h" #include "eStateChangeType.h" #include "eUseItemResponse.h" +#include "Mail.h" #include "CDComponentsRegistryTable.h" #include "CDInventoryComponentTable.h" @@ -264,17 +265,11 @@ void InventoryComponent::AddItem( } if (slot == -1) { - auto* player = dynamic_cast(GetParent()); - - if (player == nullptr) { - return; - } - outOfSpace += size; switch (sourceType) { case 0: - player->SendMail(LWOOBJID_EMPTY, "Darkflame Universe", "Lost Reward", "You received an item and didn't have room for it.", lot, size); + Mail::SendMail(LWOOBJID_EMPTY, "Darkflame Universe", m_Parent, "Lost Reward", "You received an item and didn't have room for it.", lot, size); break; case 1: diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index f4d38d43..e47e6a59 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -35,10 +35,9 @@ enum class eItemType : int32_t; * of different types, each type representing a different group of items, see `eInventoryType` for a list of * inventories. */ -class InventoryComponent : public Component -{ +class InventoryComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY; explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr); void Update(float deltaTime) override; diff --git a/dGame/dComponents/ItemComponent.h b/dGame/dComponents/ItemComponent.h index 3af6a91e..875ef0a5 100644 --- a/dGame/dComponents/ItemComponent.h +++ b/dGame/dComponents/ItemComponent.h @@ -4,9 +4,9 @@ #include "Component.h" #include "eReplicaComponentType.h" -class ItemComponent : public Component { +class ItemComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::ITEM; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ITEM; ItemComponent(Entity* entity) : Component(entity) {} diff --git a/dGame/dComponents/LUPExhibitComponent.h b/dGame/dComponents/LUPExhibitComponent.h index 47b13a17..e6653868 100644 --- a/dGame/dComponents/LUPExhibitComponent.h +++ b/dGame/dComponents/LUPExhibitComponent.h @@ -11,10 +11,10 @@ * Component that handles the LOT that is shown in the LUP exhibit in the LUP world. Works by setting a timer and * switching the LOTs around that we'd like to display. */ -class LUPExhibitComponent : public Component +class LUPExhibitComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::LUP_EXHIBIT; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::LUP_EXHIBIT; LUPExhibitComponent(Entity* parent) : Component(parent) {}; void Update(float deltaTime) override; diff --git a/dGame/dComponents/LevelProgressionComponent.h b/dGame/dComponents/LevelProgressionComponent.h index 09ccec34..6083738c 100644 --- a/dGame/dComponents/LevelProgressionComponent.h +++ b/dGame/dComponents/LevelProgressionComponent.h @@ -11,9 +11,9 @@ * */ -class LevelProgressionComponent : public Component { +class LevelProgressionComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION; /** * Constructor for this component diff --git a/dGame/dComponents/MiniGameControlComponent.h b/dGame/dComponents/MiniGameControlComponent.h index 06a9c24e..e2581b2d 100644 --- a/dGame/dComponents/MiniGameControlComponent.h +++ b/dGame/dComponents/MiniGameControlComponent.h @@ -6,7 +6,7 @@ class MiniGameControlComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MINI_GAME_CONTROL; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MINI_GAME_CONTROL; MiniGameControlComponent(Entity* parent) : Component(parent) {} void Serialize(RakNet::BitStream* outBitStream, bool isConstruction); diff --git a/dGame/dComponents/MissionComponent.h b/dGame/dComponents/MissionComponent.h index d53db64e..42c4df08 100644 --- a/dGame/dComponents/MissionComponent.h +++ b/dGame/dComponents/MissionComponent.h @@ -24,10 +24,9 @@ class AchievementCacheKey; * The mission inventory of an entity. Tracks mission state for each mission that can be accepted and allows for * progression of each of the mission task types (see eMissionTaskType). */ -class MissionComponent : public Component -{ +class MissionComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION; explicit MissionComponent(Entity* parent); ~MissionComponent() override; diff --git a/dGame/dComponents/MissionOfferComponent.h b/dGame/dComponents/MissionOfferComponent.h index 0f0d23ba..d842a92e 100644 --- a/dGame/dComponents/MissionOfferComponent.h +++ b/dGame/dComponents/MissionOfferComponent.h @@ -59,9 +59,9 @@ private: /** * Allows entities to offer missions to other entities, depending on their mission inventory progression. */ -class MissionOfferComponent : public Component { +class MissionOfferComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER; MissionOfferComponent(Entity* parent, LOT parentLot); diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelComponent.h index a36328be..0d720d04 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelComponent.h @@ -22,9 +22,9 @@ class MoveToInventoryMessage; /** * Component that represents entities that are a model, e.g. collectible models and BBB models. */ -class ModelComponent : public Component { +class ModelComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MODEL; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODEL; ModelComponent(Entity* parent); diff --git a/dGame/dComponents/ModuleAssemblyComponent.h b/dGame/dComponents/ModuleAssemblyComponent.h index 9e7301fe..47e7baa6 100644 --- a/dGame/dComponents/ModuleAssemblyComponent.h +++ b/dGame/dComponents/ModuleAssemblyComponent.h @@ -10,9 +10,9 @@ * same as having said items in your inventory (the subkey for this component) this component is the one that * renders the entity into the world. */ -class ModuleAssemblyComponent : public Component { +class ModuleAssemblyComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY; ModuleAssemblyComponent(Entity* parent); ~ModuleAssemblyComponent() override; diff --git a/dGame/dComponents/MovementAIComponent.cpp b/dGame/dComponents/MovementAIComponent.cpp index 1966638d..25cafc45 100644 --- a/dGame/dComponents/MovementAIComponent.cpp +++ b/dGame/dComponents/MovementAIComponent.cpp @@ -16,6 +16,8 @@ #include "CDComponentsRegistryTable.h" #include "CDPhysicsComponentTable.h" +#include "dNavMesh.h" + namespace { /** * Cache of all lots and their respective speeds @@ -169,8 +171,8 @@ NiPoint3 MovementAIComponent::ApproximateLocation() const { auto approximation = source + ((destination - source) * percentageToWaypoint); - if (dpWorld::Instance().IsLoaded()) { - approximation.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(approximation); + if (dpWorld::IsLoaded()) { + approximation.y = dpWorld::GetNavMesh()->GetHeightAtPoint(approximation); } return approximation; @@ -181,8 +183,8 @@ bool MovementAIComponent::Warp(const NiPoint3& point) { NiPoint3 destination = point; - if (dpWorld::Instance().IsLoaded()) { - destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point); + if (dpWorld::IsLoaded()) { + destination.y = dpWorld::GetNavMesh()->GetHeightAtPoint(point); if (std::abs(destination.y - point.y) > 3) { return false; @@ -302,8 +304,8 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) { } std::vector computedPath; - if (dpWorld::Instance().IsLoaded()) { - computedPath = dpWorld::Instance().GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed); + if (dpWorld::IsLoaded()) { + computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed); } // Somehow failed @@ -328,8 +330,8 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) { // Simply path for (auto& point : computedPath) { - if (dpWorld::Instance().IsLoaded()) { - point.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point); + if (dpWorld::IsLoaded()) { + point.y = dpWorld::GetNavMesh()->GetHeightAtPoint(point); } m_InterpolatedWaypoints.push_back(point); diff --git a/dGame/dComponents/MovementAIComponent.h b/dGame/dComponents/MovementAIComponent.h index 0ae71dc6..852f7001 100644 --- a/dGame/dComponents/MovementAIComponent.h +++ b/dGame/dComponents/MovementAIComponent.h @@ -55,9 +55,9 @@ struct MovementAIInfo { * Component that handles the movement settings of an entity. Not to be confused with the BaseCombatAI component that * actually handles attackig and following enemy entities. */ -class MovementAIComponent : public Component { +class MovementAIComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI; MovementAIComponent(Entity* parentEntity, MovementAIInfo info); diff --git a/dGame/dComponents/MovingPlatformComponent.h b/dGame/dComponents/MovingPlatformComponent.h index bb5821d4..cf47b9c3 100644 --- a/dGame/dComponents/MovingPlatformComponent.h +++ b/dGame/dComponents/MovingPlatformComponent.h @@ -104,9 +104,9 @@ public: * don't at all do what you expect them to as we don't instruct the client of changes made here. * ^^^ Trivia: This made the red blocks platform and property platforms a pain to implement. */ -class MovingPlatformComponent : public Component { +class MovingPlatformComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM; MovingPlatformComponent(Entity* parent, const std::string& pathName); ~MovingPlatformComponent() override; diff --git a/dGame/dComponents/MultiZoneEntranceComponent.h b/dGame/dComponents/MultiZoneEntranceComponent.h index c65dc0fe..8928be27 100644 --- a/dGame/dComponents/MultiZoneEntranceComponent.h +++ b/dGame/dComponents/MultiZoneEntranceComponent.h @@ -8,9 +8,9 @@ * Component that handles the LUP/WBL rocket launchpad that can be interacted with to travel to WBL worlds. * */ -class MultiZoneEntranceComponent : public Component { +class MultiZoneEntranceComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::MULTI_ZONE_ENTRANCE; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MULTI_ZONE_ENTRANCE; /** * Constructor for this component, builds the m_LUPWorlds vector diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 902edffe..7e515905 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -30,6 +30,7 @@ #include "eObjectBits.h" #include "eGameMasterLevel.h" #include "eMissionState.h" +#include "dNavMesh.h" std::unordered_map PetComponent::buildCache{}; std::unordered_map PetComponent::currentActivities{}; @@ -69,7 +70,8 @@ std::map PetComponent::petFlags = { { 13067, 838 }, // Skeleton dragon }; -PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(parent) { +PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } { + m_PetInfo = CDClientManager::Instance().GetTable()->GetByID(componentId); // TODO: Make reference when safe m_ComponentId = componentId; m_Interaction = LWOOBJID_EMPTY; @@ -81,31 +83,17 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_TimerAway = 0; m_DatabaseId = LWOOBJID_EMPTY; m_Status = 67108866; // Tamable - m_Ability = PetAbilityType::Invalid; + m_Ability = ePetAbilityType::Invalid; m_StartPosition = NiPoint3::ZERO; m_MovementAI = nullptr; m_TresureTime = 0; m_Preconditions = nullptr; - std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); + std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar(u"CheckPrecondition")); if (!checkPreconditions.empty()) { SetPreconditions(checkPreconditions); } - // Get the imagination drain rate from the CDClient - auto query = CDClientDatabase::CreatePreppedStmt("SELECT imaginationDrainRate FROM PetComponent WHERE id = ?;"); - - query.bind(1, static_cast(componentId)); - - auto result = query.execQuery(); - - // Should a result not exist for this pet default to 60 seconds. - if (!result.eof() && !result.fieldIsNull(0)) { - imaginationDrainRate = result.getFloatField(0, 60.0f); - } else { - imaginationDrainRate = 60.0f; - } - result.finalize(); } void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { @@ -114,7 +102,7 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd outBitStream->Write1(); // Always serialize as dirty for now outBitStream->Write(m_Status); - outBitStream->Write(tamed ? m_Ability : PetAbilityType::Invalid); // Something with the overhead icon? + outBitStream->Write(tamed ? m_Ability : ePetAbilityType::Invalid); // Something with the overhead icon? const bool interacting = m_Interaction != LWOOBJID_EMPTY; @@ -263,17 +251,17 @@ void PetComponent::OnUse(Entity* originator) { NiPoint3 forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector(); forward.y = 0; - if (dpWorld::Instance().IsLoaded()) { + if (dpWorld::IsLoaded()) { NiPoint3 attempt = petPosition + forward * interactionDistance; - float y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(attempt); + float y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) { const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector(); attempt = originatorPosition + forward * interactionDistance; - y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(attempt); + y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); interactionDistance -= 0.5f; } @@ -825,8 +813,8 @@ void PetComponent::Wander() { auto destination = m_StartPosition + delta; - if (dpWorld::Instance().IsLoaded()) { - destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination); + if (dpWorld::IsLoaded()) { + destination.y = dpWorld::GetNavMesh()->GetHeightAtPoint(destination); } if (Vector3::DistanceSquared(destination, m_MovementAI->GetParent()->GetPosition()) < 2 * 2) { @@ -835,11 +823,11 @@ void PetComponent::Wander() { return; } - m_MovementAI->SetMaxSpeed(info.wanderSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetDestination(destination); - m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / info.wanderSpeed; + m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_PetInfo.sprintSpeed; } void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { @@ -905,8 +893,6 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { GameMessages::SendRegisterPetDBID(m_Owner, m_DatabaseId, owner->GetSystemAddress()); } - - GameMessages::SendShowPetActionButton(m_Owner, 3, true, owner->GetSystemAddress()); } void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { @@ -928,22 +914,22 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { if (!fromTaming) playerDestroyableComponent->Imagine(-1); // Set this to a variable so when this is called back from the player the timer doesn't fire off. - m_Parent->AddCallbackTimer(imaginationDrainRate, [playerDestroyableComponent, this, item]() { + m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() { if (!playerDestroyableComponent) { LOG("No petComponent and/or no playerDestroyableComponent"); return; } - // If we are out of imagination despawn the pet. - if (playerDestroyableComponent->GetImagination() == 0) { - this->Deactivate(); - auto playerEntity = playerDestroyableComponent->GetParent(); - if (!playerEntity) return; + // If we are out of imagination despawn the pet. + if (playerDestroyableComponent->GetImagination() == 0) { + this->Deactivate(); + auto playerEntity = playerDestroyableComponent->GetParent(); + if (!playerEntity) return; - GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); - } + GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); + } - this->AddDrainImaginationTimer(item); + this->AddDrainImaginationTimer(item); }); } @@ -966,7 +952,7 @@ void PetComponent::Deactivate() { GameMessages::SendRegisterPetDBID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress()); - GameMessages::SendShowPetActionButton(m_Owner, 0, false, owner->GetSystemAddress()); + GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, owner->GetSystemAddress()); } void PetComponent::Release() { @@ -985,12 +971,9 @@ void PetComponent::Release() { item->SetCount(0, false, false); } -void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey) { +void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, const int32_t commandType, const int32_t typeId, const bool overrideObey) { auto* owner = GetOwner(); - - if (owner == nullptr) { - return; - } + if (!owner) return; if (commandType == 1) { // Emotes @@ -1030,7 +1013,7 @@ uint32_t PetComponent::GetStatus() const { return m_Status; } -PetAbilityType PetComponent::GetAbility() const { +ePetAbilityType PetComponent::GetAbility() const { return m_Ability; } @@ -1042,7 +1025,7 @@ void PetComponent::SetStatus(uint32_t value) { m_Status = value; } -void PetComponent::SetAbility(PetAbilityType value) { +void PetComponent::SetAbility(ePetAbilityType value) { m_Ability = value; } @@ -1098,7 +1081,7 @@ void PetComponent::SetPetNameForModeration(const std::string& petName) { } //Save to db: - Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{petName, approved}); + Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ petName, approved }); } void PetComponent::LoadPetNameFromModeration() { diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index ca4b5a74..6d13bea9 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -1,27 +1,22 @@ -#pragma once +#ifndef PETCOMPONENT_H +#define PETCOMPONENT_H #include "Entity.h" #include "MovementAIComponent.h" #include "Component.h" #include "Preconditions.h" +#include "ePetAbilityType.h" #include "eReplicaComponentType.h" - -enum class PetAbilityType : uint32_t -{ - Invalid, - GoToObject, - JumpOnObject, - DigAtPosition -}; +#include "CDPetComponentTable.h" /** * Represents an entity that is a pet. This pet can be tamed and consequently follows the tamer around, allowing it * to dig for treasure and activate pet bouncers. */ -class PetComponent : public Component +class PetComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PET; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET; explicit PetComponent(Entity* parentEntity, uint32_t componentId); ~PetComponent() override; @@ -103,7 +98,7 @@ public: * @param typeId extra information about the command, e.g. the emote to play * @param overrideObey unused */ - void Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey); + void Command(const NiPoint3& position, const LWOOBJID source, const int32_t commandType, const int32_t typeId, const bool overrideObey); /** * Returns the ID of the owner of this pet (if any) @@ -158,13 +153,13 @@ public: * Returns an ability the pet may perform, currently unused * @return an ability the pet may perform */ - PetAbilityType GetAbility() const; + ePetAbilityType GetAbility() const; /** * Sets the ability of the pet, currently unused * @param value the ability to set */ - void SetAbility(PetAbilityType value); + void SetAbility(ePetAbilityType value); /** * Sets preconditions for the pet that need to be met before it can be tamed @@ -323,7 +318,7 @@ private: /** * A currently active ability, mostly unused */ - PetAbilityType m_Ability; + ePetAbilityType m_Ability; /** * The time an entity has left to complete the minigame @@ -357,7 +352,10 @@ private: PreconditionExpression* m_Preconditions; /** - * The rate at which imagination is drained from the user for having the pet out. + * Pet information loaded from the CDClientDatabase + * TODO: Switch to a reference when safe to do so */ - float imaginationDrainRate; + CDPetComponent m_PetInfo; }; + +#endif // !PETCOMPONENT_H diff --git a/dGame/dComponents/PhantomPhysicsComponent.cpp b/dGame/dComponents/PhantomPhysicsComponent.cpp index 6248bfe9..45d08b97 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.cpp +++ b/dGame/dComponents/PhantomPhysicsComponent.cpp @@ -161,7 +161,7 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "miscellaneous\\misc_phys_640x640.hkx") { // Move this down by 13.521004 units so it is still effectively at the same height as before m_Position = m_Position - NiPoint3::UNIT_Y * 13.521004f; @@ -172,56 +172,56 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\trigger_wall_tall.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 10.0f, 25.0f, 1.0f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\env_gen_placeholderphysics.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 20.0f, 20.0f, 20.0f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\POI_trigger_wall.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1.0f, 12.5f, 20.0f); // Not sure what the real size is m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\NG_NinjaGo\\env_ng_gen_gate_chamber_puzzle_ceiling_tile_falling_phantom.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 18.0f, 5.0f, 15.0f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position + m_Rotation.GetForwardVector() * 7.5f); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\NG_NinjaGo\\ng_flamejet_brick_phantom.HKX") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 1.0f, 1.0f, 12.0f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position + m_Rotation.GetForwardVector() * 6.0f); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\Ring_Trigger.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 6.0f, 6.0f, 6.0f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 4.5f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 390.496826f, 111.467964f, 600.821534f, true); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_Position.y -= (111.467964f * m_Scale) / 2; m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else { //LOG("This one is supposed to have %s", info->physicsAsset.c_str()); @@ -230,7 +230,7 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } } @@ -238,7 +238,7 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsCompon PhantomPhysicsComponent::~PhantomPhysicsComponent() { if (m_dpEntity) { - dpWorld::Instance().RemoveEntity(m_dpEntity); + dpWorld::RemoveEntity(m_dpEntity); } } @@ -300,7 +300,7 @@ void PhantomPhysicsComponent::CreatePhysics() { m_dpEntity->SetPosition({ m_Position.x, m_Position.y - (height / 2), m_Position.z }); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); m_HasCreatedPhysics = true; } diff --git a/dGame/dComponents/PhantomPhysicsComponent.h b/dGame/dComponents/PhantomPhysicsComponent.h index 5fcee004..2ea9e979 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.h +++ b/dGame/dComponents/PhantomPhysicsComponent.h @@ -25,9 +25,9 @@ enum class ePhysicsEffectType : uint32_t ; * trigger gameplay events, for example the bus in Avant Gardens that moves around when the player touches its physics * body. Optionally this object can also have effects, like the fans in AG. */ -class PhantomPhysicsComponent : public PhysicsComponent { +class PhantomPhysicsComponent final : public PhysicsComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS; PhantomPhysicsComponent(Entity* parent); ~PhantomPhysicsComponent() override; diff --git a/dGame/dComponents/PlayerForcedMovementComponent.h b/dGame/dComponents/PlayerForcedMovementComponent.h index 810b727c..e5aca37d 100644 --- a/dGame/dComponents/PlayerForcedMovementComponent.h +++ b/dGame/dComponents/PlayerForcedMovementComponent.h @@ -8,9 +8,9 @@ * Component that handles player forced movement * */ -class PlayerForcedMovementComponent : public Component { +class PlayerForcedMovementComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PLAYER_FORCED_MOVEMENT; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PLAYER_FORCED_MOVEMENT; /** * Constructor for this component diff --git a/dGame/dComponents/PossessableComponent.h b/dGame/dComponents/PossessableComponent.h index 9a767ba9..3b075d33 100644 --- a/dGame/dComponents/PossessableComponent.h +++ b/dGame/dComponents/PossessableComponent.h @@ -12,9 +12,9 @@ * Represents an entity that can be controlled by some other entity, generally used by cars to indicate that some * player is controlling it. */ -class PossessableComponent : public Component { +class PossessableComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE; PossessableComponent(Entity* parentEntity, uint32_t componentId); diff --git a/dGame/dComponents/PossessorComponent.h b/dGame/dComponents/PossessorComponent.h index c225766b..e1aba048 100644 --- a/dGame/dComponents/PossessorComponent.h +++ b/dGame/dComponents/PossessorComponent.h @@ -16,9 +16,9 @@ enum class ePossessionType : uint8_t { /** * Represents an entity that can posess other entities. Generally used by players to drive a car. */ -class PossessorComponent : public Component { +class PossessorComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSOR; PossessorComponent(Entity* parent); ~PossessorComponent() override; diff --git a/dGame/dComponents/PropertyComponent.h b/dGame/dComponents/PropertyComponent.h index 135a1d26..c412ccb3 100644 --- a/dGame/dComponents/PropertyComponent.h +++ b/dGame/dComponents/PropertyComponent.h @@ -20,9 +20,9 @@ struct PropertyState { /** * This component is unused and has no functionality */ -class PropertyComponent : public Component { +class PropertyComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY; explicit PropertyComponent(Entity* parentEntity); ~PropertyComponent() override; [[nodiscard]] PropertyState* GetPropertyState() const { return m_PropertyState; }; diff --git a/dGame/dComponents/PropertyEntranceComponent.h b/dGame/dComponents/PropertyEntranceComponent.h index ef8f9810..510bb489 100644 --- a/dGame/dComponents/PropertyEntranceComponent.h +++ b/dGame/dComponents/PropertyEntranceComponent.h @@ -11,10 +11,10 @@ /** * Represents the launch pad that's used to select and browse properties */ -class PropertyEntranceComponent : public Component { +class PropertyEntranceComponent final : public Component { public: explicit PropertyEntranceComponent(Entity* parent, uint32_t componentID); - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_ENTRANCE; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_ENTRANCE; /** * Handles an OnUse request for some other entity, rendering the property browse menu diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index 833b7d73..dff1e5da 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -20,6 +20,8 @@ #include "InventoryComponent.h" #include "eMissionTaskType.h" #include "eObjectBits.h" +#include "CharacterComponent.h" +#include "PlayerManager.h" #include #include "CppScripts.h" @@ -226,7 +228,7 @@ void PropertyManagementComponent::OnStartBuilding() { if (ownerEntity == nullptr) return; - const auto players = Player::GetAllPlayers(); + const auto players = PlayerManager::GetAllPlayers(); LWOMAPID zoneId = 1100; @@ -247,7 +249,8 @@ void PropertyManagementComponent::OnStartBuilding() { for (auto* player : players) { if (player == ownerEntity) continue; - player->SendToZone(zoneId); + auto* characterComponent = player->GetComponent(); + if (characterComponent) characterComponent->SendToZone(zoneId); } auto inventoryComponent = ownerEntity->GetComponent(); @@ -519,7 +522,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet { item->SetCount(item->GetCount() - 1); - LOG("BODGE TIME, YES IT GOES HERE"); + LOG("DLU currently does not support breaking apart brick by brick models."); break; } diff --git a/dGame/dComponents/PropertyManagementComponent.h b/dGame/dComponents/PropertyManagementComponent.h index d38437c4..6a9ed09d 100644 --- a/dGame/dComponents/PropertyManagementComponent.h +++ b/dGame/dComponents/PropertyManagementComponent.h @@ -8,8 +8,7 @@ /** * Information regarding which players may visit this property */ -enum class PropertyPrivacyOption -{ +enum class PropertyPrivacyOption { /** * Default, only you can visit your property */ @@ -29,10 +28,9 @@ enum class PropertyPrivacyOption /** * Main component that handles interactions with a property, generally the plaques you see on properties. */ -class PropertyManagementComponent : public Component -{ +class PropertyManagementComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_MANAGEMENT; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_MANAGEMENT; PropertyManagementComponent(Entity* parent); static PropertyManagementComponent* Instance(); diff --git a/dGame/dComponents/PropertyVendorComponent.h b/dGame/dComponents/PropertyVendorComponent.h index 0e6c6521..fee8af9c 100644 --- a/dGame/dComponents/PropertyVendorComponent.h +++ b/dGame/dComponents/PropertyVendorComponent.h @@ -7,10 +7,9 @@ /** * The property guard that stands on a property before it's claimed, allows entities to attempt claiming this property. */ -class PropertyVendorComponent : public Component -{ +class PropertyVendorComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_VENDOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_VENDOR; explicit PropertyVendorComponent(Entity* parent); /** diff --git a/dGame/dComponents/ProximityMonitorComponent.cpp b/dGame/dComponents/ProximityMonitorComponent.cpp index acc93fde..fbac8ddb 100644 --- a/dGame/dComponents/ProximityMonitorComponent.cpp +++ b/dGame/dComponents/ProximityMonitorComponent.cpp @@ -18,7 +18,7 @@ ProximityMonitorComponent::~ProximityMonitorComponent() { for (const auto& en : m_ProximitiesData) { if (!en.second) continue; - dpWorld::Instance().RemoveEntity(en.second); + dpWorld::RemoveEntity(en.second); } m_ProximitiesData.clear(); @@ -28,12 +28,12 @@ void ProximityMonitorComponent::SetProximityRadius(float proxRadius, const std:: dpEntity* en = new dpEntity(m_Parent->GetObjectID(), proxRadius); en->SetPosition(m_Parent->GetPosition()); - dpWorld::Instance().AddEntity(en); + dpWorld::AddEntity(en); m_ProximitiesData.insert(std::make_pair(name, en)); } void ProximityMonitorComponent::SetProximityRadius(dpEntity* entity, const std::string& name) { - dpWorld::Instance().AddEntity(entity); + dpWorld::AddEntity(entity); entity->SetPosition(m_Parent->GetPosition()); m_ProximitiesData.insert(std::make_pair(name, entity)); } diff --git a/dGame/dComponents/ProximityMonitorComponent.h b/dGame/dComponents/ProximityMonitorComponent.h index 90ba7d54..512b2848 100644 --- a/dGame/dComponents/ProximityMonitorComponent.h +++ b/dGame/dComponents/ProximityMonitorComponent.h @@ -17,9 +17,9 @@ * Utility component for detecting how close entities are to named proximities for this entity. Allows you to store * proximity checks for multiple ojects. */ -class ProximityMonitorComponent : public Component { +class ProximityMonitorComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::PROXIMITY_MONITOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROXIMITY_MONITOR; ProximityMonitorComponent(Entity* parentEntity, int smallRadius = -1, int largeRadius = -1); ~ProximityMonitorComponent() override; diff --git a/dGame/dComponents/QuickBuildComponent.cpp b/dGame/dComponents/QuickBuildComponent.cpp index 16263d81..b8aec2bd 100644 --- a/dGame/dComponents/QuickBuildComponent.cpp +++ b/dGame/dComponents/QuickBuildComponent.cpp @@ -14,7 +14,6 @@ #include "eGameActivity.h" #include "dServer.h" -#include "PacketUtils.h" #include "Spawner.h" #include "MovingPlatformComponent.h" #include "Preconditions.h" diff --git a/dGame/dComponents/QuickBuildComponent.h b/dGame/dComponents/QuickBuildComponent.h index f1106a61..a6725a81 100644 --- a/dGame/dComponents/QuickBuildComponent.h +++ b/dGame/dComponents/QuickBuildComponent.h @@ -20,9 +20,9 @@ enum class eQuickBuildFailReason : uint32_t; * consists of an activator that shows a popup and then the actual entity that the bricks are built into. Note * that quick builds are also scripted activities so this shared some logic with the ScriptedActivityComponent. */ -class QuickBuildComponent : public Component { +class QuickBuildComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::QUICK_BUILD; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::QUICK_BUILD; QuickBuildComponent(Entity* entity); ~QuickBuildComponent() override; diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index 5b93dbb7..5e3d81f4 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -71,10 +71,8 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) { // If the race has already started, send the player back to the main world. if (m_Loaded || !vehicle) { - auto* playerInstance = dynamic_cast(player); - if (playerInstance) { - playerInstance->SendToZone(m_MainWorld); - } + auto* characterComponent = player->GetComponent(); + if (characterComponent) characterComponent->SendToZone(m_MainWorld); return; } @@ -105,10 +103,11 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, if (item == nullptr) { LOG("Failed to find item"); - auto* playerInstance = dynamic_cast(player); - if (playerInstance) { + auto* characterComponent = player->GetComponent(); + + if (characterComponent) { m_LoadedPlayers--; - playerInstance->SendToZone(m_MainWorld); + characterComponent->SendToZone(m_MainWorld); } return; @@ -427,9 +426,9 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu m_Parent->GetObjectID(), 3, 0, LWOOBJID_EMPTY, u"", player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - auto* playerInstance = dynamic_cast(player); + auto* characterComponent = player->GetComponent(); - playerInstance->SendToZone(m_MainWorld); + if (characterComponent) characterComponent->SendToZone(m_MainWorld); vehicle->Kill(); } @@ -561,9 +560,9 @@ void RacingControlComponent::Update(float deltaTime) { continue; } - auto* playerInstance = dynamic_cast(playerEntity); + auto* characterComponent = playerEntity->GetComponent(); - playerInstance->SendToZone(m_MainWorld); + if (characterComponent) characterComponent->SendToZone(m_MainWorld); } m_LobbyPlayers.clear(); @@ -623,9 +622,9 @@ void RacingControlComponent::Update(float deltaTime) { continue; } - auto* playerInstance = dynamic_cast(playerEntity); + auto* characterComponent = playerEntity->GetComponent(); - playerInstance->SendToZone(m_MainWorld); + if (characterComponent) characterComponent->SendToZone(m_MainWorld); } return; diff --git a/dGame/dComponents/RacingControlComponent.h b/dGame/dComponents/RacingControlComponent.h index 47341aae..4a7d387f 100644 --- a/dGame/dComponents/RacingControlComponent.h +++ b/dGame/dComponents/RacingControlComponent.h @@ -103,9 +103,9 @@ struct RacingPlayerInfo { /** * Component that's attached to a manager entity in each race zone that loads player vehicles, keep scores, etc. */ -class RacingControlComponent : public Component { +class RacingControlComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL; RacingControlComponent(Entity* parentEntity); ~RacingControlComponent(); diff --git a/dGame/dComponents/RacingSoundTriggerComponent.h b/dGame/dComponents/RacingSoundTriggerComponent.h index 0f79c67f..140bbe20 100644 --- a/dGame/dComponents/RacingSoundTriggerComponent.h +++ b/dGame/dComponents/RacingSoundTriggerComponent.h @@ -8,7 +8,7 @@ class Entity; class RacingSoundTriggerComponent : public SoundTriggerComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_SOUND_TRIGGER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RACING_SOUND_TRIGGER; RacingSoundTriggerComponent(Entity* parent) : SoundTriggerComponent(parent){}; }; diff --git a/dGame/dComponents/RacingStatsComponent.h b/dGame/dComponents/RacingStatsComponent.h index 9349ce49..ad1b35bf 100644 --- a/dGame/dComponents/RacingStatsComponent.h +++ b/dGame/dComponents/RacingStatsComponent.h @@ -6,7 +6,7 @@ class RacingStatsComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_STATS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RACING_STATS; RacingStatsComponent(Entity* parent) : Component(parent) {} }; diff --git a/dGame/dComponents/RailActivatorComponent.h b/dGame/dComponents/RailActivatorComponent.h index 28b25073..015f36b7 100644 --- a/dGame/dComponents/RailActivatorComponent.h +++ b/dGame/dComponents/RailActivatorComponent.h @@ -15,7 +15,7 @@ public: explicit RailActivatorComponent(Entity* parent, int32_t componentID); ~RailActivatorComponent() override; - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RAIL_ACTIVATOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RAIL_ACTIVATOR; /** * Handles the OnUse event from some entity, initiates the rail movement diff --git a/dGame/dComponents/RenderComponent.cpp b/dGame/dComponents/RenderComponent.cpp index f1b8ea0c..4d8ff667 100644 --- a/dGame/dComponents/RenderComponent.cpp +++ b/dGame/dComponents/RenderComponent.cpp @@ -5,7 +5,6 @@ #include #include "Entity.h" -#include "PacketUtils.h" #include "CDClientManager.h" #include "GameMessages.h" diff --git a/dGame/dComponents/RenderComponent.h b/dGame/dComponents/RenderComponent.h index 74f39625..936864fc 100644 --- a/dGame/dComponents/RenderComponent.h +++ b/dGame/dComponents/RenderComponent.h @@ -54,9 +54,9 @@ struct Effect { * Determines that a component should be visibly rendered into the world, most entities have this. This component * also handles effects that play for entities. */ -class RenderComponent : public Component { +class RenderComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RENDER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RENDER; RenderComponent(Entity* entity, int32_t componentId = -1); ~RenderComponent() override; diff --git a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h index bc60c38e..082dd1e7 100644 --- a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h +++ b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h @@ -19,7 +19,7 @@ */ class RigidbodyPhantomPhysicsComponent : public PhysicsComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS; RigidbodyPhantomPhysicsComponent(Entity* parent); diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.h b/dGame/dComponents/RocketLaunchpadControlComponent.h index 06d97cd3..03d2f141 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.h +++ b/dGame/dComponents/RocketLaunchpadControlComponent.h @@ -16,9 +16,9 @@ class PreconditionExpression; /** * Component that handles rocket launchpads that can be interacted with to travel to other worlds. */ -class RocketLaunchpadControlComponent : public Component { +class RocketLaunchpadControlComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCH; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCH; RocketLaunchpadControlComponent(Entity* parent, int rocketId); ~RocketLaunchpadControlComponent() override; diff --git a/dGame/dComponents/ScriptedActivityComponent.h b/dGame/dComponents/ScriptedActivityComponent.h index 79a9593d..308a0a86 100644 --- a/dGame/dComponents/ScriptedActivityComponent.h +++ b/dGame/dComponents/ScriptedActivityComponent.h @@ -6,9 +6,9 @@ class Entity; -class ScriptedActivityComponent : public ActivityComponent { +class ScriptedActivityComponent final : public ActivityComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPTED_ACTIVITY; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPTED_ACTIVITY; ScriptedActivityComponent(Entity* parent, int activityID) : ActivityComponent(parent, activityID){}; }; diff --git a/dGame/dComponents/ShootingGalleryComponent.h b/dGame/dComponents/ShootingGalleryComponent.h index 4024e99a..c4b8fea2 100644 --- a/dGame/dComponents/ShootingGalleryComponent.h +++ b/dGame/dComponents/ShootingGalleryComponent.h @@ -71,9 +71,9 @@ struct StaticShootingGalleryParams { * A very ancient component that was used to guide shooting galleries, it's still kind of used but a lot of logic is * also in the related scripts. */ -class ShootingGalleryComponent : public Component { +class ShootingGalleryComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SHOOTING_GALLERY; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SHOOTING_GALLERY; explicit ShootingGalleryComponent(Entity* parent); ~ShootingGalleryComponent(); diff --git a/dGame/dComponents/SimplePhysicsComponent.h b/dGame/dComponents/SimplePhysicsComponent.h index 752fef0b..707f8a41 100644 --- a/dGame/dComponents/SimplePhysicsComponent.h +++ b/dGame/dComponents/SimplePhysicsComponent.h @@ -28,7 +28,7 @@ enum class eClimbableType : int32_t { */ class SimplePhysicsComponent : public PhysicsComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SIMPLE_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SIMPLE_PHYSICS; SimplePhysicsComponent(Entity* parent, uint32_t componentID); ~SimplePhysicsComponent() override; diff --git a/dGame/dComponents/SkillComponent.h b/dGame/dComponents/SkillComponent.h index 2d8628ef..530c2a25 100644 --- a/dGame/dComponents/SkillComponent.h +++ b/dGame/dComponents/SkillComponent.h @@ -1,6 +1,6 @@ /* * Darkflame Universe - * Copyright 2018 + * Copyright 2024 */ #ifndef SKILLCOMPONENT_H @@ -55,11 +55,11 @@ struct SkillExecutionResult { * * Skills are a built up by a tree of behaviors. See dGame/dBehaviors/ for a list of behaviors. * - * This system is very conveluted and still has a lot of unknowns. + * This system is very convoluted and still has a lot of unknowns. */ -class SkillComponent : public Component { +class SkillComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SKILL; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SKILL; explicit SkillComponent(Entity* parent); ~SkillComponent() override; diff --git a/dGame/dComponents/SoundTriggerComponent.h b/dGame/dComponents/SoundTriggerComponent.h index 56c71770..48366017 100644 --- a/dGame/dComponents/SoundTriggerComponent.h +++ b/dGame/dComponents/SoundTriggerComponent.h @@ -1,4 +1,5 @@ #pragma once + #include "dCommonVars.h" #include "Entity.h" #include "GUID.h" @@ -43,7 +44,7 @@ struct GUIDResults{ void Serialize(RakNet::BitStream* outBitStream); }; -struct MixerProgram{ +struct MixerProgram { std::string name; uint32_t result; @@ -58,7 +59,7 @@ struct MixerProgram{ class SoundTriggerComponent : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SOUND_TRIGGER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SOUND_TRIGGER; explicit SoundTriggerComponent(Entity* parent); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void ActivateMusicCue(const std::string& name, float bordemTime = -1.0); @@ -66,11 +67,11 @@ public: private: - std::vector m_MusicCues = {}; - std::vector m_MusicParameters = {}; - std::vector m_2DAmbientSounds = {}; - std::vector m_3DAmbientSounds = {}; - std::vector m_MixerPrograms = {}; + std::vector m_MusicCues; + std::vector m_MusicParameters; + std::vector m_2DAmbientSounds; + std::vector m_3DAmbientSounds; + std::vector m_MixerPrograms; bool m_Dirty = false; }; diff --git a/dGame/dComponents/SwitchComponent.h b/dGame/dComponents/SwitchComponent.h index 7f2c3498..f732a8c1 100644 --- a/dGame/dComponents/SwitchComponent.h +++ b/dGame/dComponents/SwitchComponent.h @@ -14,9 +14,9 @@ /** * A component for switches in game, including pet triggered switches. */ -class SwitchComponent : public Component { +class SwitchComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SWITCH; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SWITCH; SwitchComponent(Entity* parent); ~SwitchComponent() override; diff --git a/dGame/dComponents/TriggerComponent.cpp b/dGame/dComponents/TriggerComponent.cpp index d30353ab..6d7ce3dd 100644 --- a/dGame/dComponents/TriggerComponent.cpp +++ b/dGame/dComponents/TriggerComponent.cpp @@ -13,6 +13,7 @@ #include "QuickBuildComponent.h" #include "SkillComponent.h" #include "eEndBehavior.h" +#include "PlayerManager.h" TriggerComponent::TriggerComponent(Entity* parent, const std::string triggerInfo): Component(parent) { @@ -175,7 +176,7 @@ std::vector TriggerComponent::GatherTargets(LUTriggers::Command* comman } } else if (command->target == "objGroup") entities = Game::entityManager->GetEntitiesInGroup(command->targetName); else if (command->target == "allPlayers") { - for (auto* player : Player::GetAllPlayers()) { + for (auto* player : PlayerManager::GetAllPlayers()) { entities.push_back(player); } } else if (command->target == "allNPCs") { /*UNUSED*/ } diff --git a/dGame/dComponents/TriggerComponent.h b/dGame/dComponents/TriggerComponent.h index 90ecc52c..5610f24a 100644 --- a/dGame/dComponents/TriggerComponent.h +++ b/dGame/dComponents/TriggerComponent.h @@ -5,9 +5,9 @@ #include "LUTriggers.h" #include "eReplicaComponentType.h" -class TriggerComponent : public Component { +class TriggerComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::TRIGGER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::TRIGGER; explicit TriggerComponent(Entity* parent, const std::string triggerInfo); diff --git a/dGame/dComponents/VendorComponent.h b/dGame/dComponents/VendorComponent.h index 7924a928..48b766d2 100644 --- a/dGame/dComponents/VendorComponent.h +++ b/dGame/dComponents/VendorComponent.h @@ -20,7 +20,7 @@ struct SoldItem { class VendorComponent : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::VENDOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::VENDOR; VendorComponent(Entity* parent); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; diff --git a/dGame/dEntity/EntityCallbackTimer.cpp b/dGame/dEntity/EntityCallbackTimer.cpp index e07c1189..95222c4e 100644 --- a/dGame/dEntity/EntityCallbackTimer.cpp +++ b/dGame/dEntity/EntityCallbackTimer.cpp @@ -1,22 +1,10 @@ #include "EntityCallbackTimer.h" -EntityCallbackTimer::EntityCallbackTimer(float time, std::function callback) { +EntityCallbackTimer::EntityCallbackTimer(const float time, const std::function callback) { m_Time = time; m_Callback = callback; } -EntityCallbackTimer::~EntityCallbackTimer() { - -} - -std::function EntityCallbackTimer::GetCallback() { - return m_Callback; -} - -float EntityCallbackTimer::GetTime() { - return m_Time; -} - void EntityCallbackTimer::Update(float deltaTime) { m_Time -= deltaTime; } diff --git a/dGame/dEntity/EntityCallbackTimer.h b/dGame/dEntity/EntityCallbackTimer.h index 2a7e58f0..9b37476b 100644 --- a/dGame/dEntity/EntityCallbackTimer.h +++ b/dGame/dEntity/EntityCallbackTimer.h @@ -5,11 +5,11 @@ class EntityCallbackTimer { public: - EntityCallbackTimer(float time, std::function callback); - ~EntityCallbackTimer(); + EntityCallbackTimer(const float time, const std::function callback); + + std::function GetCallback() const { return m_Callback; }; - std::function GetCallback(); - float GetTime(); + float GetTime() const { return m_Time; }; void Update(float deltaTime); diff --git a/dGame/dEntity/EntityTimer.cpp b/dGame/dEntity/EntityTimer.cpp index 0363fc5b..8ec666b6 100644 --- a/dGame/dEntity/EntityTimer.cpp +++ b/dGame/dEntity/EntityTimer.cpp @@ -1,14 +1,10 @@ #include "EntityTimer.h" -EntityTimer::EntityTimer(std::string name, float time) { +EntityTimer::EntityTimer(const std::string& name, const float time) { m_Name = name; m_Time = time; } -EntityTimer::~EntityTimer() { - -} - std::string EntityTimer::GetName() { return m_Name; } diff --git a/dGame/dEntity/EntityTimer.h b/dGame/dEntity/EntityTimer.h index 9de0345d..d5c4a19e 100644 --- a/dGame/dEntity/EntityTimer.h +++ b/dGame/dEntity/EntityTimer.h @@ -4,8 +4,15 @@ class EntityTimer { public: - EntityTimer(std::string name, float time); - ~EntityTimer(); + EntityTimer(const std::string& name, const float time); + + bool operator==(const EntityTimer& other) const { + return m_Name == other.m_Name; + } + + bool operator==(const std::string& other) const { + return m_Name == other; + } std::string GetName(); float GetTime(); diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index 6e313ab1..fa11c086 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -37,6 +37,7 @@ #include "eGameMessageType.h" #include "ePlayerFlag.h" #include "dConfig.h" +#include "GhostComponent.h" #include "StringifiedEnum.h" void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const SystemAddress& sysAddr, LWOOBJID objectID, eGameMessageType messageID) { @@ -108,9 +109,9 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System GameMessages::SendRestoreToPostLoadStats(entity, sysAddr); entity->SetPlayerReadyForUpdates(); - auto* player = dynamic_cast(entity); - if (player != nullptr) { - player->ConstructLimboEntities(); + auto* ghostComponent = entity->GetComponent(); + if (ghostComponent != nullptr) { + ghostComponent->ConstructLimboEntities(); } InventoryComponent* inv = entity->GetComponent(); @@ -137,14 +138,14 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System Entity* zoneControl = Game::entityManager->GetZoneControlEntity(); for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) { - script->OnPlayerLoaded(zoneControl, player); + script->OnPlayerLoaded(zoneControl, entity); } std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); for (Entity* scriptEntity : scriptedActs) { if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) { - script->OnPlayerLoaded(scriptEntity, player); + script->OnPlayerLoaded(scriptEntity, entity); } } } @@ -196,8 +197,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System } case eGameMessageType::MISSION_DIALOGUE_CANCELLED: { - //This message is pointless for our implementation, as the client just carries on after - //rejecting a mission offer. We dont need to do anything. This is just here to remove a warning in our logs :) + // This message is pointless for our implementation, as the client just carries on after + // rejecting a mission offer. We dont need to do anything. This is just here to remove a warning in our logs :) break; } diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index eb6f11bb..8c935afe 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -1,7 +1,6 @@ #include "GameMessages.h" #include "User.h" #include "Entity.h" -#include "PacketUtils.h" #include "BitStreamUtils.h" #include "BitStream.h" #include "Game.h" @@ -79,6 +78,7 @@ #include "RailActivatorComponent.h" #include "LevelProgressionComponent.h" #include "DonationVendorComponent.h" +#include "GhostComponent.h" // Message includes: #include "dZoneManager.h" @@ -95,7 +95,9 @@ #include "eReplicaComponentType.h" #include "eClientMessageType.h" #include "eGameMessageType.h" +#include "ePetAbilityType.h" #include "ActivityManager.h" +#include "PlayerManager.h" #include "CDComponentsRegistryTable.h" #include "CDObjectsTable.h" @@ -623,6 +625,25 @@ void GameMessages::SendUIMessageServerToSingleClient(Entity* entity, const Syste SEND_PACKET; } +void GameMessages::SendUIMessageServerToSingleClient(const std::string& message, AMFBaseValue& args, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + LWOOBJID empty = 0; + bitStream.Write(empty); + bitStream.Write(eGameMessageType::UI_MESSAGE_SERVER_TO_ALL_CLIENTS); // This is intentional to allow the server to send a ui message to a client via their system address. + + bitStream.Write(args); + uint32_t strMessageNameLength = message.size(); + bitStream.Write(strMessageNameLength); + + for (uint32_t k = 0; k < strMessageNameLength; k++) { + bitStream.Write(message[k]); + } + + SEND_PACKET; +} + void GameMessages::SendUIMessageServerToAllClients(const std::string& message, AMFBaseValue& args) { CBITSTREAM; CMSGHEADER; @@ -1726,8 +1747,6 @@ void GameMessages::SendStartCelebrationEffect(Entity* entity, const SystemAddres bitStream.Write(0); //subtext SEND_PACKET; - - //PacketUtils::SavePacket("StartCelebrationEffect.bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); } @@ -1950,7 +1969,6 @@ void GameMessages::SendBBBSaveResponse(const LWOOBJID& objectId, const LWOOBJID& bitStream.Write(buffer[i]); SEND_PACKET; - //PacketUtils::SavePacket("eGameMessageType::BBB_SAVE_RESPONSE.bin", reinterpret_cast(bitStream.GetData()), bitStream.GetNumberOfBytesUsed()); } // Property @@ -2696,7 +2714,7 @@ void GameMessages::HandlePropertyEntranceSync(RakNet::BitStream* inStream, Entit filterText.push_back(c); } - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); auto* entranceComponent = entity->GetComponent(); @@ -2723,7 +2741,7 @@ void GameMessages::HandleEnterProperty(RakNet::BitStream* inStream, Entity* enti inStream->Read(index); inStream->Read(returnToZone); - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); auto* entranceComponent = entity->GetComponent(); if (entranceComponent != nullptr) { @@ -3520,14 +3538,14 @@ void GameMessages::SendClientExitTamingMinigame(LWOOBJID objectId, bool bVolunta SEND_PACKET; } -void GameMessages::SendShowPetActionButton(LWOOBJID objectId, int32_t buttonLabel, bool bShow, const SystemAddress& sysAddr) { +void GameMessages::SendShowPetActionButton(const LWOOBJID objectId, const ePetAbilityType petAbility, const bool bShow, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; bitStream.Write(objectId); bitStream.Write(eGameMessageType::SHOW_PET_ACTION_BUTTON); - bitStream.Write(buttonLabel); + bitStream.Write(petAbility); bitStream.Write(bShow); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; @@ -4604,10 +4622,11 @@ void GameMessages::HandleToggleGhostReferenceOverride(RakNet::BitStream* inStrea inStream->Read(bOverride); - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); if (player != nullptr) { - player->SetGhostOverride(bOverride); + auto* ghostComponent = entity->GetComponent(); + if (ghostComponent) ghostComponent->SetGhostOverride(bOverride); Game::entityManager->UpdateGhosting(player); } @@ -4619,10 +4638,11 @@ void GameMessages::HandleSetGhostReferencePosition(RakNet::BitStream* inStream, inStream->Read(position); - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); if (player != nullptr) { - player->SetGhostOverridePoint(position); + auto* ghostComponent = entity->GetComponent(); + if (ghostComponent) ghostComponent->SetGhostOverridePoint(position); Game::entityManager->UpdateGhosting(player); } @@ -4630,7 +4650,7 @@ void GameMessages::HandleSetGhostReferencePosition(RakNet::BitStream* inStream, void GameMessages::HandleBuyFromVendor(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { - bool bConfirmed{}; // this doesnt even do anything, thanks ND! + bool bConfirmed{}; // This doesn't appear to do anything. Further research is needed. bool countIsDefault{}; int count = 1; LOT item; @@ -4883,7 +4903,7 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream* inStream, Entity inStream->Read(senderID); auto* sender = Game::entityManager->GetEntity(senderID); - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); if (!player) { return; diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 6d29a394..68a8471a 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -32,6 +32,7 @@ enum class eObjectWorldState : uint32_t; enum class eTerminateType : uint32_t; enum class eControlScheme : uint32_t; enum class eStateChangeType : uint32_t; +enum class ePetAbilityType : uint32_t; enum class ePetTamingNotifyType : uint32_t; enum class eUseItemResponse : uint32_t; enum class eQuickBuildFailReason : uint32_t; @@ -91,6 +92,9 @@ namespace GameMessages { void SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, eLootSourceType sourceType); void SendUIMessageServerToSingleClient(Entity* entity, const SystemAddress& sysAddr, const std::string& message, AMFBaseValue& args); + + // Specify sysAddr if you need to send a flash message to a client who you dont know the objectID of. + void SendUIMessageServerToSingleClient(const std::string& message, AMFBaseValue& args, const SystemAddress& sysAddr); void SendUIMessageServerToAllClients(const std::string& message, AMFBaseValue& args); void SendPlayEmbeddedEffectOnAllClientsNearObject(Entity* entity, std::u16string effectName, const LWOOBJID& fromObjectID, float radius); @@ -386,7 +390,7 @@ namespace GameMessages { void SendClientExitTamingMinigame(LWOOBJID objectId, bool bVoluntaryExit, const SystemAddress& sysAddr); - void SendShowPetActionButton(LWOOBJID objectId, int32_t buttonLabel, bool bShow, const SystemAddress& sysAddr); + void SendShowPetActionButton(const LWOOBJID objectId, const ePetAbilityType petAbility, const bool bShow, const SystemAddress& sysAddr); void SendPlayEmote(LWOOBJID objectId, int32_t emoteID, LWOOBJID target, const SystemAddress& sysAddr); diff --git a/dGame/dInventory/CMakeLists.txt b/dGame/dInventory/CMakeLists.txt index c31931b6..b45b27bf 100644 --- a/dGame/dInventory/CMakeLists.txt +++ b/dGame/dInventory/CMakeLists.txt @@ -25,7 +25,11 @@ target_include_directories(dInventory PUBLIC "." "${PROJECT_SOURCE_DIR}/dZoneManager" # via Item.cpp ) target_precompile_headers(dInventory REUSE_FROM dGameBase) - +# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible. +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97185 +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_source_files_properties("Item.cpp" PROPERTIES COMPILE_FLAGS "-Wno-stringop-overflow") +endif() # INTERFACE link w/o dependency #set_property(TARGET dInventory APPEND PROPERTY INTERFACE_LINK_LIBRARIES # dNet dDatabaseCDClient diff --git a/dGame/dUtilities/CheatDetection.cpp b/dGame/dUtilities/CheatDetection.cpp index a43cdaeb..bc50b2cc 100644 --- a/dGame/dUtilities/CheatDetection.cpp +++ b/dGame/dUtilities/CheatDetection.cpp @@ -10,6 +10,7 @@ #include "UserManager.h" #include "dConfig.h" #include +#include "PlayerManager.h" Entity* GetPossessedEntity(const LWOOBJID& objId) { auto* entity = Game::entityManager->GetEntity(objId); @@ -49,7 +50,7 @@ void LogAndSaveFailedAntiCheatCheck(const LWOOBJID& id, const SystemAddress& sys User* toReport = nullptr; switch (checkType) { case CheckType::Entity: { - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); auto* entity = GetPossessedEntity(id); // If player exists and entity exists in world, use both for logging info. diff --git a/dGame/dUtilities/Mail.cpp b/dGame/dUtilities/Mail.cpp index 2677c9cc..fc0c833e 100644 --- a/dGame/dUtilities/Mail.cpp +++ b/dGame/dUtilities/Mail.cpp @@ -12,7 +12,6 @@ #include "dServer.h" #include "Entity.h" #include "Character.h" -#include "PacketUtils.h" #include "BitStreamUtils.h" #include "Logger.h" #include "EntityManager.h" @@ -305,7 +304,6 @@ void Mail::HandleDataRequest(RakNet::BitStream* packet, const SystemAddress& sys } Game::server->Send(&bitStream, sysAddr, false); - // PacketUtils::SavePacket("Max_Mail_Data.bin", (const char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); } void Mail::HandleAttachmentCollect(RakNet::BitStream* packet, const SystemAddress& sysAddr, Entity* player) { diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 104b1232..35c5f7a9 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -82,11 +82,13 @@ #include "eConnectionType.h" #include "eChatInternalMessageType.h" #include "eMasterMessageType.h" +#include "PlayerManager.h" #include "CDRewardCodesTable.h" #include "CDObjectsTable.h" #include "CDZoneTableTable.h" #include "ePlayerFlag.h" +#include "dNavMesh.h" void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr) { auto commandCopy = command; @@ -214,10 +216,10 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (chatCommand == "who") { ChatPackets::SendSystemMessage( sysAddr, - u"Players in this instance: (" + GeneralUtils::to_u16string(Player::GetAllPlayers().size()) + u")" + u"Players in this instance: (" + GeneralUtils::to_u16string(PlayerManager::GetAllPlayers().size()) + u")" ); - for (auto* player : Player::GetAllPlayers()) { + for (auto* player : PlayerManager::GetAllPlayers()) { const auto& name = player->GetCharacter()->GetName(); ChatPackets::SendSystemMessage( @@ -347,17 +349,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()); @@ -387,6 +378,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); @@ -472,7 +475,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (chatCommand == "kill" && args.size() == 1 && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { ChatPackets::SendSystemMessage(sysAddr, u"Brutally murdering that player, if online on this server."); - auto* player = Player::GetPlayer(args[0]); + auto* player = PlayerManager::GetPlayer(args[0]); if (player) { player->Smash(entity->GetObjectID()); ChatPackets::SendSystemMessage(sysAddr, u"It has been done, do you feel good about yourself now?"); @@ -703,33 +706,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; - } - if (chatCommand == "playeffect" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 3) { int32_t effectID = 0; @@ -793,7 +769,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit auto control = static_cast(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); if (!control) return; - float y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(control->GetPosition()); + float y = dpWorld::GetNavMesh()->GetHeightAtPoint(control->GetPosition()); std::u16string msg = u"Navmesh height: " + (GeneralUtils::to_u16string(y)); ChatPackets::SendSystemMessage(sysAddr, msg); } @@ -1019,7 +995,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (chatCommand == "mute" && entity->GetGMLevel() >= eGameMasterLevel::JUNIOR_DEVELOPER) { if (args.size() >= 1) { - auto* player = Player::GetPlayer(args[0]); + auto* player = PlayerManager::GetPlayer(args[0]); uint32_t accountId = 0; LWOOBJID characterId = 0; @@ -1098,7 +1074,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (chatCommand == "kick" && entity->GetGMLevel() >= eGameMasterLevel::JUNIOR_MODERATOR) { if (args.size() == 1) { - auto* player = Player::GetPlayer(args[0]); + auto* player = PlayerManager::GetPlayer(args[0]); std::u16string username = GeneralUtils::UTF8ToUTF16(args[0]); if (player == nullptr) { @@ -1116,7 +1092,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (chatCommand == "ban" && entity->GetGMLevel() >= eGameMasterLevel::SENIOR_MODERATOR) { if (args.size() == 1) { - auto* player = Player::GetPlayer(args[0]); + auto* player = PlayerManager::GetPlayer(args[0]); uint32_t accountId = 0; @@ -1329,7 +1305,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (args.size() > 1) { requestedPlayerToSetLevelOf = args[1]; - auto requestedPlayer = Player::GetPlayer(requestedPlayerToSetLevelOf); + auto requestedPlayer = PlayerManager::GetPlayer(requestedPlayerToSetLevelOf); if (!requestedPlayer) { ChatPackets::SendSystemMessage(sysAddr, u"No player found with username: (" + GeneralUtils::UTF8ToUTF16(requestedPlayerToSetLevelOf) + u")."); @@ -1762,7 +1738,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit if (chatCommand == "reloadconfig" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { Game::config->ReloadConfig(); VanityUtilities::SpawnVanity(); - dpWorld::Instance().Reload(); + dpWorld::Reload(); auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); for (auto entity : entities) { auto* scriptedActivityComponent = entity->GetComponent(); diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index e5997b51..f15bb9a9 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -36,10 +36,10 @@ #include "InstanceManager.h" #include "MasterPackets.h" #include "PersistentIDManager.h" -#include "PacketUtils.h" #include "FdbToSqlite.h" #include "BitStreamUtils.h" #include "Start.h" +#include "Server.h" namespace Game { Logger* logger = nullptr; @@ -55,7 +55,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 +76,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 +88,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 +393,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"); @@ -518,17 +503,17 @@ void HandlePacket(Packet* packet) { uint32_t theirZoneID = 0; uint32_t theirInstanceID = 0; ServerType theirServerType; - std::string theirIP = ""; + LUString theirIP; inStream.Read(theirPort); inStream.Read(theirZoneID); inStream.Read(theirInstanceID); inStream.Read(theirServerType); - theirIP = PacketUtils::ReadString(24, packet, false); //24 is the current offset + inStream.Read(theirIP); if (theirServerType == ServerType::World) { if (!Game::im->IsPortInUse(theirPort)) { - Instance* in = new Instance(theirIP, theirPort, theirZoneID, theirInstanceID, 0, 12, 12); + Instance* in = new Instance(theirIP.string, theirPort, theirZoneID, theirInstanceID, 0, 12, 12); SystemAddress copy; copy.binaryAddress = packet->systemAddress.binaryAddress; @@ -567,47 +552,42 @@ void HandlePacket(Packet* packet) { } case eMasterMessageType::SET_SESSION_KEY: { - RakNet::BitStream inStream(packet->data, packet->length, false); - uint64_t header = inStream.Read(header); + CINSTREAM_SKIP_HEADER; uint32_t sessionKey = 0; - std::string username; - inStream.Read(sessionKey); - username = PacketUtils::ReadString(12, packet, false); - + LUString username; + inStream.Read(username); + for (auto it : activeSessions) { - if (it.second == username) { + if (it.second == username.string) { activeSessions.erase(it.first); CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::NEW_SESSION_ALERT); bitStream.Write(sessionKey); - bitStream.Write(username.size()); - for (auto character : username) { - bitStream.Write(character); - } + bitStream.Write(username); SEND_PACKET_BROADCAST; break; } } - activeSessions.insert(std::make_pair(sessionKey, username)); - LOG("Got sessionKey %i for user %s", sessionKey, username.c_str()); + activeSessions.insert(std::make_pair(sessionKey, username.string)); + LOG("Got sessionKey %i for user %s", sessionKey, username.string.c_str()); break; } case eMasterMessageType::REQUEST_SESSION_KEY: { - RakNet::BitStream inStream(packet->data, packet->length, false); - uint64_t header = inStream.Read(header); - std::string username = PacketUtils::ReadString(8, packet, false); - + CINSTREAM_SKIP_HEADER; + LUWString username; + inStream.Read(username); + LOG("Requesting session key for %s", username.GetAsString().c_str()); for (auto key : activeSessions) { - if (key.second == username) { + if (key.second == username.GetAsString()) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SESSION_KEY_RESPONSE); bitStream.Write(key.first); - bitStream.Write(LUString(key.second, 64)); + bitStream.Write(username); Game::server->Send(&bitStream, packet->systemAddress, false); break; } diff --git a/dNavigation/dTerrain/RawFile.cpp b/dNavigation/dTerrain/RawFile.cpp index dfad9ca4..efc2b39d 100644 --- a/dNavigation/dTerrain/RawFile.cpp +++ b/dNavigation/dTerrain/RawFile.cpp @@ -20,7 +20,7 @@ RawFile::RawFile(std::string fileName) { if (m_Version < 0x20) { - return; // Version too crusty to handle + return; // Version is too old to be supported } // Read in chunks diff --git a/dNet/AuthPackets.cpp b/dNet/AuthPackets.cpp index 7feb0cc3..2597c576 100644 --- a/dNet/AuthPackets.cpp +++ b/dNet/AuthPackets.cpp @@ -1,5 +1,4 @@ #include "AuthPackets.h" -#include "PacketUtils.h" #include "BitStreamUtils.h" #include "dNetCommon.h" @@ -24,11 +23,17 @@ #include "eServerMessageType.h" #include "eMasterMessageType.h" #include "eGameMasterLevel.h" - +#include "StringifiedEnum.h" namespace { std::vector claimCodes; } +void Stamp::Serialize(RakNet::BitStream* outBitStream){ + outBitStream->Write(type); + outBitStream->Write(value); + outBitStream->Write(timestamp); +}; + void AuthPackets::LoadClaimCodes() { if(!claimCodes.empty()) return; auto rcstring = Game::config->GetValue("rewardcodes"); @@ -42,12 +47,26 @@ void AuthPackets::LoadClaimCodes() { } void AuthPackets::HandleHandshake(dServer* server, Packet* packet) { - RakNet::BitStream inStream(packet->data, packet->length, false); - uint64_t header = inStream.Read(header); + CINSTREAM_SKIP_HEADER uint32_t clientVersion = 0; inStream.Read(clientVersion); + inStream.IgnoreBytes(4); + + ServiceId serviceId; + inStream.Read(serviceId); + if (serviceId != ServiceId::Client) LOG("WARNING: Service ID is not a Client!"); + + uint32_t processID; + inStream.Read(processID); + + uint16_t port; + inStream.Read(port); + if (port != packet->systemAddress.port) LOG("WARNING: Port written in packet does not match the port the client is connecting over!"); + + inStream.IgnoreBytes(33); + + LOG_DEBUG("Client Data [Version: %i, Service: %s, Process: %u, Port: %u, Sysaddr Port: %u]", clientVersion, StringifiedEnum::ToString(serviceId).data(), processID, port, packet->systemAddress.port); - LOG("Received client version: %i", clientVersion); SendHandshake(server, packet->systemAddress, server->GetIP(), server->GetPort(), server->GetServerType()); } @@ -60,42 +79,93 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c if (!clientNetVersionString.empty()) GeneralUtils::TryParse(clientNetVersionString, clientNetVersion); bitStream.Write(clientNetVersion); - bitStream.Write(0x93); + bitStream.Write(861228100); - if (serverType == ServerType::Auth) bitStream.Write(uint32_t(1)); //Conn: auth - else bitStream.Write(4); //Conn: world - - bitStream.Write(0); //Server process ID - bitStream.Write(nextServerPort); + if (serverType == ServerType::Auth) bitStream.Write(ServiceId::Auth); + else if (serverType == ServerType::World) bitStream.Write(ServiceId::World); + else bitStream.Write(ServiceId::General); + bitStream.Write(774909490); server->Send(&bitStream, sysAddr, false); } void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) { - std::string username = PacketUtils::ReadString(8, packet, true); - std::string password = PacketUtils::ReadString(0x4A, packet, true); - const char* szUsername = username.c_str(); + CINSTREAM_SKIP_HEADER; + + std::vector stamps; + stamps.emplace_back(eStamps::PASSPORT_AUTH_START, 0); + + LUWString usernameLUString; + inStream.Read(usernameLUString); + const auto username = usernameLUString.GetAsString(); + + LUWString password(41); + inStream.Read(password); + + LanguageCodeID locale_id; + inStream.Read(locale_id); + LOG_DEBUG("Locale ID: %s", StringifiedEnum::ToString(locale_id).data()); + + ClientOS clientOS; + inStream.Read(clientOS); + LOG_DEBUG("Operating System: %s", StringifiedEnum::ToString(clientOS).data()); + stamps.emplace_back(eStamps::PASSPORT_AUTH_CLIENT_OS, 0); + + LUWString memoryStats(256); + inStream.Read(memoryStats); + LOG_DEBUG("Memory Stats [%s]", memoryStats.GetAsString().c_str()); + + LUWString videoCard(128); + inStream.Read(videoCard); + LOG_DEBUG("VideoCard Info: [%s]", videoCard.GetAsString().c_str()); + + // Processor/CPU info + uint32_t numOfProcessors; + inStream.Read(numOfProcessors); + uint32_t processorType; + inStream.Read(processorType); + uint16_t processorLevel; + inStream.Read(processorLevel); + uint16_t processorRevision; + inStream.Read(processorRevision); + LOG_DEBUG("CPU Info: [#Processors: %i, Processor Type: %i, Processor Level: %i, Processor Revision: %i]", numOfProcessors, processorType, processorLevel, processorRevision); + + // OS Info + uint32_t osVersionInfoSize; + inStream.Read(osVersionInfoSize); + uint32_t majorVersion; + inStream.Read(majorVersion); + uint32_t minorVersion; + inStream.Read(minorVersion); + uint32_t buildNumber; + inStream.Read(buildNumber); + uint32_t platformID; + inStream.Read(platformID); + LOG_DEBUG("OS Info: [Size: %i, Major: %i, Minor %i, Buid#: %i, platformID: %i]", osVersionInfoSize, majorVersion, minorVersion, buildNumber, platformID); // Fetch account details auto accountInfo = Database::Get()->GetAccountInfo(username); if (!accountInfo) { LOG("No user by name %s found!", username.c_str()); - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username); + stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::INVALID_USER, "", "", 2001, username, stamps); return; } //If we aren't running in live mode, then only GMs are allowed to enter: const auto& closedToNonDevs = Game::config->GetValue("closed_to_non_devs"); if (closedToNonDevs.size() > 0 && bool(std::stoi(closedToNonDevs)) && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username); + stamps.emplace_back(eStamps::GM_REQUIRED, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "The server is currently only open to developers.", "", 2001, username, stamps); return; } if (Game::config->GetValue("dont_use_keys") != "1" && accountInfo->maxGmLevel == eGameMasterLevel::CIVILIAN) { //Check to see if we have a play key: if (accountInfo->playKeyId == 0) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username); + stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a play key associated with it!", "", 2001, username, stamps); LOG("User %s tried to log in, but they don't have a play key.", username.c_str()); return; } @@ -104,40 +174,50 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) { auto playKeyStatus = Database::Get()->IsPlaykeyActive(accountInfo->playKeyId); if (!playKeyStatus) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a valid play key associated with it!", "", 2001, username); + stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your account doesn't have a valid play key associated with it!", "", 2001, username, stamps); return; } if (!playKeyStatus.value()) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username); + stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, "Your play key has been disabled.", "", 2001, username, stamps); LOG("User %s tried to log in, but their play key was disabled", username.c_str()); return; } + } else if (Game::config->GetValue("dont_use_keys") == "1" || accountInfo->maxGmLevel > eGameMasterLevel::CIVILIAN){ + stamps.emplace_back(eStamps::PASSPORT_AUTH_BYPASS, 1); } if (accountInfo->banned) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username); return; + stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::BANNED, "", "", 2001, username, stamps); + return; } if (accountInfo->locked) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username); return; + stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::ACCOUNT_LOCKED, "", "", 2001, username, stamps); + return; } - bool loginSuccess = ::bcrypt_checkpw(password.c_str(), accountInfo->bcryptPassword.c_str()) == 0; + bool loginSuccess = ::bcrypt_checkpw(password.GetAsString().c_str(), accountInfo->bcryptPassword.c_str()) == 0; if (!loginSuccess) { - AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username); + stamps.emplace_back(eStamps::PASSPORT_AUTH_ERROR, 1); + AuthPackets::SendLoginResponse(server, packet->systemAddress, eLoginResponse::WRONG_PASS, "", "", 2001, username, stamps); LOG("Wrong password used"); } else { SystemAddress system = packet->systemAddress; //Copy the sysAddr before the Packet gets destroyed from main if (!server->GetIsConnectedToMaster()) { - AuthPackets::SendLoginResponse(server, system, eLoginResponse::GENERAL_FAILED, "", "", 0, username); + stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_DISCONNECT, 1); + AuthPackets::SendLoginResponse(server, system, eLoginResponse::GENERAL_FAILED, "", "", 0, username, stamps); return; } - - ZoneInstanceManager::Instance()->RequestZoneTransfer(server, 0, 0, false, [system, server, username](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string zoneIP, uint16_t zonePort) { - AuthPackets::SendLoginResponse(server, system, eLoginResponse::SUCCESS, "", zoneIP, zonePort, username); + stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH, 1); + ZoneInstanceManager::Instance()->RequestZoneTransfer(server, 0, 0, false, [system, server, username, stamps](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string zoneIP, uint16_t zonePort) mutable { + AuthPackets::SendLoginResponse(server, system, eLoginResponse::SUCCESS, "", zoneIP, zonePort, username, stamps); }); } @@ -146,21 +226,22 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) { } } -void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username) { - RakNet::BitStream packet; - BitStreamUtils::WriteHeader(packet, eConnectionType::CLIENT, eClientMessageType::LOGIN_RESPONSE); +void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username, std::vector& stamps) { + stamps.emplace_back(eStamps::PASSPORT_AUTH_IM_LOGIN_START, 1); + RakNet::BitStream loginResponse; + BitStreamUtils::WriteHeader(loginResponse, eConnectionType::CLIENT, eClientMessageType::LOGIN_RESPONSE); - packet.Write(GeneralUtils::CastUnderlyingType(responseCode)); + loginResponse.Write(GeneralUtils::CastUnderlyingType(responseCode)); // Event Gating - packet.Write(LUString(Game::config->GetValue("event_1"))); - packet.Write(LUString(Game::config->GetValue("event_2"))); - packet.Write(LUString(Game::config->GetValue("event_3"))); - packet.Write(LUString(Game::config->GetValue("event_4"))); - packet.Write(LUString(Game::config->GetValue("event_5"))); - packet.Write(LUString(Game::config->GetValue("event_6"))); - packet.Write(LUString(Game::config->GetValue("event_7"))); - packet.Write(LUString(Game::config->GetValue("event_8"))); + loginResponse.Write(LUString(Game::config->GetValue("event_1"))); + loginResponse.Write(LUString(Game::config->GetValue("event_2"))); + loginResponse.Write(LUString(Game::config->GetValue("event_3"))); + loginResponse.Write(LUString(Game::config->GetValue("event_4"))); + loginResponse.Write(LUString(Game::config->GetValue("event_5"))); + loginResponse.Write(LUString(Game::config->GetValue("event_6"))); + loginResponse.Write(LUString(Game::config->GetValue("event_7"))); + loginResponse.Write(LUString(Game::config->GetValue("event_8"))); uint16_t version_major = 1; uint16_t version_current = 10; @@ -169,60 +250,58 @@ void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAdd GeneralUtils::TryParse(Game::config->GetValue("version_current"), version_current); GeneralUtils::TryParse(Game::config->GetValue("version_minor"), version_minor); - packet.Write(version_major); - packet.Write(version_current); - packet.Write(version_minor); + loginResponse.Write(version_major); + loginResponse.Write(version_current); + loginResponse.Write(version_minor); // Writes the user key uint32_t sessionKey = GeneralUtils::GenerateRandomNumber(); std::string userHash = std::to_string(sessionKey); userHash = md5(userHash); - packet.Write(LUWString(userHash)); + loginResponse.Write(LUWString(userHash)); - // Write the Character and Chat IPs - packet.Write(LUString(wServerIP)); - packet.Write(LUString("")); + // World Server IP + loginResponse.Write(LUString(wServerIP)); + // Chat Server IP (unused) + loginResponse.Write(LUString("")); - // Write the Character and Chat Ports - packet.Write(wServerPort); - packet.Write(0); + // World Server Redirect port + loginResponse.Write(wServerPort); + // Char Server Redirect port (unused) + loginResponse.Write(static_cast(0)); // CDN Key - packet.Write(LUString("")); + loginResponse.Write(LUString("")); // CDN Ticket - packet.Write(LUString("00000000-0000-0000-0000-000000000000", 37)); + loginResponse.Write(LUString("00000000-0000-0000-0000-000000000000", 37)); - packet.Write(0); // Language + // Language + loginResponse.Write(Language::en_US); // Write the localization - packet.Write(LUString("US", 3)); + loginResponse.Write(LUString("US", 3)); - packet.Write(false); // Just upgraded from F2P - packet.Write(false); // User is F2P - packet.Write(0); // Time Remaining in F2P + loginResponse.Write(false); // Just upgraded from F2P + loginResponse.Write(false); // User is F2P + loginResponse.Write(0); // Time Remaining in F2P // Write custom error message - packet.Write(errorMsg.length()); - packet.Write(LUWString(errorMsg, static_cast(errorMsg.length()))); + loginResponse.Write(errorMsg.length()); + loginResponse.Write(LUWString(errorMsg, static_cast(errorMsg.length()))); - // Here write auth logs - packet.Write(20); - for (uint32_t i = 0; i < 20; ++i) { - packet.Write(8); - packet.Write(44); - packet.Write(14000); - packet.Write(0); - } + stamps.emplace_back(eStamps::PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH, 1); - server->Send(&packet, sysAddr, false); + loginResponse.Write((sizeof(Stamp) * stamps.size()) + sizeof(uint32_t)); + for (auto& stamp : stamps) stamp.Serialize(&loginResponse); + server->Send(&loginResponse, sysAddr, false); //Inform the master server that we've created a session for this user: if (responseCode == eLoginResponse::SUCCESS) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SET_SESSION_KEY); bitStream.Write(sessionKey); - bitStream.Write(LUString(username, 66)); + bitStream.Write(LUString(username)); server->SendToMaster(&bitStream); LOG("Set sessionKey: %i for user %s", sessionKey, username.c_str()); diff --git a/dNet/AuthPackets.h b/dNet/AuthPackets.h index eb275a46..539bae75 100644 --- a/dNet/AuthPackets.h +++ b/dNet/AuthPackets.h @@ -4,17 +4,99 @@ #define _VARIADIC_MAX 10 #include "dCommonVars.h" #include "dNetCommon.h" +#include "magic_enum.hpp" enum class ServerType : uint32_t; enum class eLoginResponse : uint8_t; class dServer; +enum class eStamps : uint32_t { + PASSPORT_AUTH_START, + PASSPORT_AUTH_BYPASS, + PASSPORT_AUTH_ERROR, + PASSPORT_AUTH_DB_SELECT_START, + PASSPORT_AUTH_DB_SELECT_FINISH, + PASSPORT_AUTH_DB_INSERT_START, + PASSPORT_AUTH_DB_INSERT_FINISH, + PASSPORT_AUTH_LEGOINT_COMMUNICATION_START, + PASSPORT_AUTH_LEGOINT_RECEIVED, + PASSPORT_AUTH_LEGOINT_THREAD_SPAWN, + PASSPORT_AUTH_LEGOINT_WEBSERVICE_START, + PASSPORT_AUTH_LEGOINT_WEBSERVICE_FINISH, + PASSPORT_AUTH_LEGOINT_LEGOCLUB_START, + PASSPORT_AUTH_LEGOINT_LEGOCLUB_FINISH, + PASSPORT_AUTH_LEGOINT_THREAD_FINISH, + PASSPORT_AUTH_LEGOINT_REPLY, + PASSPORT_AUTH_LEGOINT_ERROR, + PASSPORT_AUTH_LEGOINT_COMMUNICATION_END, + PASSPORT_AUTH_LEGOINT_DISCONNECT, + PASSPORT_AUTH_WORLD_COMMUNICATION_START, + PASSPORT_AUTH_CLIENT_OS, + PASSPORT_AUTH_WORLD_PACKET_RECEIVED, + PASSPORT_AUTH_IM_COMMUNICATION_START, + PASSPORT_AUTH_IM_LOGIN_START, + PASSPORT_AUTH_IM_LOGIN_ALREADY_LOGGED_IN, + PASSPORT_AUTH_IM_OTHER_LOGIN_REMOVED, + PASSPORT_AUTH_IM_LOGIN_QUEUED, + PASSPORT_AUTH_IM_LOGIN_RESPONSE, + PASSPORT_AUTH_IM_COMMUNICATION_END, + PASSPORT_AUTH_WORLD_SESSION_CONFIRM_TO_AUTH, + PASSPORT_AUTH_WORLD_COMMUNICATION_FINISH, + PASSPORT_AUTH_WORLD_DISCONNECT, + NO_LEGO_INTERFACE, + DB_ERROR, + GM_REQUIRED, + NO_LEGO_WEBSERVICE_XML, + LEGO_WEBSERVICE_TIMEOUT, + LEGO_WEBSERVICE_ERROR, + NO_WORLD_SERVER +}; + +struct Stamp { + eStamps type; + uint32_t value; + uint64_t timestamp; + + Stamp(eStamps type, uint32_t value, uint64_t timestamp = time(nullptr)){ + this->type = type; + this->value = value; + this->timestamp = timestamp; + } + + void Serialize(RakNet::BitStream* outBitStream); +}; + +enum class ClientOS : uint8_t { + UNKNOWN, + WINDOWS, + MACOS +}; + +enum class LanguageCodeID : uint16_t { + de_DE = 0x0407, + en_US = 0x0409, + en_GB = 0x0809 +}; + +template <> +struct magic_enum::customize::enum_range { + static constexpr int min = 1031; + static constexpr int max = 2057; +}; + +enum class Language : uint32_t { + en_US, + pl_US, + de_DE, + en_GB, +}; + namespace AuthPackets { void HandleHandshake(dServer* server, Packet* packet); void SendHandshake(dServer* server, const SystemAddress& sysAddr, const std::string& nextServerIP, uint16_t nextServerPort, const ServerType serverType); void HandleLoginRequest(dServer* server, Packet* packet); - void SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username); + void SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username, std::vector& stamps); void LoadClaimCodes(); } diff --git a/dNet/BitStreamUtils.h b/dNet/BitStreamUtils.h index 7403d0e1..1322ec95 100644 --- a/dNet/BitStreamUtils.h +++ b/dNet/BitStreamUtils.h @@ -12,7 +12,7 @@ struct LUString { std::string string; uint32_t size; - LUString(uint32_t size) { + LUString(uint32_t size = 33) { this->size = size; }; LUString(std::string string, uint32_t size = 33) { @@ -28,7 +28,7 @@ struct LUWString { std::u16string string; uint32_t size; - LUWString(uint32_t size) { + LUWString(uint32_t size = 33) { this->size = size; }; LUWString(std::u16string string, uint32_t size = 33) { diff --git a/dNet/ChatPackets.cpp b/dNet/ChatPackets.cpp index 63eff5ef..d0354659 100644 --- a/dNet/ChatPackets.cpp +++ b/dNet/ChatPackets.cpp @@ -7,7 +7,6 @@ #include "RakNetTypes.h" #include "BitStream.h" #include "Game.h" -#include "PacketUtils.h" #include "BitStreamUtils.h" #include "dServer.h" #include "eConnectionType.h" diff --git a/dNet/ClientPackets.cpp b/dNet/ClientPackets.cpp index 47513a66..a6b9f8c6 100644 --- a/dNet/ClientPackets.cpp +++ b/dNet/ClientPackets.cpp @@ -4,422 +4,120 @@ */ #include "ClientPackets.h" -#include "UserManager.h" -#include "User.h" -#include "Character.h" -#include "EntityManager.h" -#include "Entity.h" -#include "ControllablePhysicsComponent.h" -#include "Game.h" -#include "Logger.h" -#include "WorldPackets.h" -#include "NiPoint3.h" -#include "NiQuaternion.h" #include "dCommonVars.h" -#include "BitStream.h" -#include "dChatFilter.h" -#include "WorldPackets.h" -#include "ChatPackets.h" -#include "dServer.h" -#include "GameMessages.h" -#include "dZoneManager.h" -#include "Player.h" -#include "Zone.h" -#include "PossessorComponent.h" -#include "PossessableComponent.h" -#include "HavokVehiclePhysicsComponent.h" -#include "dConfig.h" -#include "CharacterComponent.h" -#include "Database.h" -#include "eGameMasterLevel.h" -#include "eReplicaComponentType.h" -#include "CheatDetection.h" -#include "Amf3.h" - -void ClientPackets::HandleChatMessage(const SystemAddress& sysAddr, Packet* packet) { - User* user = UserManager::Instance()->GetUser(sysAddr); - if (!user) { - LOG("Unable to get user to parse chat message"); - return; - } - - if (user->GetIsMuted()) { - user->GetLastUsedChar()->SendMuteNotice(); - return; - } +#include "PositionUpdate.h" +ChatMessage ClientPackets::HandleChatMessage(Packet* packet) { CINSTREAM_SKIP_HEADER; - char chatChannel; - uint16_t unknown; + ChatMessage message; uint32_t messageLength; - std::u16string message; - inStream.Read(chatChannel); - inStream.Read(unknown); + inStream.Read(message.chatChannel); + inStream.Read(message.unknown); inStream.Read(messageLength); for (uint32_t i = 0; i < (messageLength - 1); ++i) { uint16_t character; inStream.Read(character); - message.push_back(character); + message.message.push_back(character); } - std::string playerName = user->GetLastUsedChar()->GetName(); - bool isMythran = user->GetLastUsedChar()->GetGMLevel() > eGameMasterLevel::CIVILIAN; - bool isOk = Game::chatFilter->IsSentenceOkay(GeneralUtils::UTF16ToWTF8(message), user->GetLastUsedChar()->GetGMLevel()).empty(); - LOG_DEBUG("Msg: %s was approved previously? %i", GeneralUtils::UTF16ToWTF8(message).c_str(), user->GetLastChatMessageApproved()); - if (!isOk) { - // Add a limit to the string converted by general utils because it is a user received string and may be a bad actor. - CheatDetection::ReportCheat( - user, - sysAddr, - "Player %s attempted to bypass chat filter with message: %s", - playerName.c_str(), - GeneralUtils::UTF16ToWTF8(message, 512).c_str()); - } - if (!isOk && !isMythran) return; - - std::string sMessage = GeneralUtils::UTF16ToWTF8(message); - LOG("%s: %s", playerName.c_str(), sMessage.c_str()); - ChatPackets::SendChatMessage(sysAddr, chatChannel, playerName, user->GetLoggedInChar(), isMythran, message); + return message; } -void ClientPackets::HandleClientPositionUpdate(const SystemAddress& sysAddr, Packet* packet) { - User* user = UserManager::Instance()->GetUser(sysAddr); - if (!user) { - LOG("Unable to get user to parse position update"); - return; - } - +PositionUpdate ClientPackets::HandleClientPositionUpdate(Packet* packet) { + PositionUpdate update; CINSTREAM_SKIP_HEADER; - Entity* entity = Game::entityManager->GetEntity(user->GetLastUsedChar()->GetObjectID()); - if (!entity) return; + inStream.Read(update.position.x); + inStream.Read(update.position.y); + inStream.Read(update.position.z); - ControllablePhysicsComponent* comp = static_cast(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); - if (!comp) return; + inStream.Read(update.rotation.x); + inStream.Read(update.rotation.y); + inStream.Read(update.rotation.z); + inStream.Read(update.rotation.w); - /* - //If we didn't move, this will match and stop our velocity - if (packet->length == 37) { - NiPoint3 zeroVel(0.0f, 0.0f, 0.0f); - comp->SetVelocity(zeroVel); - comp->SetAngularVelocity(zeroVel); - comp->SetIsOnGround(true); //probably8 - Game::entityManager->SerializeEntity(entity); - return; - } - */ - - auto* possessorComponent = entity->GetComponent(); - - NiPoint3 position; - inStream.Read(position.x); - inStream.Read(position.y); - inStream.Read(position.z); - - NiQuaternion rotation; - inStream.Read(rotation.x); - inStream.Read(rotation.y); - inStream.Read(rotation.z); - inStream.Read(rotation.w); - - bool onGround = false; - bool onRail = false; - inStream.Read(onGround); - inStream.Read(onRail); + inStream.Read(update.onGround); + inStream.Read(update.onRail); bool velocityFlag = false; inStream.Read(velocityFlag); - NiPoint3 velocity{}; if (velocityFlag) { - inStream.Read(velocity.x); - inStream.Read(velocity.y); - inStream.Read(velocity.z); + inStream.Read(update.velocity.x); + inStream.Read(update.velocity.y); + inStream.Read(update.velocity.z); } bool angVelocityFlag = false; inStream.Read(angVelocityFlag); - NiPoint3 angVelocity{}; if (angVelocityFlag) { - inStream.Read(angVelocity.x); - inStream.Read(angVelocity.y); - inStream.Read(angVelocity.z); + inStream.Read(update.angularVelocity.x); + inStream.Read(update.angularVelocity.y); + inStream.Read(update.angularVelocity.z); } // TODO figure out how to use these. Ignoring for now, but reading in if they exist. bool hasLocalSpaceInfo{}; - LWOOBJID objectId{}; - NiPoint3 localSpacePosition{}; - bool hasLinearVelocity{}; - NiPoint3 linearVelocity{}; if (inStream.Read(hasLocalSpaceInfo) && hasLocalSpaceInfo) { - inStream.Read(objectId); - inStream.Read(localSpacePosition.x); - inStream.Read(localSpacePosition.y); - inStream.Read(localSpacePosition.z); + inStream.Read(update.localSpaceInfo.objectId); + inStream.Read(update.localSpaceInfo.position.x); + inStream.Read(update.localSpaceInfo.position.y); + inStream.Read(update.localSpaceInfo.position.z); + bool hasLinearVelocity = false; if (inStream.Read(hasLinearVelocity) && hasLinearVelocity) { - inStream.Read(linearVelocity.x); - inStream.Read(linearVelocity.y); - inStream.Read(linearVelocity.z); + inStream.Read(update.localSpaceInfo.linearVelocity.x); + inStream.Read(update.localSpaceInfo.linearVelocity.y); + inStream.Read(update.localSpaceInfo.linearVelocity.z); } } + bool hasRemoteInputInfo{}; - RemoteInputInfo remoteInput{}; - if (inStream.Read(hasRemoteInputInfo) && hasRemoteInputInfo) { - inStream.Read(remoteInput.m_RemoteInputX); - inStream.Read(remoteInput.m_RemoteInputY); - inStream.Read(remoteInput.m_IsPowersliding); - inStream.Read(remoteInput.m_IsModified); + inStream.Read(update.remoteInputInfo.m_RemoteInputX); + inStream.Read(update.remoteInputInfo.m_RemoteInputY); + inStream.Read(update.remoteInputInfo.m_IsPowersliding); + inStream.Read(update.remoteInputInfo.m_IsModified); } - bool updateChar = true; - - if (possessorComponent != nullptr) { - auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); - - if (possassableEntity != nullptr) { - auto* possessableComponent = possassableEntity->GetComponent(); - if (possessableComponent) { - // While possessing something, only update char if we are attached to the thing we are possessing - if (possessableComponent->GetPossessionType() != ePossessionType::ATTACHED_VISIBLE) updateChar = false; - } - - auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent(); - if (havokVehiclePhysicsComponent != nullptr) { - havokVehiclePhysicsComponent->SetPosition(position); - havokVehiclePhysicsComponent->SetRotation(rotation); - havokVehiclePhysicsComponent->SetIsOnGround(onGround); - havokVehiclePhysicsComponent->SetIsOnRail(onRail); - havokVehiclePhysicsComponent->SetVelocity(velocity); - havokVehiclePhysicsComponent->SetDirtyVelocity(velocityFlag); - havokVehiclePhysicsComponent->SetAngularVelocity(angVelocity); - havokVehiclePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); - havokVehiclePhysicsComponent->SetRemoteInputInfo(remoteInput); - } else { - // Need to get the mount's controllable physics - auto* controllablePhysicsComponent = possassableEntity->GetComponent(); - if (!controllablePhysicsComponent) return; - controllablePhysicsComponent->SetPosition(position); - controllablePhysicsComponent->SetRotation(rotation); - controllablePhysicsComponent->SetIsOnGround(onGround); - controllablePhysicsComponent->SetIsOnRail(onRail); - controllablePhysicsComponent->SetVelocity(velocity); - controllablePhysicsComponent->SetDirtyVelocity(velocityFlag); - controllablePhysicsComponent->SetAngularVelocity(angVelocity); - controllablePhysicsComponent->SetDirtyAngularVelocity(angVelocityFlag); - } - Game::entityManager->SerializeEntity(possassableEntity); - } - } - - if (!updateChar) { - velocity = NiPoint3::ZERO; - angVelocity = NiPoint3::ZERO; - } - - - - // Handle statistics - auto* characterComponent = entity->GetComponent(); - if (characterComponent != nullptr) { - characterComponent->TrackPositionUpdate(position); - } - - comp->SetPosition(position); - comp->SetRotation(rotation); - comp->SetIsOnGround(onGround); - comp->SetIsOnRail(onRail); - comp->SetVelocity(velocity); - comp->SetDirtyVelocity(velocityFlag); - comp->SetAngularVelocity(angVelocity); - comp->SetDirtyAngularVelocity(angVelocityFlag); - - auto* player = static_cast(entity); - player->SetGhostReferencePoint(position); - Game::entityManager->QueueGhostUpdate(player->GetObjectID()); - - if (updateChar) Game::entityManager->SerializeEntity(entity); - - //TODO: add moving platform stuffs - /*bool movingPlatformFlag; - inStream.Read(movingPlatformFlag); - if (movingPlatformFlag) { - LWOOBJID objectID; - NiPoint3 niData2; - - inStream.Read(objectID); - inStream.Read(niData2.x); - inStream.Read(niData2.y); - inStream.Read(niData2.z); - - - - bool niData3Flag; - inStream.Read(niData3Flag); - if (niData3Flag) { - NiPoint3 niData3; - inStream.Read(niData3.x); - inStream.Read(niData3.y); - inStream.Read(niData3.z); - - controllablePhysics->GetLocationData()->GetMovingPlatformData()->SetData3(niData3); - } - }*/ - - /* - for (int i = 0; i < Game::server->GetReplicaManager()->GetParticipantCount(); ++i) - { - const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(i); - - if (entity->GetSystemAddress() == player) - { - continue; - } - - Game::entityManager->SerializeEntity(entity, player); - } - */ + return update; } -void ClientPackets::HandleChatModerationRequest(const SystemAddress& sysAddr, Packet* packet) { - User* user = UserManager::Instance()->GetUser(sysAddr); - if (!user) { - LOG("Unable to get user to parse chat moderation request"); - return; - } +ChatModerationRequest ClientPackets::HandleChatModerationRequest(Packet* packet) { + CINSTREAM_SKIP_HEADER; + + ChatModerationRequest request; - auto* entity = Player::GetPlayer(sysAddr); - - if (entity == nullptr) { - LOG("Unable to get player to parse chat moderation request"); - return; - } - - // Check if the player has restricted chat access - auto* character = entity->GetCharacter(); - - if (character->HasPermission(ePermissionMap::RestrictedChatAccess)) { - // Send a message to the player - ChatPackets::SendSystemMessage( - sysAddr, - u"This character has restricted chat access." - ); - - return; - } - - RakNet::BitStream stream(packet->data, packet->length, false); - - uint64_t header; - stream.Read(header); - - // Data - uint8_t chatLevel; - uint8_t requestID; - uint16_t messageLength; - - std::string receiver = ""; - std::string message = ""; - - stream.Read(chatLevel); - stream.Read(requestID); + inStream.Read(request.chatLevel); + inStream.Read(request.requestID); for (uint32_t i = 0; i < 42; ++i) { uint16_t character; - stream.Read(character); - receiver.push_back(static_cast(character)); + inStream.Read(character); + request.receiver.push_back(static_cast(character)); } - if (!receiver.empty()) { - if (std::string(receiver.c_str(), 4) == "[GM]") { // Shift the string forward if we are speaking to a GM as the client appends "[GM]" if they are - receiver = std::string(receiver.c_str() + 4, receiver.size() - 4); + if (!request.receiver.empty()) { + if (std::string(request.receiver.c_str(), 4) == "[GM]") { // Shift the string forward if we are speaking to a GM as the client appends "[GM]" if they are + request.receiver = std::string(request.receiver.c_str() + 4, request.receiver.size() - 4); } } - stream.Read(messageLength); + uint16_t messageLength; + inStream.Read(messageLength); for (uint32_t i = 0; i < messageLength; ++i) { uint16_t character; - stream.Read(character); - message.push_back(static_cast(character)); + inStream.Read(character); + request.message.push_back(static_cast(character)); } - bool isBestFriend = false; - - if (chatLevel == 1) { - // Private chat - LWOOBJID idOfReceiver = LWOOBJID_EMPTY; - - { - auto characterIdFetch = Database::Get()->GetCharacterInfo(receiver); - - if (characterIdFetch) { - idOfReceiver = characterIdFetch->id; - } - } - const auto& bffMap = user->GetIsBestFriendMap(); - if (bffMap.find(receiver) == bffMap.end() && idOfReceiver != LWOOBJID_EMPTY) { - auto bffInfo = Database::Get()->GetBestFriendStatus(entity->GetObjectID(), idOfReceiver); - - if (bffInfo) { - isBestFriend = bffInfo->bestFriendStatus == 3; - } - - if (isBestFriend) { - user->UpdateBestFriendValue(receiver, true); - } - } else if (bffMap.find(receiver) != bffMap.end()) { - isBestFriend = true; - } - } - - std::vector> segments = Game::chatFilter->IsSentenceOkay(message, entity->GetGMLevel(), !(isBestFriend && chatLevel == 1)); - - bool bAllClean = segments.empty(); - - if (user->GetIsMuted()) { - bAllClean = false; - } - - user->SetLastChatMessageApproved(bAllClean); - WorldPackets::SendChatModerationResponse(sysAddr, bAllClean, requestID, receiver, segments); + return request; } -void ClientPackets::SendTop5HelpIssues(Packet* packet) { - auto* user = UserManager::Instance()->GetUser(packet->systemAddress); - if (!user) return; - auto* character = user->GetLastUsedChar(); - if (!character) return; - auto * entity = character->GetEntity(); - if (!entity) return; - +int32_t ClientPackets::SendTop5HelpIssues(Packet* packet) { CINSTREAM_SKIP_HEADER; int32_t language = 0; inStream.Read(language); - - // TODO: Handle different languages in a nice way - // 0: en_US - // 1: pl_US - // 2: de_DE - // 3: en_GB - - AMFArrayValue data; - // Summaries - data.Insert("Summary0", Game::config->GetValue("help_0_summary")); - data.Insert("Summary1", Game::config->GetValue("help_1_summary")); - data.Insert("Summary2", Game::config->GetValue("help_2_summary")); - data.Insert("Summary3", Game::config->GetValue("help_3_summary")); - data.Insert("Summary4", Game::config->GetValue("help_4_summary")); - - // Descriptions - data.Insert("Description0", Game::config->GetValue("help_0_description")); - data.Insert("Description1", Game::config->GetValue("help_1_description")); - data.Insert("Description2", Game::config->GetValue("help_2_description")); - data.Insert("Description3", Game::config->GetValue("help_3_description")); - data.Insert("Description4", Game::config->GetValue("help_4_description")); - - GameMessages::SendUIMessageServerToSingleClient(entity, packet->systemAddress, "UIHelpTop5", data); - + return language; } diff --git a/dNet/ClientPackets.h b/dNet/ClientPackets.h index 5b9fd76d..a7d2941b 100644 --- a/dNet/ClientPackets.h +++ b/dNet/ClientPackets.h @@ -6,13 +6,31 @@ #ifndef CLIENTPACKETS_H #define CLIENTPACKETS_H -#include "RakNetTypes.h" +#include +#include + +class PositionUpdate; + +struct Packet; + +struct ChatMessage { + uint8_t chatChannel = 0; + uint16_t unknown = 0; + std::u16string message; +}; + +struct ChatModerationRequest { + uint8_t chatLevel = 0; + uint8_t requestID = 0; + std::string receiver; + std::string message; +}; namespace ClientPackets { - void HandleChatMessage(const SystemAddress& sysAddr, Packet* packet); - void HandleClientPositionUpdate(const SystemAddress& sysAddr, Packet* packet); - void HandleChatModerationRequest(const SystemAddress& sysAddr, Packet* packet); - void SendTop5HelpIssues(Packet* packet); + ChatMessage HandleChatMessage(Packet* packet); + PositionUpdate HandleClientPositionUpdate(Packet* packet); + ChatModerationRequest HandleChatModerationRequest(Packet* packet); + int32_t SendTop5HelpIssues(Packet* packet); }; #endif // CLIENTPACKETS_H diff --git a/dNet/MasterPackets.cpp b/dNet/MasterPackets.cpp index 624b92ad..6d70fedb 100644 --- a/dNet/MasterPackets.cpp +++ b/dNet/MasterPackets.cpp @@ -1,6 +1,5 @@ #include "MasterPackets.h" #include "BitStream.h" -#include "PacketUtils.h" #include "dCommonVars.h" #include "dServer.h" #include "eConnectionType.h" @@ -88,7 +87,7 @@ void MasterPackets::SendZoneTransferResponse(dServer* server, const SystemAddres bitStream.Write(zoneInstance); bitStream.Write(zoneClone); bitStream.Write(serverPort); - bitStream.Write(LUString(serverIP, static_cast(serverIP.size() + 1))); + bitStream.Write(LUString(serverIP, 255)); server->Send(&bitStream, sysAddr, false); } @@ -100,12 +99,12 @@ void MasterPackets::HandleServerInfo(Packet* packet) { uint32_t theirPort = 0; uint32_t theirZoneID = 0; uint32_t theirInstanceID = 0; - std::string theirIP = ""; + LUString theirIP; inStream.Read(theirPort); inStream.Read(theirZoneID); inStream.Read(theirInstanceID); - theirIP = PacketUtils::ReadString(inStream.GetReadOffset(), packet, false); //20 is the current offset + inStream.Read(theirIP); //TODO: Actually mark this server as an available server in the manager } @@ -118,7 +117,7 @@ void MasterPackets::SendServerInfo(dServer* server, Packet* packet) { bitStream.Write(server->GetZoneID()); bitStream.Write(server->GetInstanceID()); bitStream.Write(server->GetServerType()); - bitStream.Write(LUString(server->GetIP(), server->GetIP().size())); + bitStream.Write(LUString(server->GetIP())); server->SendToMaster(&bitStream); } diff --git a/dNet/PacketUtils.cpp b/dNet/PacketUtils.cpp index 95c3b2b1..5394cff0 100644 --- a/dNet/PacketUtils.cpp +++ b/dNet/PacketUtils.cpp @@ -1,64 +1,8 @@ #include "PacketUtils.h" -#include #include #include "Logger.h" #include "Game.h" -uint16_t PacketUtils::ReadU16(uint32_t startLoc, Packet* packet) { - if (startLoc + 2 > packet->length) return 0; - - std::vector t; - for (uint32_t i = startLoc; i < startLoc + 2; i++) t.push_back(packet->data[i]); - return *(uint16_t*)t.data(); -} - -uint32_t PacketUtils::ReadU32(uint32_t startLoc, Packet* packet) { - if (startLoc + 4 > packet->length) return 0; - - std::vector t; - for (uint32_t i = startLoc; i < startLoc + 4; i++) { - t.push_back(packet->data[i]); - } - return *(uint32_t*)t.data(); -} - -uint64_t PacketUtils::ReadU64(uint32_t startLoc, Packet* packet) { - if (startLoc + 8 > packet->length) return 0; - - std::vector t; - for (uint32_t i = startLoc; i < startLoc + 8; i++) t.push_back(packet->data[i]); - return *(uint64_t*)t.data(); -} - -int64_t PacketUtils::ReadS64(uint32_t startLoc, Packet* packet) { - if (startLoc + 8 > packet->length) return 0; - - std::vector t; - for (size_t i = startLoc; i < startLoc + 8; i++) t.push_back(packet->data[i]); - return *(int64_t*)t.data(); -} - -std::string PacketUtils::ReadString(uint32_t startLoc, Packet* packet, bool wide, uint32_t maxLen) { - std::string readString = ""; - - if (wide) maxLen *= 2; - - if (packet->length > startLoc) { - uint32_t i = 0; - while (packet->data[startLoc + i] != '\0' && packet->length > static_cast(startLoc + i) && maxLen > i) { - readString.push_back(packet->data[startLoc + i]); - - if (wide) { - i += 2; // Wide-char string - } else { - i++; // Regular string - } - } - } - - return readString; -} - //! Saves a packet to the filesystem void PacketUtils::SavePacket(const std::string& filename, const char* data, size_t length) { //If we don't log to the console, don't save the bin files either. This takes up a lot of time. diff --git a/dNet/PacketUtils.h b/dNet/PacketUtils.h index f8558dfd..3cd44b5f 100644 --- a/dNet/PacketUtils.h +++ b/dNet/PacketUtils.h @@ -8,11 +8,6 @@ enum class eConnectionType : uint16_t; namespace PacketUtils { - uint16_t ReadU16(uint32_t startLoc, Packet* packet); - uint32_t ReadU32(uint32_t startLoc, Packet* packet); - uint64_t ReadU64(uint32_t startLoc, Packet* packet); - int64_t ReadS64(uint32_t startLoc, Packet* packet); - std::string ReadString(uint32_t startLoc, Packet* packet, bool wide, uint32_t maxLen = 33); void SavePacket(const std::string& filename, const char* data, size_t length); }; diff --git a/dNet/WorldPackets.cpp b/dNet/WorldPackets.cpp index 1ee98c83..f92a971f 100644 --- a/dNet/WorldPackets.cpp +++ b/dNet/WorldPackets.cpp @@ -1,26 +1,21 @@ -#include "dCommonVars.h" #include "WorldPackets.h" +#include "dCommonVars.h" #include "BitStream.h" -#include "PacketUtils.h" #include "GeneralUtils.h" -#include "User.h" -#include "Character.h" #include "Logger.h" -#include #include "Game.h" #include "LDFFormat.h" #include "dServer.h" -#include "dZoneManager.h" -#include "CharacterComponent.h" #include "ZCompression.h" #include "eConnectionType.h" #include "BitStreamUtils.h" -void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum) { +#include + +void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone) { RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::LOAD_STATIC_ZONE); - auto zone = Game::zoneManager->GetZone()->GetZoneID(); bitStream.Write(zone.GetMapID()); bitStream.Write(zone.GetInstanceID()); //bitStream.Write(zone.GetCloneID()); @@ -38,57 +33,6 @@ void WorldPackets::SendLoadStaticZone(const SystemAddress& sysAddr, float x, flo SEND_PACKET; } -void WorldPackets::SendCharacterList(const SystemAddress& sysAddr, User* user) { - if (!user) return; - - RakNet::BitStream bitStream; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_LIST_RESPONSE); - - std::vector characters = user->GetCharacters(); - bitStream.Write(characters.size()); - bitStream.Write(0); //character index in front, just picking 0 - - for (uint32_t i = 0; i < characters.size(); ++i) { - bitStream.Write(characters[i]->GetObjectID()); - bitStream.Write(0); - - bitStream.Write(LUWString(characters[i]->GetName())); - bitStream.Write(LUWString(characters[i]->GetUnapprovedName())); - - bitStream.Write(characters[i]->GetNameRejected()); - bitStream.Write(false); - - bitStream.Write(LUString("", 10)); - - bitStream.Write(characters[i]->GetShirtColor()); - bitStream.Write(characters[i]->GetShirtStyle()); - bitStream.Write(characters[i]->GetPantsColor()); - bitStream.Write(characters[i]->GetHairStyle()); - bitStream.Write(characters[i]->GetHairColor()); - bitStream.Write(characters[i]->GetLeftHand()); - bitStream.Write(characters[i]->GetRightHand()); - bitStream.Write(characters[i]->GetEyebrows()); - bitStream.Write(characters[i]->GetEyes()); - bitStream.Write(characters[i]->GetMouth()); - bitStream.Write(0); - - bitStream.Write(characters[i]->GetZoneID()); - bitStream.Write(characters[i]->GetZoneInstance()); - bitStream.Write(characters[i]->GetZoneClone()); - - bitStream.Write(characters[i]->GetLastLogin()); - - const auto& equippedItems = characters[i]->GetEquippedItems(); - bitStream.Write(equippedItems.size()); - - for (uint32_t j = 0; j < equippedItems.size(); ++j) { - bitStream.Write(equippedItems[j]); - } - } - - SEND_PACKET; -} - void WorldPackets::SendCharacterCreationResponse(const SystemAddress& sysAddr, eCharacterCreationResponse response) { RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_CREATE_RESPONSE); @@ -128,26 +72,20 @@ void WorldPackets::SendServerState(const SystemAddress& sysAddr) { SEND_PACKET; } -void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* entity, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm) { +void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm) { RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CREATE_CHARACTER); RakNet::BitStream data; data.Write(7); //LDF key count - auto character = entity->GetComponent(); - if (!character) { - LOG("Entity is not a character?? what??"); - return; - } - - LDFData* objid = new LDFData(u"objid", entity->GetObjectID()); - LDFData* lot = new LDFData(u"template", 1); - LDFData* xmlConfigData = new LDFData(u"xmlData", xmlData); - LDFData* name = new LDFData(u"name", username); - LDFData* gmlevel = new LDFData(u"gmlevel", static_cast(gm)); - LDFData* chatmode = new LDFData(u"chatmode", static_cast(gm)); - LDFData* reputation = new LDFData(u"reputation", character->GetReputation()); + std::unique_ptr> objid(new LDFData(u"objid", player)); + std::unique_ptr> lot(new LDFData(u"template", 1)); + std::unique_ptr> xmlConfigData(new LDFData(u"xmlData", xmlData)); + std::unique_ptr> name(new LDFData(u"name", username)); + std::unique_ptr> gmlevel(new LDFData(u"gmlevel", static_cast(gm))); + std::unique_ptr> chatmode(new LDFData(u"chatmode", static_cast(gm))); + std::unique_ptr> reputationLdf(new LDFData(u"reputation", reputation)); objid->WriteToPacket(&data); lot->WriteToPacket(&data); @@ -155,15 +93,7 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* ent gmlevel->WriteToPacket(&data); chatmode->WriteToPacket(&data); xmlConfigData->WriteToPacket(&data); - reputation->WriteToPacket(&data); - - delete objid; - delete lot; - delete xmlConfigData; - delete gmlevel; - delete chatmode; - delete name; - delete reputation; + reputationLdf->WriteToPacket(&data); //Compress the data before sending: const uint32_t reservedSize = ZCompression::GetMaxCompressedLength(data.GetNumberOfBytesUsed()); @@ -187,14 +117,12 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, Entity* ent * an assertion is done to prevent bad data from being saved or sent. */ #pragma warning(disable:6385) // C6385 Reading invalid data from 'compressedData'. - for (size_t i = 0; i < size; i++) - bitStream.Write(compressedData[i]); + bitStream.WriteAlignedBytes(compressedData, size); #pragma warning(default:6385) - // PacketUtils::SavePacket("chardata.bin", (const char*)bitStream.GetData(), static_cast(bitStream.GetNumberOfBytesUsed())); SEND_PACKET; delete[] compressedData; - LOG("Sent CreateCharacter for ID: %llu", entity->GetObjectID()); + LOG("Sent CreateCharacter for ID: %llu", player); } void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector> unacceptedItems) { diff --git a/dNet/WorldPackets.h b/dNet/WorldPackets.h index ea8186c7..0d5de079 100644 --- a/dNet/WorldPackets.h +++ b/dNet/WorldPackets.h @@ -2,9 +2,8 @@ #define WORLDPACKETS_H #include "dCommonVars.h" +#include #include -#include -#include "Entity.h" class User; struct SystemAddress; @@ -13,14 +12,13 @@ enum class eCharacterCreationResponse : uint8_t; enum class eRenameResponse : uint8_t; namespace WorldPackets { - void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum); - void SendCharacterList(const SystemAddress& sysAddr, User* user); + void SendLoadStaticZone(const SystemAddress& sysAddr, float x, float y, float z, uint32_t checksum, LWOZONEID zone); void SendCharacterCreationResponse(const SystemAddress& sysAddr, eCharacterCreationResponse response); void SendCharacterRenameResponse(const SystemAddress& sysAddr, eRenameResponse response); void SendCharacterDeleteResponse(const SystemAddress& sysAddr, bool response); void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift); void SendServerState(const SystemAddress& sysAddr); - void SendCreateCharacter(const SystemAddress& sysAddr, Entity* entity, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); + void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector> unacceptedItems); void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel); } diff --git a/dNet/ZoneInstanceManager.cpp b/dNet/ZoneInstanceManager.cpp index f9285579..354d3634 100644 --- a/dNet/ZoneInstanceManager.cpp +++ b/dNet/ZoneInstanceManager.cpp @@ -3,7 +3,6 @@ // Custom Classes #include "MasterPackets.h" -#include "PacketUtils.h" #include "dServer.h" // C++ @@ -25,20 +24,30 @@ void ZoneInstanceManager::RequestZoneTransfer(dServer* server, uint32_t zoneID, } //! Handles a zone transfer response -void ZoneInstanceManager::HandleRequestZoneTransferResponse(uint64_t requestID, Packet* packet) { - - bool mythranShift = static_cast(packet->data[16]); - uint32_t zoneID = PacketUtils::ReadU32(17, packet); - uint32_t zoneInstance = PacketUtils::ReadU32(21, packet); - uint32_t zoneClone = PacketUtils::ReadU32(25, packet); - uint16_t serverPort = PacketUtils::ReadU16(29, packet); - std::string serverIP = PacketUtils::ReadString(31, packet, false); +void ZoneInstanceManager::HandleRequestZoneTransferResponse(Packet* packet) { + CINSTREAM_SKIP_HEADER; + uint64_t requestID; + inStream.Read(requestID); + bool mythranShift; + uint8_t tmp; + inStream.Read(tmp); + mythranShift = tmp > 0; + uint32_t zoneID; + inStream.Read(zoneID); + uint32_t zoneInstance; + inStream.Read(zoneInstance); + uint32_t zoneClone; + inStream.Read(zoneClone); + uint16_t serverPort; + inStream.Read(serverPort); + LUString serverIP(255); + inStream.Read(serverIP); for (uint32_t i = 0; i < this->requests.size(); ++i) { if (this->requests[i]->requestID == requestID) { // Call the request callback - this->requests[i]->callback(mythranShift, zoneID, zoneInstance, zoneClone, serverIP, serverPort); + this->requests[i]->callback(mythranShift, zoneID, zoneInstance, zoneClone, serverIP.string, serverPort); delete this->requests[i]; this->requests.erase(this->requests.begin() + i); diff --git a/dNet/ZoneInstanceManager.h b/dNet/ZoneInstanceManager.h index 3f8da1ae..47080cde 100644 --- a/dNet/ZoneInstanceManager.h +++ b/dNet/ZoneInstanceManager.h @@ -1,7 +1,5 @@ #pragma once -// C++ -#define _VARIADIC_MAX 10 #include #include #include @@ -56,7 +54,7 @@ public: \param requestID The request ID \param packet The packet */ - void HandleRequestZoneTransferResponse(uint64_t requestID, Packet* packet); + void HandleRequestZoneTransferResponse(Packet* packet); void CreatePrivateZone(dServer* server, uint32_t zoneID, uint32_t zoneClone, const std::string& password); diff --git a/dNet/dServer.cpp b/dNet/dServer.cpp index e60e04f9..ed66b42c 100644 --- a/dNet/dServer.cpp +++ b/dNet/dServer.cpp @@ -10,7 +10,6 @@ #include "eServerMessageType.h" #include "eMasterMessageType.h" -#include "PacketUtils.h" #include "BitStreamUtils.h" #include "MasterPackets.h" #include "ZoneInstanceManager.h" @@ -127,8 +126,7 @@ Packet* dServer::ReceiveFromMaster() { if (static_cast(packet->data[1]) == eConnectionType::MASTER) { switch (static_cast(packet->data[3])) { case eMasterMessageType::REQUEST_ZONE_TRANSFER_RESPONSE: { - uint64_t requestID = PacketUtils::ReadU64(8, packet); - ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(requestID, packet); + ZoneInstanceManager::Instance()->HandleRequestZoneTransferResponse(packet); break; } case eMasterMessageType::SHUTDOWN: diff --git a/dNet/dServer.h b/dNet/dServer.h index b0a3e11d..ef47eea4 100644 --- a/dNet/dServer.h +++ b/dNet/dServer.h @@ -16,6 +16,14 @@ enum class ServerType : uint32_t { World }; +enum class ServiceId : uint32_t{ + General = 0, + Auth = 1, + Chat = 2, + World = 4, + Client = 5, +}; + namespace Game { using signal_t = volatile std::sig_atomic_t; } diff --git a/dPhysics/dpWorld.cpp b/dPhysics/dpWorld.cpp index 698ceb63..e820eac2 100644 --- a/dPhysics/dpWorld.cpp +++ b/dPhysics/dpWorld.cpp @@ -9,6 +9,21 @@ #include "Logger.h" #include "dConfig.h" +#include "dNavMesh.h" + +namespace { + dpGrid* m_Grid = nullptr; + dNavMesh* m_NavMesh = nullptr; + int32_t phys_sp_tilesize = 205; + int32_t phys_sp_tilecount = 12; + + uint32_t m_ZoneID = 0; + + std::vector m_StaticEntities; + std::vector m_DynamicEntites; + bool phys_spatial_partitioning = true; +}; + void dpWorld::Initialize(unsigned int zoneID, bool generateNewNavMesh) { const auto physSpTilecount = Game::config->GetValue("phys_sp_tilecount"); if (!physSpTilecount.empty()) GeneralUtils::TryParse(physSpTilecount, phys_sp_tilecount); @@ -51,7 +66,7 @@ void dpWorld::Reload() { } } -dpWorld::~dpWorld() { +void dpWorld::Shutdown() { if (m_Grid) { // Triple check this is true m_Grid->SetDeleteGrid(true); @@ -65,6 +80,10 @@ dpWorld::~dpWorld() { } } +bool dpWorld::IsLoaded() { + return m_NavMesh->GetdtNavMesh() != nullptr; +} + void dpWorld::StepWorld(float deltaTime) { if (m_Grid) { m_Grid->Update(deltaTime); @@ -91,6 +110,10 @@ void dpWorld::StepWorld(float deltaTime) { } } +dNavMesh* dpWorld::GetNavMesh() { + return m_NavMesh; +} + void dpWorld::AddEntity(dpEntity* entity) { if (m_Grid) entity->SetGrid(m_Grid); //This sorts this entity into the right cell else { //old method, slow @@ -125,7 +148,7 @@ void dpWorld::RemoveEntity(dpEntity* entity) { } } -bool dpWorld::ShouldUseSP(unsigned int zoneID) { +bool dpWorld::ShouldUseSP(uint32_t zoneID) { if (!phys_spatial_partitioning) return false; // TODO: Add to this list as needed. diff --git a/dPhysics/dpWorld.h b/dPhysics/dpWorld.h index bf2af06d..fc23a15c 100644 --- a/dPhysics/dpWorld.h +++ b/dPhysics/dpWorld.h @@ -1,48 +1,22 @@ #pragma once -#include "Singleton.h" +#include -//Navmesh includes: -#include "Recast.h" -#include "DetourNavMesh.h" -#include "DetourNavMeshBuilder.h" -#include "DetourNavMeshQuery.h" - -#include -#include - -#include "dNavMesh.h" - -class NiPoint3; +class dNavMesh; class dpEntity; -class dpGrid; -class dpWorld : public Singleton { -public: - void Initialize(unsigned int zoneID, bool generateNewNavMesh = true); +namespace dpWorld { + void Initialize(uint32_t zoneID, bool generateNewNavMesh = true); + void Shutdown(); void Reload(); - ~dpWorld(); - - bool ShouldUseSP(unsigned int zoneID); - bool IsLoaded() const { return m_NavMesh->GetdtNavMesh() != nullptr; } + bool ShouldUseSP(uint32_t zoneID); + bool IsLoaded(); void StepWorld(float deltaTime); void AddEntity(dpEntity* entity); void RemoveEntity(dpEntity* entity); - dNavMesh* GetNavMesh() { return m_NavMesh; } - -private: - dpGrid* m_Grid; - bool phys_spatial_partitioning = true; - int phys_sp_tilesize = 205; - int phys_sp_tilecount = 12; - - std::vector m_StaticEntities; - std::vector m_DynamicEntites; - - dNavMesh* m_NavMesh = nullptr; - uint32_t m_ZoneID = 0; + dNavMesh* GetNavMesh(); }; diff --git a/dScripts/02_server/Enemy/General/BaseEnemyMech.cpp b/dScripts/02_server/Enemy/General/BaseEnemyMech.cpp index c652dab0..8e70d4c3 100644 --- a/dScripts/02_server/Enemy/General/BaseEnemyMech.cpp +++ b/dScripts/02_server/Enemy/General/BaseEnemyMech.cpp @@ -7,6 +7,7 @@ #include "GeneralUtils.h" #include "DestroyableComponent.h" #include "eReplicaComponentType.h" +#include "dNavMesh.h" void BaseEnemyMech::OnStartup(Entity* self) { auto* destroyableComponent = self->GetComponent(); @@ -19,7 +20,7 @@ void BaseEnemyMech::OnDie(Entity* self, Entity* killer) { ControllablePhysicsComponent* controlPhys = static_cast(self->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); if (!controlPhys) return; - NiPoint3 newLoc = { controlPhys->GetPosition().x, dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(controlPhys->GetPosition()), controlPhys->GetPosition().z }; + NiPoint3 newLoc = { controlPhys->GetPosition().x, dpWorld::GetNavMesh()->GetHeightAtPoint(controlPhys->GetPosition()), controlPhys->GetPosition().z }; EntityInfo info = EntityInfo(); std::vector cfg; diff --git a/dScripts/BaseConsoleTeleportServer.cpp b/dScripts/BaseConsoleTeleportServer.cpp index 27d2bcfc..d4a49299 100644 --- a/dScripts/BaseConsoleTeleportServer.cpp +++ b/dScripts/BaseConsoleTeleportServer.cpp @@ -1,6 +1,6 @@ #include "BaseConsoleTeleportServer.h" #include "GameMessages.h" -#include "Player.h" +#include "CharacterComponent.h" #include "RenderComponent.h" #include "EntityManager.h" #include "eTerminateType.h" @@ -94,7 +94,9 @@ void BaseConsoleTeleportServer::TransferPlayer(Entity* self, Entity* player, int const auto& teleportZone = self->GetVar(u"transferZoneID"); - static_cast(player)->SendToZone(std::stoi(GeneralUtils::UTF16ToWTF8(teleportZone))); + auto* characterComponent = player->GetComponent(); + + if (characterComponent) characterComponent->SendToZone(std::stoi(GeneralUtils::UTF16ToWTF8(teleportZone))); UpdatePlayerTable(self, player, false); } diff --git a/dScripts/BaseSurvivalServer.cpp b/dScripts/BaseSurvivalServer.cpp index 93476d78..ecdcad64 100644 --- a/dScripts/BaseSurvivalServer.cpp +++ b/dScripts/BaseSurvivalServer.cpp @@ -3,7 +3,7 @@ #include "DestroyableComponent.h" #include "EntityManager.h" #include "dZoneManager.h" -#include "Player.h" +#include "CharacterComponent.h" #include "eMissionTaskType.h" #include "eMissionState.h" #include "MissionComponent.h" @@ -23,7 +23,8 @@ void BaseSurvivalServer::BasePlayerLoaded(Entity* self, Entity* player) { const auto& playersIter = std::find(state.players.begin(), state.players.end(), player->GetObjectID()); if (waitingIter != state.waitingPlayers.end() || playersIter != state.players.end()) { - static_cast(player)->SendToZone(player->GetCharacter()->GetLastNonInstanceZoneID()); + auto* characterComponent = player->GetComponent(); + if (characterComponent) characterComponent->SendToZone(player->GetCharacter()->GetLastNonInstanceZoneID()); return; } @@ -161,8 +162,8 @@ void BaseSurvivalServer::BaseMessageBoxResponse(Entity* self, Entity* sender, in if (sender->IsPlayer()) { auto* character = sender->GetCharacter(); if (character != nullptr) { - auto* player = dynamic_cast(sender); - player->SendToZone(character->GetLastNonInstanceZoneID()); + auto* characterComponent = sender->GetComponent(); + if (characterComponent) characterComponent->SendToZone(character->GetLastNonInstanceZoneID()); } } } diff --git a/dScripts/BaseWavesServer.cpp b/dScripts/BaseWavesServer.cpp index 9be19806..e2f646f9 100644 --- a/dScripts/BaseWavesServer.cpp +++ b/dScripts/BaseWavesServer.cpp @@ -3,7 +3,7 @@ #include "DestroyableComponent.h" #include "EntityManager.h" #include "dZoneManager.h" -#include "Player.h" +#include "CharacterComponent.h" #include "eMissionTaskType.h" #include "eMissionState.h" #include "MissionComponent.h" @@ -162,8 +162,8 @@ void BaseWavesServer::BaseMessageBoxResponse(Entity* self, Entity* sender, int32 if (sender->IsPlayer()) { auto* character = sender->GetCharacter(); if (character != nullptr) { - auto* player = dynamic_cast(sender); - player->SendToZone(character->GetLastNonInstanceZoneID()); + auto* characterComponent = sender->GetComponent(); + if (characterComponent) characterComponent->SendToZone(character->GetLastNonInstanceZoneID()); } } } diff --git a/dScripts/ScriptComponent.cpp b/dScripts/ScriptComponent.cpp index 2079d67a..7c44ded3 100644 --- a/dScripts/ScriptComponent.cpp +++ b/dScripts/ScriptComponent.cpp @@ -46,11 +46,7 @@ CppScripts::Script* ScriptComponent::GetScript() { } void ScriptComponent::SetScript(const std::string& scriptName) { - //we don't need to delete the script because others may be using it :) - /*if (m_Client) { - m_Script = new InvalidScript(); - return; - }*/ - + // Scripts are managed by the CppScripts class and are effecitvely singletons + // and they may also be used by other script components so DON'T delete them. m_Script = CppScripts::GetScript(m_Parent, scriptName); } diff --git a/dScripts/ScriptComponent.h b/dScripts/ScriptComponent.h index cabbf8bf..a28c9419 100644 --- a/dScripts/ScriptComponent.h +++ b/dScripts/ScriptComponent.h @@ -17,9 +17,9 @@ class Entity; * Handles the loading and execution of server side scripts on entities, scripts were originally written in Lua, * here they're written in C++ */ -class ScriptComponent : public Component { +class ScriptComponent final : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPT; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPT; ScriptComponent(Entity* parent, std::string scriptName, bool serialized, bool client = false); ~ScriptComponent() override; diff --git a/dScripts/ai/GENERAL/InstanceExitTransferPlayerToLastNonInstance.cpp b/dScripts/ai/GENERAL/InstanceExitTransferPlayerToLastNonInstance.cpp index de1c62e0..667f41bd 100644 --- a/dScripts/ai/GENERAL/InstanceExitTransferPlayerToLastNonInstance.cpp +++ b/dScripts/ai/GENERAL/InstanceExitTransferPlayerToLastNonInstance.cpp @@ -1,6 +1,6 @@ #include "InstanceExitTransferPlayerToLastNonInstance.h" #include "GameMessages.h" -#include "Player.h" +#include "CharacterComponent.h" #include "Character.h" #include "dServer.h" #include "eTerminateType.h" @@ -23,10 +23,8 @@ void InstanceExitTransferPlayerToLastNonInstance::OnUse(Entity* self, Entity* us } void InstanceExitTransferPlayerToLastNonInstance::OnMessageBoxResponse(Entity* self, Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData) { - auto* player = dynamic_cast(sender); - if (player == nullptr) - return; - + if (!sender->IsPlayer()) return; + auto* character = sender->GetCharacter(); if (character != nullptr) { if (identifier == u"Instance_Exit" && button == 1) { @@ -47,7 +45,8 @@ void InstanceExitTransferPlayerToLastNonInstance::OnMessageBoxResponse(Entity* s } } - player->SendToZone(lastInstance); + auto* characterComponent = sender->GetComponent(); + if (characterComponent) characterComponent->SendToZone(lastInstance); } } diff --git a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp index 1a298843..c8563b53 100644 --- a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp +++ b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp @@ -453,16 +453,12 @@ void SGCannon::SpawnNewModel(Entity* self) { void SGCannon::RemovePlayer(LWOOBJID playerID) { auto* player = Game::entityManager->GetEntity(playerID); - if (player == nullptr) - return; + if (!player) return; - auto* playerObject = dynamic_cast(player); - if (playerObject == nullptr) - return; - - auto* character = playerObject->GetCharacter(); - if (character != nullptr) { - playerObject->SendToZone(character->GetLastNonInstanceZoneID()); + auto* character = player->GetCharacter(); + auto* characterComponent = player->GetComponent(); + if (characterComponent && character) { + characterComponent->SendToZone(character->GetLastNonInstanceZoneID()); } } diff --git a/dScripts/zone/LUPs/WblGenericZone.cpp b/dScripts/zone/LUPs/WblGenericZone.cpp index 5a670d8e..600628fa 100644 --- a/dScripts/zone/LUPs/WblGenericZone.cpp +++ b/dScripts/zone/LUPs/WblGenericZone.cpp @@ -1,10 +1,11 @@ #include "WblGenericZone.h" -#include "Player.h" +#include "CharacterComponent.h" void WblGenericZone::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) { if (args == m_WblAbortMsg) { if (!sender) return; - auto player = dynamic_cast(sender); - if (player) player->SendToZone(m_WblMainZone); + + auto* characterComponent = sender->GetComponent(); + if (characterComponent) characterComponent->SendToZone(m_WblMainZone); } } 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 85a26ed9..5eb11a09 100644 --- a/dWorldServer/CMakeLists.txt +++ b/dWorldServer/CMakeLists.txt @@ -15,4 +15,4 @@ target_link_libraries(WorldServer ${COMMON_LIBRARIES} dUtilities dGameMessages dInventory - dGame dChatFilter dZoneManager dPhysics Detour Recast tinyxml2 dWorldServer dNavigation) \ No newline at end of file + dGame dChatFilter dZoneManager dPhysics Detour Recast tinyxml2 dWorldServer dNavigation) diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index f911f804..d704bfea 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -29,7 +29,6 @@ #include #include "AuthPackets.h" -#include "PacketUtils.h" #include "BitStreamUtils.h" #include "WorldPackets.h" #include "UserManager.h" @@ -77,11 +76,14 @@ #include "CheatDetection.h" #include "eGameMasterLevel.h" #include "StringifiedEnum.h" +#include "Server.h" +#include "PositionUpdate.h" +#include "PlayerManager.h" +#include "eLoginResponse.h" namespace Game { Logger* logger = nullptr; dServer* server = nullptr; - dpWorld* physicsWorld = nullptr; dChatFilter* chatFilter = nullptr; dConfig* config = nullptr; AssetManager* assetManager = nullptr; @@ -102,8 +104,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 +145,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()); @@ -182,7 +181,7 @@ int main(int argc, char** argv) { return EXIT_FAILURE; } - CDClientManager::Instance(); + CDClientManager::Instance().LoadValuesFromDatabase(); Diagnostics::SetProduceMemoryDump(Game::config->GetValue("generate_dump") == "1"); @@ -257,7 +256,7 @@ int main(int argc, char** argv) { Game::zoneManager = new dZoneManager(); //Load our level: if (zoneID != 0) { - dpWorld::Instance().Initialize(zoneID); + dpWorld::Initialize(zoneID); Game::zoneManager->Initialize(LWOZONEID(zoneID, instanceID, cloneID)); g_CloneID = cloneID; @@ -388,7 +387,7 @@ int main(int argc, char** argv) { if (zoneID != 0 && deltaTime > 0.0f) { Metrics::StartMeasurement(MetricVariable::Physics); - dpWorld::Instance().StepWorld(deltaTime); + dpWorld::StepWorld(deltaTime); Metrics::EndMeasurement(MetricVariable::Physics); Metrics::StartMeasurement(MetricVariable::UpdateEntities); @@ -412,7 +411,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 +528,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 +659,132 @@ 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: { + CINSTREAM_SKIP_HEADER; + uint64_t requestID; + inStream.Read(requestID); + uint32_t objectID; + inStream.Read(objectID); + ObjectIDManager::HandleRequestPersistentIDResponse(requestID, objectID); + break; + } + + case eMasterMessageType::SESSION_KEY_RESPONSE: { + //Read our session key and to which user it belongs: + CINSTREAM_SKIP_HEADER; + uint32_t sessionKey = 0; + inStream.Read(sessionKey); + LUWString username; + inStream.Read(username); + + //Find them: + auto it = m_PendingUsers.find(username.GetAsString()); + 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.GetAsString().c_str()); + + UserManager::Instance()->DeleteUser(packet->systemAddress); + + //Create our user and send them in: + UserManager::Instance()->CreateUser(it->second.sysAddr, username.GetAsString(), 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(), Game::zoneManager->GetZoneID()); + } + + 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.GetAsString()); + + //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: { + CINSTREAM_SKIP_HEADER; + uint64_t requestID; + inStream.Read(requestID); + 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: { + CINSTREAM_SKIP_HEADER; + uint32_t sessionKey = inStream.Read(sessionKey); + + LUString username; + inStream.Read(username); + LOG("Got new session alert for user %s", username.string.c_str()); + //Find them: + User* user = UserManager::Instance()->GetUser(username.string.c_str()); + if (!user) { + LOG("But they're not logged in?"); + return; + } + + //Check the key: + if (sessionKey != std::atoi(user->GetSessionKey().c_str())) { + LOG("But the session key is invalid!", username.string.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); @@ -686,7 +799,7 @@ void HandlePacket(Packet* packet) { auto* entity = Game::entityManager->GetEntity(c->GetObjectID()); if (!entity) { - entity = Player::GetPlayer(packet->systemAddress); + entity = PlayerManager::GetPlayer(packet->systemAddress); } if (entity) { @@ -730,159 +843,23 @@ 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])) { case eWorldMessageType::VALIDATION: { - std::string username = PacketUtils::ReadString(0x08, packet, true); - std::string sessionKey = PacketUtils::ReadString(74, packet, true); - std::string clientDatabaseChecksum = PacketUtils::ReadString(packet->length - 33, packet, false); + CINSTREAM_SKIP_HEADER; + LUWString username; + inStream.Read(username); + LUWString sessionKey; // sometimes client puts a null terminator at the end of the checksum and sometimes doesn't, weird - clientDatabaseChecksum = clientDatabaseChecksum.substr(0, 32); + inStream.Read(sessionKey); + LUString clientDatabaseChecksum(32); + inStream.Read(clientDatabaseChecksum); // If the check is turned on, validate the client's database checksum. if (Game::config->GetValue("check_fdb") == "1" && !databaseChecksum.empty()) { - auto accountInfo = Database::Get()->GetAccountInfo(username); + auto accountInfo = Database::Get()->GetAccountInfo(username.GetAsString()); if (!accountInfo) { LOG("Client's account does not exist in the database, aborting connection."); Game::server->Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::CHARACTER_NOT_FOUND); @@ -890,24 +867,42 @@ void HandlePacket(Packet* packet) { } // Developers may skip this check - if (accountInfo->maxGmLevel < eGameMasterLevel::DEVELOPER && clientDatabaseChecksum != databaseChecksum) { - LOG("Client's database checksum does not match the server's, aborting connection."); - Game::server->Disconnect(packet->systemAddress, eServerDisconnectIdentifiers::WRONG_GAME_VERSION); - return; + if (clientDatabaseChecksum.string != databaseChecksum) { + + if (accountInfo->maxGmLevel < eGameMasterLevel::DEVELOPER) { + LOG("Client's database checksum does not match the server's, aborting connection."); + std::vector stamps; + + // Using the LoginResponse here since the UI is still in the login screen state + // and we have a way to send a message about the client mismatch. + AuthPackets::SendLoginResponse( + Game::server, packet->systemAddress, eLoginResponse::PERMISSIONS_NOT_HIGH_ENOUGH, + Game::config->GetValue("cdclient_mismatch_message"), "", 0, "", stamps); + return; + } else { + AMFArrayValue args; + + args.Insert("title", Game::config->GetValue("cdclient_mismatch_title")); + args.Insert("message", Game::config->GetValue("cdclient_mismatch_message")); + + GameMessages::SendUIMessageServerToSingleClient("ToggleAnnounce", args, packet->systemAddress); + LOG("Account (%s) with GmLevel (%s) does not have a matching FDB, but is a developer and will skip this check." + , username.GetAsString().c_str(), StringifiedEnum::ToString(accountInfo->maxGmLevel).data()); + } } } //Request the session info from Master: CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::REQUEST_SESSION_KEY); - bitStream.Write(LUString(username, 64)); + bitStream.Write(username); Game::server->SendToMaster(&bitStream); //Insert info into our pending list tempSessionInfo info; info.sysAddr = SystemAddress(packet->systemAddress); - info.hash = sessionKey; - m_PendingUsers.insert(std::make_pair(username, info)); + info.hash = sessionKey.GetAsString(); + m_PendingUsers.insert(std::make_pair(username.GetAsString(), info)); break; } @@ -1026,7 +1021,10 @@ void HandlePacket(Packet* packet) { info.lot = 1; Entity* player = Game::entityManager->CreateEntity(info, UserManager::Instance()->GetUser(packet->systemAddress)); - WorldPackets::SendCreateCharacter(packet->systemAddress, player, c->GetXMLData(), username, c->GetGMLevel()); + auto* characterComponent = player->GetComponent(); + if (!characterComponent) return; + + WorldPackets::SendCreateCharacter(packet->systemAddress, player->GetComponent()->GetReputation(), player->GetObjectID(), c->GetXMLData(), username, c->GetGMLevel()); WorldPackets::SendServerState(packet->systemAddress); const auto respawnPoint = player->GetCharacter()->GetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID()); @@ -1039,8 +1037,6 @@ void HandlePacket(Packet* packet) { Game::entityManager->ConstructAllEntities(packet->systemAddress); - auto* characterComponent = player->GetComponent(); - if (!characterComponent) return; characterComponent->RocketUnEquip(player); // Do charxml fixes here @@ -1117,7 +1113,6 @@ void HandlePacket(Packet* packet) { SystemAddress sysAddr = packet->systemAddress; SEND_PACKET; - // PacketUtils::SavePacket("lxfml packet " + std::to_string(bbbModel.id) + ".bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); } } @@ -1149,6 +1144,7 @@ void HandlePacket(Packet* packet) { bitStream.Write(zone.GetInstanceID()); bitStream.Write(zone.GetCloneID()); bitStream.Write(player->GetParentUser()->GetMuteExpire()); + bitStream.Write(player->GetGMLevel()); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); } @@ -1163,7 +1159,16 @@ void HandlePacket(Packet* packet) { } case eWorldMessageType::POSITION_UPDATE: { - ClientPackets::HandleClientPositionUpdate(packet->systemAddress, packet); + auto positionUpdate = ClientPackets::HandleClientPositionUpdate(packet); + + User* user = UserManager::Instance()->GetUser(packet->systemAddress); + if (!user) { + LOG("Unable to get user to parse position update"); + return; + } + + Entity* entity = Game::entityManager->GetEntity(user->GetLastUsedChar()->GetObjectID()); + if (entity) entity->ProcessPositionUpdate(positionUpdate); break; } @@ -1211,7 +1216,74 @@ void HandlePacket(Packet* packet) { } case eWorldMessageType::STRING_CHECK: { - ClientPackets::HandleChatModerationRequest(packet->systemAddress, packet); + auto request = ClientPackets::HandleChatModerationRequest(packet); + + // TODO: Find a good home for the logic in this case. + User* user = UserManager::Instance()->GetUser(packet->systemAddress); + if (!user) { + LOG("Unable to get user to parse chat moderation request"); + return; + } + + auto* entity = PlayerManager::GetPlayer(packet->systemAddress); + + if (entity == nullptr) { + LOG("Unable to get player to parse chat moderation request"); + return; + } + + // Check if the player has restricted chat access + auto* character = entity->GetCharacter(); + + if (character->HasPermission(ePermissionMap::RestrictedChatAccess)) { + // Send a message to the player + ChatPackets::SendSystemMessage( + packet->systemAddress, + u"This character has restricted chat access." + ); + + return; + } + + bool isBestFriend = false; + + if (request.chatLevel == 1) { + // Private chat + LWOOBJID idOfReceiver = LWOOBJID_EMPTY; + + { + auto characterIdFetch = Database::Get()->GetCharacterInfo(request.receiver); + + if (characterIdFetch) { + idOfReceiver = characterIdFetch->id; + } + } + const auto& bffMap = user->GetIsBestFriendMap(); + if (bffMap.find(request.receiver) == bffMap.end() && idOfReceiver != LWOOBJID_EMPTY) { + auto bffInfo = Database::Get()->GetBestFriendStatus(entity->GetObjectID(), idOfReceiver); + + if (bffInfo) { + isBestFriend = bffInfo->bestFriendStatus == 3; + } + + if (isBestFriend) { + user->UpdateBestFriendValue(request.receiver, true); + } + } else if (bffMap.find(request.receiver) != bffMap.end()) { + isBestFriend = true; + } + } + + std::vector> segments = Game::chatFilter->IsSentenceOkay(request.message, entity->GetGMLevel(), !(isBestFriend && request.chatLevel == 1)); + + bool bAllClean = segments.empty(); + + if (user->GetIsMuted()) { + bAllClean = false; + } + + user->SetLastChatMessageApproved(bAllClean); + WorldPackets::SendChatModerationResponse(packet->systemAddress, bAllClean, request.requestID, request.receiver, segments); break; } @@ -1219,7 +1291,29 @@ void HandlePacket(Packet* packet) { if (chatDisabled) { ChatPackets::SendMessageFail(packet->systemAddress); } else { - ClientPackets::HandleChatMessage(packet->systemAddress, packet); + auto chatMessage = ClientPackets::HandleChatMessage(packet); + + // TODO: Find a good home for the logic in this case. + User* user = UserManager::Instance()->GetUser(packet->systemAddress); + if (!user) { + LOG("Unable to get user to parse chat message"); + return; + } + + if (user->GetIsMuted()) { + user->GetLastUsedChar()->SendMuteNotice(); + return; + } + std::string playerName = user->GetLastUsedChar()->GetName(); + bool isMythran = user->GetLastUsedChar()->GetGMLevel() > eGameMasterLevel::CIVILIAN; + bool isOk = Game::chatFilter->IsSentenceOkay(GeneralUtils::UTF16ToWTF8(chatMessage.message), user->GetLastUsedChar()->GetGMLevel()).empty(); + LOG_DEBUG("Msg: %s was approved previously? %i", GeneralUtils::UTF16ToWTF8(chatMessage.message).c_str(), user->GetLastChatMessageApproved()); + if (!isOk) return; + if (!isOk && !isMythran) return; + + std::string sMessage = GeneralUtils::UTF16ToWTF8(chatMessage.message); + LOG("%s: %s", playerName.c_str(), sMessage.c_str()); + ChatPackets::SendChatMessage(packet->systemAddress, chatMessage.chatChannel, playerName, user->GetLoggedInChar(), isMythran, chatMessage.message); } break; @@ -1245,7 +1339,37 @@ void HandlePacket(Packet* packet) { case eWorldMessageType::UI_HELP_TOP_5: { - ClientPackets::SendTop5HelpIssues(packet); + auto language = ClientPackets::SendTop5HelpIssues(packet); + // TODO: Handle different languages in a nice way + // 0: en_US + // 1: pl_US + // 2: de_DE + // 3: en_GB + + // TODO: Find a good home for the logic in this case. + auto* user = UserManager::Instance()->GetUser(packet->systemAddress); + if (!user) return; + auto* character = user->GetLastUsedChar(); + if (!character) return; + auto* entity = character->GetEntity(); + if (!entity) return; + + AMFArrayValue data; + // Summaries + data.Insert("Summary0", Game::config->GetValue("help_0_summary")); + data.Insert("Summary1", Game::config->GetValue("help_1_summary")); + data.Insert("Summary2", Game::config->GetValue("help_2_summary")); + data.Insert("Summary3", Game::config->GetValue("help_3_summary")); + data.Insert("Summary4", Game::config->GetValue("help_4_summary")); + + // Descriptions + data.Insert("Description0", Game::config->GetValue("help_0_description")); + data.Insert("Description1", Game::config->GetValue("help_1_description")); + data.Insert("Description2", Game::config->GetValue("help_2_description")); + data.Insert("Description3", Game::config->GetValue("help_3_description")); + data.Insert("Description4", Game::config->GetValue("help_4_description")); + + GameMessages::SendUIMessageServerToSingleClient(entity, packet->systemAddress, "UIHelpTop5", data); break; } @@ -1261,7 +1385,7 @@ void WorldShutdownProcess(uint32_t zoneId) { for (auto i = 0; i < Game::server->GetReplicaManager()->GetParticipantCount(); ++i) { const auto& player = Game::server->GetReplicaManager()->GetParticipantAtIndex(i); - auto* entity = Player::GetPlayer(player); + auto* entity = PlayerManager::GetPlayer(player); LOG("Saving data!"); if (entity != nullptr && entity->GetCharacter() != nullptr) { auto* skillComponent = entity->GetComponent(); @@ -1314,6 +1438,7 @@ void FinalizeShutdown() { //Delete our objects here: Metrics::Clear(); + dpWorld::Shutdown(); Database::Destroy("WorldServer"); if (Game::chatFilter) delete Game::chatFilter; Game::chatFilter = nullptr; diff --git a/dZoneManager/Zone.cpp b/dZoneManager/Zone.cpp index cbaccded..3f306c05 100644 --- a/dZoneManager/Zone.cpp +++ b/dZoneManager/Zone.cpp @@ -17,6 +17,7 @@ #include "eTriggerCommandType.h" #include "eTriggerEventType.h" +#include "dNavMesh.h" Zone::Zone(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) : m_ZoneID(mapID, instanceID, cloneID) { @@ -463,9 +464,9 @@ void Zone::LoadPath(std::istream& file) { // We verify the waypoint heights against the navmesh because in many movement paths, // the waypoint is located near 0 height, if (path.pathType == PathType::Movement) { - if (dpWorld::Instance().IsLoaded()) { + if (dpWorld::IsLoaded()) { // 2000 should be large enough for every world. - waypoint.position.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(waypoint.position, 2000.0f); + waypoint.position.y = dpWorld::GetNavMesh()->GetHeightAtPoint(waypoint.position, 2000.0f); } } path.pathWaypoints.push_back(waypoint); diff --git a/docker-compose.yml b/docker-compose.yml index 1d83f010..a7954718 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,58 +1,73 @@ version: "3" +name: dlu services: darkflamedb: - image: mariadb:latest - environment: - - MARIADB_USER=${MARIADB_USER:-darkflame} - - MARIADB_PASSWORD=${MARIADB_PASSWORD:-darkflame} - - MARIADB_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD:-darkflame} - - MARIADB_DATABASE=${MARIADB_DATABASE:-darkflame} networks: - darkflame + image: mariadb:latest + volumes: + - ${DB_DATA_DIR:-./db/data}:/var/lib/mysql + environment: + - MARIADB_RANDOM_ROOT_PASSWORD=1 + - MARIADB_USER=${MARIADB_USER:-darkflame} + - MARIADB_PASSWORD=${MARIADB_PASSWORD:?error} + - MARIADB_DATABASE=${MARIADB_DATABASE:-darkflame} + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + start_period: 10s + interval: 10s + timeout: 5s + retries: 3 darkflameserver: networks: - darkflame image: ghcr.io/darkflameuniverse/darkflameserver:latest volumes: - - $HOST_CONFIG_DIR:/app/configs/ - - $HOST_CLIENT_LOCATION:$CLIENT_LOCATION:ro - - $HOST_RESSERVER_LOCATION:/app/resServer/ - - $HOST_DUMP_FOLDER:$DUMP_FOLDER - - $HOST_LOGS_DIR:/app/logs/ + - ${HOST_CONFIG_DIR:-./server/config}:/app/configs/ + - ${CLIENT_PATH:-./client}:/app/luclient:ro + - ${HOST_RESSERVER_LOCATION:-./server/res}:/app/resServer/ + - ${HOST_DUMP_FOLDER:-./server/dump}:/app/dump/ + - ${HOST_LOGS_DIR:-./server/logs}:/app/logs/ environment: + - CLIENT_LOCATION=/app/luclient - DLU_CONFIG_DIR=/app/configs + - DUMP_FOLDER=/app/dump - MYSQL_HOST=darkflamedb - MYSQL_DATABASE=${MARIADB_DATABASE:-darkflame} - MYSQL_USERNAME=${MARIADB_USER:-darkflame} - - MYSQL_PASSWORD=${MARIADB_PASSWORD:-darkflame} + - MYSQL_PASSWORD=${MARIADB_PASSWORD:?error} - EXTERNAL_IP=${EXTERNAL_IP:-localhost} - - DUMP_FOLDER=$DUMP_FOLDER - - CLIENT_LOCATION=$CLIENT_LOCATION + - CLIENT_NET_VERSION=${CLIENT_NET_VERSION:-171022} depends_on: - darkflamedb ports: - "1001:1001/udp" - "2005:2005/udp" - "3000-3300:3000-3300/udp" + healthcheck: + test: ["CMD", "ls", "/app/resServer/CDServer.sqlite"] darkflameweb: networks: - darkflame image: ghcr.io/darkflameuniverse/nexusdashboard:latest volumes: - - $HOST_CLIENT_LOCATION:/app/luclient:ro - - $HOST_CACHE_LOCATION:/app/cache - - $HOST_CD_SQLITE_LOCATION:/app/cdclient:ro + - ${CLIENT_PATH:-./client}:/app/luclient:ro + - ${HOST_CACHE_LOCATION:-./web/cache}:/app/cache + - ${HOST_RESSERVER_LOCATION:-./server/res}:/app/cdclient:ro + - ${HOST_ND_LOGS_LOCATION:-./web/logs}:/logs environment: - - APP_SECRET_KEY=$APP_SECRET_KEY - - APP_DATABASE_URI=mysql+pymysql://${MARIADB_USER:-darkflame}:${MARIADB_PASSWORD:-darkflame}@darkflamedb:3306/${MARIADB_DATABASE:-darkflame} - - CLIENT_LOCATION=/app/luclient - - CACHE_LOCATION=/app/cache - - CD_SQLITE_LOCATION=/app/cdclient + - APP_SECRET_KEY=${ACCOUNT_MANAGER_SECRET:?error} + - APP_DATABASE_URI=mysql+pymysql://${MARIADB_USER:-darkflame}:${MARIADB_PASSWORD:?error}@darkflamedb:3306/${MARIADB_DATABASE:-darkflame} + - CLIENT_LOCATION=/app/luclient/ + - CACHE_LOCATION=/app/cache/ + - CD_SQLITE_LOCATION=/app/cdclient/ + - USER_ENABLE_REGISTER=1 # "0" is _not_ false, to disable, remove this line depends_on: - darkflamedb + - darkflameserver ports: - 8000:8000 healthcheck: diff --git a/docs/Commands.md b/docs/Commands.md index 0ba7d86e..62607939 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| @@ -95,9 +96,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| @@ -106,14 +108,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/resources/navmeshes.zip b/resources/navmeshes.zip index c3053520..95948656 100644 Binary files a/resources/navmeshes.zip and b/resources/navmeshes.zip differ diff --git a/resources/worldconfig.ini b/resources/worldconfig.ini index c68b42d2..91028ffe 100644 --- a/resources/worldconfig.ini +++ b/resources/worldconfig.ini @@ -73,3 +73,7 @@ help_4_description=Visit Discussions on the DarkflameServer GitHub page<br/>to a # Toggleable quality of life feature to allow users to skip most cinematics. allow_players_to_skip_cinematics=0 + +# Customizable message for what to say when there is a cdclient fdb mismatch +cdclient_mismatch_title=Version out of date +cdclient_mismatch_message=We detected that your client is out of date. Please update your client to the latest version. diff --git a/tests/dCommonTests/TestLUString.cpp b/tests/dCommonTests/TestLUString.cpp index 3abec985..30cc0f73 100644 --- a/tests/dCommonTests/TestLUString.cpp +++ b/tests/dCommonTests/TestLUString.cpp @@ -101,7 +101,7 @@ TEST(LUString33Test, SerializeReadTestNew) { std::string testString; for (int i = 0; i < 33; i++) testString += "a"; bitStream.Write(LUString(testString, 33)); - LUString result(33); + LUString result; ASSERT_EQ(result.size, 33); ASSERT_TRUE(bitStream.Read(result)); ASSERT_EQ(bitStream.GetNumberOfUnreadBits(), 0); @@ -113,7 +113,7 @@ TEST(LUString33Test, SerializeReadTestNewPartial) { std::string testString; for (int i = 0; i < 15; i++) testString += "a"; bitStream.Write(LUString(testString, 33)); - LUString result(33); + LUString result; ASSERT_EQ(result.size, 33); ASSERT_TRUE(bitStream.Read(result)); ASSERT_EQ(bitStream.GetNumberOfUnreadBits(), 0); diff --git a/tests/dCommonTests/TestLUWString.cpp b/tests/dCommonTests/TestLUWString.cpp index a16dd911..e812ae0c 100644 --- a/tests/dCommonTests/TestLUWString.cpp +++ b/tests/dCommonTests/TestLUWString.cpp @@ -101,7 +101,7 @@ TEST(LUWString33Test, SerializeReadTestNew) { std::u16string testString; for (int i = 0; i < 33; i++) testString += u'ü'; bitStream.Write(LUWString(testString, 33)); - LUWString result(33); + LUWString result; ASSERT_EQ(result.size, 33); ASSERT_TRUE(bitStream.Read(result)); ASSERT_EQ(bitStream.GetNumberOfUnreadBits(), 0); @@ -113,7 +113,7 @@ TEST(LUWString33Test, SerializeReadTestNewPartial) { std::u16string testString; for (int i = 0; i < 15; i++) testString += u'ü'; bitStream.Write(LUWString(testString, 33)); - LUWString result(33); + LUWString result; ASSERT_EQ(result.size, 33); ASSERT_TRUE(bitStream.Read(result)); ASSERT_EQ(bitStream.GetNumberOfUnreadBits(), 0); 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; } diff --git a/tests/dGameTests/GameDependencies.h b/tests/dGameTests/GameDependencies.h index 096dcb13..8aefaa3d 100644 --- a/tests/dGameTests/GameDependencies.h +++ b/tests/dGameTests/GameDependencies.h @@ -4,6 +4,7 @@ #include "Game.h" #include "Logger.h" #include "dServer.h" +#include "CDClientManager.h" #include "EntityInfo.h" #include "EntityManager.h" #include "dConfig.h" @@ -33,6 +34,9 @@ protected: Game::server = new dServerMock(); Game::config = new dConfig("worldconfig.ini"); Game::entityManager = new EntityManager(); + + // Create a CDClientManager instance and load from defaults + CDClientManager::Instance().LoadValuesFromDefaults(); } void TearDownDependencies() { diff --git a/tests/dGameTests/dComponentsTests/CMakeLists.txt b/tests/dGameTests/dComponentsTests/CMakeLists.txt index e38f7a53..374095af 100644 --- a/tests/dGameTests/dComponentsTests/CMakeLists.txt +++ b/tests/dGameTests/dComponentsTests/CMakeLists.txt @@ -1,5 +1,6 @@ set(DCOMPONENTS_TESTS "DestroyableComponentTests.cpp" + "PetComponentTests.cpp" "SimplePhysicsComponentTests.cpp" ) diff --git a/tests/dGameTests/dComponentsTests/PetComponentTests.cpp b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp new file mode 100644 index 00000000..75ce4ec8 --- /dev/null +++ b/tests/dGameTests/dComponentsTests/PetComponentTests.cpp @@ -0,0 +1,43 @@ +#include "GameDependencies.h" +#include <gtest/gtest.h> + +#include "BitStream.h" +#include "PetComponent.h" +#include "Entity.h" +#include "eReplicaComponentType.h" +#include "ePetAbilityType.h" +#include "eStateChangeType.h" + +class PetTest : public GameDependenciesTest { +protected: + Entity* baseEntity; + PetComponent* petComponent; + CBITSTREAM + + void SetUp() override { + SetUpDependencies(); + + // Set up entity and pet component + baseEntity = new Entity(15, GameDependenciesTest::info); + petComponent = baseEntity->AddComponent<PetComponent>(1); + + // Initialize some values to be not default + + } + + void TearDown() override { + delete baseEntity; + TearDownDependencies(); + } +}; + +TEST_F(PetTest, PlacementNewAddComponentTest) { + // Test adding component + ASSERT_NE(petComponent, nullptr); + baseEntity->AddComponent<PetComponent>(1); + ASSERT_NE(baseEntity->GetComponent<PetComponent>(), nullptr); + + // Test getting initial status + ASSERT_EQ(petComponent->GetParent()->GetObjectID(), 15); + ASSERT_EQ(petComponent->GetAbility(), ePetAbilityType::Invalid); +} diff --git a/thirdparty/SQLite/CMakeLists.txt b/thirdparty/SQLite/CMakeLists.txt index aa7a6423..ba45f015 100644 --- a/thirdparty/SQLite/CMakeLists.txt +++ b/thirdparty/SQLite/CMakeLists.txt @@ -10,5 +10,10 @@ if(UNIX) target_link_libraries(sqlite3 pthread dl m) # -Wno-unused-result -Wno-unknown-pragmas -fpermissive - target_compile_options(sqlite3 PRIVATE "-Wno-return-local-addr" "-Wno-maybe-uninitialized") + target_compile_options(sqlite3 PRIVATE) + if(NOT APPLE) + target_compile_options(sqlite3 PRIVATE "-Wno-return-local-addr" "-Wno-maybe-uninitialized") + else() + target_compile_options(sqlite3 PRIVATE "-Wno-return-stack-address" "-Wno-uninitialized") + endif() endif()