Skip to content

Commit

Permalink
feat: improve command registration
Browse files Browse the repository at this point in the history
  • Loading branch information
wu-vincent committed Mar 3, 2024
1 parent b0b8af6 commit a51e2bb
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 14 deletions.
86 changes: 81 additions & 5 deletions include/endstone/command/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@

#pragma once

#include <algorithm>
#include <map>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "endstone/command/command_map.h"
Expand All @@ -29,11 +29,13 @@ namespace endstone {
*/
class Command {
public:
explicit Command(std::string name, std::string description = "", std::vector<std::string> usages = {"/command"},
std::vector<std::string> aliases = {}) noexcept
: name_(std::move(name)), description_(std::move(description)), usages_(std::move(usages)),
aliases_(std::move(aliases))
explicit Command(std::string name, std::string description = "", std::vector<std::string> usages = {},
const std::vector<std::string> &aliases = {}) noexcept
{
setName(std::move(name));
setDescription(std::move(description));
setUsages(std::move(usages));
setAliases(aliases);
}
virtual ~Command() = default;

Expand All @@ -56,6 +58,25 @@ class Command {
return name_;
}

/**
* Sets the name of this command.
*
* May only be used before registering the command.
*
* @param name New command name
* @return returns true if the name is changed, false otherwise
*/
bool setName(std::string name) noexcept
{
if (!isRegistered()) {
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); });
name_ = std::move(name);
return true;
}

return false;
}

/**
* Gets a brief description of this command
*
Expand All @@ -66,6 +87,21 @@ class Command {
return description_;
}

/**
* Sets a brief description of this command.
*
* @param description new command description
* @return this command object, for chaining
*/
Command &setDescription(std::string description) noexcept
{
if (!isRegistered()) {
description_ = std::move(description);
}

return *this;
}

/**
* Returns a list of aliases of this command
*
Expand All @@ -76,6 +112,27 @@ class Command {
return aliases_;
}

/**
* Sets the list of aliases to request on registration for this command.
*
* @param aliases aliases to register to this command
* @return this command object, for chaining
*/
template <typename... Alias>
Command &setAliases(Alias... aliases) noexcept
{
std::vector<std::string> all_aliases = {aliases...};
if (!isRegistered()) {
aliases_.clear();
for (auto alias : all_aliases) {
std::transform(alias.begin(), alias.end(), alias.begin(),
[](unsigned char c) { return std::tolower(c); });
aliases_.push_back(alias);
}
}
return *this;
}

/**
* Returns a list of usages of this command
*
Expand All @@ -86,6 +143,25 @@ class Command {
return usages_;
}

/**
* Sets the example usage of this command
*
* @param usages new example usage
* @return this command object, for chaining
*/
template <typename... Usage>
Command &setUsages(Usage... usages) noexcept
{
std::vector<std::string> all_usages = {usages...};
if (!isRegistered()) {
if (all_usages.empty()) {
all_usages.push_back("/" + getName());
}
usages_ = std::move(all_usages);
}
return *this;
}

/**
* Registers this command to a CommandMap.
*
Expand Down
34 changes: 26 additions & 8 deletions src/endstone_core/command/command_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@
#include "bedrock/command/command.h"
#include "bedrock/command/command_registry.h"
#include "endstone/detail/command/defaults/version_command.h"
#include "endstone/detail/server.h"

namespace endstone::detail {

EndstoneCommandMap::EndstoneCommandMap(EndstoneServer &server) : server_(server) {}
EndstoneCommandMap::EndstoneCommandMap(EndstoneServer &server) : server_(server)
{
setDefaultCommands();
}

void EndstoneCommandMap::clearCommands()
{
Expand Down Expand Up @@ -62,22 +66,36 @@ class CommandWrapper : public Command {

bool endstone::detail::EndstoneCommandMap::registerCommand(std::shared_ptr<endstone::Command> command)
{
std::lock_guard lock(mutex_);

if (!command) {
return false;
}

auto name = command->getName();
std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); });
auto it = known_commands_.find(name);
if (it != known_commands_.end() && it->second->getName() == it->first) {
// the name was registered and is not an alias
return false;
}

auto &registry = server_.getMinecraftCommands().getRegistry();
registry.registerCommand(name, command->getDescription().c_str(), CommandPermissionLevel::Any, {128}, {0});

for (auto alias : command->getAliases()) {
std::transform(alias.begin(), alias.end(), alias.begin(), [](unsigned char c) { return std::tolower(c); });
registry.registerAlias(name, alias);
known_commands_.emplace(name, command);

std::vector<std::string> registered_alias;
for (const auto &alias : command->getAliases()) {
if (known_commands_.find(alias) == known_commands_.end()) {
registry.registerAlias(name, alias);
known_commands_.emplace(alias, command);
registered_alias.push_back(alias);
}
}

// TODO: register the overloads from usage
const auto *overload = registry.registerOverload<CommandWrapper>("test", {1, INT_MAX});
return overload != nullptr;
registry.registerOverload<CommandWrapper>(name.c_str(), {1, INT_MAX});

command->setAliases(registered_alias);
command->registerTo(*this);
return true;
}
8 changes: 7 additions & 1 deletion src/endstone_core/command/defaults/version_command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@

#include "endstone/detail/command/defaults/version_command.h"

#include "endstone/detail/command/command_map.h"
#include "endstone/detail/server.h"
#include "endstone/detail/singleton.h"

endstone::detail::VersionCommand::VersionCommand() : Command("version", "Version command", {"/version"}) {}
endstone::detail::VersionCommand::VersionCommand() : Command("version")
{
setDescription("Gets the version of this server");
setUsages("/version");
setAliases("ver", "about");
}

bool endstone::detail::VersionCommand::execute(CommandSender &sender,
const std::map<std::string, std::string> &args) const
Expand Down

0 comments on commit a51e2bb

Please sign in to comment.