Skip to content

File registry.h

File List > endstone > registry.h

Go to the documentation of this file

// Copyright (c) 2024, 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 <concepts>
#include <functional>
#include <stdexcept>
#include <string>

#include <fmt/format.h>

#include "detail.h"
#include "identifier.h"
#include "server.h"

namespace endstone {
namespace python {
class PyRegistry;
}

class IRegistry {
public:
    virtual ~IRegistry() = default;
    [[nodiscard]] virtual std::size_t size() const = 0;

protected:
    friend class python::PyRegistry;
    [[nodiscard]] virtual const void *get0(std::string_view id) const noexcept = 0;
    virtual void forEach0(std::function<bool(const void *)> func) const = 0;
    [[nodiscard]] virtual const std::type_info &getTypeId() const noexcept = 0;
};

template <typename T>
class Registry : public IRegistry {
public:
    class Type {
    public:
        using Id = Identifier<T>;

        virtual ~Type() = default;

        [[nodiscard]] virtual Id getId() const = 0;

        [[nodiscard]] virtual std::string getTranslationKey() const = 0;

        static const T *get(Id id)
        {
            return detail::getServer().getRegistry<T>().get(id);
        }

        bool operator==(const Id &other) const { return getId() == other; }
        bool operator!=(const Id &other) const { return !(*this == other); }
        bool operator==(const T &other) const { return getId() == other.getId(); }
        bool operator!=(const T &other) const { return !(*this == other); }
        operator Id() const { return getId(); }
    };

    virtual T *get(Identifier<T> id) noexcept = 0;

    virtual const T *get(Identifier<T> id) const noexcept = 0;

    virtual T &getOrThrow(Identifier<T> id)
    {
        if (auto *p = get(id)) {
            return *p;
        }
        throw std::invalid_argument(fmt::format("No registry entry found for identifier: {}", id));
    }

    virtual const T &getOrThrow(Identifier<T> id) const
    {
        if (auto *p = get(id)) {
            return *p;
        }
        throw std::invalid_argument(fmt::format("No registry entry found for identifier: {}", id));
    }

    virtual void forEach(std::function<bool(const T &)> func) const = 0;

    [[nodiscard]] std::size_t size() const override = 0;

private:
    [[nodiscard]] const void *get0(std::string_view id) const noexcept override
    {
        return get(Identifier<T>(id));
    }

    void forEach0(std::function<bool(const void *)> func) const override
    {
        forEach([&func](const T &elem) { return func(&elem); });
    }

    [[nodiscard]] const std::type_info &getTypeId() const noexcept override { return typeid(T); }
};
}  // namespace endstone

template <typename T>
    requires requires(const T &t) { { t.getId() } -> std::convertible_to<endstone::Identifier<T>>; }
struct fmt::formatter<T> : formatter<string_view> {
    template <typename FormatContext>
    auto format(const T &val, FormatContext &ctx) const -> format_context::iterator
    {
        return fmt::format_to(ctx.out(), "{}", val.getId());
    }
};