From d067a8d12f8faf4c6b8c715d1d679b45ea1ecb29 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 9 Apr 2024 20:15:51 -0500 Subject: [PATCH] chore: split out slash commands into multiple files (#1539) * chore: split out slash commands into multiple files Breakup the monolithic file don't register slashcommands on startup * fix typo --- dGame/dUtilities/CMakeLists.txt | 8 +- dGame/dUtilities/SlashCommandHandler.cpp | 2219 +---------------- dGame/dUtilities/SlashCommandHandler.h | 102 +- dGame/dUtilities/SlashCommands/CMakeLists.txt | 6 + .../SlashCommands/DEVGMCommands.cpp | 1592 ++++++++++++ .../dUtilities/SlashCommands/DEVGMCommands.h | 73 + .../GMGreaterThanZeroCommands.cpp | 290 +++ .../SlashCommands/GMGreaterThanZeroCommands.h | 13 + .../SlashCommands/GMZeroCommands.cpp | 228 ++ .../dUtilities/SlashCommands/GMZeroCommands.h | 15 + dWorldServer/WorldServer.cpp | 4 +- 11 files changed, 2297 insertions(+), 2253 deletions(-) create mode 100644 dGame/dUtilities/SlashCommands/CMakeLists.txt create mode 100644 dGame/dUtilities/SlashCommands/DEVGMCommands.cpp create mode 100644 dGame/dUtilities/SlashCommands/DEVGMCommands.h create mode 100644 dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp create mode 100644 dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h create mode 100644 dGame/dUtilities/SlashCommands/GMZeroCommands.cpp create mode 100644 dGame/dUtilities/SlashCommands/GMZeroCommands.h diff --git a/dGame/dUtilities/CMakeLists.txt b/dGame/dUtilities/CMakeLists.txt index 1e54b59b..2202fddc 100644 --- a/dGame/dUtilities/CMakeLists.txt +++ b/dGame/dUtilities/CMakeLists.txt @@ -8,9 +8,15 @@ set(DGAME_DUTILITIES_SOURCES "BrickDatabase.cpp" "SlashCommandHandler.cpp" "VanityUtilities.cpp") +add_subdirectory(SlashCommands) + +foreach(file ${DGAME_DUTILITIES_SLASHCOMMANDS}) + set(DGAME_DUTILITIES_SOURCES ${DGAME_DUTILITIES_SOURCES} "SlashCommands/${file}") +endforeach() + add_library(dUtilities OBJECT ${DGAME_DUTILITIES_SOURCES}) target_precompile_headers(dUtilities REUSE_FROM dGameBase) -target_include_directories(dUtilities PUBLIC "." +target_include_directories(dUtilities PUBLIC "." "SlashCommands" PRIVATE "${PROJECT_SOURCE_DIR}/dGame/dComponents" "${PROJECT_SOURCE_DIR}/dGame/dInventory" # transitive via PossessableComponent.h diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 34ae013c..bd7d5f28 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -3,83 +3,20 @@ * Copyright 2024 */ + + #include "SlashCommandHandler.h" -#include -#include -#include -#include -#include "dZoneManager.h" +#include -#include "Metrics.hpp" +#include "DEVGMCommands.h" +#include "GMGreaterThanZeroCommands.h" +#include "GMZeroCommands.h" -#include "User.h" -#include "UserManager.h" -#include "BitStream.h" -#include "dCommonVars.h" -#include "GeneralUtils.h" -#include "Entity.h" -#include "EntityManager.h" -#include "Logger.h" -#include "WorldPackets.h" -#include "GameMessages.h" -#include "CDClientDatabase.h" -#include "ZoneInstanceManager.h" -#include "ControllablePhysicsComponent.h" -#include "NiPoint3.h" -#include "NiQuaternion.h" -#include "ChatPackets.h" -#include "InventoryComponent.h" -#include "Game.h" -#include "CharacterComponent.h" -#include "Database.h" -#include "DestroyableComponent.h" -#include "dServer.h" -#include "MissionComponent.h" -#include "Mail.h" -#include "dpWorld.h" -#include "Item.h" -#include "PropertyManagementComponent.h" -#include "BitStreamUtils.h" -#include "Loot.h" -#include "EntityInfo.h" -#include "LUTriggers.h" -#include "PhantomPhysicsComponent.h" -#include "ProximityMonitorComponent.h" -#include "dpShapeSphere.h" -#include "PossessableComponent.h" -#include "PossessorComponent.h" -#include "HavokVehiclePhysicsComponent.h" -#include "BuffComponent.h" -#include "SkillComponent.h" -#include "VanityUtilities.h" -#include "ScriptedActivityComponent.h" -#include "LevelProgressionComponent.h" -#include "AssetManager.h" -#include "BinaryPathFinder.h" -#include "dConfig.h" -#include "eBubbleType.h" #include "Amf3.h" -#include "MovingPlatformComponent.h" -#include "eMissionState.h" -#include "TriggerComponent.h" -#include "eServerDisconnectIdentifiers.h" -#include "eObjectBits.h" -#include "eGameMasterLevel.h" -#include "eReplicaComponentType.h" -#include "RenderComponent.h" -#include "eControlScheme.h" -#include "eConnectionType.h" +#include "Database.h" #include "eChatMessageType.h" -#include "eMasterMessageType.h" -#include "PlayerManager.h" - -#include "CDRewardCodesTable.h" -#include "CDObjectsTable.h" -#include "CDZoneTableTable.h" -#include "ePlayerFlag.h" -#include "dNavMesh.h" -#include +#include "dServer.h" namespace { std::vector CommandInfos; @@ -137,8 +74,69 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* } } -void SlashCommandHandler::Startup() { +// This commands in here so we can access the CommandInfos to display info +void GMZeroCommands::Help(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + std::ostringstream feedback; + if (args.empty()) { + feedback << "----- Commands -----"; + for (const auto& command : CommandInfos) { + // TODO: Limit displaying commands based on GM level they require + if (command.requiredLevel > entity->GetGMLevel()) continue; + LOG("Help command: %s", command.aliases[0].c_str()); + feedback << "\n/" << command.aliases[0] << ": " << command.help; + } + } else { + bool foundCommand = false; + for (const auto& command : CommandInfos) { + if (std::ranges::find(command.aliases, args) == command.aliases.end()) continue; + if (entity->GetGMLevel() < command.requiredLevel) break; + foundCommand = true; + feedback << "----- " << command.aliases.at(0) << " -----\n"; + // info can be a localizable string + feedback << command.info; + if (command.aliases.size() == 1) break; + + feedback << "\nAliases: "; + for (size_t i = 0; i < command.aliases.size(); i++) { + if (i > 0) feedback << ", "; + feedback << command.aliases[i]; + } + } + + // Let GameMasters know if the command doesn't exist + if (!foundCommand && entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) feedback << "Command " << std::quoted(args) << " does not exist!"; + } + const auto feedbackStr = feedback.str(); + if (!feedbackStr.empty()) GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); +} + +void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) { + AMFArrayValue args; + + args.Insert("title", title); + args.Insert("message", message); + + GameMessages::SendUIMessageServerToAllClients("ToggleAnnounce", args); + + //Notify chat about it + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_ANNOUNCE); + + bitStream.Write(title.size()); + for (auto character : title) { + bitStream.Write(character); + } + + bitStream.Write(message.size()); + for (auto character : message) { + bitStream.Write(character); + } + + Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); +} + +void SlashCommandHandler::Startup() { // Register Dev Commands Command SetGMLevelCommand{ .help = "Change the GM level of your character", @@ -1000,2080 +998,3 @@ void SlashCommandHandler::Startup() { RegisterCommand(InstanceInfoCommand); } - -namespace GMZeroCommands { - // The star delimiter is to be used for marking the start and end of a localized string. - void Help(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - std::ostringstream feedback; - if (args.empty()) { - feedback << "----- Commands -----"; - for (const auto& command : CommandInfos) { - // TODO: Limit displaying commands based on GM level they require - if (command.requiredLevel > entity->GetGMLevel()) continue; - LOG("Help command: %s", command.aliases[0].c_str()); - feedback << "\n/" << command.aliases[0] << ": " << command.help; - } - } else { - bool foundCommand = false; - for (const auto& command : CommandInfos) { - if (std::ranges::find(command.aliases, args) == command.aliases.end()) continue; - - if (entity->GetGMLevel() < command.requiredLevel) break; - foundCommand = true; - feedback << "----- " << command.aliases.at(0) << " -----\n"; - // info can be a localizable string - feedback << command.info; - if (command.aliases.size() == 1) break; - - feedback << "\nAliases: "; - for (size_t i = 0; i < command.aliases.size(); i++) { - if (i > 0) feedback << ", "; - feedback << command.aliases[i]; - } - } - - // Let GameMasters know if the command doesn't exist - if (!foundCommand && entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) feedback << "Command " << std::quoted(args) << " does not exist!"; - } - const auto feedbackStr = feedback.str(); - if (!feedbackStr.empty()) GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); - } - - void Pvp(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto* character = entity->GetComponent(); - - if (character == nullptr) { - LOG("Failed to find character component!"); - return; - } - - character->SetPvpEnabled(!character->GetPvpEnabled()); - Game::entityManager->SerializeEntity(entity); - - std::stringstream message; - message << character->GetName() << " changed their PVP flag to " << std::to_string(character->GetPvpEnabled()) << "!"; - - ChatPackets::SendSystemMessage(UNASSIGNED_SYSTEM_ADDRESS, GeneralUtils::UTF8ToUTF16(message.str()), true); - } - - void Who(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - ChatPackets::SendSystemMessage( - sysAddr, - u"Players in this instance: (" + GeneralUtils::to_u16string(PlayerManager::GetAllPlayers().size()) + u")" - ); - - for (auto* player : PlayerManager::GetAllPlayers()) { - const auto& name = player->GetCharacter()->GetName(); - - ChatPackets::SendSystemMessage( - sysAddr, - GeneralUtils::UTF8ToUTF16(player == entity ? name + " (you)" : name) - ); - } - } - - void Ping(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - if (!args.empty() && args.starts_with("-l")) { - std::stringstream message; - message << "Your latest ping: " << std::to_string(Game::server->GetLatestPing(sysAddr)) << "ms"; - - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(message.str())); - } else { - std::stringstream message; - message << "Your average ping: " << std::to_string(Game::server->GetPing(sysAddr)) << "ms"; - - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(message.str())); - } - } - - void FixStats(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - // Reset skill component and buff component - auto* skillComponent = entity->GetComponent(); - auto* buffComponent = entity->GetComponent(); - auto* destroyableComponent = entity->GetComponent(); - - // If any of the components are nullptr, return - if (skillComponent == nullptr || buffComponent == nullptr || destroyableComponent == nullptr) { - return; - } - - // Reset skill component - skillComponent->Reset(); - - // Reset buff component - buffComponent->Reset(); - - // Fix the destroyable component - destroyableComponent->FixStats(); - } - - void Credits(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto& customText = VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/CREDITS.md").string()); - - { - AMFArrayValue args; - - args.Insert("state", "Story"); - - GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", args); - } - - entity->AddCallbackTimer(0.5f, [customText, entity]() { - AMFArrayValue args; - - args.Insert("visible", true); - args.Insert("text", customText); - - LOG("Sending %s", customText.c_str()); - - GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "ToggleStoryBox", args); - }); - } - - void Info(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto& customText = VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/INFO.md").string()); - - { - AMFArrayValue args; - - args.Insert("state", "Story"); - - GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", args); - } - - entity->AddCallbackTimer(0.5f, [customText, entity]() { - AMFArrayValue args; - - args.Insert("visible", true); - args.Insert("text", customText); - - LOG("Sending %s", customText.c_str()); - - GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "ToggleStoryBox", args); - }); - } - - void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto currentZone = Game::zoneManager->GetZone()->GetZoneID().GetMapID(); - LWOMAPID newZone = 0; - - if (currentZone == 1001 || currentZone % 100 == 0) { - ChatPackets::SendSystemMessage(sysAddr, u"You are not in an instanced zone."); - return; - } else { - newZone = (currentZone / 100) * 100; - } - // If new zone would be inaccessible, then default to Avant Gardens. - if (!Game::zoneManager->CheckIfAccessibleZone(newZone)) newZone = 1100; - - ChatPackets::SendSystemMessage(sysAddr, u"Leaving zone..."); - - const auto objid = entity->GetObjectID(); - - ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, newZone, 0, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { - auto* entity = Game::entityManager->GetEntity(objid); - - if (entity == nullptr) { - return; - } - - const auto sysAddr = entity->GetSystemAddress(); - - LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", entity->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); - - if (entity->GetCharacter()) { - entity->GetCharacter()->SetZoneID(zoneID); - entity->GetCharacter()->SetZoneInstance(zoneInstance); - entity->GetCharacter()->SetZoneClone(zoneClone); - } - - entity->GetCharacter()->SaveXMLToDatabase(); - - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); - }); - } - - void Join(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - ChatPackets::SendSystemMessage(sysAddr, u"Requesting private map..."); - const auto& password = splitArgs[0]; - - ZoneInstanceManager::Instance()->RequestPrivateZone(Game::server, false, password, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { - LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); - - if (entity->GetCharacter()) { - entity->GetCharacter()->SetZoneID(zoneID); - entity->GetCharacter()->SetZoneInstance(zoneInstance); - entity->GetCharacter()->SetZoneClone(zoneClone); - } - - entity->GetCharacter()->SaveXMLToDatabase(); - - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); - }); - } - - void Die(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - entity->Smash(entity->GetObjectID()); - } - - void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - ScriptedActivityComponent* scriptedActivityComponent = Game::zoneManager->GetZoneControlObject()->GetComponent(); - - if (scriptedActivityComponent) { // check if user is in activity world and if so, they can't resurrect - ChatPackets::SendSystemMessage(sysAddr, u"You cannot resurrect in an activity world."); - return; - } - - GameMessages::SendResurrect(entity); - } - - void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - Mail::HandleNotificationRequest(entity->GetSystemAddress(), entity->GetObjectID()); - } - - void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto zoneId = Game::zoneManager->GetZone()->GetZoneID(); - - ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID()))); - } -}; - -namespace DEVGMCommands { - void SetGMLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - User* user = UserManager::Instance()->GetUser(entity->GetSystemAddress()); - - const auto level_intermed = GeneralUtils::TryParse(args); - if (!level_intermed) { - GameMessages::SendSlashCommandFeedbackText(entity, u"Invalid GM level."); - return; - } - eGameMasterLevel level = static_cast(level_intermed.value()); - -#ifndef DEVELOPER_SERVER - if (user->GetMaxGMLevel() == eGameMasterLevel::JUNIOR_DEVELOPER) { - level = eGameMasterLevel::CIVILIAN; - } -#endif - - if (level > user->GetMaxGMLevel()) level = user->GetMaxGMLevel(); - - if (level == entity->GetGMLevel()) return; - bool success = user->GetMaxGMLevel() >= level; - - if (success) { - WorldPackets::SendGMLevelChange(entity->GetSystemAddress(), success, user->GetMaxGMLevel(), entity->GetGMLevel(), level); - GameMessages::SendChatModeUpdate(entity->GetObjectID(), level); - entity->SetGMLevel(level); - LOG("User %s (%i) has changed their GM level to %i for charID %llu", user->GetUsername().c_str(), user->GetAccountID(), level, entity->GetObjectID()); - } - -#ifndef DEVELOPER_SERVER - if ((entity->GetGMLevel() > user->GetMaxGMLevel()) || (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN && user->GetMaxGMLevel() == eGameMasterLevel::JUNIOR_DEVELOPER)) { - WorldPackets::SendGMLevelChange(entity->GetSystemAddress(), true, user->GetMaxGMLevel(), entity->GetGMLevel(), eGameMasterLevel::CIVILIAN); - GameMessages::SendChatModeUpdate(entity->GetObjectID(), eGameMasterLevel::CIVILIAN); - entity->SetGMLevel(eGameMasterLevel::CIVILIAN); - - GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); - - GameMessages::SendSlashCommandFeedbackText(entity, u"Your game master level has been changed, you may not be able to use all commands."); - } -#endif - } - - - void ToggleNameplate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - if ((Game::config->GetValue("allow_nameplate_off") != "1" && entity->GetGMLevel() < eGameMasterLevel::DEVELOPER)) return; - - auto* character = entity->GetCharacter(); - if (character && character->GetBillboardVisible()) { - character->SetBillboardVisible(false); - GameMessages::SendSlashCommandFeedbackText(entity, u"Your nameplate has been turned off and is not visible to players currently in this zone."); - } else { - character->SetBillboardVisible(true); - GameMessages::SendSlashCommandFeedbackText(entity, u"Your nameplate is now on and visible to all players."); - } - } - - void ToggleSkipCinematics(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" && entity->GetGMLevel() < eGameMasterLevel::DEVELOPER) return; - auto* character = entity->GetCharacter(); - if (!character) return; - bool current = character->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS); - character->SetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS, !current); - if (!current) { - GameMessages::SendSlashCommandFeedbackText(entity, u"You have elected to skip cinematics. Note that not all cinematics can be skipped, but most will be skipped now."); - } else { - GameMessages::SendSlashCommandFeedbackText(entity, u"Cinematics will no longer be skipped."); - } - } - - void ResetMission(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto missionId = GeneralUtils::TryParse(splitArgs[0]); - if (!missionId) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission ID."); - return; - } - - auto* missionComponent = entity->GetComponent(); - if (!missionComponent) return; - missionComponent->ResetMission(missionId.value()); - } - - void SetMinifig(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() < 2) return; - - const auto minifigItemIdExists = GeneralUtils::TryParse(splitArgs[1]); - if (!minifigItemIdExists) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid Minifig Item Id ID."); - return; - } - const int32_t minifigItemId = minifigItemIdExists.value(); - Game::entityManager->DestructEntity(entity, sysAddr); - auto* charComp = entity->GetComponent(); - std::string lowerName = splitArgs[0]; - if (lowerName.empty()) return; - std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower); - if (lowerName == "eyebrows") { - charComp->m_Character->SetEyebrows(minifigItemId); - } else if (lowerName == "eyes") { - charComp->m_Character->SetEyes(minifigItemId); - } else if (lowerName == "haircolor") { - charComp->m_Character->SetHairColor(minifigItemId); - } else if (lowerName == "hairstyle") { - charComp->m_Character->SetHairStyle(minifigItemId); - } else if (lowerName == "pants") { - charComp->m_Character->SetPantsColor(minifigItemId); - } else if (lowerName == "lefthand") { - charComp->m_Character->SetLeftHand(minifigItemId); - } else if (lowerName == "mouth") { - charComp->m_Character->SetMouth(minifigItemId); - } else if (lowerName == "righthand") { - charComp->m_Character->SetRightHand(minifigItemId); - } else if (lowerName == "shirtcolor") { - charComp->m_Character->SetShirtColor(minifigItemId); - } else if (lowerName == "hands") { - charComp->m_Character->SetLeftHand(minifigItemId); - charComp->m_Character->SetRightHand(minifigItemId); - } else { - Game::entityManager->ConstructEntity(entity); - ChatPackets::SendSystemMessage(sysAddr, u"Invalid Minifig item to change, try one of the following: Eyebrows, Eyes, HairColor, HairStyle, Pants, LeftHand, Mouth, RightHand, Shirt, Hands"); - return; - } - - Game::entityManager->ConstructEntity(entity); - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(lowerName) + u" set to " + (GeneralUtils::to_u16string(minifigItemId))); - - GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); // need to retoggle because it gets reenabled on creation of new character - } - - void PlayAnimation(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - std::u16string anim = GeneralUtils::ASCIIToUTF16(splitArgs[0], splitArgs[0].size()); - RenderComponent::PlayAnimation(entity, anim); - auto* possessorComponent = entity->GetComponent(); - if (possessorComponent) { - auto* possessedComponent = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); - if (possessedComponent) RenderComponent::PlayAnimation(possessedComponent, anim); - } - } - - void ListSpawns(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - for (const auto& pair : Game::entityManager->GetSpawnPointEntities()) { - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(pair.first)); - } - - ChatPackets::SendSystemMessage(sysAddr, u"Current: " + GeneralUtils::ASCIIToUTF16(entity->GetCharacter()->GetTargetScene())); - } - - void UnlockEmote(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto emoteID = GeneralUtils::TryParse(splitArgs[0]); - - if (!emoteID) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid emote ID."); - return; - } - - entity->GetCharacter()->UnlockEmote(emoteID.value()); - } - - void ForceSave(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - entity->GetCharacter()->SaveXMLToDatabase(); - } - - void Kill(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - ChatPackets::SendSystemMessage(sysAddr, u"Brutally murdering that player, if online on this server."); - - auto* player = PlayerManager::GetPlayer(splitArgs[0]); - if (player) { - player->Smash(entity->GetObjectID()); - ChatPackets::SendSystemMessage(sysAddr, u"It has been done, do you feel good about yourself now?"); - return; - } - - ChatPackets::SendSystemMessage(sysAddr, u"They were saved from your carnage."); - return; - } - - void SpeedBoost(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto boostOptional = GeneralUtils::TryParse(splitArgs[0]); - if (!boostOptional) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid boost."); - return; - } - const float boost = boostOptional.value(); - - auto* controllablePhysicsComponent = entity->GetComponent(); - - if (!controllablePhysicsComponent) return; - controllablePhysicsComponent->SetSpeedMultiplier(boost); - - // speedboost possessables - auto possessor = entity->GetComponent(); - if (possessor) { - auto possessedID = possessor->GetPossessable(); - if (possessedID != LWOOBJID_EMPTY) { - auto possessable = Game::entityManager->GetEntity(possessedID); - if (possessable) { - auto* possessControllablePhysicsComponent = possessable->GetComponent(); - if (possessControllablePhysicsComponent) { - possessControllablePhysicsComponent->SetSpeedMultiplier(boost); - } - } - } - } - - Game::entityManager->SerializeEntity(entity); - } - - void Freecam(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto state = !entity->GetVar(u"freecam"); - entity->SetVar(u"freecam", state); - - GameMessages::SendSetPlayerControlScheme(entity, static_cast(state ? 9 : 1)); - - ChatPackets::SendSystemMessage(sysAddr, u"Toggled freecam."); - } - - void SetControlScheme(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto scheme = GeneralUtils::TryParse(splitArgs[0]); - - if (!scheme) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid control scheme."); - return; - } - - GameMessages::SendSetPlayerControlScheme(entity, static_cast(scheme.value())); - - ChatPackets::SendSystemMessage(sysAddr, u"Switched control scheme."); - } - - void SetUiState(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - AMFArrayValue uiState; - - uiState.Insert("state", splitArgs[0]); - - GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, "pushGameState", uiState); - - ChatPackets::SendSystemMessage(sysAddr, u"Switched UI state."); - } - - void Toggle(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - AMFArrayValue amfArgs; - - amfArgs.Insert("visible", true); - - GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, splitArgs[0], amfArgs); - - ChatPackets::SendSystemMessage(sysAddr, u"Toggled UI state."); - } - - void SetInventorySize(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto sizeOptional = GeneralUtils::TryParse(splitArgs[0]); - if (!sizeOptional) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid size."); - return; - } - const uint32_t size = sizeOptional.value(); - - eInventoryType selectedInventory = eInventoryType::ITEMS; - - // a possible inventory was provided if we got more than 1 argument - if (splitArgs.size() >= 2) { - selectedInventory = GeneralUtils::TryParse(splitArgs.at(1)).value_or(eInventoryType::INVALID); - if (selectedInventory == eInventoryType::INVALID) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid inventory."); - return; - } else { - // In this case, we treat the input as a string and try to find it in the reflection list - std::transform(splitArgs.at(1).begin(), splitArgs.at(1).end(), splitArgs.at(1).begin(), ::toupper); - for (uint32_t index = 0; index < NUMBER_OF_INVENTORIES; index++) { - if (std::string_view(splitArgs.at(1)) == std::string_view(InventoryType::InventoryTypeToString(static_cast(index)))) selectedInventory = static_cast(index); - } - } - - ChatPackets::SendSystemMessage(sysAddr, u"Setting inventory " + - GeneralUtils::ASCIIToUTF16(splitArgs.at(1)) + - u" to size " + - GeneralUtils::to_u16string(size)); - } else ChatPackets::SendSystemMessage(sysAddr, u"Setting inventory ITEMS to size " + GeneralUtils::to_u16string(size)); - - auto* inventoryComponent = entity->GetComponent(); - if (inventoryComponent) { - auto* inventory = inventoryComponent->GetInventory(selectedInventory); - - inventory->SetSize(size); - } - } - - void RunMacro(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - // Only process if input does not contain separator charaters - if (splitArgs[0].find("/") != std::string::npos) return; - if (splitArgs[0].find("\\") != std::string::npos) return; - - auto infile = Game::assetManager->GetFile(("macros/" + splitArgs[0] + ".scm").c_str()); - - if (!infile) { - ChatPackets::SendSystemMessage(sysAddr, u"Unknown macro! Is the filename right?"); - return; - } - - if (infile.good()) { - std::string line; - while (std::getline(infile, line)) { - // Do this in two separate calls to catch both \n and \r\n - line.erase(std::remove(line.begin(), line.end(), '\n'), line.end()); - line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); - SlashCommandHandler::HandleChatCommand(GeneralUtils::ASCIIToUTF16(line), entity, sysAddr); - } - } else { - ChatPackets::SendSystemMessage(sysAddr, u"Unknown macro! Is the filename right?"); - } - } - - void AddMission(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto missionID = GeneralUtils::TryParse(splitArgs.at(0)); - - if (!missionID) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission id."); - return; - } - - auto comp = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); - if (comp) comp->AcceptMission(missionID.value(), true); - } - - void CompleteMission(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto missionID = GeneralUtils::TryParse(splitArgs.at(0)); - - if (!missionID) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission id."); - return; - } - - auto comp = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); - if (comp) comp->CompleteMission(missionID.value(), true); - } - - void SetFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() == 1) { - const auto flagId = GeneralUtils::TryParse(splitArgs.at(0)); - - if (!flagId) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag id."); - return; - } - - entity->GetCharacter()->SetPlayerFlag(flagId.value(), true); - } else if (splitArgs.size() >= 2) { - const auto flagId = GeneralUtils::TryParse(splitArgs.at(1)); - std::string onOffFlag = splitArgs.at(0); - if (!flagId) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag id."); - return; - } - - if (onOffFlag != "off" && onOffFlag != "on") { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag type."); - return; - } - - entity->GetCharacter()->SetPlayerFlag(flagId.value(), onOffFlag == "on"); - } - } - - void ClearFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto flagId = GeneralUtils::TryParse(splitArgs.at(0)); - - if (!flagId) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag id."); - return; - } - - entity->GetCharacter()->SetPlayerFlag(flagId.value(), false); - } - - void PlayEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() < 3) return; - - const auto effectID = GeneralUtils::TryParse(splitArgs.at(0)); - - if (!effectID) return; - - // FIXME: use fallible ASCIIToUTF16 conversion, because non-ascii isn't valid anyway - GameMessages::SendPlayFXEffect(entity->GetObjectID(), effectID.value(), GeneralUtils::ASCIIToUTF16(splitArgs.at(1)), splitArgs.at(2)); - } - - void StopEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - GameMessages::SendStopFXEffect(entity, true, splitArgs[0]); - } - - void SetAnnTitle(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - entity->GetCharacter()->SetAnnouncementTitle(args); - } - - void SetAnnMsg(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - entity->GetCharacter()->SetAnnouncementMessage(args); - } - - void Announce(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - if (entity->GetCharacter()->GetAnnouncementTitle().empty() || entity->GetCharacter()->GetAnnouncementMessage().empty()) { - ChatPackets::SendSystemMessage(sysAddr, u"Use /setanntitle & /setannmsg <msg> first!"); - return; - } - - SlashCommandHandler::SendAnnouncement(entity->GetCharacter()->GetAnnouncementTitle(), entity->GetCharacter()->GetAnnouncementMessage()); - } - - void ShutdownUniverse(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - //Tell the master server that we're going to be shutting down whole "universe": - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SHUTDOWN_UNIVERSE); - Game::server->SendToMaster(bitStream); - ChatPackets::SendSystemMessage(sysAddr, u"Sent universe shutdown notification to master."); - - //Tell chat to send an announcement to all servers - SlashCommandHandler::SendAnnouncement("Servers Closing Soon!", "DLU servers will close for maintenance in 10 minutes from now."); - } - - void GetNavmeshHeight(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto control = static_cast<ControllablePhysicsComponent*>(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); - if (!control) return; - - float y = dpWorld::GetNavMesh()->GetHeightAtPoint(control->GetPosition()); - std::u16string msg = u"Navmesh height: " + (GeneralUtils::to_u16string(y)); - ChatPackets::SendSystemMessage(sysAddr, msg); - } - - void GmAddItem(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - - if (splitArgs.size() == 1) { - const auto itemLOT = GeneralUtils::TryParse<uint32_t>(splitArgs.at(0)); - - if (!itemLOT) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid item LOT."); - return; - } - - InventoryComponent* inventory = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY)); - - inventory->AddItem(itemLOT.value(), 1, eLootSourceType::MODERATION); - } else if (splitArgs.size() == 2) { - const auto itemLOT = GeneralUtils::TryParse<uint32_t>(splitArgs.at(0)); - if (!itemLOT) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid item LOT."); - return; - } - - const auto count = GeneralUtils::TryParse<uint32_t>(splitArgs.at(1)); - if (!count) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid item count."); - return; - } - - InventoryComponent* inventory = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY)); - - inventory->AddItem(itemLOT.value(), count.value(), eLootSourceType::MODERATION); - } else { - ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /gmadditem <lot>"); - } - } - - void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - - NiPoint3 pos{}; - if (splitArgs.size() == 3) { - - const auto x = GeneralUtils::TryParse<float>(splitArgs.at(0)); - if (!x) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid x."); - return; - } - - const auto y = GeneralUtils::TryParse<float>(splitArgs.at(1)); - if (!y) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid y."); - return; - } - - const auto z = GeneralUtils::TryParse<float>(splitArgs.at(2)); - if (!z) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid z."); - return; - } - - pos.SetX(x.value()); - pos.SetY(y.value()); - pos.SetZ(z.value()); - - LOG("Teleporting objectID: %llu to %f, %f, %f", entity->GetObjectID(), pos.x, pos.y, pos.z); - GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr); - } else if (splitArgs.size() == 2) { - - const auto x = GeneralUtils::TryParse<float>(splitArgs.at(0)); - if (!x) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid x."); - return; - } - - const auto z = GeneralUtils::TryParse<float>(splitArgs.at(1)); - if (!z) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid z."); - return; - } - - pos.SetX(x.value()); - pos.SetY(0.0f); - pos.SetZ(z.value()); - - LOG("Teleporting objectID: %llu to X: %f, Z: %f", entity->GetObjectID(), pos.x, pos.z); - GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr); - } else { - ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /teleport <x> (<y>) <z> - if no Y given, will teleport to the height of the terrain (or any physics object)."); - } - - auto* possessorComponent = entity->GetComponent<PossessorComponent>(); - if (possessorComponent) { - auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); - - if (possassableEntity != nullptr) { - auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent<HavokVehiclePhysicsComponent>(); - if (havokVehiclePhysicsComponent) { - havokVehiclePhysicsComponent->SetPosition(pos); - Game::entityManager->SerializeEntity(possassableEntity); - } else GameMessages::SendTeleport(possassableEntity->GetObjectID(), pos, NiQuaternion(), sysAddr); - } - } - } - - void TpAll(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto pos = entity->GetPosition(); - - const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER); - - for (auto* character : characters) { - GameMessages::SendTeleport(character->GetObjectID(), pos, NiQuaternion(), character->GetSystemAddress()); - } - } - - void Dismount(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto* possessorComponent = entity->GetComponent<PossessorComponent>(); - if (possessorComponent) { - auto possessableId = possessorComponent->GetPossessable(); - if (possessableId != LWOOBJID_EMPTY) { - auto* possessableEntity = Game::entityManager->GetEntity(possessableId); - if (possessableEntity) possessorComponent->Dismount(possessableEntity, true); - } - } - } - - void BuffMe(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto dest = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); - if (dest) { - dest->SetHealth(999); - dest->SetMaxHealth(999.0f); - dest->SetArmor(999); - dest->SetMaxArmor(999.0f); - dest->SetImagination(999); - dest->SetMaxImagination(999.0f); - } - Game::entityManager->SerializeEntity(entity); - } - - void StartCelebration(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto celebration = GeneralUtils::TryParse<int32_t>(splitArgs.at(0)); - - if (!celebration) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid celebration."); - return; - } - - GameMessages::SendStartCelebrationEffect(entity, entity->GetSystemAddress(), celebration.value()); - } - - void BuffMed(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto dest = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); - if (dest) { - dest->SetHealth(9); - dest->SetMaxHealth(9.0f); - dest->SetArmor(9); - dest->SetMaxArmor(9.0f); - dest->SetImagination(9); - dest->SetMaxImagination(9.0f); - } - Game::entityManager->SerializeEntity(entity); - } - - void RefillStats(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto dest = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); - if (dest) { - dest->SetHealth(static_cast<int32_t>(dest->GetMaxHealth())); - dest->SetArmor(static_cast<int32_t>(dest->GetMaxArmor())); - dest->SetImagination(static_cast<int32_t>(dest->GetMaxImagination())); - } - - Game::entityManager->SerializeEntity(entity); - } - - void Lookup(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT `id`, `name` FROM `Objects` WHERE `displayName` LIKE ?1 OR `name` LIKE ?1 OR `description` LIKE ?1 LIMIT 50"); - - const std::string query_text = "%" + args + "%"; - query.bind(1, query_text.c_str()); - - auto tables = query.execQuery(); - - while (!tables.eof()) { - std::string message = std::to_string(tables.getIntField(0)) + " - " + tables.getStringField(1); - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(message, message.size())); - tables.nextRow(); - } - } - - void Spawn(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - ControllablePhysicsComponent* comp = static_cast<ControllablePhysicsComponent*>(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); - if (!comp) return; - - const auto lot = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); - - if (!lot) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid lot."); - return; - } - - EntityInfo info; - info.lot = lot.value(); - info.pos = comp->GetPosition(); - info.rot = comp->GetRotation(); - info.spawner = nullptr; - info.spawnerID = entity->GetObjectID(); - info.spawnerNodeID = 0; - - Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); - - if (newEntity == nullptr) { - ChatPackets::SendSystemMessage(sysAddr, u"Failed to spawn entity."); - return; - } - - Game::entityManager->ConstructEntity(newEntity); - } - - void SpawnGroup(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() < 3) return; - - const auto lot = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); - if (!lot) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid lot."); - return; - } - - const auto numberToSpawnOptional = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); - if (!numberToSpawnOptional && numberToSpawnOptional.value() > 0) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid number of enemies to spawn."); - return; - } - uint32_t numberToSpawn = numberToSpawnOptional.value(); - - // Must spawn within a radius of at least 0.0f - const auto radiusToSpawnWithinOptional = GeneralUtils::TryParse<float>(splitArgs[2]); - if (!radiusToSpawnWithinOptional && radiusToSpawnWithinOptional.value() < 0.0f) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid radius to spawn within."); - return; - } - const float radiusToSpawnWithin = radiusToSpawnWithinOptional.value(); - - EntityInfo info; - info.lot = lot.value(); - info.spawner = nullptr; - info.spawnerID = entity->GetObjectID(); - info.spawnerNodeID = 0; - - auto playerPosition = entity->GetPosition(); - while (numberToSpawn > 0) { - auto randomAngle = GeneralUtils::GenerateRandomNumber<float>(0.0f, 2 * PI); - auto randomRadius = GeneralUtils::GenerateRandomNumber<float>(0.0f, radiusToSpawnWithin); - - // Set the position to the generated random position plus the player position. This will - // spawn the entity in a circle around the player. As you get further from the player, the angle chosen will get less accurate. - info.pos = playerPosition + NiPoint3(cos(randomAngle) * randomRadius, 0.0f, sin(randomAngle) * randomRadius); - info.rot = NiQuaternion(); - - auto newEntity = Game::entityManager->CreateEntity(info); - if (newEntity == nullptr) { - ChatPackets::SendSystemMessage(sysAddr, u"Failed to spawn entity."); - return; - } - - Game::entityManager->ConstructEntity(newEntity); - numberToSpawn--; - } - } - - void GiveUScore(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto uscoreOptional = GeneralUtils::TryParse<int32_t>(splitArgs[0]); - if (!uscoreOptional) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid uscore."); - return; - } - const int32_t uscore = uscoreOptional.value(); - - CharacterComponent* character = entity->GetComponent<CharacterComponent>(); - if (character) character->SetUScore(character->GetUScore() + uscore); - // MODERATION should work but it doesn't. Relog to see uscore changes - - eLootSourceType lootType = eLootSourceType::MODERATION; - - if (splitArgs.size() >= 2) { - const auto type = GeneralUtils::TryParse<eLootSourceType>(splitArgs[1]); - lootType = type.value_or(lootType); - } - - GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), uscore, lootType); - } - - void SetLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - // We may be trying to set a specific players level to a level. If so override the entity with the requested players. - std::string requestedPlayerToSetLevelOf = ""; - if (splitArgs.size() > 1) { - requestedPlayerToSetLevelOf = splitArgs[1]; - - auto requestedPlayer = PlayerManager::GetPlayer(requestedPlayerToSetLevelOf); - - if (!requestedPlayer) { - ChatPackets::SendSystemMessage(sysAddr, u"No player found with username: (" + GeneralUtils::UTF8ToUTF16(requestedPlayerToSetLevelOf) + u")."); - return; - } - - if (!requestedPlayer->GetOwner()) { - ChatPackets::SendSystemMessage(sysAddr, u"No entity found with username: (" + GeneralUtils::UTF8ToUTF16(requestedPlayerToSetLevelOf) + u")."); - return; - } - - entity = requestedPlayer->GetOwner(); - } - const auto requestedLevelOptional = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); - uint32_t oldLevel; - - // first check the level is valid - if (!requestedLevelOptional) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid level."); - return; - } - uint32_t requestedLevel = requestedLevelOptional.value(); - // query to set our uscore to the correct value for this level - - auto characterComponent = entity->GetComponent<CharacterComponent>(); - if (!characterComponent) return; - auto levelComponent = entity->GetComponent<LevelProgressionComponent>(); - auto query = CDClientDatabase::CreatePreppedStmt("SELECT requiredUScore from LevelProgressionLookup WHERE id = ?;"); - query.bind(1, static_cast<int>(requestedLevel)); - auto result = query.execQuery(); - - if (result.eof()) return; - - // Set the UScore first - oldLevel = levelComponent->GetLevel(); - characterComponent->SetUScore(result.getIntField(0, characterComponent->GetUScore())); - - // handle level up for each level we have passed if we set our level to be higher than the current one. - if (oldLevel < requestedLevel) { - while (oldLevel < requestedLevel) { - oldLevel += 1; - levelComponent->SetLevel(oldLevel); - levelComponent->HandleLevelUp(); - } - } else { - levelComponent->SetLevel(requestedLevel); - } - - if (requestedPlayerToSetLevelOf != "") { - ChatPackets::SendSystemMessage( - sysAddr, u"Set " + GeneralUtils::UTF8ToUTF16(requestedPlayerToSetLevelOf) + u"'s level to " + GeneralUtils::to_u16string(requestedLevel) + - u" and UScore to " + GeneralUtils::to_u16string(characterComponent->GetUScore()) + - u". Relog to see changes."); - } else { - ChatPackets::SendSystemMessage( - sysAddr, u"Set your level to " + GeneralUtils::to_u16string(requestedLevel) + - u" and UScore to " + GeneralUtils::to_u16string(characterComponent->GetUScore()) + - u". Relog to see changes."); - } - } - - void Pos(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto position = entity->GetPosition(); - - ChatPackets::SendSystemMessage(sysAddr, u"<" + (GeneralUtils::to_u16string(position.x)) + u", " + (GeneralUtils::to_u16string(position.y)) + u", " + (GeneralUtils::to_u16string(position.z)) + u">"); - - LOG("Position: %f, %f, %f", position.x, position.y, position.z); - } - - void Rot(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto rotation = entity->GetRotation(); - - ChatPackets::SendSystemMessage(sysAddr, u"<" + (GeneralUtils::to_u16string(rotation.w)) + u", " + (GeneralUtils::to_u16string(rotation.x)) + u", " + (GeneralUtils::to_u16string(rotation.y)) + u", " + (GeneralUtils::to_u16string(rotation.z)) + u">"); - - LOG("Rotation: %f, %f, %f, %f", rotation.w, rotation.x, rotation.y, rotation.z); - } - - void LocRow(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto position = entity->GetPosition(); - const auto rotation = entity->GetRotation(); - - LOG("<location x=\"%f\" y=\"%f\" z=\"%f\" rw=\"%f\" rx=\"%f\" ry=\"%f\" rz=\"%f\" />", position.x, position.y, position.z, rotation.w, rotation.x, rotation.y, rotation.z); - } - - void PlayLvlFx(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - GameMessages::SendPlayFXEffect(entity, 7074, u"create", "7074", LWOOBJID_EMPTY, 1.0f, 1.0f, true); - } - - void PlayRebuildFx(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - GameMessages::SendPlayFXEffect(entity, 230, u"rebuild", "230", LWOOBJID_EMPTY, 1.0f, 1.0f, true); - } - - void FreeMoney(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto money = GeneralUtils::TryParse<int64_t>(splitArgs[0]); - - if (!money) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid money."); - return; - } - - auto* ch = entity->GetCharacter(); - ch->SetCoins(ch->GetCoins() + money.value(), eLootSourceType::MODERATION); - } - - void SetCurrency(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - const auto money = GeneralUtils::TryParse<int64_t>(splitArgs[0]); - - if (!money) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid money."); - return; - } - - auto* ch = entity->GetCharacter(); - ch->SetCoins(money.value(), eLootSourceType::MODERATION); - } - - void Buff(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() < 2) return; - - auto* buffComponent = entity->GetComponent<BuffComponent>(); - - const auto id = GeneralUtils::TryParse<int32_t>(splitArgs[0]); - if (!id) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid buff id."); - return; - } - - const auto duration = GeneralUtils::TryParse<int32_t>(splitArgs[1]); - if (!duration) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid buff duration."); - return; - } - - if (buffComponent) buffComponent->ApplyBuff(id.value(), duration.value(), entity->GetObjectID()); - } - - void TestMap(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - ChatPackets::SendSystemMessage(sysAddr, u"Requesting map change..."); - LWOCLONEID cloneId = 0; - bool force = false; - - const auto reqZoneOptional = GeneralUtils::TryParse<LWOMAPID>(splitArgs[0]); - if (!reqZoneOptional) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid zone."); - return; - } - const LWOMAPID reqZone = reqZoneOptional.value(); - - if (splitArgs.size() > 1) { - auto index = 1; - - if (splitArgs[index] == "force") { - index++; - - force = true; - } - - if (splitArgs.size() > index) { - const auto cloneIdOptional = GeneralUtils::TryParse<LWOCLONEID>(splitArgs[index]); - if (!cloneIdOptional) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid clone id."); - return; - } - cloneId = cloneIdOptional.value(); - } - } - - const auto objid = entity->GetObjectID(); - - if (force || Game::zoneManager->CheckIfAccessibleZone(reqZone)) { // to prevent tomfoolery - - ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, reqZone, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { - - auto* entity = Game::entityManager->GetEntity(objid); - if (!entity) return; - - const auto sysAddr = entity->GetSystemAddress(); - - ChatPackets::SendSystemMessage(sysAddr, u"Transfering map..."); - - LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); - if (entity->GetCharacter()) { - entity->GetCharacter()->SetZoneID(zoneID); - entity->GetCharacter()->SetZoneInstance(zoneInstance); - entity->GetCharacter()->SetZoneClone(zoneClone); - entity->GetComponent<CharacterComponent>()->SetLastRocketConfig(u""); - } - - entity->GetCharacter()->SaveXMLToDatabase(); - - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); - return; - }); - } else { - std::string msg = "ZoneID not found or allowed: "; - msg.append(splitArgs[0]); // FIXME: unnecessary utf16 re-encoding just for error - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(msg, msg.size())); - } - } - - void CreatePrivate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() < 3) return; - - const auto zone = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); - if (!zone) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid zone."); - return; - } - - const auto clone = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); - if (!clone) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid clone."); - return; - } - - const auto& password = splitArgs[2]; - - ZoneInstanceManager::Instance()->CreatePrivateZone(Game::server, zone.value(), clone.value(), password); - - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16("Sent request for private zone with password: " + password)); - } - - void DebugUi(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - ChatPackets::SendSystemMessage(sysAddr, u"Opening UIDebugger..."); - } - - void Boost(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - - auto* possessorComponent = entity->GetComponent<PossessorComponent>(); - - if (possessorComponent == nullptr) { - return; - } - - auto* vehicle = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); - - if (vehicle == nullptr) { - return; - } - - if (splitArgs.size() >= 1) { - const auto time = GeneralUtils::TryParse<float>(splitArgs[0]); - - if (!time) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid boost time."); - return; - } else { - GameMessages::SendVehicleAddPassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - entity->AddCallbackTimer(time.value(), [vehicle]() { - if (!vehicle) return; - GameMessages::SendVehicleRemovePassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - }); - } - } else { - GameMessages::SendVehicleAddPassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - } - } - - void Unboost(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto* possessorComponent = entity->GetComponent<PossessorComponent>(); - - if (possessorComponent == nullptr) return; - auto* vehicle = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); - - if (vehicle == nullptr) return; - GameMessages::SendVehicleRemovePassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - } - - void ActivateSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - auto spawners = Game::zoneManager->GetSpawnersByName(splitArgs[0]); - - for (auto* spawner : spawners) { - spawner->Activate(); - } - - spawners = Game::zoneManager->GetSpawnersInGroup(splitArgs[0]); - - for (auto* spawner : spawners) { - spawner->Activate(); - } - } - - void SpawnPhysicsVerts(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - //Go tell physics to spawn all the vertices: - auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PHANTOM_PHYSICS); - for (auto en : entities) { - auto phys = static_cast<PhantomPhysicsComponent*>(en->GetComponent(eReplicaComponentType::PHANTOM_PHYSICS)); - if (phys) - phys->SpawnVertices(); - } - } - - void ReportProxPhys(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PROXIMITY_MONITOR); - for (auto en : entities) { - auto phys = static_cast<ProximityMonitorComponent*>(en->GetComponent(eReplicaComponentType::PROXIMITY_MONITOR)); - if (phys) { - for (auto prox : phys->GetProximitiesData()) { - if (!prox.second) continue; - - auto sphere = static_cast<dpShapeSphere*>(prox.second->GetShape()); - auto pos = prox.second->GetPosition(); - LOG("Proximity: %s, r: %f, pos: %f, %f, %f", prox.first.c_str(), sphere->GetRadius(), pos.x, pos.y, pos.z); - } - } - } - } - - void TriggerSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - auto spawners = Game::zoneManager->GetSpawnersByName(splitArgs[0]); - - for (auto* spawner : spawners) { - spawner->Spawn(); - } - - spawners = Game::zoneManager->GetSpawnersInGroup(splitArgs[0]); - - for (auto* spawner : spawners) { - spawner->Spawn(); - } - } - - void Reforge(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() < 2) return; - - const auto baseItem = GeneralUtils::TryParse<LOT>(splitArgs[0]); - if (!baseItem) return; - - const auto reforgedItem = GeneralUtils::TryParse<LOT>(splitArgs[1]); - if (!reforgedItem) return; - - auto* inventoryComponent = entity->GetComponent<InventoryComponent>(); - if (!inventoryComponent) return; - - std::vector<LDFBaseData*> data{}; - data.push_back(new LDFData<int32_t>(u"reforgedLOT", reforgedItem.value())); - - inventoryComponent->AddItem(baseItem.value(), 1, eLootSourceType::MODERATION, eInventoryType::INVALID, data); - } - - void Crash(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - ChatPackets::SendSystemMessage(sysAddr, u"Crashing..."); - - int* badPtr = nullptr; - *badPtr = 0; - } - - void Metrics(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - for (const auto variable : Metrics::GetAllMetrics()) { - auto* metric = Metrics::GetMetric(variable); - - if (metric == nullptr) { - continue; - } - - ChatPackets::SendSystemMessage( - sysAddr, - GeneralUtils::ASCIIToUTF16(Metrics::MetricVariableToString(variable)) + - u": " + - GeneralUtils::to_u16string(Metrics::ToMiliseconds(metric->average)) + - u"ms" - ); - } - - ChatPackets::SendSystemMessage( - sysAddr, - u"Peak RSS: " + GeneralUtils::to_u16string(static_cast<float>(static_cast<double>(Metrics::GetPeakRSS()) / 1.024e6)) + - u"MB" - ); - - ChatPackets::SendSystemMessage( - sysAddr, - u"Current RSS: " + GeneralUtils::to_u16string(static_cast<float>(static_cast<double>(Metrics::GetCurrentRSS()) / 1.024e6)) + - u"MB" - ); - - ChatPackets::SendSystemMessage( - sysAddr, - u"Process ID: " + GeneralUtils::to_u16string(Metrics::GetProcessID()) - ); - } - - void ReloadConfig(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - Game::config->ReloadConfig(); - VanityUtilities::SpawnVanity(); - dpWorld::Reload(); - auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); - for (const auto* const entity : entities) { - auto* const scriptedActivityComponent = entity->GetComponent<ScriptedActivityComponent>(); - if (!scriptedActivityComponent) continue; - - scriptedActivityComponent->ReloadConfig(); - } - Game::server->UpdateMaximumMtuSize(); - Game::server->UpdateBandwidthLimit(); - ChatPackets::SendSystemMessage(sysAddr, u"Successfully reloaded config for world!"); - } - - void RollLoot(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() < 3) return; - - const auto lootMatrixIndex = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); - if (!lootMatrixIndex) return; - - const auto targetLot = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); - if (!targetLot) return; - - const auto loops = GeneralUtils::TryParse<uint32_t>(splitArgs[2]); - if (!loops) return; - - uint64_t totalRuns = 0; - - for (uint32_t i = 0; i < loops; i++) { - while (true) { - auto lootRoll = Loot::RollLootMatrix(lootMatrixIndex.value()); - totalRuns += 1; - bool doBreak = false; - for (const auto& kv : lootRoll) { - if (static_cast<uint32_t>(kv.first) == targetLot) { - doBreak = true; - } - } - if (doBreak) break; - } - } - - std::u16string message = u"Ran loot drops looking for " - + GeneralUtils::to_u16string(targetLot.value()) - + u", " - + GeneralUtils::to_u16string(loops.value()) - + u" times. It ran " - + GeneralUtils::to_u16string(totalRuns) - + u" times. Averaging out at " - + GeneralUtils::to_u16string(static_cast<float>(totalRuns) / loops.value()); - - ChatPackets::SendSystemMessage(sysAddr, message); - } - - void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - eInventoryType inventoryType = eInventoryType::INVALID; - - const auto inventoryTypeOptional = GeneralUtils::TryParse<eInventoryType>(splitArgs[0]); - if (!inventoryTypeOptional) { - // In this case, we treat the input as a string and try to find it in the reflection list - std::transform(splitArgs[0].begin(), splitArgs[0].end(), splitArgs[0].begin(), ::toupper); - LOG("looking for inventory %s", splitArgs[0].c_str()); - for (uint32_t index = 0; index < NUMBER_OF_INVENTORIES; index++) { - if (std::string_view(splitArgs[0]) == std::string_view(InventoryType::InventoryTypeToString(static_cast<eInventoryType>(index)))) inventoryType = static_cast<eInventoryType>(index); - } - } else { - inventoryType = inventoryTypeOptional.value(); - } - - if (inventoryType == eInventoryType::INVALID || inventoryType >= NUMBER_OF_INVENTORIES) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid inventory provided."); - return; - } - - auto* inventoryComponent = entity->GetComponent<InventoryComponent>(); - if (!inventoryComponent) return; - - auto* inventoryToDelete = inventoryComponent->GetInventory(inventoryType); - if (!inventoryToDelete) return; - - inventoryToDelete->DeleteAllItems(); - LOG("Deleted inventory %s for user %llu", splitArgs[0].c_str(), entity->GetObjectID()); - ChatPackets::SendSystemMessage(sysAddr, u"Deleted inventory " + GeneralUtils::UTF8ToUTF16(splitArgs[0])); - } - - void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - auto* skillComponent = entity->GetComponent<SkillComponent>(); - if (skillComponent) { - const auto skillId = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); - - if (!skillId) { - ChatPackets::SendSystemMessage(sysAddr, u"Error getting skill ID."); - return; - } else { - skillComponent->CastSkill(skillId.value(), entity->GetObjectID(), entity->GetObjectID()); - ChatPackets::SendSystemMessage(sysAddr, u"Cast skill"); - } - } - } - - void SetSkillSlot(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() < 2) return; - - auto* const inventoryComponent = entity->GetComponent<InventoryComponent>(); - if (inventoryComponent) { - const auto slot = GeneralUtils::TryParse<BehaviorSlot>(splitArgs[0]); - if (!slot) { - ChatPackets::SendSystemMessage(sysAddr, u"Error getting slot."); - return; - } else { - const auto skillId = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); - if (!skillId) { - ChatPackets::SendSystemMessage(sysAddr, u"Error getting skill."); - return; - } else { - if (inventoryComponent->SetSkill(slot.value(), skillId.value())) ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot successfully"); - else ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot failed"); - } - } - } - } - - void SetFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); - if (destroyableComponent) { - const auto faction = GeneralUtils::TryParse<int32_t>(splitArgs[0]); - - if (!faction) { - ChatPackets::SendSystemMessage(sysAddr, u"Error getting faction."); - return; - } else { - destroyableComponent->SetFaction(faction.value()); - ChatPackets::SendSystemMessage(sysAddr, u"Set faction and updated enemies list"); - } - } - } - - void AddFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); - if (destroyableComponent) { - const auto faction = GeneralUtils::TryParse<int32_t>(splitArgs[0]); - - if (!faction) { - ChatPackets::SendSystemMessage(sysAddr, u"Error getting faction."); - return; - } else { - destroyableComponent->AddFaction(faction.value()); - ChatPackets::SendSystemMessage(sysAddr, u"Added faction and updated enemies list"); - } - } - } - - void GetFactions(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); - if (destroyableComponent) { - ChatPackets::SendSystemMessage(sysAddr, u"Friendly factions:"); - for (const auto entry : destroyableComponent->GetFactionIDs()) { - ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry))); - } - - ChatPackets::SendSystemMessage(sysAddr, u"Enemy factions:"); - for (const auto entry : destroyableComponent->GetEnemyFactionsIDs()) { - ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry))); - } - } - } - - void SetRewardCode(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - auto* character = entity->GetCharacter(); - if (!character) return; - auto* user = character->GetParentUser(); - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - auto* cdrewardCodes = CDClientManager::GetTable<CDRewardCodesTable>(); - - auto id = cdrewardCodes->GetCodeID(splitArgs[0]); - if (id != -1) Database::Get()->InsertRewardCode(user->GetAccountID(), id); - } - - void Inspect(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - Entity* closest = nullptr; - - std::u16string ldf; - - bool isLDF = false; - - auto component = GeneralUtils::TryParse<eReplicaComponentType>(splitArgs[0]); - if (!component) { - component = eReplicaComponentType::INVALID; - - ldf = GeneralUtils::UTF8ToUTF16(splitArgs[0]); - - isLDF = true; - } - - auto reference = entity->GetPosition(); - - auto closestDistance = 0.0f; - - const auto candidates = Game::entityManager->GetEntitiesByComponent(component.value()); - - for (auto* candidate : candidates) { - if (candidate->GetLOT() == 1 || candidate->GetLOT() == 8092) { - continue; - } - - if (isLDF && !candidate->HasVar(ldf)) { - continue; - } - - if (!closest) { - closest = candidate; - - closestDistance = NiPoint3::Distance(candidate->GetPosition(), reference); - - continue; - } - - const auto distance = NiPoint3::Distance(candidate->GetPosition(), reference); - - if (distance < closestDistance) { - closest = candidate; - - closestDistance = distance; - } - } - - if (!closest) return; - - Game::entityManager->SerializeEntity(closest); - - auto* table = CDClientManager::GetTable<CDObjectsTable>(); - - const auto& info = table->GetByID(closest->GetLOT()); - - std::stringstream header; - - header << info.name << " [" << std::to_string(info.id) << "]" << " " << std::to_string(closestDistance) << " " << std::to_string(closest->IsSleeping()); - - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(header.str())); - - for (const auto& pair : closest->GetComponents()) { - auto id = pair.first; - - std::stringstream stream; - - stream << "Component [" << std::to_string(static_cast<uint32_t>(id)) << "]"; - - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(stream.str())); - } - - if (splitArgs.size() >= 2) { - if (splitArgs[1] == "-m" && splitArgs.size() >= 3) { - auto* const movingPlatformComponent = closest->GetComponent<MovingPlatformComponent>(); - - const auto mValue = GeneralUtils::TryParse<int32_t>(splitArgs[2]); - - if (!movingPlatformComponent || !mValue) return; - - movingPlatformComponent->SetSerialized(true); - - if (mValue == -1) { - movingPlatformComponent->StopPathing(); - } else { - movingPlatformComponent->GotoWaypoint(mValue.value()); - } - - Game::entityManager->SerializeEntity(closest); - } else if (splitArgs[1] == "-a" && splitArgs.size() >= 3) { - RenderComponent::PlayAnimation(closest, splitArgs.at(2)); - } else if (splitArgs[1] == "-s") { - for (auto* entry : closest->GetSettings()) { - ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(entry->GetString())); - } - - ChatPackets::SendSystemMessage(sysAddr, u"------"); - ChatPackets::SendSystemMessage(sysAddr, u"Spawner ID: " + GeneralUtils::to_u16string(closest->GetSpawnerID())); - } else if (splitArgs[1] == "-p") { - const auto postion = closest->GetPosition(); - - ChatPackets::SendSystemMessage( - sysAddr, - GeneralUtils::ASCIIToUTF16("< " + std::to_string(postion.x) + ", " + std::to_string(postion.y) + ", " + std::to_string(postion.z) + " >") - ); - } else if (splitArgs[1] == "-f") { - auto* destuctable = closest->GetComponent<DestroyableComponent>(); - - if (destuctable == nullptr) { - ChatPackets::SendSystemMessage(sysAddr, u"No destroyable component on this entity!"); - return; - } - - ChatPackets::SendSystemMessage(sysAddr, u"Smashable: " + (GeneralUtils::to_u16string(destuctable->GetIsSmashable()))); - - ChatPackets::SendSystemMessage(sysAddr, u"Friendly factions:"); - for (const auto entry : destuctable->GetFactionIDs()) { - ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry))); - } - - ChatPackets::SendSystemMessage(sysAddr, u"Enemy factions:"); - for (const auto entry : destuctable->GetEnemyFactionsIDs()) { - ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry))); - } - - if (splitArgs.size() >= 3) { - const auto faction = GeneralUtils::TryParse<int32_t>(splitArgs[2]); - if (!faction) return; - - destuctable->SetFaction(-1); - destuctable->AddFaction(faction.value(), true); - } - } else if (splitArgs[1] == "-cf") { - auto* destuctable = entity->GetComponent<DestroyableComponent>(); - if (!destuctable) { - ChatPackets::SendSystemMessage(sysAddr, u"No destroyable component on this entity!"); - return; - } - if (destuctable->IsEnemy(closest)) ChatPackets::SendSystemMessage(sysAddr, u"They are our enemy"); - else ChatPackets::SendSystemMessage(sysAddr, u"They are NOT our enemy"); - } else if (splitArgs[1] == "-t") { - auto* phantomPhysicsComponent = closest->GetComponent<PhantomPhysicsComponent>(); - - if (phantomPhysicsComponent != nullptr) { - ChatPackets::SendSystemMessage(sysAddr, u"Type: " + (GeneralUtils::to_u16string(static_cast<uint32_t>(phantomPhysicsComponent->GetEffectType())))); - const auto dir = phantomPhysicsComponent->GetDirection(); - ChatPackets::SendSystemMessage(sysAddr, u"Direction: <" + (GeneralUtils::to_u16string(dir.x)) + u", " + (GeneralUtils::to_u16string(dir.y)) + u", " + (GeneralUtils::to_u16string(dir.z)) + u">"); - ChatPackets::SendSystemMessage(sysAddr, u"Multiplier: " + (GeneralUtils::to_u16string(phantomPhysicsComponent->GetDirectionalMultiplier()))); - ChatPackets::SendSystemMessage(sysAddr, u"Active: " + (GeneralUtils::to_u16string(phantomPhysicsComponent->GetPhysicsEffectActive()))); - } - - auto* triggerComponent = closest->GetComponent<TriggerComponent>(); - if (triggerComponent) { - auto trigger = triggerComponent->GetTrigger(); - if (trigger) { - ChatPackets::SendSystemMessage(sysAddr, u"Trigger: " + (GeneralUtils::to_u16string(trigger->id))); - } - } - } - } - } -}; - -namespace GMGreaterThanZeroCommands { - - void Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() == 1) { - auto* player = PlayerManager::GetPlayer(splitArgs[0]); - - std::u16string username = GeneralUtils::UTF8ToUTF16(splitArgs[0]); - if (player == nullptr) { - ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + username); - return; - } - - Game::server->Disconnect(player->GetSystemAddress(), eServerDisconnectIdentifiers::KICK); - - ChatPackets::SendSystemMessage(sysAddr, u"Kicked: " + username); - } else { - ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /kick <username>"); - } - } - - void Ban(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - - if (splitArgs.size() == 1) { - auto* player = PlayerManager::GetPlayer(splitArgs[0]); - - uint32_t accountId = 0; - - if (player == nullptr) { - auto characterInfo = Database::Get()->GetCharacterInfo(splitArgs[0]); - - if (characterInfo) { - accountId = characterInfo->accountId; - } - - if (accountId == 0) { - ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(splitArgs[0])); - - return; - } - } else { - auto* character = player->GetCharacter(); - auto* user = character != nullptr ? character->GetParentUser() : nullptr; - if (user) accountId = user->GetAccountID(); - } - - if (accountId != 0) Database::Get()->UpdateAccountBan(accountId, true); - - if (player != nullptr) { - Game::server->Disconnect(player->GetSystemAddress(), eServerDisconnectIdentifiers::FREE_TRIAL_EXPIRED); - } - - ChatPackets::SendSystemMessage(sysAddr, u"Banned: " + GeneralUtils::ASCIIToUTF16(splitArgs[0])); - } else { - ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /ban <username>"); - } - } - - void MailItem(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.size() < 2) return; - - const auto& playerName = splitArgs[0]; - - auto playerInfo = Database::Get()->GetCharacterInfo(playerName); - - uint32_t receiverID = 0; - if (!playerInfo) { - ChatPackets::SendSystemMessage(sysAddr, u"Failed to find that player"); - - return; - } - - receiverID = playerInfo->id; - - const auto lot = GeneralUtils::TryParse<LOT>(splitArgs.at(1)); - - if (!lot) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid item lot."); - return; - } - - IMail::MailInfo mailInsert; - mailInsert.senderId = entity->GetObjectID(); - mailInsert.senderUsername = "Darkflame Universe"; - mailInsert.receiverId = receiverID; - mailInsert.recipient = playerName; - mailInsert.subject = "Lost item"; - mailInsert.body = "This is a replacement item for one you lost."; - mailInsert.itemID = LWOOBJID_EMPTY; - mailInsert.itemLOT = lot.value(); - mailInsert.itemSubkey = LWOOBJID_EMPTY; - mailInsert.itemCount = 1; - Database::Get()->InsertNewMail(mailInsert); - - ChatPackets::SendSystemMessage(sysAddr, u"Mail sent"); - } - - void ApproveProperty(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - if (PropertyManagementComponent::Instance() != nullptr) { - PropertyManagementComponent::Instance()->UpdateApprovedStatus(true); - } - } - - void Mute(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - - if (splitArgs.size() >= 1) { - auto* player = PlayerManager::GetPlayer(splitArgs[0]); - - uint32_t accountId = 0; - LWOOBJID characterId = 0; - - if (player == nullptr) { - auto characterInfo = Database::Get()->GetCharacterInfo(splitArgs[0]); - - if (characterInfo) { - accountId = characterInfo->accountId; - characterId = characterInfo->id; - - GeneralUtils::SetBit(characterId, eObjectBits::CHARACTER); - GeneralUtils::SetBit(characterId, eObjectBits::PERSISTENT); - } - - if (accountId == 0) { - ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(splitArgs[0])); - - return; - } - } else { - auto* character = player->GetCharacter(); - auto* user = character != nullptr ? character->GetParentUser() : nullptr; - if (user) accountId = user->GetAccountID(); - characterId = player->GetObjectID(); - } - - time_t expire = 1; // Default to indefinate mute - - if (splitArgs.size() >= 2) { - const auto days = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); - if (!days) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid days."); - - return; - } - - std::optional<uint32_t> hours; - if (splitArgs.size() >= 3) { - hours = GeneralUtils::TryParse<uint32_t>(splitArgs[2]); - if (!hours) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid hours."); - - return; - } - } - - expire = time(NULL); - expire += 24 * 60 * 60 * days.value(); - expire += 60 * 60 * hours.value_or(0); - } - - if (accountId != 0) Database::Get()->UpdateAccountUnmuteTime(accountId, expire); - - char buffer[32] = "brought up for review.\0"; - - if (expire != 1) { - std::tm* ptm = std::localtime(&expire); - // Format: Mo, 15.06.2009 20:20:00 - std::strftime(buffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm); - } - - const auto timeStr = GeneralUtils::ASCIIToUTF16(std::string(buffer)); - - ChatPackets::SendSystemMessage(sysAddr, u"Muted: " + GeneralUtils::UTF8ToUTF16(splitArgs[0]) + u" until " + timeStr); - - //Notify chat about it - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE); - - bitStream.Write(characterId); - bitStream.Write(expire); - - Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); - } else { - ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /mute <username> <days (optional)> <hours (optional)>"); - } - } - - void Fly(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - auto* character = entity->GetCharacter(); - - if (character) { - bool isFlying = character->GetIsFlying(); - - if (isFlying) { - GameMessages::SendSetJetPackMode(entity, false); - - character->SetIsFlying(false); - } else { - float speedScale = 1.0f; - - if (splitArgs.size() >= 1) { - const auto tempScaleStore = GeneralUtils::TryParse<float>(splitArgs.at(0)); - - if (tempScaleStore) { - speedScale = tempScaleStore.value(); - } else { - ChatPackets::SendSystemMessage(sysAddr, u"Failed to parse speed scale argument."); - } - } - - float airSpeed = 20 * speedScale; - float maxAirSpeed = 30 * speedScale; - float verticalVelocity = 1.5 * speedScale; - - GameMessages::SendSetJetPackMode(entity, true, true, false, 167, airSpeed, maxAirSpeed, verticalVelocity); - - character->SetIsFlying(true); - } - } - } - - void AttackImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); - - const auto state = GeneralUtils::TryParse<int32_t>(splitArgs[0]); - - if (!state) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid state."); - return; - } - - if (destroyableComponent) destroyableComponent->SetIsImmune(state.value()); - } - - void GmImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - const auto splitArgs = GeneralUtils::SplitString(args, ' '); - if (splitArgs.empty()) return; - - auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); - - const auto state = GeneralUtils::TryParse<int32_t>(splitArgs[0]); - - if (!state) { - ChatPackets::SendSystemMessage(sysAddr, u"Invalid state."); - return; - } - - if (destroyableComponent) destroyableComponent->SetIsGMImmune(state.value()); - } - - void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - GameMessages::SendToggleGMInvis(entity->GetObjectID(), true, UNASSIGNED_SYSTEM_ADDRESS); - } - - void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(args), UNASSIGNED_SYSTEM_ADDRESS); - } - - void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args) { - std::string name = entity->GetCharacter()->GetName() + " - " + args; - GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS); - } -} - -void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) { - AMFArrayValue args; - - args.Insert("title", title); - args.Insert("message", message); - - GameMessages::SendUIMessageServerToAllClients("ToggleAnnounce", args); - - //Notify chat about it - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_ANNOUNCE); - - bitStream.Write<uint32_t>(title.size()); - for (auto character : title) { - bitStream.Write<char>(character); - } - - bitStream.Write<uint32_t>(message.size()); - for (auto character : message) { - bitStream.Write<char>(character); - } - - Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); -} diff --git a/dGame/dUtilities/SlashCommandHandler.h b/dGame/dUtilities/SlashCommandHandler.h index e986487b..6a94dd3b 100644 --- a/dGame/dUtilities/SlashCommandHandler.h +++ b/dGame/dUtilities/SlashCommandHandler.h @@ -21,114 +21,14 @@ struct Command { }; namespace SlashCommandHandler { - void Startup(); void HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr); void SendAnnouncement(const std::string& title, const std::string& message); void RegisterCommand(Command info); + void Startup(); }; -namespace DEVGMCommands { - void SetGMLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ToggleNameplate(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ToggleSkipCinematics(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Kill(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Metrics(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Announce(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetAnnTitle(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetAnnMsg(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ShutdownUniverse(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetMinifig(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void TestMap(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ReportProxPhys(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SpawnPhysicsVerts(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ActivateSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void AddMission(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Boost(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Unboost(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Buff(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void BuffMe(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void BuffMed(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ClearFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void CompleteMission(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void CreatePrivate(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void DebugUi(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Dismount(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ReloadConfig(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ForceSave(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Freecam(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void FreeMoney(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void GetNavmeshHeight(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void GiveUScore(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void GmAddItem(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Inspect(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ListSpawns(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void LocRow(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Lookup(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void PlayAnimation(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void PlayEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void PlayLvlFx(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void PlayRebuildFx(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Pos(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void RefillStats(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Reforge(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ResetMission(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Rot(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void RunMacro(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetControlScheme(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetCurrency(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetInventorySize(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetUiState(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Spawn(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SpawnGroup(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SpeedBoost(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void StartCelebration(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void StopEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Toggle(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void TpAll(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void TriggerSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void UnlockEmote(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetSkillSlot(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void AddFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void GetFactions(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetRewardCode(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Crash(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void RollLoot(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args); -} - namespace GMZeroCommands { void Help(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Credits(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Info(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Die(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Ping(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Pvp(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Who(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void FixStats(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Join(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args); -} - -namespace GMGreaterThanZeroCommands { - void Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void MailItem(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Ban(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void ApproveProperty(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Mute(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Fly(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void AttackImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void GmImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args); - void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args); } #endif // SLASHCOMMANDHANDLER_H diff --git a/dGame/dUtilities/SlashCommands/CMakeLists.txt b/dGame/dUtilities/SlashCommands/CMakeLists.txt new file mode 100644 index 00000000..999cd0ec --- /dev/null +++ b/dGame/dUtilities/SlashCommands/CMakeLists.txt @@ -0,0 +1,6 @@ +set(DGAME_DUTILITIES_SLASHCOMMANDS + "DEVGMCommands.cpp" + "GMGreaterThanZeroCommands.cpp" + "GMZeroCommands.cpp" + PARENT_SCOPE +) diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp new file mode 100644 index 00000000..b8d6b9f3 --- /dev/null +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.cpp @@ -0,0 +1,1592 @@ +#include "DEVGMCommands.h" + +// Classes +#include "AssetManager.h" +#include "Character.h" +#include "ChatPackets.h" +#include "dConfig.h" +#include "dNavMesh.h" +#include "dpWorld.h" +#include "dServer.h" +#include "dpShapeSphere.h" +#include "dZoneManager.h" +#include "EntityInfo.h" +#include "Metrics.hpp" +#include "PlayerManager.h" +#include "SlashCommandHandler.h" +#include "UserManager.h" +#include "User.h" +#include "VanityUtilities.h" +#include "WorldPackets.h" +#include "ZoneInstanceManager.h" + +// Database +#include "Database.h" +#include "CDObjectsTable.h" +#include "CDRewardCodesTable.h" + +// Components +#include "BuffComponent.h" +#include "CharacterComponent.h" +#include "ControllablePhysicsComponent.h" +#include "DestroyableComponent.h" +#include "HavokVehiclePhysicsComponent.h" +#include "InventoryComponent.h" +#include "LevelProgressionComponent.h" +#include "MissionComponent.h" +#include "MovingPlatformComponent.h" +#include "PossessorComponent.h" +#include "ProximityMonitorComponent.h" +#include "RenderComponent.h" +#include "ScriptedActivityComponent.h" +#include "SkillComponent.h" +#include "TriggerComponent.h" + +// Enums +#include "eGameMasterLevel.h" +#include "eMasterMessageType.h" +#include "eInventoryType.h" +#include "ePlayerFlag.h" + + +namespace DEVGMCommands { + void SetGMLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + User* user = UserManager::Instance()->GetUser(entity->GetSystemAddress()); + + const auto level_intermed = GeneralUtils::TryParse<uint32_t>(args); + if (!level_intermed) { + GameMessages::SendSlashCommandFeedbackText(entity, u"Invalid GM level."); + return; + } + eGameMasterLevel level = static_cast<eGameMasterLevel>(level_intermed.value()); + +#ifndef DEVELOPER_SERVER + if (user->GetMaxGMLevel() == eGameMasterLevel::JUNIOR_DEVELOPER) { + level = eGameMasterLevel::CIVILIAN; + } +#endif + + if (level > user->GetMaxGMLevel()) level = user->GetMaxGMLevel(); + + if (level == entity->GetGMLevel()) return; + bool success = user->GetMaxGMLevel() >= level; + + if (success) { + WorldPackets::SendGMLevelChange(entity->GetSystemAddress(), success, user->GetMaxGMLevel(), entity->GetGMLevel(), level); + GameMessages::SendChatModeUpdate(entity->GetObjectID(), level); + entity->SetGMLevel(level); + LOG("User %s (%i) has changed their GM level to %i for charID %llu", user->GetUsername().c_str(), user->GetAccountID(), level, entity->GetObjectID()); + } + +#ifndef DEVELOPER_SERVER + if ((entity->GetGMLevel() > user->GetMaxGMLevel()) || (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN && user->GetMaxGMLevel() == eGameMasterLevel::JUNIOR_DEVELOPER)) { + WorldPackets::SendGMLevelChange(entity->GetSystemAddress(), true, user->GetMaxGMLevel(), entity->GetGMLevel(), eGameMasterLevel::CIVILIAN); + GameMessages::SendChatModeUpdate(entity->GetObjectID(), eGameMasterLevel::CIVILIAN); + entity->SetGMLevel(eGameMasterLevel::CIVILIAN); + + GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); + + GameMessages::SendSlashCommandFeedbackText(entity, u"Your game master level has been changed, you may not be able to use all commands."); + } +#endif + } + + + void ToggleNameplate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + if ((Game::config->GetValue("allow_nameplate_off") != "1" && entity->GetGMLevel() < eGameMasterLevel::DEVELOPER)) return; + + auto* character = entity->GetCharacter(); + if (character && character->GetBillboardVisible()) { + character->SetBillboardVisible(false); + GameMessages::SendSlashCommandFeedbackText(entity, u"Your nameplate has been turned off and is not visible to players currently in this zone."); + } else { + character->SetBillboardVisible(true); + GameMessages::SendSlashCommandFeedbackText(entity, u"Your nameplate is now on and visible to all players."); + } + } + + void ToggleSkipCinematics(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" && entity->GetGMLevel() < eGameMasterLevel::DEVELOPER) return; + auto* character = entity->GetCharacter(); + if (!character) return; + bool current = character->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS); + character->SetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS, !current); + if (!current) { + GameMessages::SendSlashCommandFeedbackText(entity, u"You have elected to skip cinematics. Note that not all cinematics can be skipped, but most will be skipped now."); + } else { + GameMessages::SendSlashCommandFeedbackText(entity, u"Cinematics will no longer be skipped."); + } + } + + void ResetMission(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto missionId = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); + if (!missionId) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission ID."); + return; + } + + auto* missionComponent = entity->GetComponent<MissionComponent>(); + if (!missionComponent) return; + missionComponent->ResetMission(missionId.value()); + } + + void SetMinifig(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() < 2) return; + + const auto minifigItemIdExists = GeneralUtils::TryParse<int32_t>(splitArgs[1]); + if (!minifigItemIdExists) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid Minifig Item Id ID."); + return; + } + const int32_t minifigItemId = minifigItemIdExists.value(); + Game::entityManager->DestructEntity(entity, sysAddr); + auto* charComp = entity->GetComponent<CharacterComponent>(); + std::string lowerName = splitArgs[0]; + if (lowerName.empty()) return; + std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(), ::tolower); + if (lowerName == "eyebrows") { + charComp->m_Character->SetEyebrows(minifigItemId); + } else if (lowerName == "eyes") { + charComp->m_Character->SetEyes(minifigItemId); + } else if (lowerName == "haircolor") { + charComp->m_Character->SetHairColor(minifigItemId); + } else if (lowerName == "hairstyle") { + charComp->m_Character->SetHairStyle(minifigItemId); + } else if (lowerName == "pants") { + charComp->m_Character->SetPantsColor(minifigItemId); + } else if (lowerName == "lefthand") { + charComp->m_Character->SetLeftHand(minifigItemId); + } else if (lowerName == "mouth") { + charComp->m_Character->SetMouth(minifigItemId); + } else if (lowerName == "righthand") { + charComp->m_Character->SetRightHand(minifigItemId); + } else if (lowerName == "shirtcolor") { + charComp->m_Character->SetShirtColor(minifigItemId); + } else if (lowerName == "hands") { + charComp->m_Character->SetLeftHand(minifigItemId); + charComp->m_Character->SetRightHand(minifigItemId); + } else { + Game::entityManager->ConstructEntity(entity); + ChatPackets::SendSystemMessage(sysAddr, u"Invalid Minifig item to change, try one of the following: Eyebrows, Eyes, HairColor, HairStyle, Pants, LeftHand, Mouth, RightHand, Shirt, Hands"); + return; + } + + Game::entityManager->ConstructEntity(entity); + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(lowerName) + u" set to " + (GeneralUtils::to_u16string(minifigItemId))); + + GameMessages::SendToggleGMInvis(entity->GetObjectID(), false, UNASSIGNED_SYSTEM_ADDRESS); // need to retoggle because it gets reenabled on creation of new character + } + + void PlayAnimation(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + std::u16string anim = GeneralUtils::ASCIIToUTF16(splitArgs[0], splitArgs[0].size()); + RenderComponent::PlayAnimation(entity, anim); + auto* possessorComponent = entity->GetComponent<PossessorComponent>(); + if (possessorComponent) { + auto* possessedComponent = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); + if (possessedComponent) RenderComponent::PlayAnimation(possessedComponent, anim); + } + } + + void ListSpawns(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + for (const auto& pair : Game::entityManager->GetSpawnPointEntities()) { + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(pair.first)); + } + + ChatPackets::SendSystemMessage(sysAddr, u"Current: " + GeneralUtils::ASCIIToUTF16(entity->GetCharacter()->GetTargetScene())); + } + + void UnlockEmote(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto emoteID = GeneralUtils::TryParse<int32_t>(splitArgs[0]); + + if (!emoteID) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid emote ID."); + return; + } + + entity->GetCharacter()->UnlockEmote(emoteID.value()); + } + + void ForceSave(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + entity->GetCharacter()->SaveXMLToDatabase(); + } + + void Kill(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + ChatPackets::SendSystemMessage(sysAddr, u"Brutally murdering that player, if online on this server."); + + auto* player = PlayerManager::GetPlayer(splitArgs[0]); + if (player) { + player->Smash(entity->GetObjectID()); + ChatPackets::SendSystemMessage(sysAddr, u"It has been done, do you feel good about yourself now?"); + return; + } + + ChatPackets::SendSystemMessage(sysAddr, u"They were saved from your carnage."); + return; + } + + void SpeedBoost(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto boostOptional = GeneralUtils::TryParse<float>(splitArgs[0]); + if (!boostOptional) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid boost."); + return; + } + const float boost = boostOptional.value(); + + auto* controllablePhysicsComponent = entity->GetComponent<ControllablePhysicsComponent>(); + + if (!controllablePhysicsComponent) return; + controllablePhysicsComponent->SetSpeedMultiplier(boost); + + // speedboost possessables + auto possessor = entity->GetComponent<PossessorComponent>(); + if (possessor) { + auto possessedID = possessor->GetPossessable(); + if (possessedID != LWOOBJID_EMPTY) { + auto possessable = Game::entityManager->GetEntity(possessedID); + if (possessable) { + auto* possessControllablePhysicsComponent = possessable->GetComponent<ControllablePhysicsComponent>(); + if (possessControllablePhysicsComponent) { + possessControllablePhysicsComponent->SetSpeedMultiplier(boost); + } + } + } + } + + Game::entityManager->SerializeEntity(entity); + } + + void Freecam(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto state = !entity->GetVar<bool>(u"freecam"); + entity->SetVar<bool>(u"freecam", state); + + GameMessages::SendSetPlayerControlScheme(entity, static_cast<eControlScheme>(state ? 9 : 1)); + + ChatPackets::SendSystemMessage(sysAddr, u"Toggled freecam."); + } + + void SetControlScheme(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto scheme = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); + + if (!scheme) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid control scheme."); + return; + } + + GameMessages::SendSetPlayerControlScheme(entity, static_cast<eControlScheme>(scheme.value())); + + ChatPackets::SendSystemMessage(sysAddr, u"Switched control scheme."); + } + + void SetUiState(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + AMFArrayValue uiState; + + uiState.Insert("state", splitArgs[0]); + + GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, "pushGameState", uiState); + + ChatPackets::SendSystemMessage(sysAddr, u"Switched UI state."); + } + + void Toggle(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + AMFArrayValue amfArgs; + + amfArgs.Insert("visible", true); + + GameMessages::SendUIMessageServerToSingleClient(entity, sysAddr, splitArgs[0], amfArgs); + + ChatPackets::SendSystemMessage(sysAddr, u"Toggled UI state."); + } + + void SetInventorySize(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto sizeOptional = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); + if (!sizeOptional) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid size."); + return; + } + const uint32_t size = sizeOptional.value(); + + eInventoryType selectedInventory = eInventoryType::ITEMS; + + // a possible inventory was provided if we got more than 1 argument + if (splitArgs.size() >= 2) { + selectedInventory = GeneralUtils::TryParse<eInventoryType>(splitArgs.at(1)).value_or(eInventoryType::INVALID); + if (selectedInventory == eInventoryType::INVALID) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid inventory."); + return; + } else { + // In this case, we treat the input as a string and try to find it in the reflection list + std::transform(splitArgs.at(1).begin(), splitArgs.at(1).end(), splitArgs.at(1).begin(), ::toupper); + for (uint32_t index = 0; index < NUMBER_OF_INVENTORIES; index++) { + if (std::string_view(splitArgs.at(1)) == std::string_view(InventoryType::InventoryTypeToString(static_cast<eInventoryType>(index)))) selectedInventory = static_cast<eInventoryType>(index); + } + } + + ChatPackets::SendSystemMessage(sysAddr, u"Setting inventory " + + GeneralUtils::ASCIIToUTF16(splitArgs.at(1)) + + u" to size " + + GeneralUtils::to_u16string(size)); + } else ChatPackets::SendSystemMessage(sysAddr, u"Setting inventory ITEMS to size " + GeneralUtils::to_u16string(size)); + + auto* inventoryComponent = entity->GetComponent<InventoryComponent>(); + if (inventoryComponent) { + auto* inventory = inventoryComponent->GetInventory(selectedInventory); + + inventory->SetSize(size); + } + } + + void RunMacro(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + // Only process if input does not contain separator charaters + if (splitArgs[0].find("/") != std::string::npos) return; + if (splitArgs[0].find("\\") != std::string::npos) return; + + auto infile = Game::assetManager->GetFile(("macros/" + splitArgs[0] + ".scm").c_str()); + + if (!infile) { + ChatPackets::SendSystemMessage(sysAddr, u"Unknown macro! Is the filename right?"); + return; + } + + if (infile.good()) { + std::string line; + while (std::getline(infile, line)) { + // Do this in two separate calls to catch both \n and \r\n + line.erase(std::remove(line.begin(), line.end(), '\n'), line.end()); + line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); + SlashCommandHandler::HandleChatCommand(GeneralUtils::ASCIIToUTF16(line), entity, sysAddr); + } + } else { + ChatPackets::SendSystemMessage(sysAddr, u"Unknown macro! Is the filename right?"); + } + } + + void AddMission(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto missionID = GeneralUtils::TryParse<uint32_t>(splitArgs.at(0)); + + if (!missionID) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission id."); + return; + } + + auto comp = static_cast<MissionComponent*>(entity->GetComponent(eReplicaComponentType::MISSION)); + if (comp) comp->AcceptMission(missionID.value(), true); + } + + void CompleteMission(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto missionID = GeneralUtils::TryParse<uint32_t>(splitArgs.at(0)); + + if (!missionID) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid mission id."); + return; + } + + auto comp = static_cast<MissionComponent*>(entity->GetComponent(eReplicaComponentType::MISSION)); + if (comp) comp->CompleteMission(missionID.value(), true); + } + + void SetFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() == 1) { + const auto flagId = GeneralUtils::TryParse<int32_t>(splitArgs.at(0)); + + if (!flagId) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag id."); + return; + } + + entity->GetCharacter()->SetPlayerFlag(flagId.value(), true); + } else if (splitArgs.size() >= 2) { + const auto flagId = GeneralUtils::TryParse<int32_t>(splitArgs.at(1)); + std::string onOffFlag = splitArgs.at(0); + if (!flagId) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag id."); + return; + } + + if (onOffFlag != "off" && onOffFlag != "on") { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag type."); + return; + } + + entity->GetCharacter()->SetPlayerFlag(flagId.value(), onOffFlag == "on"); + } + } + + void ClearFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto flagId = GeneralUtils::TryParse<int32_t>(splitArgs.at(0)); + + if (!flagId) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag id."); + return; + } + + entity->GetCharacter()->SetPlayerFlag(flagId.value(), false); + } + + void PlayEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() < 3) return; + + const auto effectID = GeneralUtils::TryParse<int32_t>(splitArgs.at(0)); + + if (!effectID) return; + + // FIXME: use fallible ASCIIToUTF16 conversion, because non-ascii isn't valid anyway + GameMessages::SendPlayFXEffect(entity->GetObjectID(), effectID.value(), GeneralUtils::ASCIIToUTF16(splitArgs.at(1)), splitArgs.at(2)); + } + + void StopEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + GameMessages::SendStopFXEffect(entity, true, splitArgs[0]); + } + + void SetAnnTitle(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + entity->GetCharacter()->SetAnnouncementTitle(args); + } + + void SetAnnMsg(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + entity->GetCharacter()->SetAnnouncementMessage(args); + } + + void Announce(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + if (entity->GetCharacter()->GetAnnouncementTitle().empty() || entity->GetCharacter()->GetAnnouncementMessage().empty()) { + ChatPackets::SendSystemMessage(sysAddr, u"Use /setanntitle <title> & /setannmsg <msg> first!"); + return; + } + + SlashCommandHandler::SendAnnouncement(entity->GetCharacter()->GetAnnouncementTitle(), entity->GetCharacter()->GetAnnouncementMessage()); + } + + void ShutdownUniverse(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + //Tell the master server that we're going to be shutting down whole "universe": + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::SHUTDOWN_UNIVERSE); + Game::server->SendToMaster(bitStream); + ChatPackets::SendSystemMessage(sysAddr, u"Sent universe shutdown notification to master."); + + //Tell chat to send an announcement to all servers + SlashCommandHandler::SendAnnouncement("Servers Closing Soon!", "DLU servers will close for maintenance in 10 minutes from now."); + } + + void GetNavmeshHeight(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto control = static_cast<ControllablePhysicsComponent*>(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); + if (!control) return; + + float y = dpWorld::GetNavMesh()->GetHeightAtPoint(control->GetPosition()); + std::u16string msg = u"Navmesh height: " + (GeneralUtils::to_u16string(y)); + ChatPackets::SendSystemMessage(sysAddr, msg); + } + + void GmAddItem(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + + if (splitArgs.size() == 1) { + const auto itemLOT = GeneralUtils::TryParse<uint32_t>(splitArgs.at(0)); + + if (!itemLOT) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid item LOT."); + return; + } + + InventoryComponent* inventory = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY)); + + inventory->AddItem(itemLOT.value(), 1, eLootSourceType::MODERATION); + } else if (splitArgs.size() == 2) { + const auto itemLOT = GeneralUtils::TryParse<uint32_t>(splitArgs.at(0)); + if (!itemLOT) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid item LOT."); + return; + } + + const auto count = GeneralUtils::TryParse<uint32_t>(splitArgs.at(1)); + if (!count) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid item count."); + return; + } + + InventoryComponent* inventory = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY)); + + inventory->AddItem(itemLOT.value(), count.value(), eLootSourceType::MODERATION); + } else { + ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /gmadditem <lot>"); + } + } + + void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + + NiPoint3 pos{}; + if (splitArgs.size() == 3) { + + const auto x = GeneralUtils::TryParse<float>(splitArgs.at(0)); + if (!x) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid x."); + return; + } + + const auto y = GeneralUtils::TryParse<float>(splitArgs.at(1)); + if (!y) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid y."); + return; + } + + const auto z = GeneralUtils::TryParse<float>(splitArgs.at(2)); + if (!z) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid z."); + return; + } + + pos.SetX(x.value()); + pos.SetY(y.value()); + pos.SetZ(z.value()); + + LOG("Teleporting objectID: %llu to %f, %f, %f", entity->GetObjectID(), pos.x, pos.y, pos.z); + GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr); + } else if (splitArgs.size() == 2) { + + const auto x = GeneralUtils::TryParse<float>(splitArgs.at(0)); + if (!x) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid x."); + return; + } + + const auto z = GeneralUtils::TryParse<float>(splitArgs.at(1)); + if (!z) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid z."); + return; + } + + pos.SetX(x.value()); + pos.SetY(0.0f); + pos.SetZ(z.value()); + + LOG("Teleporting objectID: %llu to X: %f, Z: %f", entity->GetObjectID(), pos.x, pos.z); + GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr); + } else { + ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /teleport <x> (<y>) <z> - if no Y given, will teleport to the height of the terrain (or any physics object)."); + } + + auto* possessorComponent = entity->GetComponent<PossessorComponent>(); + if (possessorComponent) { + auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); + + if (possassableEntity != nullptr) { + auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent<HavokVehiclePhysicsComponent>(); + if (havokVehiclePhysicsComponent) { + havokVehiclePhysicsComponent->SetPosition(pos); + Game::entityManager->SerializeEntity(possassableEntity); + } else GameMessages::SendTeleport(possassableEntity->GetObjectID(), pos, NiQuaternion(), sysAddr); + } + } + } + + void TpAll(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto pos = entity->GetPosition(); + + const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER); + + for (auto* character : characters) { + GameMessages::SendTeleport(character->GetObjectID(), pos, NiQuaternion(), character->GetSystemAddress()); + } + } + + void Dismount(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto* possessorComponent = entity->GetComponent<PossessorComponent>(); + if (possessorComponent) { + auto possessableId = possessorComponent->GetPossessable(); + if (possessableId != LWOOBJID_EMPTY) { + auto* possessableEntity = Game::entityManager->GetEntity(possessableId); + if (possessableEntity) possessorComponent->Dismount(possessableEntity, true); + } + } + } + + void BuffMe(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto dest = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); + if (dest) { + dest->SetHealth(999); + dest->SetMaxHealth(999.0f); + dest->SetArmor(999); + dest->SetMaxArmor(999.0f); + dest->SetImagination(999); + dest->SetMaxImagination(999.0f); + } + Game::entityManager->SerializeEntity(entity); + } + + void StartCelebration(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto celebration = GeneralUtils::TryParse<int32_t>(splitArgs.at(0)); + + if (!celebration) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid celebration."); + return; + } + + GameMessages::SendStartCelebrationEffect(entity, entity->GetSystemAddress(), celebration.value()); + } + + void BuffMed(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto dest = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); + if (dest) { + dest->SetHealth(9); + dest->SetMaxHealth(9.0f); + dest->SetArmor(9); + dest->SetMaxArmor(9.0f); + dest->SetImagination(9); + dest->SetMaxImagination(9.0f); + } + Game::entityManager->SerializeEntity(entity); + } + + void RefillStats(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto dest = static_cast<DestroyableComponent*>(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); + if (dest) { + dest->SetHealth(static_cast<int32_t>(dest->GetMaxHealth())); + dest->SetArmor(static_cast<int32_t>(dest->GetMaxArmor())); + dest->SetImagination(static_cast<int32_t>(dest->GetMaxImagination())); + } + + Game::entityManager->SerializeEntity(entity); + } + + void Lookup(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto query = CDClientDatabase::CreatePreppedStmt( + "SELECT `id`, `name` FROM `Objects` WHERE `displayName` LIKE ?1 OR `name` LIKE ?1 OR `description` LIKE ?1 LIMIT 50"); + + const std::string query_text = "%" + args + "%"; + query.bind(1, query_text.c_str()); + + auto tables = query.execQuery(); + + while (!tables.eof()) { + std::string message = std::to_string(tables.getIntField(0)) + " - " + tables.getStringField(1); + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(message, message.size())); + tables.nextRow(); + } + } + + void Spawn(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + ControllablePhysicsComponent* comp = static_cast<ControllablePhysicsComponent*>(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); + if (!comp) return; + + const auto lot = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); + + if (!lot) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid lot."); + return; + } + + EntityInfo info; + info.lot = lot.value(); + info.pos = comp->GetPosition(); + info.rot = comp->GetRotation(); + info.spawner = nullptr; + info.spawnerID = entity->GetObjectID(); + info.spawnerNodeID = 0; + + Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); + + if (newEntity == nullptr) { + ChatPackets::SendSystemMessage(sysAddr, u"Failed to spawn entity."); + return; + } + + Game::entityManager->ConstructEntity(newEntity); + } + + void SpawnGroup(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() < 3) return; + + const auto lot = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); + if (!lot) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid lot."); + return; + } + + const auto numberToSpawnOptional = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); + if (!numberToSpawnOptional && numberToSpawnOptional.value() > 0) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid number of enemies to spawn."); + return; + } + uint32_t numberToSpawn = numberToSpawnOptional.value(); + + // Must spawn within a radius of at least 0.0f + const auto radiusToSpawnWithinOptional = GeneralUtils::TryParse<float>(splitArgs[2]); + if (!radiusToSpawnWithinOptional && radiusToSpawnWithinOptional.value() < 0.0f) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid radius to spawn within."); + return; + } + const float radiusToSpawnWithin = radiusToSpawnWithinOptional.value(); + + EntityInfo info; + info.lot = lot.value(); + info.spawner = nullptr; + info.spawnerID = entity->GetObjectID(); + info.spawnerNodeID = 0; + + auto playerPosition = entity->GetPosition(); + while (numberToSpawn > 0) { + auto randomAngle = GeneralUtils::GenerateRandomNumber<float>(0.0f, 2 * PI); + auto randomRadius = GeneralUtils::GenerateRandomNumber<float>(0.0f, radiusToSpawnWithin); + + // Set the position to the generated random position plus the player position. This will + // spawn the entity in a circle around the player. As you get further from the player, the angle chosen will get less accurate. + info.pos = playerPosition + NiPoint3(cos(randomAngle) * randomRadius, 0.0f, sin(randomAngle) * randomRadius); + info.rot = NiQuaternion(); + + auto newEntity = Game::entityManager->CreateEntity(info); + if (newEntity == nullptr) { + ChatPackets::SendSystemMessage(sysAddr, u"Failed to spawn entity."); + return; + } + + Game::entityManager->ConstructEntity(newEntity); + numberToSpawn--; + } + } + + void GiveUScore(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto uscoreOptional = GeneralUtils::TryParse<int32_t>(splitArgs[0]); + if (!uscoreOptional) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid uscore."); + return; + } + const int32_t uscore = uscoreOptional.value(); + + CharacterComponent* character = entity->GetComponent<CharacterComponent>(); + if (character) character->SetUScore(character->GetUScore() + uscore); + // MODERATION should work but it doesn't. Relog to see uscore changes + + eLootSourceType lootType = eLootSourceType::MODERATION; + + if (splitArgs.size() >= 2) { + const auto type = GeneralUtils::TryParse<eLootSourceType>(splitArgs[1]); + lootType = type.value_or(lootType); + } + + GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), uscore, lootType); + } + + void SetLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + // We may be trying to set a specific players level to a level. If so override the entity with the requested players. + std::string requestedPlayerToSetLevelOf = ""; + if (splitArgs.size() > 1) { + requestedPlayerToSetLevelOf = splitArgs[1]; + + auto requestedPlayer = PlayerManager::GetPlayer(requestedPlayerToSetLevelOf); + + if (!requestedPlayer) { + ChatPackets::SendSystemMessage(sysAddr, u"No player found with username: (" + GeneralUtils::UTF8ToUTF16(requestedPlayerToSetLevelOf) + u")."); + return; + } + + if (!requestedPlayer->GetOwner()) { + ChatPackets::SendSystemMessage(sysAddr, u"No entity found with username: (" + GeneralUtils::UTF8ToUTF16(requestedPlayerToSetLevelOf) + u")."); + return; + } + + entity = requestedPlayer->GetOwner(); + } + const auto requestedLevelOptional = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); + uint32_t oldLevel; + + // first check the level is valid + if (!requestedLevelOptional) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid level."); + return; + } + uint32_t requestedLevel = requestedLevelOptional.value(); + // query to set our uscore to the correct value for this level + + auto characterComponent = entity->GetComponent<CharacterComponent>(); + if (!characterComponent) return; + auto levelComponent = entity->GetComponent<LevelProgressionComponent>(); + auto query = CDClientDatabase::CreatePreppedStmt("SELECT requiredUScore from LevelProgressionLookup WHERE id = ?;"); + query.bind(1, static_cast<int>(requestedLevel)); + auto result = query.execQuery(); + + if (result.eof()) return; + + // Set the UScore first + oldLevel = levelComponent->GetLevel(); + characterComponent->SetUScore(result.getIntField(0, characterComponent->GetUScore())); + + // handle level up for each level we have passed if we set our level to be higher than the current one. + if (oldLevel < requestedLevel) { + while (oldLevel < requestedLevel) { + oldLevel += 1; + levelComponent->SetLevel(oldLevel); + levelComponent->HandleLevelUp(); + } + } else { + levelComponent->SetLevel(requestedLevel); + } + + if (requestedPlayerToSetLevelOf != "") { + ChatPackets::SendSystemMessage( + sysAddr, u"Set " + GeneralUtils::UTF8ToUTF16(requestedPlayerToSetLevelOf) + u"'s level to " + GeneralUtils::to_u16string(requestedLevel) + + u" and UScore to " + GeneralUtils::to_u16string(characterComponent->GetUScore()) + + u". Relog to see changes."); + } else { + ChatPackets::SendSystemMessage( + sysAddr, u"Set your level to " + GeneralUtils::to_u16string(requestedLevel) + + u" and UScore to " + GeneralUtils::to_u16string(characterComponent->GetUScore()) + + u". Relog to see changes."); + } + } + + void Pos(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto position = entity->GetPosition(); + + ChatPackets::SendSystemMessage(sysAddr, u"<" + (GeneralUtils::to_u16string(position.x)) + u", " + (GeneralUtils::to_u16string(position.y)) + u", " + (GeneralUtils::to_u16string(position.z)) + u">"); + + LOG("Position: %f, %f, %f", position.x, position.y, position.z); + } + + void Rot(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto rotation = entity->GetRotation(); + + ChatPackets::SendSystemMessage(sysAddr, u"<" + (GeneralUtils::to_u16string(rotation.w)) + u", " + (GeneralUtils::to_u16string(rotation.x)) + u", " + (GeneralUtils::to_u16string(rotation.y)) + u", " + (GeneralUtils::to_u16string(rotation.z)) + u">"); + + LOG("Rotation: %f, %f, %f, %f", rotation.w, rotation.x, rotation.y, rotation.z); + } + + void LocRow(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto position = entity->GetPosition(); + const auto rotation = entity->GetRotation(); + + LOG("<location x=\"%f\" y=\"%f\" z=\"%f\" rw=\"%f\" rx=\"%f\" ry=\"%f\" rz=\"%f\" />", position.x, position.y, position.z, rotation.w, rotation.x, rotation.y, rotation.z); + } + + void PlayLvlFx(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + GameMessages::SendPlayFXEffect(entity, 7074, u"create", "7074", LWOOBJID_EMPTY, 1.0f, 1.0f, true); + } + + void PlayRebuildFx(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + GameMessages::SendPlayFXEffect(entity, 230, u"rebuild", "230", LWOOBJID_EMPTY, 1.0f, 1.0f, true); + } + + void FreeMoney(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto money = GeneralUtils::TryParse<int64_t>(splitArgs[0]); + + if (!money) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid money."); + return; + } + + auto* ch = entity->GetCharacter(); + ch->SetCoins(ch->GetCoins() + money.value(), eLootSourceType::MODERATION); + } + + void SetCurrency(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + const auto money = GeneralUtils::TryParse<int64_t>(splitArgs[0]); + + if (!money) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid money."); + return; + } + + auto* ch = entity->GetCharacter(); + ch->SetCoins(money.value(), eLootSourceType::MODERATION); + } + + void Buff(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() < 2) return; + + auto* buffComponent = entity->GetComponent<BuffComponent>(); + + const auto id = GeneralUtils::TryParse<int32_t>(splitArgs[0]); + if (!id) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid buff id."); + return; + } + + const auto duration = GeneralUtils::TryParse<int32_t>(splitArgs[1]); + if (!duration) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid buff duration."); + return; + } + + if (buffComponent) buffComponent->ApplyBuff(id.value(), duration.value(), entity->GetObjectID()); + } + + void TestMap(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + ChatPackets::SendSystemMessage(sysAddr, u"Requesting map change..."); + LWOCLONEID cloneId = 0; + bool force = false; + + const auto reqZoneOptional = GeneralUtils::TryParse<LWOMAPID>(splitArgs[0]); + if (!reqZoneOptional) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid zone."); + return; + } + const LWOMAPID reqZone = reqZoneOptional.value(); + + if (splitArgs.size() > 1) { + auto index = 1; + + if (splitArgs[index] == "force") { + index++; + + force = true; + } + + if (splitArgs.size() > index) { + const auto cloneIdOptional = GeneralUtils::TryParse<LWOCLONEID>(splitArgs[index]); + if (!cloneIdOptional) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid clone id."); + return; + } + cloneId = cloneIdOptional.value(); + } + } + + const auto objid = entity->GetObjectID(); + + if (force || Game::zoneManager->CheckIfAccessibleZone(reqZone)) { // to prevent tomfoolery + + ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, reqZone, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { + + auto* entity = Game::entityManager->GetEntity(objid); + if (!entity) return; + + const auto sysAddr = entity->GetSystemAddress(); + + ChatPackets::SendSystemMessage(sysAddr, u"Transfering map..."); + + LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); + if (entity->GetCharacter()) { + entity->GetCharacter()->SetZoneID(zoneID); + entity->GetCharacter()->SetZoneInstance(zoneInstance); + entity->GetCharacter()->SetZoneClone(zoneClone); + entity->GetComponent<CharacterComponent>()->SetLastRocketConfig(u""); + } + + entity->GetCharacter()->SaveXMLToDatabase(); + + WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + return; + }); + } else { + std::string msg = "ZoneID not found or allowed: "; + msg.append(splitArgs[0]); // FIXME: unnecessary utf16 re-encoding just for error + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(msg, msg.size())); + } + } + + void CreatePrivate(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() < 3) return; + + const auto zone = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); + if (!zone) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid zone."); + return; + } + + const auto clone = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); + if (!clone) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid clone."); + return; + } + + const auto& password = splitArgs[2]; + + ZoneInstanceManager::Instance()->CreatePrivateZone(Game::server, zone.value(), clone.value(), password); + + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16("Sent request for private zone with password: " + password)); + } + + void DebugUi(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + ChatPackets::SendSystemMessage(sysAddr, u"Opening UIDebugger..."); + } + + void Boost(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + + auto* possessorComponent = entity->GetComponent<PossessorComponent>(); + + if (possessorComponent == nullptr) { + return; + } + + auto* vehicle = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); + + if (vehicle == nullptr) { + return; + } + + if (splitArgs.size() >= 1) { + const auto time = GeneralUtils::TryParse<float>(splitArgs[0]); + + if (!time) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid boost time."); + return; + } else { + GameMessages::SendVehicleAddPassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); + entity->AddCallbackTimer(time.value(), [vehicle]() { + if (!vehicle) return; + GameMessages::SendVehicleRemovePassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); + }); + } + } else { + GameMessages::SendVehicleAddPassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); + } + } + + void Unboost(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto* possessorComponent = entity->GetComponent<PossessorComponent>(); + + if (possessorComponent == nullptr) return; + auto* vehicle = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); + + if (vehicle == nullptr) return; + GameMessages::SendVehicleRemovePassiveBoostAction(vehicle->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); + } + + void ActivateSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + auto spawners = Game::zoneManager->GetSpawnersByName(splitArgs[0]); + + for (auto* spawner : spawners) { + spawner->Activate(); + } + + spawners = Game::zoneManager->GetSpawnersInGroup(splitArgs[0]); + + for (auto* spawner : spawners) { + spawner->Activate(); + } + } + + void SpawnPhysicsVerts(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + //Go tell physics to spawn all the vertices: + auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PHANTOM_PHYSICS); + for (auto en : entities) { + auto phys = static_cast<PhantomPhysicsComponent*>(en->GetComponent(eReplicaComponentType::PHANTOM_PHYSICS)); + if (phys) + phys->SpawnVertices(); + } + } + + void ReportProxPhys(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PROXIMITY_MONITOR); + for (auto en : entities) { + auto phys = static_cast<ProximityMonitorComponent*>(en->GetComponent(eReplicaComponentType::PROXIMITY_MONITOR)); + if (phys) { + for (auto prox : phys->GetProximitiesData()) { + if (!prox.second) continue; + + auto sphere = static_cast<dpShapeSphere*>(prox.second->GetShape()); + auto pos = prox.second->GetPosition(); + LOG("Proximity: %s, r: %f, pos: %f, %f, %f", prox.first.c_str(), sphere->GetRadius(), pos.x, pos.y, pos.z); + } + } + } + } + + void TriggerSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + auto spawners = Game::zoneManager->GetSpawnersByName(splitArgs[0]); + + for (auto* spawner : spawners) { + spawner->Spawn(); + } + + spawners = Game::zoneManager->GetSpawnersInGroup(splitArgs[0]); + + for (auto* spawner : spawners) { + spawner->Spawn(); + } + } + + void Reforge(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() < 2) return; + + const auto baseItem = GeneralUtils::TryParse<LOT>(splitArgs[0]); + if (!baseItem) return; + + const auto reforgedItem = GeneralUtils::TryParse<LOT>(splitArgs[1]); + if (!reforgedItem) return; + + auto* inventoryComponent = entity->GetComponent<InventoryComponent>(); + if (!inventoryComponent) return; + + std::vector<LDFBaseData*> data{}; + data.push_back(new LDFData<int32_t>(u"reforgedLOT", reforgedItem.value())); + + inventoryComponent->AddItem(baseItem.value(), 1, eLootSourceType::MODERATION, eInventoryType::INVALID, data); + } + + void Crash(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + ChatPackets::SendSystemMessage(sysAddr, u"Crashing..."); + + int* badPtr = nullptr; + *badPtr = 0; + } + + void Metrics(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + for (const auto variable : Metrics::GetAllMetrics()) { + auto* metric = Metrics::GetMetric(variable); + + if (metric == nullptr) { + continue; + } + + ChatPackets::SendSystemMessage( + sysAddr, + GeneralUtils::ASCIIToUTF16(Metrics::MetricVariableToString(variable)) + + u": " + + GeneralUtils::to_u16string(Metrics::ToMiliseconds(metric->average)) + + u"ms" + ); + } + + ChatPackets::SendSystemMessage( + sysAddr, + u"Peak RSS: " + GeneralUtils::to_u16string(static_cast<float>(static_cast<double>(Metrics::GetPeakRSS()) / 1.024e6)) + + u"MB" + ); + + ChatPackets::SendSystemMessage( + sysAddr, + u"Current RSS: " + GeneralUtils::to_u16string(static_cast<float>(static_cast<double>(Metrics::GetCurrentRSS()) / 1.024e6)) + + u"MB" + ); + + ChatPackets::SendSystemMessage( + sysAddr, + u"Process ID: " + GeneralUtils::to_u16string(Metrics::GetProcessID()) + ); + } + + void ReloadConfig(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + Game::config->ReloadConfig(); + VanityUtilities::SpawnVanity(); + dpWorld::Reload(); + auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); + for (const auto* const entity : entities) { + auto* const scriptedActivityComponent = entity->GetComponent<ScriptedActivityComponent>(); + if (!scriptedActivityComponent) continue; + + scriptedActivityComponent->ReloadConfig(); + } + Game::server->UpdateMaximumMtuSize(); + Game::server->UpdateBandwidthLimit(); + ChatPackets::SendSystemMessage(sysAddr, u"Successfully reloaded config for world!"); + } + + void RollLoot(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() < 3) return; + + const auto lootMatrixIndex = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); + if (!lootMatrixIndex) return; + + const auto targetLot = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); + if (!targetLot) return; + + const auto loops = GeneralUtils::TryParse<uint32_t>(splitArgs[2]); + if (!loops) return; + + uint64_t totalRuns = 0; + + for (uint32_t i = 0; i < loops; i++) { + while (true) { + auto lootRoll = Loot::RollLootMatrix(lootMatrixIndex.value()); + totalRuns += 1; + bool doBreak = false; + for (const auto& kv : lootRoll) { + if (static_cast<uint32_t>(kv.first) == targetLot) { + doBreak = true; + } + } + if (doBreak) break; + } + } + + std::u16string message = u"Ran loot drops looking for " + + GeneralUtils::to_u16string(targetLot.value()) + + u", " + + GeneralUtils::to_u16string(loops.value()) + + u" times. It ran " + + GeneralUtils::to_u16string(totalRuns) + + u" times. Averaging out at " + + GeneralUtils::to_u16string(static_cast<float>(totalRuns) / loops.value()); + + ChatPackets::SendSystemMessage(sysAddr, message); + } + + void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + eInventoryType inventoryType = eInventoryType::INVALID; + + const auto inventoryTypeOptional = GeneralUtils::TryParse<eInventoryType>(splitArgs[0]); + if (!inventoryTypeOptional) { + // In this case, we treat the input as a string and try to find it in the reflection list + std::transform(splitArgs[0].begin(), splitArgs[0].end(), splitArgs[0].begin(), ::toupper); + LOG("looking for inventory %s", splitArgs[0].c_str()); + for (uint32_t index = 0; index < NUMBER_OF_INVENTORIES; index++) { + if (std::string_view(splitArgs[0]) == std::string_view(InventoryType::InventoryTypeToString(static_cast<eInventoryType>(index)))) inventoryType = static_cast<eInventoryType>(index); + } + } else { + inventoryType = inventoryTypeOptional.value(); + } + + if (inventoryType == eInventoryType::INVALID || inventoryType >= NUMBER_OF_INVENTORIES) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid inventory provided."); + return; + } + + auto* inventoryComponent = entity->GetComponent<InventoryComponent>(); + if (!inventoryComponent) return; + + auto* inventoryToDelete = inventoryComponent->GetInventory(inventoryType); + if (!inventoryToDelete) return; + + inventoryToDelete->DeleteAllItems(); + LOG("Deleted inventory %s for user %llu", splitArgs[0].c_str(), entity->GetObjectID()); + ChatPackets::SendSystemMessage(sysAddr, u"Deleted inventory " + GeneralUtils::UTF8ToUTF16(splitArgs[0])); + } + + void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + auto* skillComponent = entity->GetComponent<SkillComponent>(); + if (skillComponent) { + const auto skillId = GeneralUtils::TryParse<uint32_t>(splitArgs[0]); + + if (!skillId) { + ChatPackets::SendSystemMessage(sysAddr, u"Error getting skill ID."); + return; + } else { + skillComponent->CastSkill(skillId.value(), entity->GetObjectID(), entity->GetObjectID()); + ChatPackets::SendSystemMessage(sysAddr, u"Cast skill"); + } + } + } + + void SetSkillSlot(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() < 2) return; + + auto* const inventoryComponent = entity->GetComponent<InventoryComponent>(); + if (inventoryComponent) { + const auto slot = GeneralUtils::TryParse<BehaviorSlot>(splitArgs[0]); + if (!slot) { + ChatPackets::SendSystemMessage(sysAddr, u"Error getting slot."); + return; + } else { + const auto skillId = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); + if (!skillId) { + ChatPackets::SendSystemMessage(sysAddr, u"Error getting skill."); + return; + } else { + if (inventoryComponent->SetSkill(slot.value(), skillId.value())) ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot successfully"); + else ChatPackets::SendSystemMessage(sysAddr, u"Set skill to slot failed"); + } + } + } + } + + void SetFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); + if (destroyableComponent) { + const auto faction = GeneralUtils::TryParse<int32_t>(splitArgs[0]); + + if (!faction) { + ChatPackets::SendSystemMessage(sysAddr, u"Error getting faction."); + return; + } else { + destroyableComponent->SetFaction(faction.value()); + ChatPackets::SendSystemMessage(sysAddr, u"Set faction and updated enemies list"); + } + } + } + + void AddFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); + if (destroyableComponent) { + const auto faction = GeneralUtils::TryParse<int32_t>(splitArgs[0]); + + if (!faction) { + ChatPackets::SendSystemMessage(sysAddr, u"Error getting faction."); + return; + } else { + destroyableComponent->AddFaction(faction.value()); + ChatPackets::SendSystemMessage(sysAddr, u"Added faction and updated enemies list"); + } + } + } + + void GetFactions(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); + if (destroyableComponent) { + ChatPackets::SendSystemMessage(sysAddr, u"Friendly factions:"); + for (const auto entry : destroyableComponent->GetFactionIDs()) { + ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry))); + } + + ChatPackets::SendSystemMessage(sysAddr, u"Enemy factions:"); + for (const auto entry : destroyableComponent->GetEnemyFactionsIDs()) { + ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry))); + } + } + } + + void SetRewardCode(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto* character = entity->GetCharacter(); + if (!character) return; + auto* user = character->GetParentUser(); + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + auto* cdrewardCodes = CDClientManager::GetTable<CDRewardCodesTable>(); + + auto id = cdrewardCodes->GetCodeID(splitArgs[0]); + if (id != -1) Database::Get()->InsertRewardCode(user->GetAccountID(), id); + } + + void Inspect(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + Entity* closest = nullptr; + + std::u16string ldf; + + bool isLDF = false; + + auto component = GeneralUtils::TryParse<eReplicaComponentType>(splitArgs[0]); + if (!component) { + component = eReplicaComponentType::INVALID; + + ldf = GeneralUtils::UTF8ToUTF16(splitArgs[0]); + + isLDF = true; + } + + auto reference = entity->GetPosition(); + + auto closestDistance = 0.0f; + + const auto candidates = Game::entityManager->GetEntitiesByComponent(component.value()); + + for (auto* candidate : candidates) { + if (candidate->GetLOT() == 1 || candidate->GetLOT() == 8092) { + continue; + } + + if (isLDF && !candidate->HasVar(ldf)) { + continue; + } + + if (!closest) { + closest = candidate; + + closestDistance = NiPoint3::Distance(candidate->GetPosition(), reference); + + continue; + } + + const auto distance = NiPoint3::Distance(candidate->GetPosition(), reference); + + if (distance < closestDistance) { + closest = candidate; + + closestDistance = distance; + } + } + + if (!closest) return; + + Game::entityManager->SerializeEntity(closest); + + auto* table = CDClientManager::GetTable<CDObjectsTable>(); + + const auto& info = table->GetByID(closest->GetLOT()); + + std::stringstream header; + + header << info.name << " [" << std::to_string(info.id) << "]" << " " << std::to_string(closestDistance) << " " << std::to_string(closest->IsSleeping()); + + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(header.str())); + + for (const auto& pair : closest->GetComponents()) { + auto id = pair.first; + + std::stringstream stream; + + stream << "Component [" << std::to_string(static_cast<uint32_t>(id)) << "]"; + + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(stream.str())); + } + + if (splitArgs.size() >= 2) { + if (splitArgs[1] == "-m" && splitArgs.size() >= 3) { + auto* const movingPlatformComponent = closest->GetComponent<MovingPlatformComponent>(); + + const auto mValue = GeneralUtils::TryParse<int32_t>(splitArgs[2]); + + if (!movingPlatformComponent || !mValue) return; + + movingPlatformComponent->SetSerialized(true); + + if (mValue == -1) { + movingPlatformComponent->StopPathing(); + } else { + movingPlatformComponent->GotoWaypoint(mValue.value()); + } + + Game::entityManager->SerializeEntity(closest); + } else if (splitArgs[1] == "-a" && splitArgs.size() >= 3) { + RenderComponent::PlayAnimation(closest, splitArgs.at(2)); + } else if (splitArgs[1] == "-s") { + for (auto* entry : closest->GetSettings()) { + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::UTF8ToUTF16(entry->GetString())); + } + + ChatPackets::SendSystemMessage(sysAddr, u"------"); + ChatPackets::SendSystemMessage(sysAddr, u"Spawner ID: " + GeneralUtils::to_u16string(closest->GetSpawnerID())); + } else if (splitArgs[1] == "-p") { + const auto postion = closest->GetPosition(); + + ChatPackets::SendSystemMessage( + sysAddr, + GeneralUtils::ASCIIToUTF16("< " + std::to_string(postion.x) + ", " + std::to_string(postion.y) + ", " + std::to_string(postion.z) + " >") + ); + } else if (splitArgs[1] == "-f") { + auto* destuctable = closest->GetComponent<DestroyableComponent>(); + + if (destuctable == nullptr) { + ChatPackets::SendSystemMessage(sysAddr, u"No destroyable component on this entity!"); + return; + } + + ChatPackets::SendSystemMessage(sysAddr, u"Smashable: " + (GeneralUtils::to_u16string(destuctable->GetIsSmashable()))); + + ChatPackets::SendSystemMessage(sysAddr, u"Friendly factions:"); + for (const auto entry : destuctable->GetFactionIDs()) { + ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry))); + } + + ChatPackets::SendSystemMessage(sysAddr, u"Enemy factions:"); + for (const auto entry : destuctable->GetEnemyFactionsIDs()) { + ChatPackets::SendSystemMessage(sysAddr, (GeneralUtils::to_u16string(entry))); + } + + if (splitArgs.size() >= 3) { + const auto faction = GeneralUtils::TryParse<int32_t>(splitArgs[2]); + if (!faction) return; + + destuctable->SetFaction(-1); + destuctable->AddFaction(faction.value(), true); + } + } else if (splitArgs[1] == "-cf") { + auto* destuctable = entity->GetComponent<DestroyableComponent>(); + if (!destuctable) { + ChatPackets::SendSystemMessage(sysAddr, u"No destroyable component on this entity!"); + return; + } + if (destuctable->IsEnemy(closest)) ChatPackets::SendSystemMessage(sysAddr, u"They are our enemy"); + else ChatPackets::SendSystemMessage(sysAddr, u"They are NOT our enemy"); + } else if (splitArgs[1] == "-t") { + auto* phantomPhysicsComponent = closest->GetComponent<PhantomPhysicsComponent>(); + + if (phantomPhysicsComponent != nullptr) { + ChatPackets::SendSystemMessage(sysAddr, u"Type: " + (GeneralUtils::to_u16string(static_cast<uint32_t>(phantomPhysicsComponent->GetEffectType())))); + const auto dir = phantomPhysicsComponent->GetDirection(); + ChatPackets::SendSystemMessage(sysAddr, u"Direction: <" + (GeneralUtils::to_u16string(dir.x)) + u", " + (GeneralUtils::to_u16string(dir.y)) + u", " + (GeneralUtils::to_u16string(dir.z)) + u">"); + ChatPackets::SendSystemMessage(sysAddr, u"Multiplier: " + (GeneralUtils::to_u16string(phantomPhysicsComponent->GetDirectionalMultiplier()))); + ChatPackets::SendSystemMessage(sysAddr, u"Active: " + (GeneralUtils::to_u16string(phantomPhysicsComponent->GetPhysicsEffectActive()))); + } + + auto* triggerComponent = closest->GetComponent<TriggerComponent>(); + if (triggerComponent) { + auto trigger = triggerComponent->GetTrigger(); + if (trigger) { + ChatPackets::SendSystemMessage(sysAddr, u"Trigger: " + (GeneralUtils::to_u16string(trigger->id))); + } + } + } + } + } +}; diff --git a/dGame/dUtilities/SlashCommands/DEVGMCommands.h b/dGame/dUtilities/SlashCommands/DEVGMCommands.h new file mode 100644 index 00000000..149348a1 --- /dev/null +++ b/dGame/dUtilities/SlashCommands/DEVGMCommands.h @@ -0,0 +1,73 @@ +namespace DEVGMCommands { + void SetGMLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ToggleNameplate(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ToggleSkipCinematics(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Kill(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Metrics(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Announce(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetAnnTitle(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetAnnMsg(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ShutdownUniverse(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetMinifig(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void TestMap(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ReportProxPhys(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SpawnPhysicsVerts(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ActivateSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void AddMission(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Boost(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Unboost(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Buff(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void BuffMe(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void BuffMed(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ClearFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void CompleteMission(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void CreatePrivate(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void DebugUi(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Dismount(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ReloadConfig(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ForceSave(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Freecam(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void FreeMoney(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void GetNavmeshHeight(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void GiveUScore(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void GmAddItem(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Inspect(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ListSpawns(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void LocRow(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Lookup(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void PlayAnimation(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void PlayEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void PlayLvlFx(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void PlayRebuildFx(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Pos(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void RefillStats(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Reforge(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ResetMission(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Rot(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void RunMacro(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetControlScheme(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetCurrency(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetInventorySize(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetUiState(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Spawn(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SpawnGroup(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SpeedBoost(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void StartCelebration(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void StopEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Toggle(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void TpAll(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void TriggerSpawner(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void UnlockEmote(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetLevel(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetSkillSlot(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void AddFaction(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void GetFactions(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetRewardCode(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Crash(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void RollLoot(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void CastSkill(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void DeleteInven(Entity* entity, const SystemAddress& sysAddr, const std::string args); +} \ No newline at end of file diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp new file mode 100644 index 00000000..d0aa66aa --- /dev/null +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.cpp @@ -0,0 +1,290 @@ +#include "GMGreaterThanZeroCommands.h" + +// Classes +#include "Character.h" +#include "ChatPackets.h" +#include "dServer.h" +#include "PlayerManager.h" +#include "User.h" + +// Database +#include "Database.h" + +// Components +#include "DestroyableComponent.h" +#include "PropertyManagementComponent.h" + +// Enums +#include "eChatMessageType.h" +#include "eServerDisconnectIdentifiers.h" +#include "eObjectBits.h" + +namespace GMGreaterThanZeroCommands { + + void Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() == 1) { + auto* player = PlayerManager::GetPlayer(splitArgs[0]); + + std::u16string username = GeneralUtils::UTF8ToUTF16(splitArgs[0]); + if (player == nullptr) { + ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + username); + return; + } + + Game::server->Disconnect(player->GetSystemAddress(), eServerDisconnectIdentifiers::KICK); + + ChatPackets::SendSystemMessage(sysAddr, u"Kicked: " + username); + } else { + ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /kick <username>"); + } + } + + void Ban(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + + if (splitArgs.size() == 1) { + auto* player = PlayerManager::GetPlayer(splitArgs[0]); + + uint32_t accountId = 0; + + if (player == nullptr) { + auto characterInfo = Database::Get()->GetCharacterInfo(splitArgs[0]); + + if (characterInfo) { + accountId = characterInfo->accountId; + } + + if (accountId == 0) { + ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(splitArgs[0])); + + return; + } + } else { + auto* character = player->GetCharacter(); + auto* user = character != nullptr ? character->GetParentUser() : nullptr; + if (user) accountId = user->GetAccountID(); + } + + if (accountId != 0) Database::Get()->UpdateAccountBan(accountId, true); + + if (player != nullptr) { + Game::server->Disconnect(player->GetSystemAddress(), eServerDisconnectIdentifiers::FREE_TRIAL_EXPIRED); + } + + ChatPackets::SendSystemMessage(sysAddr, u"Banned: " + GeneralUtils::ASCIIToUTF16(splitArgs[0])); + } else { + ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /ban <username>"); + } + } + + void MailItem(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.size() < 2) return; + + const auto& playerName = splitArgs[0]; + + auto playerInfo = Database::Get()->GetCharacterInfo(playerName); + + uint32_t receiverID = 0; + if (!playerInfo) { + ChatPackets::SendSystemMessage(sysAddr, u"Failed to find that player"); + + return; + } + + receiverID = playerInfo->id; + + const auto lot = GeneralUtils::TryParse<LOT>(splitArgs.at(1)); + + if (!lot) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid item lot."); + return; + } + + IMail::MailInfo mailInsert; + mailInsert.senderId = entity->GetObjectID(); + mailInsert.senderUsername = "Darkflame Universe"; + mailInsert.receiverId = receiverID; + mailInsert.recipient = playerName; + mailInsert.subject = "Lost item"; + mailInsert.body = "This is a replacement item for one you lost."; + mailInsert.itemID = LWOOBJID_EMPTY; + mailInsert.itemLOT = lot.value(); + mailInsert.itemSubkey = LWOOBJID_EMPTY; + mailInsert.itemCount = 1; + Database::Get()->InsertNewMail(mailInsert); + + ChatPackets::SendSystemMessage(sysAddr, u"Mail sent"); + } + + void ApproveProperty(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + if (PropertyManagementComponent::Instance() != nullptr) { + PropertyManagementComponent::Instance()->UpdateApprovedStatus(true); + } + } + + void Mute(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + + if (splitArgs.size() >= 1) { + auto* player = PlayerManager::GetPlayer(splitArgs[0]); + + uint32_t accountId = 0; + LWOOBJID characterId = 0; + + if (player == nullptr) { + auto characterInfo = Database::Get()->GetCharacterInfo(splitArgs[0]); + + if (characterInfo) { + accountId = characterInfo->accountId; + characterId = characterInfo->id; + + GeneralUtils::SetBit(characterId, eObjectBits::CHARACTER); + GeneralUtils::SetBit(characterId, eObjectBits::PERSISTENT); + } + + if (accountId == 0) { + ChatPackets::SendSystemMessage(sysAddr, u"Count not find player of name: " + GeneralUtils::UTF8ToUTF16(splitArgs[0])); + + return; + } + } else { + auto* character = player->GetCharacter(); + auto* user = character != nullptr ? character->GetParentUser() : nullptr; + if (user) accountId = user->GetAccountID(); + characterId = player->GetObjectID(); + } + + time_t expire = 1; // Default to indefinate mute + + if (splitArgs.size() >= 2) { + const auto days = GeneralUtils::TryParse<uint32_t>(splitArgs[1]); + if (!days) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid days."); + + return; + } + + std::optional<uint32_t> hours; + if (splitArgs.size() >= 3) { + hours = GeneralUtils::TryParse<uint32_t>(splitArgs[2]); + if (!hours) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid hours."); + + return; + } + } + + expire = time(NULL); + expire += 24 * 60 * 60 * days.value(); + expire += 60 * 60 * hours.value_or(0); + } + + if (accountId != 0) Database::Get()->UpdateAccountUnmuteTime(accountId, expire); + + char buffer[32] = "brought up for review.\0"; + + if (expire != 1) { + std::tm* ptm = std::localtime(&expire); + // Format: Mo, 15.06.2009 20:20:00 + std::strftime(buffer, 32, "%a, %d.%m.%Y %H:%M:%S", ptm); + } + + const auto timeStr = GeneralUtils::ASCIIToUTF16(std::string(buffer)); + + ChatPackets::SendSystemMessage(sysAddr, u"Muted: " + GeneralUtils::UTF8ToUTF16(splitArgs[0]) + u" until " + timeStr); + + //Notify chat about it + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE); + + bitStream.Write(characterId); + bitStream.Write(expire); + + Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); + } else { + ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /mute <username> <days (optional)> <hours (optional)>"); + } + } + + void Fly(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + auto* character = entity->GetCharacter(); + + if (character) { + bool isFlying = character->GetIsFlying(); + + if (isFlying) { + GameMessages::SendSetJetPackMode(entity, false); + + character->SetIsFlying(false); + } else { + float speedScale = 1.0f; + + if (splitArgs.size() >= 1) { + const auto tempScaleStore = GeneralUtils::TryParse<float>(splitArgs.at(0)); + + if (tempScaleStore) { + speedScale = tempScaleStore.value(); + } else { + ChatPackets::SendSystemMessage(sysAddr, u"Failed to parse speed scale argument."); + } + } + + float airSpeed = 20 * speedScale; + float maxAirSpeed = 30 * speedScale; + float verticalVelocity = 1.5 * speedScale; + + GameMessages::SendSetJetPackMode(entity, true, true, false, 167, airSpeed, maxAirSpeed, verticalVelocity); + + character->SetIsFlying(true); + } + } + } + + void AttackImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); + + const auto state = GeneralUtils::TryParse<int32_t>(splitArgs[0]); + + if (!state) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid state."); + return; + } + + if (destroyableComponent) destroyableComponent->SetIsImmune(state.value()); + } + + void GmImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); + + const auto state = GeneralUtils::TryParse<int32_t>(splitArgs[0]); + + if (!state) { + ChatPackets::SendSystemMessage(sysAddr, u"Invalid state."); + return; + } + + if (destroyableComponent) destroyableComponent->SetIsGMImmune(state.value()); + } + + void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + GameMessages::SendToggleGMInvis(entity->GetObjectID(), true, UNASSIGNED_SYSTEM_ADDRESS); + } + + void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(args), UNASSIGNED_SYSTEM_ADDRESS); + } + + void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + std::string name = entity->GetCharacter()->GetName() + " - " + args; + GameMessages::SendSetName(entity->GetObjectID(), GeneralUtils::UTF8ToUTF16(name), UNASSIGNED_SYSTEM_ADDRESS); + } +} diff --git a/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h new file mode 100644 index 00000000..4b03441f --- /dev/null +++ b/dGame/dUtilities/SlashCommands/GMGreaterThanZeroCommands.h @@ -0,0 +1,13 @@ +namespace GMGreaterThanZeroCommands { + void Kick(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void MailItem(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Ban(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void ApproveProperty(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Mute(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Fly(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void AttackImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void GmImmune(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void GmInvis(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void SetName(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Title(Entity* entity, const SystemAddress& sysAddr, const std::string args); +} \ No newline at end of file diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp new file mode 100644 index 00000000..f183d5ea --- /dev/null +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.cpp @@ -0,0 +1,228 @@ +#include "GMZeroCommands.h" + +// Classes +#include "Amf3.h" +#include "BinaryPathFinder.h" +#include "ChatPackets.h" +#include "dServer.h" +#include "dZoneManager.h" +#include "Mail.h" +#include "PlayerManager.h" +#include "SlashCommandHandler.h" +#include "VanityUtilities.h" +#include "WorldPackets.h" +#include "ZoneInstanceManager.h" + +// Components +#include "BuffComponent.h" +#include "CharacterComponent.h" +#include "DestroyableComponent.h" +#include "ScriptedActivityComponent.h" +#include "SkillComponent.h" + +// Emuns +#include "eGameMasterLevel.h" + +namespace GMZeroCommands { + void Pvp(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto* character = entity->GetComponent<CharacterComponent>(); + + if (character == nullptr) { + LOG("Failed to find character component!"); + return; + } + + character->SetPvpEnabled(!character->GetPvpEnabled()); + Game::entityManager->SerializeEntity(entity); + + std::stringstream message; + message << character->GetName() << " changed their PVP flag to " << std::to_string(character->GetPvpEnabled()) << "!"; + + ChatPackets::SendSystemMessage(UNASSIGNED_SYSTEM_ADDRESS, GeneralUtils::UTF8ToUTF16(message.str()), true); + } + + void Who(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + ChatPackets::SendSystemMessage( + sysAddr, + u"Players in this instance: (" + GeneralUtils::to_u16string(PlayerManager::GetAllPlayers().size()) + u")" + ); + + for (auto* player : PlayerManager::GetAllPlayers()) { + const auto& name = player->GetCharacter()->GetName(); + + ChatPackets::SendSystemMessage( + sysAddr, + GeneralUtils::UTF8ToUTF16(player == entity ? name + " (you)" : name) + ); + } + } + + void Ping(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + if (!args.empty() && args.starts_with("-l")) { + std::stringstream message; + message << "Your latest ping: " << std::to_string(Game::server->GetLatestPing(sysAddr)) << "ms"; + + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(message.str())); + } else { + std::stringstream message; + message << "Your average ping: " << std::to_string(Game::server->GetPing(sysAddr)) << "ms"; + + ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(message.str())); + } + } + + void FixStats(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + // Reset skill component and buff component + auto* skillComponent = entity->GetComponent<SkillComponent>(); + auto* buffComponent = entity->GetComponent<BuffComponent>(); + auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); + + // If any of the components are nullptr, return + if (skillComponent == nullptr || buffComponent == nullptr || destroyableComponent == nullptr) { + return; + } + + // Reset skill component + skillComponent->Reset(); + + // Reset buff component + buffComponent->Reset(); + + // Fix the destroyable component + destroyableComponent->FixStats(); + } + + void Credits(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto& customText = VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/CREDITS.md").string()); + + { + AMFArrayValue args; + + args.Insert("state", "Story"); + + GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", args); + } + + entity->AddCallbackTimer(0.5f, [customText, entity]() { + AMFArrayValue args; + + args.Insert("visible", true); + args.Insert("text", customText); + + LOG("Sending %s", customText.c_str()); + + GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "ToggleStoryBox", args); + }); + } + + void Info(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto& customText = VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/INFO.md").string()); + + { + AMFArrayValue args; + + args.Insert("state", "Story"); + + GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", args); + } + + entity->AddCallbackTimer(0.5f, [customText, entity]() { + AMFArrayValue args; + + args.Insert("visible", true); + args.Insert("text", customText); + + LOG("Sending %s", customText.c_str()); + + GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "ToggleStoryBox", args); + }); + } + + void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto currentZone = Game::zoneManager->GetZone()->GetZoneID().GetMapID(); + LWOMAPID newZone = 0; + + if (currentZone == 1001 || currentZone % 100 == 0) { + ChatPackets::SendSystemMessage(sysAddr, u"You are not in an instanced zone."); + return; + } else { + newZone = (currentZone / 100) * 100; + } + // If new zone would be inaccessible, then default to Avant Gardens. + if (!Game::zoneManager->CheckIfAccessibleZone(newZone)) newZone = 1100; + + ChatPackets::SendSystemMessage(sysAddr, u"Leaving zone..."); + + const auto objid = entity->GetObjectID(); + + ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, newZone, 0, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { + auto* entity = Game::entityManager->GetEntity(objid); + + if (entity == nullptr) { + return; + } + + const auto sysAddr = entity->GetSystemAddress(); + + LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", entity->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); + + if (entity->GetCharacter()) { + entity->GetCharacter()->SetZoneID(zoneID); + entity->GetCharacter()->SetZoneInstance(zoneInstance); + entity->GetCharacter()->SetZoneClone(zoneClone); + } + + entity->GetCharacter()->SaveXMLToDatabase(); + + WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + }); + } + + void Join(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + auto splitArgs = GeneralUtils::SplitString(args, ' '); + if (splitArgs.empty()) return; + + ChatPackets::SendSystemMessage(sysAddr, u"Requesting private map..."); + const auto& password = splitArgs[0]; + + ZoneInstanceManager::Instance()->RequestPrivateZone(Game::server, false, password, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { + LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); + + if (entity->GetCharacter()) { + entity->GetCharacter()->SetZoneID(zoneID); + entity->GetCharacter()->SetZoneInstance(zoneInstance); + entity->GetCharacter()->SetZoneClone(zoneClone); + } + + entity->GetCharacter()->SaveXMLToDatabase(); + + WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + }); + } + + void Die(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + entity->Smash(entity->GetObjectID()); + } + + void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + ScriptedActivityComponent* scriptedActivityComponent = Game::zoneManager->GetZoneControlObject()->GetComponent<ScriptedActivityComponent>(); + + if (scriptedActivityComponent) { // check if user is in activity world and if so, they can't resurrect + ChatPackets::SendSystemMessage(sysAddr, u"You cannot resurrect in an activity world."); + return; + } + + GameMessages::SendResurrect(entity); + } + + void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + Mail::HandleNotificationRequest(entity->GetSystemAddress(), entity->GetObjectID()); + } + + void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args) { + const auto zoneId = Game::zoneManager->GetZone()->GetZoneID(); + + ChatPackets::SendSystemMessage(sysAddr, u"Map: " + (GeneralUtils::to_u16string(zoneId.GetMapID())) + u"\nClone: " + (GeneralUtils::to_u16string(zoneId.GetCloneID())) + u"\nInstance: " + (GeneralUtils::to_u16string(zoneId.GetInstanceID()))); + } +}; + diff --git a/dGame/dUtilities/SlashCommands/GMZeroCommands.h b/dGame/dUtilities/SlashCommands/GMZeroCommands.h new file mode 100644 index 00000000..29aa8f36 --- /dev/null +++ b/dGame/dUtilities/SlashCommands/GMZeroCommands.h @@ -0,0 +1,15 @@ +namespace GMZeroCommands { + void Help(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Credits(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Info(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Die(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Ping(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Pvp(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void RequestMailCount(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Who(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void FixStats(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Join(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void LeaveZone(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void Resurrect(Entity* entity, const SystemAddress& sysAddr, const std::string args); + void InstanceInfo(Entity* entity, const SystemAddress& sysAddr, const std::string args); +} diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 9f6d63bc..b6954ca2 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -314,8 +314,8 @@ int main(int argc, char** argv) { uint32_t sqlPingTime = 10 * 60 * currentFramerate; // 10 minutes in frames uint32_t emptyShutdownTime = (cloneID == 0 ? 30 : 5) * 60 * currentFramerate; // 30 minutes for main worlds, 5 for all others. - // Register slash commands - SlashCommandHandler::Startup(); + // Register slash commands if not in zone 0 + if (zoneID != 0) SlashCommandHandler::Startup(); Game::logger->Flush(); // once immediately before the main loop while (true) {