diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 3ba1ab38..9eccc268 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -8,6 +8,7 @@ #include "SlashCommandHandler.h" #include +#include #include "DEVGMCommands.h" #include "GMGreaterThanZeroCommands.h" @@ -60,11 +61,9 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* if (commandHandle.requiredLevel > eGameMasterLevel::CIVILIAN) Database::Get()->InsertSlashCommandUsage(entity->GetObjectID(), input); commandHandle.handle(entity, sysAddr, args); } else if (entity->GetGMLevel() != eGameMasterLevel::CIVILIAN) { - // We don't need to tell civilians they aren't high enough level error = "You are not high enough GM level to use \"" + command + "\""; } } else if (entity->GetGMLevel() == eGameMasterLevel::CIVILIAN) { - // We don't need to tell civilians commands don't exist error = "Command " + command + " does not exist!"; } @@ -75,32 +74,74 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& chat, Entity* void GMZeroCommands::Help(Entity* entity, const SystemAddress& sysAddr, const std::string args) { std::ostringstream feedback; - if (args.empty()) { - feedback << "----- Commands -----"; - for (const auto& [alias, command] : CommandInfos) { - // TODO: Limit displaying commands based on GM level they require - if (command.requiredLevel > entity->GetGMLevel()) continue; - LOG("Help command: %s", alias.c_str()); - feedback << "\n/" << alias << ": " << command.help; + constexpr size_t pageSize = 10; + + std::string trimmedArgs = args; + trimmedArgs.erase(trimmedArgs.begin(), std::find_if_not(trimmedArgs.begin(), trimmedArgs.end(), [](unsigned char ch) { + return std::isspace(ch); + })); + trimmedArgs.erase(std::find_if_not(trimmedArgs.rbegin(), trimmedArgs.rend(), [](unsigned char ch) { + return std::isspace(ch); + }).base(), trimmedArgs.end()); + + std::optional parsedPage = GeneralUtils::TryParse(trimmedArgs); + if (trimmedArgs.empty() || parsedPage.has_value()) { + size_t page = parsedPage.value_or(1); + + std::map accessibleCommands; + for (const auto& [commandName, command] : CommandInfos) { + if (command.requiredLevel <= entity->GetGMLevel()) { + accessibleCommands.emplace(commandName, command); + } + } + + size_t totalPages = (accessibleCommands.size() + pageSize - 1) / pageSize; + + if (page < 1 || page > totalPages) { + feedback << "Invalid page number. Total pages: " << totalPages; + GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedback.str())); + return; + } + + auto it = accessibleCommands.begin(); + std::advance(it, (page - 1) * pageSize); + size_t endIdx = std::min(page * pageSize, accessibleCommands.size()); + + feedback << "----- Commands (Page " << page << " of " << totalPages << ") -----"; + for (size_t i = (page - 1) * pageSize; i < endIdx; ++i, ++it) { + feedback << "\n/" << it->first << ": " << it->second.help; + } + + const auto feedbackStr = feedback.str(); + if (!feedbackStr.empty()) { + GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); + } + return; + } + + auto it = std::ranges::find_if(CommandInfos, [&trimmedArgs](const auto& pair) { + return std::ranges::find(pair.second.aliases, trimmedArgs) != pair.second.aliases.end(); + }); + + if (it != CommandInfos.end() && entity->GetGMLevel() >= it->second.requiredLevel) { + const auto& command = it->second; + feedback << "----- " << it->first << " Info -----\n"; + feedback << command.info << "\n"; + if (command.aliases.size() > 1) { + feedback << "Aliases: "; + for (size_t i = 0; i < command.aliases.size(); ++i) { + if (i > 0) feedback << ", "; + feedback << command.aliases[i]; + } } } else { - auto it = CommandInfos.find(args); - if (it != CommandInfos.end() && entity->GetGMLevel() >= it->second.requiredLevel) { - feedback << "----- " << args << " -----\n"; - feedback << it->second.info; - if (it->second.aliases.size() > 1) { - feedback << "\nAliases: "; - for (size_t i = 0; i < it->second.aliases.size(); i++) { - if (i > 0) feedback << ", "; - feedback << it->second.aliases[i]; - } - } - } else if (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) { - feedback << "Command " << std::quoted(args) << " does not exist!"; - } + feedback << "Command not found."; } + const auto feedbackStr = feedback.str(); - if (!feedbackStr.empty()) GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); + if (!feedbackStr.empty()) { + GameMessages::SendSlashCommandFeedbackText(entity, GeneralUtils::ASCIIToUTF16(feedbackStr)); + } } void SlashCommandHandler::SendAnnouncement(const std::string& title, const std::string& message) {