Skip to content

File plugin.h

File List > endstone > plugin > plugin.h

Go to the documentation of this file

// Copyright (c) 2023, The Endstone Project. (https://endstone.dev) All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#pragma once

#include <algorithm>
#include <filesystem>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

#include "endstone/command/command_executor.h"
#include "endstone/logger.h"
#include "endstone/permissions/permission.h"
#include "endstone/plugin/plugin_description.h"
#include "endstone/server.h"

namespace endstone {
class PluginCommand;
class PluginLoader;
namespace core {
class EndstonePluginManager;
}

class Plugin : public CommandExecutor {
public:
    Plugin() = default;
    Plugin(const Plugin &) = delete;
    Plugin &operator=(const Plugin &) = delete;

    ~Plugin() override = default;

    [[nodiscard]] virtual const PluginDescription &getDescription() const = 0;

    virtual void onLoad() {}

    virtual void onEnable() {}

    virtual void onDisable() {}

    [[nodiscard]] Logger &getLogger() const
    {
        return *logger_;
    }

    [[nodiscard]] bool isEnabled() const
    {
        return enabled_;
    }

    [[nodiscard]] PluginLoader &getPluginLoader() const
    {
        return *loader_;
    }

    [[nodiscard]] Server &getServer() const
    {
        return *server_;
    }

    [[nodiscard]] std::string getName() const
    {
        return getDescription().getName();
    };

    [[nodiscard]] PluginCommand *getCommand(std::string name) const
    {
        std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); });
        return getServer().getPluginCommand(name);
    }

    [[nodiscard]] const std::filesystem::path &getDataFolder() const
    {
        return data_folder_;
    }

    template <typename EventType, typename T>
    void registerEvent(void (T::*func)(EventType &), T &instance, EventPriority priority = EventPriority::Normal,
                       bool ignore_cancelled = false)
    {
        auto result = getServer().getPluginManager().registerEvent(
            EventType::NAME, [func, &instance](Event &e) { (instance.*func)(static_cast<EventType &>(e)); }, priority,
            *this, ignore_cancelled);
        if (!result) {
            server_->getLogger().error(result.error());
        }
    }

    template <typename EventType>
    void registerEvent(std::function<void(EventType &)> func, EventPriority priority = EventPriority::Normal,
                       bool ignore_cancelled = false)
    {
        auto result = getServer().getPluginManager().registerEvent(
            EventType::NAME, [func](Event &e) { func(static_cast<EventType &>(e)); }, priority, *this,
            ignore_cancelled);
        if (!result) {
            server_->getLogger().error(result.error());
        }
    }

protected:
    friend class PluginLoader;
    friend class core::EndstonePluginManager;

    void setEnabled(bool enabled)
    {
        if (enabled_ != enabled) {
            enabled_ = enabled;

            if (enabled_) {
                onEnable();
            }
            else {
                onDisable();
            }
        }
    }

private:
    bool enabled_{false};
    PluginLoader *loader_{nullptr};
    Server *server_{nullptr};
    Logger *logger_{nullptr};
    std::filesystem::path data_folder_;
};

namespace detail {
class CommandBuilder {
public:
    explicit CommandBuilder(std::string name) : name_(std::move(name)) {}

    CommandBuilder &description(std::string description)
    {
        description_ = std::move(description);
        return *this;
    }

    template <typename... Usage>
    CommandBuilder &usages(Usage... usages)
    {
        (usages_.insert(usages), ...);
        return *this;
    }

    template <typename... Alias>
    CommandBuilder &aliases(Alias... aliases)
    {
        (aliases_.insert(aliases), ...);
        return *this;
    }

    template <typename... Permissions>
    CommandBuilder &permissions(Permissions... permissions)
    {
        (permissions_.insert(permissions), ...);
        return *this;
    }

    [[nodiscard]] Command build() const
    {
        return Command(name_, description_, std::vector<std::string>(usages_.begin(), usages_.end()),
                       std::vector<std::string>(aliases_.begin(), aliases_.end()),
                       std::vector<std::string>(permissions_.begin(), permissions_.end()));
    }

private:
    std::string name_;
    std::string description_;
    std::set<std::string> usages_;
    std::set<std::string> aliases_;
    std::set<std::string> permissions_;
};

class PermissionBuilder {
public:
    explicit PermissionBuilder(std::string name) : name_(std::move(name)) {}

    PermissionBuilder &description(std::string description)
    {
        description_ = std::move(description);
        return *this;
    }

    PermissionBuilder &default_(PermissionDefault default_value)  // NOLINT(*-identifier-naming)
    {
        default_value_ = default_value;
        return *this;
    }

    PermissionBuilder &children(const std::string &name, bool value)
    {
        children_[name] = value;
        return *this;
    }

    [[nodiscard]] Permission build() const
    {
        return Permission(name_, description_, default_value_, children_);
    }

private:
    std::string name_;
    std::string description_;
    PermissionDefault default_value_ = Permission::DefaultPermission;
    std::unordered_map<std::string, bool> children_ = {};
};

struct PluginDescriptionBuilder {
    std::string description;
    PluginLoadOrder load = PluginLoadOrder::PostWorld;
    std::vector<std::string> authors;
    std::vector<std::string> contributors;
    std::string website;
    std::string prefix;
    std::vector<std::string> provides;
    std::vector<std::string> depend;
    std::vector<std::string> soft_depend;
    std::vector<std::string> load_before;
    PermissionDefault default_permission = PermissionDefault::Operator;
    std::unordered_map<std::string, CommandBuilder> commands;
    std::unordered_map<std::string, PermissionBuilder> permissions;

    CommandBuilder &command(std::string name)
    {
        std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); });
        return commands.emplace(name, name).first->second;
    }

    PermissionBuilder &permission(std::string name)
    {
        std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); });
        return permissions.emplace(name, name).first->second;
    }

    [[nodiscard]] PluginDescription build(std::string name, std::string version) const
    {
        return {std::move(name),
                std::move(version),
                description,
                load,
                authors,
                contributors,
                website,
                prefix,
                provides,
                depend,
                soft_depend,
                load_before,
                default_permission,
                buildCommands(),
                buildPermissions()};
    }

private:
    [[nodiscard]] std::vector<Command> buildCommands() const
    {
        std::vector<Command> result;
        result.reserve(commands.size());
        for (const auto &pair : commands) {
            result.push_back(pair.second.build());
        }
        return result;
    }

    [[nodiscard]] std::vector<Permission> buildPermissions() const
    {
        std::vector<Permission> result;
        result.reserve(permissions.size());
        for (const auto &pair : permissions) {
            result.push_back(pair.second.build());
        }
        return result;
    }
};
}  // namespace detail

}  // namespace endstone

#ifndef ENDSTONE_EXPORT
#if defined(WIN32) || defined(_WIN32)
#define ENDSTONE_EXPORT __declspec(dllexport)
#else
#define ENDSTONE_EXPORT __attribute__((visibility("default")))
#endif
#endif

#ifndef ENDSTONE_PLUGIN
#define ENDSTONE_PLUGIN(Name, Version, MainClass)                                            \
    class PluginDescriptionBuilderImpl : public endstone::detail::PluginDescriptionBuilder { \
    public:                                                                                  \
        PluginDescriptionBuilderImpl();                                                      \
    };                                                                                       \
    static PluginDescriptionBuilderImpl builder;                                             \
    class EndstonePluginImpl : public MainClass {                                            \
    public:                                                                                  \
        EndstonePluginImpl() = default;                                                      \
        const endstone::PluginDescription &getDescription() const override                   \
        {                                                                                    \
            return description_;                                                             \
        }                                                                                    \
                                                                                             \
    private:                                                                                 \
        endstone::PluginDescription description_ = builder.build(Name, Version);             \
    };                                                                                       \
    extern "C" [[maybe_unused]] ENDSTONE_EXPORT endstone::Plugin *init_endstone_plugin()     \
    {                                                                                        \
        auto *p = new EndstonePluginImpl();                                                  \
        return p;                                                                            \
    }                                                                                        \
    PluginDescriptionBuilderImpl::PluginDescriptionBuilderImpl()
#endif