From d8f079cb1bb8d3285ff72598860b60661afc1c82 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Mon, 20 May 2024 00:43:57 -0700 Subject: [PATCH 01/26] fix mpc resetting on each world load (#1588) --- dGame/dGameMessages/GameMessages.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 93b10cec..1f757d7e 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -412,7 +412,8 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd bitStream.Write(qUnexpectedRotation.w); } - SEND_PACKET_BROADCAST; + if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; + SEND_PACKET; } void GameMessages::SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr) { From d6cac65a8dbb9254950ae0b28b612baf4b0ff193 Mon Sep 17 00:00:00 2001 From: TAHuntling <38479763+TAHuntling@users.noreply.github.com> Date: Tue, 21 May 2024 20:01:44 -0500 Subject: [PATCH 02/26] fix: Falling Off Edge in Pet Puzzle (#1584) * FloatFix * game activity setting * Update dNavMesh.cpp --------- Co-authored-by: David Markowitz --- dGame/dComponents/PetComponent.cpp | 41 +++++++++++++++++++++++------- dNavigation/dNavMesh.cpp | 25 ++++++++++++++++++ dNavigation/dNavMesh.h | 4 +-- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index debe0bd8..073c09e1 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -32,6 +32,8 @@ #include "eGameMasterLevel.h" #include "eMissionState.h" #include "dNavMesh.h" +#include "eGameActivity.h" +#include "eStateChangeType.h" std::unordered_map PetComponent::currentActivities{}; std::unordered_map PetComponent::activePets{}; @@ -210,24 +212,23 @@ void PetComponent::OnUse(Entity* originator) { if (dpWorld::IsLoaded()) { NiPoint3 attempt = petPosition + forward * interactionDistance; - float y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); + NiPoint3 nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt); - while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) { + while (std::abs(nearestPoint.y - petPosition.y) > 4 && interactionDistance > 10) { const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector(); attempt = originatorPosition + forward * interactionDistance; - y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); + nearestPoint = dpWorld::GetNavMesh()->NearestPoint(attempt); interactionDistance -= 0.5f; } - position = attempt; + position = nearestPoint; } else { position = petPosition + forward * interactionDistance; } - auto rotation = NiQuaternion::LookAt(position, petPosition); GameMessages::SendNotifyPetTamingMinigame( @@ -246,11 +247,11 @@ void PetComponent::OnUse(Entity* originator) { m_Parent->GetObjectID(), LWOOBJID_EMPTY, originator->GetObjectID(), - true, + false, ePetTamingNotifyType::BEGIN, - petPosition, - position, - rotation, + NiPoint3Constant::ZERO, + NiPoint3Constant::ZERO, + NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f), UNASSIGNED_SYSTEM_ADDRESS ); @@ -258,11 +259,18 @@ void PetComponent::OnUse(Entity* originator) { m_Tamer = originator->GetObjectID(); SetStatus(5); + Game::entityManager->SerializeEntity(m_Parent); currentActivities.insert_or_assign(m_Tamer, m_Parent->GetObjectID()); // Notify the start of a pet taming minigame m_Parent->GetScript()->OnNotifyPetTamingMinigame(m_Parent, originator, ePetTamingNotifyType::BEGIN); + + auto* characterComponent = originator->GetComponent(); + if (characterComponent != nullptr) { + characterComponent->SetCurrentActivity(eGameActivity::PET_TAMING); + Game::entityManager->SerializeEntity(originator); + } } void PetComponent::Update(float deltaTime) { @@ -627,6 +635,11 @@ void PetComponent::RequestSetPetName(std::u16string name) { UNASSIGNED_SYSTEM_ADDRESS ); + auto* characterComponent = tamer->GetComponent(); + if (characterComponent != nullptr) { + characterComponent->SetCurrentActivity(eGameActivity::NONE); + Game::entityManager->SerializeEntity(tamer); + } GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); auto* modelEntity = Game::entityManager->GetEntity(m_ModelId); @@ -666,6 +679,11 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { UNASSIGNED_SYSTEM_ADDRESS ); + auto* characterComponent = tamer->GetComponent(); + if (characterComponent != nullptr) { + characterComponent->SetCurrentActivity(eGameActivity::NONE); + Game::entityManager->SerializeEntity(tamer); + } GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); @@ -712,6 +730,11 @@ void PetComponent::ClientFailTamingMinigame() { UNASSIGNED_SYSTEM_ADDRESS ); + auto* characterComponent = tamer->GetComponent(); + if (characterComponent != nullptr) { + characterComponent->SetCurrentActivity(eGameActivity::NONE); + Game::entityManager->SerializeEntity(tamer); + } GameMessages::SendNotifyTamingModelLoadedOnServer(m_Tamer, tamer->GetSystemAddress()); GameMessages::SendTerminateInteraction(m_Tamer, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); diff --git a/dNavigation/dNavMesh.cpp b/dNavigation/dNavMesh.cpp index f49dd31e..d9584b00 100644 --- a/dNavigation/dNavMesh.cpp +++ b/dNavigation/dNavMesh.cpp @@ -112,6 +112,31 @@ void dNavMesh::LoadNavmesh() { m_NavMesh = mesh; } +NiPoint3 dNavMesh::NearestPoint(const NiPoint3& location, const float halfExtent) const { + NiPoint3 toReturn = location; + if (m_NavMesh != nullptr) { + float pos[3]; + pos[0] = location.x; + pos[1] = location.y; + pos[2] = location.z; + + dtPolyRef nearestRef = 0; + float polyPickExt[3] = { halfExtent, halfExtent, halfExtent }; + float nearestPoint[3] = { 0.0f, 0.0f, 0.0f }; + dtQueryFilter filter{}; + + auto hasPoly = m_NavQuery->findNearestPoly(pos, polyPickExt, &filter, &nearestRef, nearestPoint); + if (hasPoly != DT_SUCCESS) { + toReturn = location; + } else { + toReturn.x = nearestPoint[0]; + toReturn.y = nearestPoint[1]; + toReturn.z = nearestPoint[2]; + } + } + return toReturn; +} + float dNavMesh::GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight) const { if (m_NavMesh == nullptr) { return location.y; diff --git a/dNavigation/dNavMesh.h b/dNavigation/dNavMesh.h index 8a55c649..60e07e7c 100644 --- a/dNavigation/dNavMesh.h +++ b/dNavigation/dNavMesh.h @@ -21,7 +21,7 @@ public: /** * Get the height at a point - * + * * @param location The location to check for height at. This is the center of the search area. * @param halfExtentsHeight The half extents height of the search area. This is the distance from the center to the top and bottom of the search area. * The larger the value of halfExtentsHeight is, the larger the performance cost of the query. @@ -29,7 +29,7 @@ public: */ float GetHeightAtPoint(const NiPoint3& location, const float halfExtentsHeight = 32.0f) const; std::vector GetPath(const NiPoint3& startPos, const NiPoint3& endPos, float speed = 10.0f); - + NiPoint3 NearestPoint(const NiPoint3& location, const float halfExtent = 32.0f) const; bool IsNavmeshLoaded() { return m_NavMesh != nullptr; } private: From ed00551982a76a5af8d90e27a653fc5902e72cdb Mon Sep 17 00:00:00 2001 From: TAHuntling <38479763+TAHuntling@users.noreply.github.com> Date: Tue, 21 May 2024 20:02:07 -0500 Subject: [PATCH 03/26] feat: Help Command Pagination (#1581) * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Fixed Comments Now able to do /command help to see info for said command. Additionally this works for aliases. Fixed serialization missing from merge. * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp * Update SlashCommandHandler.cpp --- dGame/dUtilities/SlashCommandHandler.cpp | 89 +++++++++++++++++------- 1 file changed, 65 insertions(+), 24 deletions(-) diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 3ba1ab38..9eccc268 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -8,6 +8,7 @@ #include "SlashCommandHandler.h" #include +#include #include "DEVGMCommands.h" #include "GMGreaterThanZeroCommands.h" @@ -60,11 +61,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* if (commandHandle.requiredLevel > eGameMasterLevel::CIVILIAN) Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), input); commandHandle.handle(entity, sysAddr, args); } else if (entity->GetGMLevel() != eGameMasterLevel::CIVILIAN) { - // We don't need to tell civilians they aren't high enough level error = "You are not high enough GM level to use \"" + command + "\""; } } else if (entity->GetGMLevel() == eGameMasterLevel::CIVILIAN) { - // We don't need to tell civilians commands don't exist error = "Command " + command + " does not exist!"; } @@ -75,32 +74,74 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* void GMZeroCommands::Help(Entity* entity, const SystemAddress& sysAddr, const std::string args) { std::ostringstream feedback; - if (args.empty()) { - feedback << "----- Commands -----"; - for (const auto& [alias, command] : CommandInfos) { - // TODO: Limit displaying commands based on GM level they require - if (command.requiredLevel > entity->GetGMLevel()) continue; - LOG("Help command: %s", alias.c_str()); - feedback << "\n/" << alias << ": " << command.help; + constexpr size_t pageSize = 10; + + std::string trimmedArgs = args; + trimmedArgs.erase(trimmedArgs.begin(), std::find_if_not(trimmedArgs.begin(), trimmedArgs.end(), [](unsigned char ch) { + return std::isspace(ch); + })); + trimmedArgs.erase(std::find_if_not(trimmedArgs.rbegin(), trimmedArgs.rend(), [](unsigned char ch) { + return std::isspace(ch); + }).base(), trimmedArgs.end()); + + std::optional parsedPage = GeneralUtils::TryParse(trimmedArgs); + if (trimmedArgs.empty() || parsedPage.has_value()) { + size_t page = parsedPage.value_or(1); + + std::map accessibleCommands; + for (const auto& [commandName, command] : CommandInfos) { + if (command.requiredLevel <= entity->GetGMLevel()) { + accessibleCommands.emplace(commandName, command); + } + } + + size_t totalPages = (accessibleCommands.size() + pageSize - 1) / pageSize; + + if (page < 1 || page > totalPages) { + feedback << "Invalid page number. Total pages: " << totalPages; + GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedback.str())); + return; + } + + auto it = accessibleCommands.begin(); + std::advance(it, (page - 1) * pageSize); + size_t endIdx = std::min(page * pageSize, accessibleCommands.size()); + + feedback << "----- Commands (Page " << page << " of " << totalPages << ") -----"; + for (size_t i = (page - 1) * pageSize; i < endIdx; ++i, ++it) { + feedback << "\n/" << it->first << ": " << it->second.help; + } + + const auto feedbackStr = feedback.str(); + if (!feedbackStr.empty()) { + GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); + } + return; + } + + auto it = std::ranges::find_if(CommandInfos, [&trimmedArgs](const auto& pair) { + return std::ranges::find(pair.second.aliases, trimmedArgs) != pair.second.aliases.end(); + }); + + if (it != CommandInfos.end() && entity->GetGMLevel() >= it->second.requiredLevel) { + const auto& command = it->second; + feedback << "----- " << it->first << " Info -----\n"; + feedback << command.info << "\n"; + if (command.aliases.size() > 1) { + feedback << "Aliases: "; + for (size_t i = 0; i < command.aliases.size(); ++i) { + if (i > 0) feedback << ", "; + feedback << command.aliases[i]; + } } } else { - auto it = CommandInfos.find(args); - if (it != CommandInfos.end() && entity->GetGMLevel() >= it->second.requiredLevel) { - feedback << "----- " << args << " -----\n"; - feedback << it->second.info; - if (it->second.aliases.size() > 1) { - feedback << "\nAliases: "; - for (size_t i = 0; i < it->second.aliases.size(); i++) { - if (i > 0) feedback << ", "; - feedback << it->second.aliases[i]; - } - } - } else if (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) { - feedback << "Command " << std::quoted(args) << " does not exist!"; - } + feedback << "Command not found."; } + const auto feedbackStr = feedback.str(); - if (!feedbackStr.empty()) GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); + if (!feedbackStr.empty()) { + GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); + } } void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) { From dea10c6d56dbb568df32b967843941af65b60182 Mon Sep 17 00:00:00 2001 From: TAHuntling <38479763+TAHuntling@users.noreply.github.com> Date: Wed, 22 May 2024 08:32:24 -0500 Subject: [PATCH 04/26] Client commands implementation (#1592) * Adding Client Commands Adding list of client commands provided to me by EmosewaMC * Finished adding client commands --- dGame/dUtilities/SlashCommandHandler.cpp | 379 ++++++++++++++++++ .../SlashCommands/GMZeroCommands.cpp | 4 + .../dUtilities/SlashCommands/GMZeroCommands.h | 1 + 3 files changed, 384 insertions(+) diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 9eccc268..428ccbcb 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -1048,4 +1048,383 @@ void SlashCommandHandler::Startup() { }; RegisterCommand(InstanceInfoCommand); + //Commands that are handled by the client + + Command faqCommand{ + .help = "Show the LU FAQ Page", + .info = "Show the LU FAQ Page", + .aliases = {"faq","faqs"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(faqCommand); + + Command teamChatCommand{ + .help = "Send a message to your teammates.", + .info = "Send a message to your teammates.", + .aliases = {"team","t"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(teamChatCommand); + + Command showStoreCommand{ + .help = "Show the LEGO shop page.", + .info = "Show the LEGO shop page.", + .aliases = {"shop","store"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(showStoreCommand); + + Command minigamesCommand{ + .help = "Show the LEGO minigames page!", + .info = "Show the LEGO minigames page!", + .aliases = {"minigames"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(minigamesCommand); + + Command forumsCommand{ + .help = "Show the LU Forums!", + .info = "Show the LU Forums!", + .aliases = {"forums"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(forumsCommand); + + Command exitGameCommand{ + .help = "Exit to desktop", + .info = "Exit to desktop", + .aliases = {"exit","quit"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(exitGameCommand); + + Command thumbsUpCommand{ + .help = "Oh, yeah!", + .info = "Oh, yeah!", + .aliases = {"thumb","thumbs","thumbsup"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(thumbsUpCommand); + + Command victoryCommand{ + .help = "Victory!", + .info = "Victory!", + .aliases = {"victory!"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(victoryCommand); + + Command backflipCommand{ + .help = "Do a flip!", + .info = "Do a flip!", + .aliases = {"backflip"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(backflipCommand); + + Command clapCommand{ + .help = "A round of applause!", + .info = "A round of applause!", + .aliases = {"clap"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(clapCommand); + + Command logoutCharacterCommand{ + .help = "Returns you to the character select screen.", + .info = "Returns you to the character select screen.", + .aliases = {"camp","logoutcharacter"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(logoutCharacterCommand); + + Command sayCommand{ + .help = "Say something outloud so that everyone can hear you", + .info = "Say something outloud so that everyone can hear you", + .aliases = {"s","say"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(sayCommand); + + Command whisperCommand{ + .help = "Send a private message to another player.", + .info = "Send a private message to another player.", + .aliases = {"tell","w","whisper"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(whisperCommand); + + Command locationCommand{ + .help = "Output your current location on the map to the chat box.", + .info = "Output your current location on the map to the chat box.", + .aliases = {"loc","locate","location"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(locationCommand); + + Command logoutCommand{ + .help = "Returns you to the login screen.", + .info = "Returns you to the login screen.", + .aliases = {"logout","logoutaccount"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(logoutCommand); + + Command shrugCommand{ + .help = "I dunno...", + .info = "I dunno...", + .aliases = {"shrug"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(shrugCommand); + + Command leaveTeamCommand{ + .help = "Leave your current team.", + .info = "Leave your current team.", + .aliases = {"leave","leaveteam","teamleave","tleave"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(leaveTeamCommand); + + Command teamLootTypeCommand{ + .help = "[rr|ffa] Set the loot for your current team (round-robin/free for all).", + .info = "[rr|ffa] Set the loot for your current team (round-robin/free for all).", + .aliases = {"setloot","teamsetloot","tloot","tsetloot"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(teamLootTypeCommand); + + Command removeFriendCommand{ + .help = "[name] Removes a player from your friends list.", + .info = "[name] Removes a player from your friends list.", + .aliases = {"removefriend"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(removeFriendCommand); + + Command yesCommand{ + .help = "Aye aye, captain!", + .info = "Aye aye, captain!", + .aliases = {"yes"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(yesCommand); + + Command teamInviteCommand{ + .help = "[name] Invite a player to your team.", + .info = "[name] Invite a player to your team.", + .aliases = {"invite","inviteteam","teaminvite","tinvite"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(teamInviteCommand); + + Command danceCommand{ + .help = "Dance 'til you can't dance no more.", + .info = "Dance 'til you can't dance no more.", + .aliases = {"dance"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(danceCommand); + + Command sighCommand{ + .help = "Another day, another brick.", + .info = "Another day, another brick.", + .aliases = {"sigh"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(sighCommand); + + Command recommendedOptionsCommand{ + .help = "Sets the recommended performance options in the cfg file", + .info = "Sets the recommended performance options in the cfg file", + .aliases = {"recommendedperfoptions"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(recommendedOptionsCommand); + + Command setTeamLeaderCommand{ + .help = "[name] Set the leader for your current team.", + .info = "[name] Set the leader for your current team.", + .aliases = {"leader","setleader","teamsetleader","tleader","tsetleader"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(setTeamLeaderCommand); + + Command cringeCommand{ + .help = "I don't even want to talk about it...", + .info = "I don't even want to talk about it...", + .aliases = {"cringe"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(cringeCommand); + + Command talkCommand{ + .help = "Jibber Jabber", + .info = "Jibber Jabber", + .aliases = {"talk"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(talkCommand); + + Command cancelQueueCommand{ + .help = "Cancel Your position in the queue if you are in one.", + .info = "Cancel Your position in the queue if you are in one.", + .aliases = {"cancelqueue"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(cancelQueueCommand); + + Command lowPerformanceCommand{ + .help = "Sets the default low-spec performance options in the cfg file", + .info = "Sets the default low-spec performance options in the cfg file", + .aliases = {"perfoptionslow"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(lowPerformanceCommand); + + Command kickFromTeamCommand{ + .help = "[name] Kick a player from your current team.", + .info = "[name] Kick a player from your current team.", + .aliases = {"kick","kickplayer","teamkickplayer","tkick","tkickplayer"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(kickFromTeamCommand); + + Command thanksCommand{ + .help = "Express your gratitude for another.", + .info = "Express your gratitude for another.", + .aliases = {"thanks"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(thanksCommand); + + Command waveCommand{ + .help = "Wave to other players.", + .info = "Wave to other players.", + .aliases = {"wave"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(waveCommand); + + Command whyCommand{ + .help = "Why|!?!!", + .info = "Why|!?!!", + .aliases = {"why"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(whyCommand); + + Command midPerformanceCommand{ + .help = "Sets the default medium-spec performance options in the cfg file", + .info = "Sets the default medium-spec performance options in the cfg file", + .aliases = {"perfoptionsmid"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(midPerformanceCommand); + + Command highPerformanceCommand{ + .help = "Sets the default high-spec performance options in the cfg file", + .info = "Sets the default high-spec performance options in the cfg file", + .aliases = {"perfoptionshigh"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(highPerformanceCommand); + + Command gaspCommand{ + .help = "Oh my goodness!", + .info = "Oh my goodness!", + .aliases = {"gasp"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(gaspCommand); + + Command ignoreCommand{ + .help = "[name] Add a player to your ignore list.", + .info = "[name] Add a player to your ignore list.", + .aliases = {"addignore"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(ignoreCommand); + + Command addFriendCommand{ + .help = "[name] Add a player to your friends list.", + .info = "[name] Add a player to your friends list.", + .aliases = {"addfriend"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(addFriendCommand); + + Command cryCommand{ + .help = "Show everyone your 'Aw' face.", + .info = "Show everyone your 'Aw' face.", + .aliases = {"cry"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(cryCommand); + + Command giggleCommand{ + .help = "A good little chuckle", + .info = "A good little chuckle", + .aliases = {"giggle"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(giggleCommand); + + Command saluteCommand{ + .help = "For those about to build...", + .info = "For those about to build...", + .aliases = {"salute"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(saluteCommand); + + Command removeIgnoreCommand{ + .help = "[name] Removes a player from your ignore list.", + .info = "[name] Removes a player from your ignore list.", + .aliases = {"removeIgnore"}, + .handle = GMZeroCommands::ClientHandled, + .requiredLevel = eGameMasterLevel::CIVILIAN + }; + RegisterCommand(removeIgnoreCommand); } diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp index f183d5ea..6c9811c2 100644 --- a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp @@ -224,5 +224,9 @@ namespace GMZeroCommands { ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID()))); } + + //For client side commands + void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args) {} + }; diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.h b/dGame/dUtilities/SlashCommands/GMZeroCommands.h index 3b2389b5..d3f6753d 100644 --- a/dGame/dUtilities/SlashCommands/GMZeroCommands.h +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.h @@ -15,6 +15,7 @@ namespace GMZeroCommands { void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args); void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args); void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ClientHandled(Entity* entity, const SystemAddress& sysAddr, const std::string args); } #endif //!GMZEROCOMMANDS_H From dc430d975894e53f199282e75ff7c94d32151d09 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Wed, 22 May 2024 16:35:45 -0700 Subject: [PATCH 05/26] Add reputation as a repeatable mission reward (#1590) This reverts commit 7d1a28b492b263aba2008a5984dc0f5e7348a068. Add stubbing for abbreviations Reward reputation always if possible --- dGame/dMission/Mission.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp index c2ed2a42..2a841e39 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -454,6 +454,16 @@ void Mission::YieldRewards() { } } + // Even with no repeatable column, reputation is repeatable + if (info.reward_reputation > 0) { + missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, LWOOBJID_EMPTY, "", info.reward_reputation); + auto* const character = entity->GetComponent(); + if (character) { + character->SetReputation(character->GetReputation() + info.reward_reputation); + GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress()); + } + } + if (m_Completions > 0) { std::vector> items; @@ -532,15 +542,6 @@ void Mission::YieldRewards() { modelInventory->SetSize(modelInventory->GetSize() + info.reward_bankinventory); } - if (info.reward_reputation > 0) { - missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, 0L, "", info.reward_reputation); - auto character = entity->GetComponent(); - if (character) { - character->SetReputation(character->GetReputation() + info.reward_reputation); - GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress()); - } - } - if (info.reward_maxhealth > 0) { destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast(info.reward_maxhealth), true); } From f0960d48b21d4d558ad378d6bcf9c7c0d6f8ba3d Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Wed, 22 May 2024 17:06:52 -0700 Subject: [PATCH 06/26] Add more modular saving of config data for items (#1591) * stubbing for saving item extra data * add declaration to header * modularize loading for all possible extra data * move logic to Item * remove extra map --- dCommon/LDFFormat.h | 74 ++++++++++++------------ dGame/dComponents/InventoryComponent.cpp | 41 ++++--------- dGame/dInventory/Item.cpp | 55 +++++++++++++++++- dGame/dInventory/Item.h | 14 +++++ 4 files changed, 115 insertions(+), 69 deletions(-) diff --git a/dCommon/LDFFormat.h b/dCommon/LDFFormat.h index 2cd9156c..054ddb42 100644 --- a/dCommon/LDFFormat.h +++ b/dCommon/LDFFormat.h @@ -31,22 +31,22 @@ public: virtual ~LDFBaseData() {} - virtual void WriteToPacket(RakNet::BitStream& packet) = 0; + virtual void WriteToPacket(RakNet::BitStream& packet) const = 0; - virtual const std::u16string& GetKey() = 0; + virtual const std::u16string& GetKey() const = 0; - virtual eLDFType GetValueType() = 0; + virtual eLDFType GetValueType() const = 0; /** Gets a string from the key/value pair * @param includeKey Whether or not to include the key in the data * @param includeTypeId Whether or not to include the type id in the data * @return The string representation of the data */ - virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) = 0; + virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) const = 0; - virtual std::string GetValueAsString() = 0; + virtual std::string GetValueAsString() const = 0; - virtual LDFBaseData* Copy() = 0; + virtual LDFBaseData* Copy() const = 0; /** * Given an input string, return the data as a LDF key. @@ -62,7 +62,7 @@ private: T value; //! Writes the key to the packet - void WriteKey(RakNet::BitStream& packet) { + void WriteKey(RakNet::BitStream& packet) const { packet.Write(this->key.length() * sizeof(uint16_t)); for (uint32_t i = 0; i < this->key.length(); ++i) { packet.Write(this->key[i]); @@ -70,7 +70,7 @@ private: } //! Writes the value to the packet - void WriteValue(RakNet::BitStream& packet) { + void WriteValue(RakNet::BitStream& packet) const { packet.Write(this->GetValueType()); packet.Write(this->value); } @@ -90,7 +90,7 @@ public: /*! \return The value */ - const T& GetValue(void) { return this->value; } + const T& GetValue(void) const { return this->value; } //! Sets the value /*! @@ -102,13 +102,13 @@ public: /*! \return The value string */ - std::string GetValueString(void) { return ""; } + std::string GetValueString(void) const { return ""; } //! Writes the data to a packet /*! \param packet The packet */ - void WriteToPacket(RakNet::BitStream& packet) override { + void WriteToPacket(RakNet::BitStream& packet) const override { this->WriteKey(packet); this->WriteValue(packet); } @@ -117,13 +117,13 @@ public: /*! \return The key */ - const std::u16string& GetKey(void) override { return this->key; } + const std::u16string& GetKey(void) const override { return this->key; } //! Gets the LDF Type /*! \return The LDF value type */ - eLDFType GetValueType(void) override { return LDF_TYPE_UNKNOWN; } + eLDFType GetValueType(void) const override { return LDF_TYPE_UNKNOWN; } //! Gets the string data /*! @@ -131,7 +131,7 @@ public: \param includeTypeId Whether or not to include the type id in the data \return The string representation of the data */ - std::string GetString(const bool includeKey = true, const bool includeTypeId = true) override { + std::string GetString(const bool includeKey = true, const bool includeTypeId = true) const override { if (GetValueType() == -1) { return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:"; } @@ -154,11 +154,11 @@ public: return stream.str(); } - std::string GetValueAsString() override { + std::string GetValueAsString() const override { return this->GetValueString(); } - LDFBaseData* Copy() override { + LDFBaseData* Copy() const override { return new LDFData(key, value); } @@ -166,19 +166,19 @@ public: }; // LDF Types -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_UTF_16; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_S32; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_FLOAT; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_DOUBLE; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_U32; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_BOOLEAN; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_U64; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_OBJID; }; -template<> inline eLDFType LDFData::GetValueType(void) { return LDF_TYPE_UTF_8; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_UTF_16; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_S32; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_FLOAT; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_DOUBLE; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_U32; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_BOOLEAN; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_U64; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_OBJID; }; +template<> inline eLDFType LDFData::GetValueType(void) const { return LDF_TYPE_UTF_8; }; // The specialized version for std::u16string (UTF-16) template<> -inline void LDFData::WriteValue(RakNet::BitStream& packet) { +inline void LDFData::WriteValue(RakNet::BitStream& packet) const { packet.Write(this->GetValueType()); packet.Write(this->value.length()); @@ -189,7 +189,7 @@ inline void LDFData::WriteValue(RakNet::BitStream& packet) { // The specialized version for bool template<> -inline void LDFData::WriteValue(RakNet::BitStream& packet) { +inline void LDFData::WriteValue(RakNet::BitStream& packet) const { packet.Write(this->GetValueType()); packet.Write(this->value); @@ -197,7 +197,7 @@ inline void LDFData::WriteValue(RakNet::BitStream& packet) { // The specialized version for std::string (UTF-8) template<> -inline void LDFData::WriteValue(RakNet::BitStream& packet) { +inline void LDFData::WriteValue(RakNet::BitStream& packet) const { packet.Write(this->GetValueType()); packet.Write(this->value.length()); @@ -206,18 +206,18 @@ inline void LDFData::WriteValue(RakNet::BitStream& packet) { } } -template<> inline std::string LDFData::GetValueString() { +template<> inline std::string LDFData::GetValueString() const { return GeneralUtils::UTF16ToWTF8(this->value, this->value.size()); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } +template<> inline std::string LDFData::GetValueString() const { return std::to_string(this->value); } -template<> inline std::string LDFData::GetValueString() { return this->value; } +template<> inline std::string LDFData::GetValueString() const { return this->value; } #endif //!__LDFFORMAT__H__ diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 60dd071c..acb27796 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -558,19 +558,9 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) { itemElement->QueryAttribute("parent", &parent); // End custom xml - std::vector config; + auto* item = new Item(id, lot, inventory, slot, count, bound, {}, parent, subKey); - auto* extraInfo = itemElement->FirstChildElement("x"); - - if (extraInfo) { - std::string modInfo = extraInfo->Attribute("ma"); - - LDFBaseData* moduleAssembly = new LDFData(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(modInfo.substr(2, modInfo.size() - 1))); - - config.push_back(moduleAssembly); - } - - const auto* item = new Item(id, lot, inventory, slot, count, bound, config, parent, subKey); + item->LoadConfigXml(*itemElement); if (equipped) { const auto info = Inventory::FindItemComponent(lot); @@ -676,17 +666,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) { itemElement->SetAttribute("parent", item->GetParent()); // End custom xml - for (auto* data : item->GetConfig()) { - if (data->GetKey() != u"assemblyPartLOTs") { - continue; - } - - auto* extraInfo = document.NewElement("x"); - - extraInfo->SetAttribute("ma", data->GetString(false).c_str()); - - itemElement->LinkEndChild(extraInfo); - } + item->SaveConfigXml(*itemElement); bagElement->LinkEndChild(itemElement); } @@ -1600,18 +1580,18 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument& document) { } -bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId){ +bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId) { BehaviorSlot behaviorSlot = BehaviorSlot::Invalid; - if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary; - else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand; - else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck; - else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head; - else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable; + if (slot == 1) behaviorSlot = BehaviorSlot::Primary; + else if (slot == 2) behaviorSlot = BehaviorSlot::Offhand; + else if (slot == 3) behaviorSlot = BehaviorSlot::Neck; + else if (slot == 4) behaviorSlot = BehaviorSlot::Head; + else if (slot == 5) behaviorSlot = BehaviorSlot::Consumable; else return false; return SetSkill(behaviorSlot, skillId); } -bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){ +bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) { if (skillId == 0) return false; const auto index = m_Skills.find(slot); if (index != m_Skills.end()) { @@ -1623,4 +1603,3 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){ m_Skills.insert_or_assign(slot, skillId); return true; } - diff --git a/dGame/dInventory/Item.cpp b/dGame/dInventory/Item.cpp index b6193692..32603761 100644 --- a/dGame/dInventory/Item.cpp +++ b/dGame/dInventory/Item.cpp @@ -27,6 +27,23 @@ #include "CDComponentsRegistryTable.h" #include "CDPackageComponentTable.h" +namespace { + const std::map ExtraSettingAbbreviations = { + { "assemblyPartLOTs", "ma" }, + { "blueprintID", "b" }, + { "userModelID", "ui" }, + { "userModelName", "un" }, + { "userModelDesc", "ud" }, + { "userModelHasBhvr", "ub" }, + { "userModelBehaviors", "ubh" }, + { "userModelBehaviorSourceID", "ubs" }, + { "userModelPhysicsType", "up" }, + { "userModelMod", "um" }, + { "userModelOpt", "uo" }, + { "reforgedLOT", "rl" }, + }; +} + Item::Item(const LWOOBJID id, const LOT lot, Inventory* inventory, const uint32_t slot, const uint32_t count, const bool bound, const std::vector& config, const LWOOBJID parent, LWOOBJID subKey, eLootSourceType lootSourceType) { if (!Inventory::IsValidItem(lot)) { return; @@ -122,6 +139,10 @@ uint32_t Item::GetSlot() const { return slot; } +std::vector Item::GetConfig() const { + return config; +} + std::vector& Item::GetConfig() { return config; } @@ -251,7 +272,7 @@ bool Item::Consume() { auto skills = skillsTable->Query([this](const CDObjectSkills entry) { return entry.objectTemplate == static_cast(lot); - }); + }); auto success = false; @@ -515,3 +536,35 @@ Item::~Item() { config.clear(); } + +void Item::SaveConfigXml(tinyxml2::XMLElement& i) const { + tinyxml2::XMLElement* x = nullptr; + + for (const auto* config : this->config) { + const auto& key = GeneralUtils::UTF16ToWTF8(config->GetKey()); + const auto saveKey = ExtraSettingAbbreviations.find(key); + if (saveKey == ExtraSettingAbbreviations.end()) { + continue; + } + + if (!x) { + x = i.InsertNewChildElement("x"); + } + + const auto dataToSave = config->GetString(false); + x->SetAttribute(saveKey->second.c_str(), dataToSave.c_str()); + } +} + +void Item::LoadConfigXml(const tinyxml2::XMLElement& i) { + const auto* x = i.FirstChildElement("x"); + if (!x) return; + + for (const auto& pair : ExtraSettingAbbreviations) { + const auto* data = x->Attribute(pair.second.c_str()); + if (!data) continue; + + const auto value = pair.first + "=" + data; + config.push_back(LDFBaseData::DataFromString(value)); + } +} diff --git a/dGame/dInventory/Item.h b/dGame/dInventory/Item.h index 04d05d7c..72ff264c 100644 --- a/dGame/dInventory/Item.h +++ b/dGame/dInventory/Item.h @@ -9,6 +9,10 @@ #include "eInventoryType.h" #include "eLootSourceType.h" +namespace tinyxml2 { + class XMLElement; +}; + /** * An item that can be stored in an inventory and optionally consumed or equipped * TODO: ideally this should be a component @@ -116,6 +120,12 @@ public: */ std::vector& GetConfig(); + /** + * Returns current config info for this item, e.g. for rockets + * @return current config info for this item + */ + std::vector GetConfig() const; + /** * Returns the database info for this item * @return the database info for this item @@ -214,6 +224,10 @@ public: */ void RemoveFromInventory(); + void SaveConfigXml(tinyxml2::XMLElement& i) const; + + void LoadConfigXml(const tinyxml2::XMLElement& i); + private: /** * The object ID of this item From 8ae1a8ff7cb2b472eb42997b59976fc77b644b85 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Fri, 24 May 2024 07:15:30 -0700 Subject: [PATCH 07/26] fix stale reference (#1594) --- dGame/dComponents/VendorComponent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dGame/dComponents/VendorComponent.cpp b/dGame/dComponents/VendorComponent.cpp index abe11ea5..b9286d25 100644 --- a/dGame/dComponents/VendorComponent.cpp +++ b/dGame/dComponents/VendorComponent.cpp @@ -76,8 +76,8 @@ void VendorComponent::RefreshInventory(bool isCreation) { if (vendorItems.empty()) break; auto randomItemIndex = GeneralUtils::GenerateRandomNumber(0, vendorItems.size() - 1); const auto& randomItem = vendorItems.at(randomItemIndex); - vendorItems.erase(vendorItems.begin() + randomItemIndex); if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority)); + vendorItems.erase(vendorItems.begin() + randomItemIndex); } } } From 8ca05241f260e8b8d92b42e497aa5dfe970450fc Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Fri, 24 May 2024 21:35:14 -0500 Subject: [PATCH 08/26] fix: prevent moving items between inventories under cetain circumsances --- ...eReponseMoveItemBetweenInventoryTypeCode.h | 21 ++++++++++ dGame/dGameMessages/GameMessages.cpp | 42 ++++++++++++++++++- dGame/dGameMessages/GameMessages.h | 2 + 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h diff --git a/dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h b/dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h new file mode 100644 index 00000000..b309f36d --- /dev/null +++ b/dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h @@ -0,0 +1,21 @@ +#ifndef __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__ +#define __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__ + +#include + +enum class eReponseMoveItemBetweenInventoryTypeCode : uint32_t { + SUCCESS, + FAIL_GENERIC, + FAIL_INV_FULL, + FAIL_ITEM_NOT_FOUND, + FAIL_CANT_MOVE_TO_THAT_INV_TYPE, + FAIL_NOT_NEAR_BANK, + FAIL_CANT_SWAP_ITEMS, + FAIL_SOURCE_TYPE, + FAIL_WRONG_DEST_TYPE, + FAIL_SWAP_DEST_TYPE, + FAIL_CANT_MOVE_THINKING_HAT, + FAIL_DISMOUNT_BEFORE_MOVING +}; + +#endif //!__EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__ diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 1f757d7e..846a75ed 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -99,6 +99,7 @@ #include "ActivityManager.h" #include "PlayerManager.h" #include "eVendorTransactionResult.h" +#include "eReponseMoveItemBetweenInventoryTypeCode.h" #include "CDComponentsRegistryTable.h" #include "CDObjectsTable.h" @@ -4564,16 +4565,31 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& if (inStream.ReadBit()) inStream.Read(itemLOT); if (invTypeDst == invTypeSrc) { + SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC); return; } auto* inventoryComponent = entity->GetComponent(); - if (inventoryComponent != nullptr) { + if (inventoryComponent) { if (itemID != LWOOBJID_EMPTY) { auto* item = inventoryComponent->FindItemById(itemID); - if (!item) return; + if (!item) { + SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_ITEM_NOT_FOUND); + return; + } + + if (item->GetLot() == 6086) { // Thinking hat + SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_CANT_MOVE_THINKING_HAT); + return; + } + + auto* destInv = inventoryComponent->GetInventory(invTypeDst); + if (destInv && destInv->GetEmptySlots() == 0) { + SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_INV_FULL); + return; + } // Despawn the pet if we are moving that pet to the vault. auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID()); @@ -4582,10 +4598,32 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& } inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot); + SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::SUCCESS); } + } else { + SendResponseMoveItemBetweenInventoryTypes(entity->GetObjectID(), sysAddr, invTypeDst, invTypeSrc, eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC); } } +void GameMessages::SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(objectId); + bitStream.Write(eGameMessageType::RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES); + + bitStream.Write(inventoryTypeDestination != eInventoryType::ITEMS); + if (inventoryTypeDestination != eInventoryType::ITEMS) bitStream.Write(inventoryTypeDestination); + + bitStream.Write(inventoryTypeSource != eInventoryType::ITEMS); + if (inventoryTypeSource != eInventoryType::ITEMS) bitStream.Write(inventoryTypeSource); + + bitStream.Write(response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC); + if (response != eReponseMoveItemBetweenInventoryTypeCode::FAIL_GENERIC) bitStream.Write(response); + + SEND_PACKET; +} + void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr) { CBITSTREAM; diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index b842710e..02c3e514 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -39,6 +39,7 @@ enum class eQuickBuildFailReason : uint32_t; enum class eQuickBuildState : uint32_t; enum class BehaviorSlot : int32_t; enum class eVendorTransactionResult : uint32_t; +enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t; namespace GameMessages { class PropertyDataMessage; @@ -589,6 +590,7 @@ namespace GameMessages { //NT: void HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); + void SendResponseMoveItemBetweenInventoryTypes(LWOOBJID objectId, const SystemAddress& sysAddr, eInventoryType inventoryTypeDestination, eInventoryType inventoryTypeSource, eReponseMoveItemBetweenInventoryTypeCode response); void SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditionalSound, bool bPlayCountdownSound, std::u16string sndName, int32_t stateToPlaySoundOn, const SystemAddress& sysAddr); From 86f335d64b7896865d04e5c4da759db6eeeb04a1 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Fri, 24 May 2024 21:43:54 -0500 Subject: [PATCH 09/26] fix type --- dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h b/dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h index b309f36d..b99687d0 100644 --- a/dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h +++ b/dCommon/dEnums/eReponseMoveItemBetweenInventoryTypeCode.h @@ -3,7 +3,7 @@ #include -enum class eReponseMoveItemBetweenInventoryTypeCode : uint32_t { +enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t { SUCCESS, FAIL_GENERIC, FAIL_INV_FULL, From debc2a96e288c82f81aa8639e6ca724eb7772ae4 Mon Sep 17 00:00:00 2001 From: TAHuntling <38479763+TAHuntling@users.noreply.github.com> Date: Sat, 25 May 2024 03:43:32 -0500 Subject: [PATCH 10/26] Update CppScripts exclusion list (#1597) --- dScripts/CppScripts.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index 784edbdb..73452c47 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -700,7 +700,8 @@ CppScripts::Script* const CppScripts::GetScript(Entity* parent, const std::strin (scriptName == "scripts\\02_server\\Enemy\\General\\L_BASE_ENEMY_SPIDERLING.lua") || (scriptName == "scripts\\ai\\FV\\L_ACT_NINJA_STUDENT.lua") || (scriptName == "scripts\\ai\\WILD\\L_WILD_GF_FROG.lua") || - (scriptName == "scripts\\empty.lua") + (scriptName == "scripts\\empty.lua") || + (scriptName == "scripts\\ai\\AG\\L_AG_SENTINEL_GUARD.lua") )) LOG_DEBUG("LOT %i attempted to load CppScript for '%s', but returned InvalidScript.", parent->GetLOT(), scriptName.c_str()); } From 0348db72a593dbdb166e37e8672201aa9aee5a22 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sat, 25 May 2024 10:24:02 -0700 Subject: [PATCH 11/26] fiux mission (#1596) --- .../02_server/Enemy/AG/BossSpiderQueenEnemyServer.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dScripts/02_server/Enemy/AG/BossSpiderQueenEnemyServer.cpp b/dScripts/02_server/Enemy/AG/BossSpiderQueenEnemyServer.cpp index 40b248f5..b64bb7a8 100644 --- a/dScripts/02_server/Enemy/AG/BossSpiderQueenEnemyServer.cpp +++ b/dScripts/02_server/Enemy/AG/BossSpiderQueenEnemyServer.cpp @@ -15,6 +15,7 @@ #include "SkillComponent.h" #include "eReplicaComponentType.h" #include "RenderComponent.h" +#include "PlayerManager.h" #include @@ -53,11 +54,13 @@ void BossSpiderQueenEnemyServer::OnStartup(Entity* self) { void BossSpiderQueenEnemyServer::OnDie(Entity* self, Entity* killer) { if (Game::zoneManager->GetZoneID().GetMapID() == instanceZoneID && killer) { - auto* missionComponent = killer->GetComponent(); - if (missionComponent == nullptr) - return; + for (const auto& player : PlayerManager::GetAllPlayers()) { + auto* missionComponent = player->GetComponent(); + if (missionComponent == nullptr) + return; - missionComponent->CompleteMission(instanceMissionID); + missionComponent->CompleteMission(instanceMissionID); + } } // There is suppose to be a 0.1 second delay here but that may be admitted? From e59525d2ae95acab72de1115c4dd76f6d00a970d Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 25 May 2024 19:32:18 -0700 Subject: [PATCH 12/26] Update CppScripts.cpp --- dScripts/CppScripts.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index 73452c47..58ef948e 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -580,6 +580,7 @@ namespace { {"scripts\\02_server\\Map\\AM\\L_SKULLKIN_DRILL_STAND.lua", []() {return new AmSkullkinDrillStand();}}, {"scripts\\02_server\\Map\\AM\\L_SKULLKIN_TOWER.lua", []() {return new AmSkullkinTower();}}, {"scripts\\02_server\\Enemy\\AM\\L_AM_NAMED_DARKLING_DRAGON.lua", []() {return new AmDarklingDragon();}}, + {"scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_DRAGON.lua", []() {return new AmDarklingDragon();}}, {"scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_APE.lua", []() {return new BaseEnemyApe();}}, {"scripts\\02_server\\Map\\AM\\L_BLUE_X.lua", []() {return new AmBlueX();}}, {"scripts\\02_server\\Map\\AM\\L_TEAPOT_SERVER.lua", []() {return new AmTeapotServer();}}, @@ -654,6 +655,7 @@ namespace { //Pickups {"scripts\\ai\\SPEC\\L_SPECIAL_1_BRONZE-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(1);}}, + {"scripts\\ai\\SPEC\\L_SPECIAL_1_GOLD-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(10000);}}, {"scripts\\ai\\SPEC\\L_SPECIAL_1_SILVER-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(100);}}, {"scripts\\ai\\SPEC\\L_SPECIAL_10_BRONZE-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(10);}}, {"scripts\\ai\\SPEC\\L_SPECIAL_10_GOLD-COIN-SPAWNER.lua", []() {return new SpecialCoinSpawner(100000);}}, From efa658bc31cd1597d4f50ded3dc688f223cb7f56 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 25 May 2024 19:59:15 -0700 Subject: [PATCH 13/26] fix players using non-car skills in a race --- dGame/dComponents/RacingControlComponent.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index 21d39249..b896d3f2 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -25,6 +25,7 @@ #include "LeaderboardManager.h" #include "dZoneManager.h" #include "CDActivitiesTable.h" +#include "eStateChangeType.h" #include #ifndef M_PI @@ -77,6 +78,9 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) { m_LoadedPlayers++; + // not live accurate to stun the player but prevents them from using skills during the race that are not meant to be used. + GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, true, true, true, true, true, true, true, true, true); + LOG("Loading player %i", m_LoadedPlayers); m_LobbyPlayers.push_back(player->GetObjectID()); From e966d3a644f7a2b34efbc3855cdee6c99aeaef01 Mon Sep 17 00:00:00 2001 From: TAHuntling <38479763+TAHuntling@users.noreply.github.com> Date: Mon, 27 May 2024 01:24:48 -0500 Subject: [PATCH 14/26] Chore: split VE script up (#1598) * Testing Scripts Testing splitting AgSpaceStuff into AgSpaceStuff and AgShipShake * fixed inclusions * Removed DoShake * cleaning up * consistent if statements * Update dScripts/ai/AG/AgShipShake.h Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> --------- Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> --- dScripts/CppScripts.cpp | 2 + dScripts/ai/AG/AgShipShake.cpp | 81 +++++++++++++++++++++++++++++++++ dScripts/ai/AG/AgShipShake.h | 16 +++++++ dScripts/ai/AG/AgSpaceStuff.cpp | 80 -------------------------------- dScripts/ai/AG/AgSpaceStuff.h | 9 ---- dScripts/ai/AG/CMakeLists.txt | 1 + 6 files changed, 100 insertions(+), 89 deletions(-) create mode 100644 dScripts/ai/AG/AgShipShake.cpp create mode 100644 dScripts/ai/AG/AgShipShake.h diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index 58ef948e..ed0de2ba 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -14,6 +14,7 @@ #include "AgShipPlayerDeathTrigger.h" #include "AgShipPlayerShockServer.h" #include "AgSpaceStuff.h" +#include "AgShipShake.h" #include "AgImagSmashable.h" #include "NpcNpSpacemanBob.h" #include "StoryBoxInteractServer.h" @@ -341,6 +342,7 @@ namespace { { "scripts\\ai\\AG\\L_AG_SHIP_PLAYER_DEATH_TRIGGER.lua", []() { return new AgShipPlayerDeathTrigger(); } }, {"scripts\\ai\\NP\\L_NPC_NP_SPACEMAN_BOB.lua", []() { return new NpcNpSpacemanBob(); } }, {"scripts\\ai\\AG\\L_AG_SPACE_STUFF.lua", []() { return new AgSpaceStuff();} }, + {"scripts\\ai\\AG\\L_AG_SHIP_SHAKE.lua", []() { return new AgShipShake();}}, {"scripts\\ai\\AG\\L_AG_SHIP_PLAYER_SHOCK_SERVER.lua", []() { return new AgShipPlayerShockServer();} }, {"scripts\\ai\\AG\\L_AG_IMAG_SMASHABLE.lua", []() { return new AgImagSmashable();} }, {"scripts\\02_server\\Map\\General\\L_STORY_BOX_INTERACT_SERVER.lua", []() { return new StoryBoxInteractServer();} }, diff --git a/dScripts/ai/AG/AgShipShake.cpp b/dScripts/ai/AG/AgShipShake.cpp new file mode 100644 index 00000000..7dd2323a --- /dev/null +++ b/dScripts/ai/AG/AgShipShake.cpp @@ -0,0 +1,81 @@ +#include "AgShipShake.h" +#include "EntityInfo.h" +#include "GeneralUtils.h" +#include "GameMessages.h" +#include "EntityManager.h" +#include "RenderComponent.h" +#include "Entity.h" + +void AgShipShake::OnStartup(Entity* self) { + EntityInfo info{}; + + info.pos = { -418, 585, -30 }; + info.lot = 33; + info.spawnerID = self->GetObjectID(); + + auto* ref = Game::entityManager->CreateEntity(info); + + Game::entityManager->ConstructEntity(ref); + + self->SetVar(u"ShakeObject", ref->GetObjectID()); + + self->AddTimer("ShipShakeIdle", 2.0f); + self->SetVar(u"RandomTime", 10); +} + +void AgShipShake::OnTimerDone(Entity* self, std::string timerName) { + auto* shipFxObject = GetEntityInGroup(ShipFX); + auto* shipFxObject2 = GetEntityInGroup(ShipFX2); + auto* debrisObject = GetEntityInGroup(DebrisFX); + if (timerName == "ShipShakeIdle") { + auto* ref = Game::entityManager->GetEntity(self->GetVar(u"ShakeObject")); + + const auto randomTime = self->GetVar(u"RandomTime"); + auto time = GeneralUtils::GenerateRandomNumber(0, randomTime + 1); + + if (time < randomTime / 2) { + time += randomTime / 2; + } + + self->AddTimer("ShipShakeIdle", static_cast(time)); + + if (ref) + GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f); + + + if (debrisObject) + GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true); + + const auto randomFx = GeneralUtils::GenerateRandomNumber(0, 3); + + if (shipFxObject) { + std::string effectType = "shipboom" + std::to_string(randomFx); + GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true); + } + + self->AddTimer("ShipShakeExplode", 5.0f); + + if (shipFxObject2) + RenderComponent::PlayAnimation(shipFxObject2, u"explosion"); + } else if (timerName == "ShipShakeExplode") { + if (shipFxObject) + RenderComponent::PlayAnimation(shipFxObject, u"idle"); + if (shipFxObject2) + RenderComponent::PlayAnimation(shipFxObject2, u"idle"); + } +} + +Entity* AgShipShake::GetEntityInGroup(const std::string& group) { + auto entities = Game::entityManager->GetEntitiesInGroup(group); + Entity* en = nullptr; + + for (auto entity : entities) { + if (entity) { + en = entity; + break; + } + } + + return en; +} + diff --git a/dScripts/ai/AG/AgShipShake.h b/dScripts/ai/AG/AgShipShake.h new file mode 100644 index 00000000..4cc26f96 --- /dev/null +++ b/dScripts/ai/AG/AgShipShake.h @@ -0,0 +1,16 @@ +#pragma once +#include "CppScripts.h" + +class AgShipShake : public CppScripts::Script { +public: + void OnStartup(Entity* self) override; + void OnTimerDone(Entity* self, std::string timerName) override; + + std::string DebrisFX = "DebrisFX"; + std::string ShipFX = "ShipFX"; + std::string ShipFX2 = "ShipFX2"; + std::u16string FXName = u"camshake-bridge"; + +private: + Entity* GetEntityInGroup(const std::string& group); +}; diff --git a/dScripts/ai/AG/AgSpaceStuff.cpp b/dScripts/ai/AG/AgSpaceStuff.cpp index 130d4354..dedbd08c 100644 --- a/dScripts/ai/AG/AgSpaceStuff.cpp +++ b/dScripts/ai/AG/AgSpaceStuff.cpp @@ -8,21 +8,6 @@ void AgSpaceStuff::OnStartup(Entity* self) { self->AddTimer("FloaterScale", 5.0f); - - EntityInfo info{}; - - info.pos = { -418, 585, -30 }; - info.lot = 33; - info.spawnerID = self->GetObjectID(); - - auto* ref = Game::entityManager->CreateEntity(info); - - Game::entityManager->ConstructEntity(ref); - - self->SetVar(u"ShakeObject", ref->GetObjectID()); - - self->AddTimer("ShipShakeIdle", 2.0f); - self->SetVar(u"RandomTime", 10); } void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) { @@ -37,70 +22,5 @@ void AgSpaceStuff::OnTimerDone(Entity* self, std::string timerName) { RenderComponent::PlayAnimation(self, u"path_0" + (GeneralUtils::to_u16string(pathType))); self->AddTimer("FloaterScale", randTime); - } else if (timerName == "ShipShakeExplode") { - DoShake(self, true); - } else if (timerName == "ShipShakeIdle") { - DoShake(self, false); } } - -void AgSpaceStuff::DoShake(Entity* self, bool explodeIdle) { - - if (!explodeIdle) { - auto* ref = Game::entityManager->GetEntity(self->GetVar(u"ShakeObject")); - - const auto randomTime = self->GetVar(u"RandomTime"); - auto time = GeneralUtils::GenerateRandomNumber(0, randomTime + 1); - - if (time < randomTime / 2) { - time += randomTime / 2; - } - - self->AddTimer("ShipShakeIdle", static_cast(time)); - - if (ref) - GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(ref, FXName, ref->GetObjectID(), 500.0f); - - auto* debrisObject = GetEntityInGroup(DebrisFX); - - if (debrisObject) - GameMessages::SendPlayFXEffect(debrisObject, -1, u"DebrisFall", "Debris", LWOOBJID_EMPTY, 1.0f, 1.0f, true); - - const auto randomFx = GeneralUtils::GenerateRandomNumber(0, 3); - - auto* shipFxObject = GetEntityInGroup(ShipFX); - if (shipFxObject) { - std::string effectType = "shipboom" + std::to_string(randomFx); - GameMessages::SendPlayFXEffect(shipFxObject, 559, GeneralUtils::ASCIIToUTF16(effectType), "FX", LWOOBJID_EMPTY, 1.0f, 1.0f, true); - } - - self->AddTimer("ShipShakeExplode", 5.0f); - - auto* shipFxObject2 = GetEntityInGroup(ShipFX2); - if (shipFxObject2) - RenderComponent::PlayAnimation(shipFxObject2, u"explosion"); - } else { - auto* shipFxObject = GetEntityInGroup(ShipFX); - auto* shipFxObject2 = GetEntityInGroup(ShipFX2); - - if (shipFxObject) - RenderComponent::PlayAnimation(shipFxObject, u"idle"); - - if (shipFxObject2) - RenderComponent::PlayAnimation(shipFxObject2, u"idle"); - } -} - -Entity* AgSpaceStuff::GetEntityInGroup(const std::string& group) { - auto entities = Game::entityManager->GetEntitiesInGroup(group); - Entity* en = nullptr; - - for (auto entity : entities) { - if (entity) { - en = entity; - break; - } - } - - return en; -} diff --git a/dScripts/ai/AG/AgSpaceStuff.h b/dScripts/ai/AG/AgSpaceStuff.h index 8d816691..568f2baf 100644 --- a/dScripts/ai/AG/AgSpaceStuff.h +++ b/dScripts/ai/AG/AgSpaceStuff.h @@ -5,14 +5,5 @@ class AgSpaceStuff : public CppScripts::Script { public: void OnStartup(Entity* self); void OnTimerDone(Entity* self, std::string timerName); - void DoShake(Entity* self, bool explodeIdle); - - std::string DebrisFX = "DebrisFX"; - std::string ShipFX = "ShipFX"; - std::string ShipFX2 = "ShipFX2"; - std::u16string FXName = u"camshake-bridge"; - -private: - Entity* GetEntityInGroup(const std::string& group); }; diff --git a/dScripts/ai/AG/CMakeLists.txt b/dScripts/ai/AG/CMakeLists.txt index e74aac78..101f86fd 100644 --- a/dScripts/ai/AG/CMakeLists.txt +++ b/dScripts/ai/AG/CMakeLists.txt @@ -1,6 +1,7 @@ set(DSCRIPTS_SOURCES_AI_AG "AgShipPlayerDeathTrigger.cpp" "AgSpaceStuff.cpp" + "AgShipShake.cpp" "AgShipPlayerShockServer.cpp" "AgImagSmashable.cpp" "ActSharkPlayerDeathTrigger.cpp" From cce57553661c9287607a2965960fcfaaf4a62318 Mon Sep 17 00:00:00 2001 From: Remco Hofman Date: Tue, 28 May 2024 02:46:09 +0200 Subject: [PATCH 15/26] Fix Dockerfile vanity COPY (#1604) Corrected an unintended mistake in the COPY commands for adding the vanity files to the Docker container, causing only the last file contents to be added to the file '/app/vanity/*' --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index efb82b42..9086cf17 100644 --- a/Dockerfile +++ b/Dockerfile @@ -31,7 +31,7 @@ COPY --from=build /app/build/*Server /app/ # Necessary suplimentary files COPY --from=build /app/build/*.ini /app/configs/ -COPY --from=build /app/build/vanity/*.* /app/vanity/* +COPY --from=build /app/build/vanity/*.* /app/vanity/ COPY --from=build /app/build/navmeshes /app/navmeshes COPY --from=build /app/build/migrations /app/migrations COPY --from=build /app/build/*.dcf /app/ @@ -39,7 +39,7 @@ COPY --from=build /app/build/*.dcf /app/ # backup of config and vanity files to copy to the host incase # of a mount clobbering the copy from above COPY --from=build /app/build/*.ini /app/default-configs/ -COPY --from=build /app/build/vanity/*.* /app/default-vanity/* +COPY --from=build /app/build/vanity/*.* /app/default-vanity/ # needed as the container runs with the root user # and therefore sudo doesn't exist From 01086d05c8dd9c08fb9220262b887fae2f7e525e Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Thu, 30 May 2024 21:53:03 -0700 Subject: [PATCH 16/26] fix: use after free and uninitialized memory (#1603) * fix use after free and uninitialized memory * add if check for packet lengths * move purge down further Its used in the if check too... --- dChatServer/ChatServer.cpp | 1 + dCommon/Diagnostics.cpp | 2 +- dGame/Character.cpp | 2 ++ dGame/Character.h | 18 +++++++++--------- dGame/User.cpp | 1 + dGame/dComponents/BaseCombatAIComponent.cpp | 3 ++- dGame/dComponents/InventoryComponent.cpp | 10 +++++----- dMasterServer/MasterServer.cpp | 1 + dWorldServer/WorldServer.cpp | 15 +++++++++------ 9 files changed, 31 insertions(+), 22 deletions(-) diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index 81b6ddef..d6e99230 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -179,6 +179,7 @@ int main(int argc, char** argv) { } void HandlePacket(Packet* packet) { + if (packet->length < 1) return; 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."); } else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) { diff --git a/dCommon/Diagnostics.cpp b/dCommon/Diagnostics.cpp index 2ed27fef..9f129194 100644 --- a/dCommon/Diagnostics.cpp +++ b/dCommon/Diagnostics.cpp @@ -201,7 +201,7 @@ void OnTerminate() { } void MakeBacktrace() { - struct sigaction sigact; + struct sigaction sigact{}; sigact.sa_sigaction = CritErrHdlr; sigact.sa_flags = SA_RESTART | SA_SIGINFO; diff --git a/dGame/Character.cpp b/dGame/Character.cpp index 59a67462..57a951d9 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -27,6 +27,8 @@ Character::Character(uint32_t id, User* parentUser) { m_ID = id; m_ParentUser = parentUser; m_OurEntity = nullptr; + m_GMLevel = eGameMasterLevel::CIVILIAN; + m_PermissionMap = static_cast(0); } Character::~Character() { diff --git a/dGame/Character.h b/dGame/Character.h index 77f286f0..7a83325b 100644 --- a/dGame/Character.h +++ b/dGame/Character.h @@ -464,22 +464,22 @@ private: /** * The ID of this character. First 32 bits of the ObjectID. */ - uint32_t m_ID; + uint32_t m_ID{}; /** * The 64-bit unique ID used in the game. */ - LWOOBJID m_ObjectID; + LWOOBJID m_ObjectID{ LWOOBJID_EMPTY }; /** * The user that owns this character. */ - User* m_ParentUser; + User* m_ParentUser{}; /** * If the character is in game, this is the entity that it represents, else nullptr. */ - Entity* m_OurEntity; + Entity* m_OurEntity{}; /** * 0-9, the Game Master level of this character. @@ -506,17 +506,17 @@ private: /** * Whether the custom name of this character is rejected */ - bool m_NameRejected; + bool m_NameRejected{}; /** * The current amount of coins of this character */ - int64_t m_Coins; + int64_t m_Coins{}; /** * Whether the character is building */ - bool m_BuildMode; + bool m_BuildMode{}; /** * The items equipped by the character on world load @@ -583,7 +583,7 @@ private: /** * The ID of the properties of this character */ - uint32_t m_PropertyCloneID; + uint32_t m_PropertyCloneID{}; /** * The XML data for this character, stored as string @@ -613,7 +613,7 @@ private: /** * The last time this character logged in */ - uint64_t m_LastLogin; + uint64_t m_LastLogin{}; /** * The gameplay flags this character has (not just true values) diff --git a/dGame/User.cpp b/dGame/User.cpp index 0b2c3c3f..806d4611 100644 --- a/dGame/User.cpp +++ b/dGame/User.cpp @@ -12,6 +12,7 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std: m_AccountID = 0; m_Username = ""; m_SessionKey = ""; + m_MuteExpire = 0; m_MaxGMLevel = eGameMasterLevel::CIVILIAN; //The max GM level this account can assign to it's characters m_LastCharID = 0; diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index 60dceeef..bfb0bbfa 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -29,7 +29,8 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) { m_Target = LWOOBJID_EMPTY; - SetAiState(AiState::spawn); + m_DirtyStateOrTarget = true; + m_State = AiState::spawn; m_Timer = 1.0f; m_StartPosition = parent->GetPosition(); m_MovementAI = nullptr; diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index acb27796..172877b0 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -875,8 +875,6 @@ void InventoryComponent::UnEquipItem(Item* item) { RemoveSlot(item->GetInfo().equipLocation); - PurgeProxies(item); - UnequipScripts(item); Game::entityManager->SerializeEntity(m_Parent); @@ -886,6 +884,8 @@ void InventoryComponent::UnEquipItem(Item* item) { PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent); Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent); } + + PurgeProxies(item); } @@ -1505,10 +1505,10 @@ void InventoryComponent::PurgeProxies(Item* item) { const auto root = item->GetParent(); if (root != LWOOBJID_EMPTY) { - item = FindItemById(root); + Item* itemRoot = FindItemById(root); - if (item != nullptr) { - UnEquipItem(item); + if (itemRoot != nullptr) { + UnEquipItem(itemRoot); } return; diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 3d5f4aff..09d1bba9 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -382,6 +382,7 @@ int main(int argc, char** argv) { } void HandlePacket(Packet* packet) { + if (packet->length < 1) return; if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION) { LOG("A server has disconnected"); diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 9e55d8b7..d3dc78cc 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -529,6 +529,7 @@ int main(int argc, char** argv) { } void HandlePacketChat(Packet* packet) { + if (packet->length < 1) return; 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()); @@ -542,7 +543,7 @@ void HandlePacketChat(Packet* packet) { chatConnected = true; } - if (packet->data[0] == ID_USER_PACKET_ENUM) { + if (packet->data[0] == ID_USER_PACKET_ENUM && packet->length >= 4) { if (static_cast(packet->data[1]) == eConnectionType::CHAT) { switch (static_cast(packet->data[3])) { case eChatMessageType::WORLD_ROUTE_PACKET: { @@ -557,8 +558,9 @@ void HandlePacketChat(Packet* packet) { //Write our stream outwards: CBITSTREAM; - for (BitSize_t i = 0; i < inStream.GetNumberOfBytesUsed(); i++) { - bitStream.Write(packet->data[i + 16]); //16 bytes == header + playerID to skip + unsigned char data; + while (inStream.Read(data)) { + bitStream.Write(data); } SEND_PACKET; //send routed packet to player @@ -659,7 +661,7 @@ void HandlePacketChat(Packet* packet) { } void HandleMasterPacket(Packet* packet) { - + if (packet->length < 2) return; if (static_cast(packet->data[1]) != eConnectionType::MASTER || packet->length < 4) return; switch (static_cast(packet->data[3])) { case eMasterMessageType::REQUEST_PERSISTENT_ID_RESPONSE: { @@ -785,6 +787,7 @@ void HandleMasterPacket(Packet* packet) { } void HandlePacket(Packet* packet) { + if (packet->length < 1) return; if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { auto user = UserManager::Instance()->GetUser(packet->systemAddress); if (!user) return; @@ -1207,8 +1210,8 @@ void HandlePacket(Packet* packet) { //Now write the rest of the data: auto data = inStream.GetData(); - for (uint32_t i = 0; i < size; ++i) { - bitStream.Write(data[i + 23]); + for (uint32_t i = 23; i - 23 < size && i < packet->length; ++i) { + bitStream.Write(data[i]); } Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, Game::chatSysAddr, false); From 342da927f5cd23eb69c8d682b0847b5742cae838 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Thu, 30 May 2024 21:53:13 -0700 Subject: [PATCH 17/26] fix dimantling items from not the model inventory (#1605) --- dGame/dGameMessages/GameMessages.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 1f757d7e..a97407d8 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -102,6 +102,7 @@ #include "CDComponentsRegistryTable.h" #include "CDObjectsTable.h" +#include "eItemType.h" void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) { CBITSTREAM; @@ -5352,7 +5353,8 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream& inStream, En iStackCount = std::min(item->GetCount(), iStackCount); if (bConfirmed) { - if (eInvType == eInventoryType::MODELS) { + const auto itemType = static_cast(item->GetInfo().itemType); + if (itemType == eItemType::MODEL || itemType == eItemType::LOOT_MODEL) { item->DisassembleModel(iStackCount); } auto lot = item->GetLot(); From a54600b41ed871f80bb8791f7ae321e7f9012661 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Fri, 31 May 2024 11:46:18 -0700 Subject: [PATCH 18/26] busting out the multimap ig (#1602) --- dGame/dBehaviors/BehaviorContext.cpp | 7 ++++--- dGame/dBehaviors/BehaviorContext.h | 2 +- dGame/dComponents/SkillComponent.cpp | 23 +++++++++++++++-------- dGame/dComponents/SkillComponent.h | 2 +- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/dGame/dBehaviors/BehaviorContext.cpp b/dGame/dBehaviors/BehaviorContext.cpp index a3721d8f..abba74be 100644 --- a/dGame/dBehaviors/BehaviorContext.cpp +++ b/dGame/dBehaviors/BehaviorContext.cpp @@ -105,7 +105,7 @@ void BehaviorContext::ExecuteUpdates() { this->scheduledUpdates.clear(); } -void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) { +bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) { BehaviorSyncEntry entry; auto found = false; @@ -128,7 +128,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit if (!found) { LOG("Failed to find behavior sync entry with sync id (%i)!", syncId); - return; + return false; } auto* behavior = entry.behavior; @@ -137,10 +137,11 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit if (behavior == nullptr) { LOG("Invalid behavior for sync id (%i)!", syncId); - return; + return false; } behavior->Sync(this, bitStream, branch); + return true; } diff --git a/dGame/dBehaviors/BehaviorContext.h b/dGame/dBehaviors/BehaviorContext.h index 3e6c0b1d..4922f736 100644 --- a/dGame/dBehaviors/BehaviorContext.h +++ b/dGame/dBehaviors/BehaviorContext.h @@ -93,7 +93,7 @@ struct BehaviorContext void ExecuteUpdates(); - void SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream); + bool SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream); void Update(float deltaTime); diff --git a/dGame/dComponents/SkillComponent.cpp b/dGame/dComponents/SkillComponent.cpp index d926ff2c..58b13dc5 100644 --- a/dGame/dComponents/SkillComponent.cpp +++ b/dGame/dComponents/SkillComponent.cpp @@ -38,7 +38,7 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s context->skillID = skillID; - this->m_managedBehaviors.insert_or_assign(skillUid, context); + this->m_managedBehaviors.insert({ skillUid, context }); auto* behavior = Behavior::CreateBehavior(behaviorId); @@ -52,17 +52,24 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s } void SkillComponent::SyncPlayerSkill(const uint32_t skillUid, const uint32_t syncId, RakNet::BitStream& bitStream) { - const auto index = this->m_managedBehaviors.find(skillUid); + const auto index = this->m_managedBehaviors.equal_range(skillUid); - if (index == this->m_managedBehaviors.end()) { + if (index.first == this->m_managedBehaviors.end()) { LOG("Failed to find skill with uid (%i)!", skillUid, syncId); return; } - auto* context = index->second; + bool foundSyncId = false; + for (auto it = index.first; it != index.second && !foundSyncId; ++it) { + const auto& context = it->second; - context->SyncBehavior(syncId, bitStream); + foundSyncId = context->SyncBehavior(syncId, bitStream); + } + + if (!foundSyncId) { + LOG("Failed to find sync id (%i) for skill with uid (%i)!", syncId, skillUid); + } } @@ -138,7 +145,7 @@ void SkillComponent::Update(const float deltaTime) { for (const auto& pair : this->m_managedBehaviors) pair.second->UpdatePlayerSyncs(deltaTime); } - std::map keep{}; + std::multimap keep{}; for (const auto& pair : this->m_managedBehaviors) { auto* context = pair.second; @@ -176,7 +183,7 @@ void SkillComponent::Update(const float deltaTime) { } } - keep.insert_or_assign(pair.first, context); + keep.insert({ pair.first, context }); } this->m_managedBehaviors = keep; @@ -285,7 +292,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior( return { false, 0 }; } - this->m_managedBehaviors.insert_or_assign(context->skillUId, context); + this->m_managedBehaviors.insert({ context->skillUId, context }); if (!clientInitalized) { // Echo start skill diff --git a/dGame/dComponents/SkillComponent.h b/dGame/dComponents/SkillComponent.h index 24d92148..f74b4411 100644 --- a/dGame/dComponents/SkillComponent.h +++ b/dGame/dComponents/SkillComponent.h @@ -188,7 +188,7 @@ private: /** * All of the active skills mapped by their unique ID. */ - std::map m_managedBehaviors; + std::multimap m_managedBehaviors; /** * All active projectiles. From b56d077892f73d1ff4d1626a20e0717a541ca2a5 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Mon, 3 Jun 2024 21:50:12 -0500 Subject: [PATCH 19/26] feat: spectate command --- dGame/dGameMessages/GameMessages.cpp | 14 ++++++++++++++ dGame/dGameMessages/GameMessages.h | 6 ++++++ dGame/dUtilities/SlashCommandHandler.cpp | 9 +++++++++ .../SlashCommands/GMGreaterThanZeroCommands.cpp | 8 ++++++++ .../SlashCommands/GMGreaterThanZeroCommands.h | 1 + 5 files changed, 38 insertions(+) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index a97407d8..c6a4cf3d 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -6212,3 +6212,17 @@ void GameMessages::SendSlashCommandFeedbackText(Entity* entity, std::u16string t auto sysAddr = entity->GetSystemAddress(); SEND_PACKET; } + +void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(eGameMessageType::FORCE_CAMERA_TARGET_CYCLE); + bitStream.Write(bForceCycling); + bitStream.Write(cyclingMode); + bitStream.Write(optionalTargetID); + + auto sysAddr = entity->GetSystemAddress(); + SEND_PACKET; +} diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index b842710e..21bdfb41 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -40,6 +40,11 @@ enum class eQuickBuildState : uint32_t; enum class BehaviorSlot : int32_t; enum class eVendorTransactionResult : uint32_t; +enum class eCameraTargetCyclingMode : int32_t { + ALLOW_CYCLE_TEAMMATES, + DISALLOW_CYCLING +}; + namespace GameMessages { class PropertyDataMessage; void SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender); @@ -666,6 +671,7 @@ namespace GameMessages { void HandleCancelDonationOnPlayer(RakNet::BitStream& inStream, Entity* entity); void SendSlashCommandFeedbackText(Entity* entity, std::u16string text); + void SendForceCameraTargetCycle(Entity* entity, bool bForceCycling, eCameraTargetCyclingMode cyclingMode, LWOOBJID optionalTargetID); }; #endif // GAMEMESSAGES_H diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 428ccbcb..20f2634a 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -929,6 +929,15 @@ void SlashCommandHandler::Startup() { }; RegisterCommand(FindPlayerCommand); + Command SpectateCommand{ + .help = "Spectate a player", + .info = "Specify a player name to spectate. They must be in the same world as you", + .aliases = { "spectate", "follow" }, + .handle = GMGreaterThanZeroCommands::Spectate, + .requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR + }; + RegisterCommand(SpectateCommand); + // Register GM Zero Commands Command HelpCommand{ diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp index ea33aa03..e2802d62 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp @@ -322,4 +322,12 @@ namespace GMGreaterThanZeroCommands { request.Serialize(bitStream); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); } + + void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + if (args.length() <= 0) GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given"); + auto player = PlayerManager::GetPlayer(args); + if (!player) GameMessages::SendSlashCommandFeedbackText(entity, u"Player not found"); + GameMessages::SendSlashCommandFeedbackText(entity, u"Spectating Player"); + GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, player->GetObjectID()); + } } diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h index 7cb3d8d7..c278fc0a 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h @@ -15,6 +15,7 @@ namespace GMGreaterThanZeroCommands { void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args); void ShowAll(Entity* entity, const SystemAddress& sysAddr, const std::string args); void FindPlayer(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args); } #endif //!GMGREATERTHANZEROCOMMANDS_H From 2ef45bd7ee8bf466c52a90e286370f5221bc59e0 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Mon, 3 Jun 2024 22:28:37 -0500 Subject: [PATCH 20/26] use empty --- dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp index e2802d62..e71c0058 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp @@ -324,7 +324,7 @@ namespace GMGreaterThanZeroCommands { } void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - if (args.length() <= 0) GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given"); + if (args.empty()) GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given"); auto player = PlayerManager::GetPlayer(args); if (!player) GameMessages::SendSlashCommandFeedbackText(entity, u"Player not found"); GameMessages::SendSlashCommandFeedbackText(entity, u"Spectating Player"); From 1a14c29c39ff8e00f999cfe9ee5fd1ea73d4bcdf Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Mon, 3 Jun 2024 22:29:21 -0500 Subject: [PATCH 21/26] add returns, lol --- .../SlashCommands/GMGreaterThanZeroCommands.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp index e71c0058..c1cb6b4b 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp @@ -324,9 +324,16 @@ namespace GMGreaterThanZeroCommands { } void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - if (args.empty()) GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given"); + if (args.empty()) { + GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given"); + return; + } + auto player = PlayerManager::GetPlayer(args); - if (!player) GameMessages::SendSlashCommandFeedbackText(entity, u"Player not found"); + if (!player) { + GameMessages::SendSlashCommandFeedbackText(entity, u"Player not found"); + return; + } GameMessages::SendSlashCommandFeedbackText(entity, u"Spectating Player"); GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, player->GetObjectID()); } From 9d5d2a68eedc9172455370a3af8367fd4b949691 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Mon, 3 Jun 2024 22:30:57 -0500 Subject: [PATCH 22/26] fix gm serialization --- dGame/dGameMessages/GameMessages.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index c6a4cf3d..a80bc1aa 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -6220,7 +6220,8 @@ void GameMessages::SendForceCameraTargetCycle(Entity* entity, bool bForceCycling bitStream.Write(entity->GetObjectID()); bitStream.Write(eGameMessageType::FORCE_CAMERA_TARGET_CYCLE); bitStream.Write(bForceCycling); - bitStream.Write(cyclingMode); + bitStream.Write(cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES); + if (cyclingMode != eCameraTargetCyclingMode::ALLOW_CYCLE_TEAMMATES) bitStream.Write(cyclingMode); bitStream.Write(optionalTargetID); auto sysAddr = entity->GetSystemAddress(); From 3f22bf5cc04e914273481ea51a0f82a6d79d9aa1 Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Mon, 3 Jun 2024 22:44:54 -0500 Subject: [PATCH 23/26] Add an easy way to stop spectating --- dGame/dUtilities/SlashCommandHandler.cpp | 2 +- dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 20f2634a..477044b1 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -931,7 +931,7 @@ void SlashCommandHandler::Startup() { Command SpectateCommand{ .help = "Spectate a player", - .info = "Specify a player name to spectate. They must be in the same world as you", + .info = "Specify a player name to spectate. They must be in the same world as you. Leave blank to stop spectating", .aliases = { "spectate", "follow" }, .handle = GMGreaterThanZeroCommands::Spectate, .requiredLevel = eGameMasterLevel::JUNIOR_MODERATOR diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp index c1cb6b4b..19a40983 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp @@ -325,6 +325,7 @@ namespace GMGreaterThanZeroCommands { void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { if (args.empty()) { + GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, entity->GetObjectID()); GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given"); return; } From ff38503597cf2c50e75ae60cd4fa4a7f7787a88d Mon Sep 17 00:00:00 2001 From: Aaron Kimbre Date: Mon, 3 Jun 2024 22:51:46 -0500 Subject: [PATCH 24/26] no feedback if empty --- dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp index 19a40983..b9eaf7bf 100644 --- a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp @@ -326,7 +326,6 @@ namespace GMGreaterThanZeroCommands { void Spectate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { if (args.empty()) { GameMessages::SendForceCameraTargetCycle(entity, false, eCameraTargetCyclingMode::DISALLOW_CYCLING, entity->GetObjectID()); - GameMessages::SendSlashCommandFeedbackText(entity, u"No player Given"); return; } From 1454fcd00376a022f1568b23c633878f600cba1d Mon Sep 17 00:00:00 2001 From: Wincent Holm Date: Thu, 6 Jun 2024 11:00:44 +0200 Subject: [PATCH 25/26] Fix g++ 14 (#1610) * Fix g++ 14 * Update thirdparty/CMakeLists.txt Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> --------- Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> --- dCommon/dConfig.cpp | 1 + dNet/BitStreamUtils.h | 1 + thirdparty/CMakeLists.txt | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/dCommon/dConfig.cpp b/dCommon/dConfig.cpp index 56a43848..bed274b0 100644 --- a/dCommon/dConfig.cpp +++ b/dCommon/dConfig.cpp @@ -1,6 +1,7 @@ #include "dConfig.h" #include +#include #include "BinaryPathFinder.h" #include "GeneralUtils.h" diff --git a/dNet/BitStreamUtils.h b/dNet/BitStreamUtils.h index 1322ec95..33fde564 100644 --- a/dNet/BitStreamUtils.h +++ b/dNet/BitStreamUtils.h @@ -5,6 +5,7 @@ #include "MessageIdentifiers.h" #include "BitStream.h" #include +#include enum class eConnectionType : uint16_t; diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index 41135a80..f6056476 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -23,6 +23,11 @@ if(NOT WIN32) target_include_directories(bcrypt PRIVATE "libbcrypt/include/bcrypt") endif() +# Need to define this on Clang and GNU for 'strdup' support +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU") + target_compile_definitions(bcrypt PRIVATE "_POSIX_C_SOURCE=200809L") +endif() + target_include_directories(bcrypt INTERFACE "libbcrypt/include") target_include_directories(bcrypt PRIVATE "libbcrypt/src") From fee0238e790febe2d7afcbbddbe124854af8f27d Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sun, 9 Jun 2024 15:31:57 -0700 Subject: [PATCH 26/26] fix: master not using table data, remove 2 noisy logs (#1613) Tested with logs that queries to get soft and hard cap actually succeed now Logs about slash command handler command registration and vanity NPC creation in mis matched worlds are now removed. --- dGame/dUtilities/SlashCommandHandler.cpp | 1 - dGame/dUtilities/VanityUtilities.cpp | 1 - dMasterServer/MasterServer.cpp | 12 ++++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 477044b1..ca4e03d4 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -31,7 +31,6 @@ void SlashCommandHandler::RegisterCommand(Command command) { } for (const auto& alias : command.aliases) { - LOG_DEBUG("Registering command %s", alias.c_str()); auto [_, success] = RegisteredCommands.try_emplace(alias, command); // Don't allow duplicate commands if (!success) { diff --git a/dGame/dUtilities/VanityUtilities.cpp b/dGame/dUtilities/VanityUtilities.cpp index cb893da3..6043fe63 100644 --- a/dGame/dUtilities/VanityUtilities.cpp +++ b/dGame/dUtilities/VanityUtilities.cpp @@ -285,7 +285,6 @@ void ParseXml(const std::string& file) { } if (zoneID.value() != currentZoneID) { - LOG_DEBUG("Skipping (%s) %i location because it is in %i and not the current zone (%i)", name, lot, zoneID.value(), currentZoneID); continue; } diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 09d1bba9..0f36ee37 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -40,6 +40,7 @@ #include "BitStreamUtils.h" #include "Start.h" #include "Server.h" +#include "CDZoneTableTable.h" namespace Game { Logger* logger = nullptr; @@ -277,6 +278,17 @@ int main(int argc, char** argv) { PersistentIDManager::Initialize(); Game::im = new InstanceManager(Game::logger, Game::server->GetIP()); + //Get CDClient initial information + try { + CDClientManager::LoadValuesFromDatabase(); + } catch (CppSQLite3Exception& e) { + LOG("Failed to initialize CDServer SQLite Database"); + LOG("May be caused by corrupted file: %s", (Game::assetManager->GetResPath() / "CDServer.sqlite").string().c_str()); + LOG("Error: %s", e.errorMessage()); + LOG("Error Code: %i", e.errorCode()); + return EXIT_FAILURE; + } + //Depending on the config, start up servers: if (Game::config->GetValue("prestart_servers") != "0") { StartChatServer();