From 9cf94b27af9b1c0383ecb8cc6466ab69d20d56e2 Mon Sep 17 00:00:00 2001 From: Vincent Date: Wed, 6 Mar 2024 15:45:32 +0000 Subject: [PATCH] refactor: python bindings --- src/endstone_python/endstone_python.cpp | 18 +- .../python.h => src/endstone_python/forward.h | 15 +- src/endstone_python/logger.cpp | 45 +++-- src/endstone_python/plugin.cpp | 156 ++++++++++++++++++ src/endstone_python/plugin/plugin.cpp | 92 ----------- .../plugin/plugin_description.cpp | 56 ------- src/endstone_python/plugin/plugin_loader.cpp | 58 ------- src/endstone_python/plugin/plugin_manager.cpp | 46 ------ src/endstone_python/server.cpp | 7 +- .../{color_format.cpp => util.cpp} | 6 +- 10 files changed, 201 insertions(+), 298 deletions(-) rename include/endstone/detail/python.h => src/endstone_python/forward.h (65%) create mode 100644 src/endstone_python/plugin.cpp delete mode 100644 src/endstone_python/plugin/plugin.cpp delete mode 100644 src/endstone_python/plugin/plugin_description.cpp delete mode 100644 src/endstone_python/plugin/plugin_loader.cpp delete mode 100644 src/endstone_python/plugin/plugin_manager.cpp rename src/endstone_python/{color_format.cpp => util.cpp} (96%) diff --git a/src/endstone_python/endstone_python.cpp b/src/endstone_python/endstone_python.cpp index efdc0f92c..760805a48 100644 --- a/src/endstone_python/endstone_python.cpp +++ b/src/endstone_python/endstone_python.cpp @@ -14,19 +14,21 @@ #include -#include "endstone/detail/python.h" +namespace py = pybind11; namespace endstone::detail { +void init_logger(py::module_ &); +void init_server(py::module_ &); +void init_plugin(py::module_ &); +void init_util(py::module_ &); + PYBIND11_MODULE(endstone_python, m) // NOLINT(*-use-anonymous-namespace) { - def_color_format(m); - def_logger(m); - def_plugin(m); - def_plugin_description(m); - def_plugin_loader(m); - def_plugin_manager(m); - def_server(m); + init_logger(m); + init_util(m); + init_server(m); + init_plugin(m); } } // namespace endstone::detail diff --git a/include/endstone/detail/python.h b/src/endstone_python/forward.h similarity index 65% rename from include/endstone/detail/python.h rename to src/endstone_python/forward.h index d17bc01f8..23ba33f82 100644 --- a/include/endstone/detail/python.h +++ b/src/endstone_python/forward.h @@ -1,4 +1,4 @@ -// Copyright (c) 2023, The Endstone Project. (https://endstone.dev) All Rights Reserved. +// 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. @@ -18,12 +18,11 @@ namespace endstone::detail { -void def_color_format(pybind11::module &m); -void def_logger(pybind11::module &m); -void def_server(pybind11::module &m); -void def_plugin(pybind11::module &m); -void def_plugin_description(pybind11::module &m); -void def_plugin_loader(pybind11::module &m); -void def_plugin_manager(pybind11::module &m); +template +pybind11::class_ py_class(pybind11::handle scope, const char *name) +{ + static pybind11::class_ instance = pybind11::class_(scope, name); + return instance; +} } // namespace endstone::detail diff --git a/src/endstone_python/logger.cpp b/src/endstone_python/logger.cpp index aebb96c48..20d293b60 100644 --- a/src/endstone_python/logger.cpp +++ b/src/endstone_python/logger.cpp @@ -16,36 +16,13 @@ #include -#include "endstone/detail/python.h" - namespace py = pybind11; namespace endstone::detail { -void def_logger(py::module &m) +void init_logger(py::module &m) { - auto logger = // - py::class_(m, "Logger") - .def("set_level", &Logger::setLevel, py::arg("level")) - .def("is_enabled_for", &Logger::isEnabledFor, py::arg("level")) - .def( - "trace", [](const Logger &logger, const std::string &message) { logger.trace(message); }, - py::arg("msg")) - .def( - "debug", [](const Logger &logger, const std::string &message) { logger.debug(message); }, - py::arg("msg")) - .def( - "info", [](const Logger &logger, const std::string &message) { logger.info(message); }, py::arg("msg")) - .def( - "warning", [](const Logger &logger, const std::string &message) { logger.warning(message); }, - py::arg("msg")) - .def( - "error", [](const Logger &logger, const std::string &message) { logger.error(message); }, - py::arg("msg")) - .def( - "critical", [](const Logger &logger, const std::string &message) { logger.critical(message); }, - py::arg("msg")) - .def_property_readonly("name", &Logger::getName); + auto logger = py::class_(m, "Logger"); py::enum_(logger, "Level") .value("TRACE", Logger::Level::Trace) @@ -55,6 +32,24 @@ void def_logger(py::module &m) .value("ERROR", Logger::Level::Error) .value("CRITICAL", Logger::Level::Critical) .export_values(); + + logger.def("set_level", &Logger::setLevel, py::arg("level")) + .def("is_enabled_for", &Logger::isEnabledFor, py::arg("level")) + .def( + "trace", [](const Logger &logger, const std::string &message) { logger.trace(message); }, py::arg("msg")) + .def( + "debug", [](const Logger &logger, const std::string &message) { logger.debug(message); }, py::arg("msg")) + .def( + "info", [](const Logger &logger, const std::string &message) { logger.info(message); }, py::arg("msg")) + .def( + "warning", [](const Logger &logger, const std::string &message) { logger.warning(message); }, + py::arg("msg")) + .def( + "error", [](const Logger &logger, const std::string &message) { logger.error(message); }, py::arg("msg")) + .def( + "critical", [](const Logger &logger, const std::string &message) { logger.critical(message); }, + py::arg("msg")) + .def_property_readonly("name", &Logger::getName); } } // namespace endstone::detail diff --git a/src/endstone_python/plugin.cpp b/src/endstone_python/plugin.cpp new file mode 100644 index 000000000..0a45708ff --- /dev/null +++ b/src/endstone_python/plugin.cpp @@ -0,0 +1,156 @@ +// 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. + +#include "endstone/plugin/plugin.h" + +#include +#include + +#include "endstone/logger.h" +#include "endstone/plugin/plugin_loader.h" +#include "endstone/plugin/plugin_manager.h" +#include "endstone/server.h" +#include "forward.h" + +namespace py = pybind11; + +namespace endstone::detail { +namespace { +PluginDescription createPluginDescription(std::string name, std::string version, + const std::optional &description, + const std::optional> &authors, + const std::optional &prefix, const py::args & /*args*/, + const py::kwargs & /*kwargs*/) +{ + return {std::move(name), std::move(version), description.value_or(""), authors.value_or(std::vector()), + prefix.value_or("")}; +} +} // namespace + +class PyPlugin : public Plugin { +public: + using Plugin::Plugin; + + [[nodiscard]] const PluginDescription &getDescription() const override + { + try { + PYBIND11_OVERRIDE_PURE_NAME(const PluginDescription &, Plugin, "_get_description", getDescription); + } + catch (std::exception &e) { + getLogger().error(e.what()); + std::terminate(); + } + } + + void onLoad() override + { + try { + PYBIND11_OVERRIDE_NAME(void, Plugin, "on_load", onLoad); + } + catch (std::exception &e) { + getLogger().error("Error occurred when loading {}", getDescription().getFullName()); + getLogger().error(e.what()); + } + } + + void onEnable() override + { + try { + PYBIND11_OVERRIDE_NAME(void, Plugin, "on_enable", onEnable); + } + catch (std::exception &e) { + getLogger().error("Error occurred when enabling {}.", getDescription().getFullName()); + getLogger().error(e.what()); + } + } + + void onDisable() override + { + try { + PYBIND11_OVERRIDE_NAME(void, Plugin, "on_disable", onDisable); + } + catch (std::exception &e) { + getLogger().error("Error occurred when disabling {}.", getDescription().getFullName()); + getLogger().error(e.what()); + } + } +}; + +class PyPluginLoader : public PluginLoader { +public: + using PluginLoader::PluginLoader; + + std::vector loadPlugins(const std::string &directory) override + { + try { + PYBIND11_OVERRIDE_PURE_NAME(std::vector, PluginLoader, "load_plugins", loadPlugins, + std::ref(directory)); + } + catch (std::exception &e) { + getServer().getLogger().error("Error occurred when trying to load plugins in '{}': {}", directory, + e.what()); + return {}; + } + } +}; + +void init_plugin(py::module &m) +{ + py::class_(m, "PluginDescription") + .def(py::init(&createPluginDescription), py::arg("name"), py::arg("version"), + py::arg("description") = py::none(), py::arg("authors") = py::none(), py::arg("prefix") = py::none()) + .def_property_readonly("name", &PluginDescription::getName) + .def_property_readonly("version", &PluginDescription::getVersion) + .def_property_readonly("full_name", &PluginDescription::getFullName) + .def_property_readonly("description", &PluginDescription::getDescription) + .def_property_readonly("authors", &PluginDescription::getAuthors) + .def_property_readonly("prefix", &PluginDescription::getPrefix); + + py_class(m, "PluginLoader"); + + py::class_(m, "Plugin") + .def(py::init<>()) + .def("on_load", &Plugin::onLoad) + .def("on_enable", &Plugin::onEnable) + .def("on_disable", &Plugin::onDisable) + .def("_get_description", &Plugin::getDescription, py::return_value_policy::reference) + .def_property_readonly("logger", &Plugin::getLogger, py::return_value_policy::reference) + .def_property_readonly("plugin_loader", &Plugin::getPluginLoader, py::return_value_policy::reference) + .def_property_readonly("server", &Plugin::getServer, py::return_value_policy::reference) + .def_property_readonly("enabled", &Plugin::isEnabled) + .def_property_readonly("name", &Plugin::getName); + + py_class(m, "PluginLoader").def(py::init(), py::arg("server")) + .def("load_plugins", &PluginLoader::loadPlugins, py::arg("directory"), + py::return_value_policy::reference_internal) + .def("enable_plugin", &PluginLoader::enablePlugin, py::arg("plugin")) + .def("disable_plugin", &PluginLoader::enablePlugin, py::arg("plugin")) + .def_property_readonly("server", &PluginLoader::getServer, py::return_value_policy::reference); + + py_class(m, "PluginManager") + .def("get_plugin", &PluginManager::getPlugin, py::arg("name"), py::return_value_policy::reference) + .def_property_readonly("plugins", &PluginManager::getPlugins) + .def("is_plugin_enabled", py::overload_cast(&PluginManager::isPluginEnabled, py::const_), + py::arg("plugin")) + .def("is_plugin_enabled", py::overload_cast(&PluginManager::isPluginEnabled, py::const_), + py::arg("plugin")) + .def("load_plugins", &PluginManager::loadPlugins, py::arg("directory")) + .def("enable_plugin", &PluginManager::enablePlugin, py::arg("plugin")) + .def("enable_plugins", &PluginManager::enablePlugins) + .def("disable_plugin", &PluginManager::disablePlugin, py::arg("plugin")) + .def("disable_plugins", &PluginManager::disablePlugins) + .def("clear_plugins", &PluginManager::clearPlugins); +} + +} // namespace endstone::detail diff --git a/src/endstone_python/plugin/plugin.cpp b/src/endstone_python/plugin/plugin.cpp deleted file mode 100644 index 73d12bea1..000000000 --- a/src/endstone_python/plugin/plugin.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// 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. - -#include "endstone/plugin/plugin.h" - -#include - -#include "endstone/detail/python.h" -#include "endstone/logger.h" -#include "endstone/plugin/plugin_loader.h" -#include "endstone/server.h" - -namespace py = pybind11; - -namespace endstone::detail { - -class PyPlugin : public Plugin { -public: - using Plugin::Plugin; - - [[nodiscard]] const PluginDescription &getDescription() const override - { - try { - PYBIND11_OVERRIDE_PURE_NAME(const PluginDescription &, Plugin, "_get_description", getDescription); - } - catch (std::exception &e) { - getLogger().error(e.what()); - std::terminate(); - } - } - - void onLoad() override - { - try { - PYBIND11_OVERRIDE_NAME(void, Plugin, "on_load", onLoad); - } - catch (std::exception &e) { - getLogger().error("Error occurred when loading {}", getDescription().getFullName()); - getLogger().error(e.what()); - } - } - - void onEnable() override - { - try { - PYBIND11_OVERRIDE_NAME(void, Plugin, "on_enable", onEnable); - } - catch (std::exception &e) { - getLogger().error("Error occurred when enabling {}.", getDescription().getFullName()); - getLogger().error(e.what()); - } - } - - void onDisable() override - { - try { - PYBIND11_OVERRIDE_NAME(void, Plugin, "on_disable", onDisable); - } - catch (std::exception &e) { - getLogger().error("Error occurred when disabling {}.", getDescription().getFullName()); - getLogger().error(e.what()); - } - } -}; - -void def_plugin(py::module &m) -{ - py::class_(m, "Plugin") - .def(py::init<>()) - .def("on_load", &Plugin::onLoad) - .def("on_enable", &Plugin::onEnable) - .def("on_disable", &Plugin::onDisable) - .def("_get_description", &Plugin::getDescription, py::return_value_policy::reference) - .def_property_readonly("logger", &Plugin::getLogger, py::return_value_policy::reference) - .def_property_readonly("plugin_loader", &Plugin::getPluginLoader, py::return_value_policy::reference) - .def_property_readonly("server", &Plugin::getServer, py::return_value_policy::reference) - .def_property_readonly("enabled", &Plugin::isEnabled) - .def_property_readonly("name", &Plugin::getName); -} - -} // namespace endstone::detail diff --git a/src/endstone_python/plugin/plugin_description.cpp b/src/endstone_python/plugin/plugin_description.cpp deleted file mode 100644 index 60c578ba1..000000000 --- a/src/endstone_python/plugin/plugin_description.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// 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. - -#include "endstone/plugin/plugin_description.h" - -#include - -#include -#include - -#include "endstone/detail/python.h" -#include "endstone/logger.h" -#include "endstone/plugin/plugin.h" -#include "endstone/server.h" - -namespace py = pybind11; - -namespace endstone::detail { - -namespace { -PluginDescription createPluginDescription(std::string name, std::string version, - const std::optional &description, - const std::optional> &authors, - const std::optional &prefix, const py::args & /*args*/, - const py::kwargs & /*kwargs*/) -{ - return {std::move(name), std::move(version), description.value_or(""), authors.value_or(std::vector()), - prefix.value_or("")}; -} -} // namespace - -void def_plugin_description(py::module &m) -{ - py::class_(m, "PluginDescription") - .def(py::init(&createPluginDescription), py::arg("name"), py::arg("version"), - py::arg("description") = py::none(), py::arg("authors") = py::none(), py::arg("prefix") = py::none()) - .def_property_readonly("name", &PluginDescription::getName) - .def_property_readonly("version", &PluginDescription::getVersion) - .def_property_readonly("full_name", &PluginDescription::getFullName) - .def_property_readonly("description", &PluginDescription::getDescription) - .def_property_readonly("authors", &PluginDescription::getAuthors) - .def_property_readonly("prefix", &PluginDescription::getPrefix); -} - -} // namespace endstone::detail diff --git a/src/endstone_python/plugin/plugin_loader.cpp b/src/endstone_python/plugin/plugin_loader.cpp deleted file mode 100644 index 410464094..000000000 --- a/src/endstone_python/plugin/plugin_loader.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// 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. - -#include "endstone/plugin/plugin_loader.h" - -#include -#include - -#include "endstone/detail/python.h" -#include "endstone/logger.h" -#include "endstone/plugin/plugin.h" -#include "endstone/server.h" - -namespace py = pybind11; - -namespace endstone::detail { - -class PyPluginLoader : public PluginLoader { -public: - using PluginLoader::PluginLoader; - - std::vector loadPlugins(const std::string &directory) override - { - try { - PYBIND11_OVERRIDE_PURE_NAME(std::vector, PluginLoader, "load_plugins", loadPlugins, - std::ref(directory)); - } - catch (std::exception &e) { - getServer().getLogger().error("Error occurred when trying to load plugins in '{}': {}", directory, - e.what()); - return {}; - } - } -}; - -void def_plugin_loader(py::module &m) -{ - py::class_(m, "PluginLoader") - .def(py::init(), py::arg("server")) - .def("load_plugins", &PluginLoader::loadPlugins, py::arg("directory"), - py::return_value_policy::reference_internal) - .def("enable_plugin", &PluginLoader::enablePlugin, py::arg("plugin")) - .def("disable_plugin", &PluginLoader::enablePlugin, py::arg("plugin")) - .def_property_readonly("server", &PluginLoader::getServer, py::return_value_policy::reference); -} - -} // namespace endstone::detail diff --git a/src/endstone_python/plugin/plugin_manager.cpp b/src/endstone_python/plugin/plugin_manager.cpp deleted file mode 100644 index 219e3b3d8..000000000 --- a/src/endstone_python/plugin/plugin_manager.cpp +++ /dev/null @@ -1,46 +0,0 @@ -// 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. - -#include "endstone/plugin/plugin_manager.h" - -#include -#include - -#include "endstone/detail/python.h" -#include "endstone/logger.h" -#include "endstone/plugin/plugin.h" -#include "endstone/server.h" - -namespace py = pybind11; - -namespace endstone::detail { - -void def_plugin_manager(py::module &m) -{ - py::class_(m, "PluginManager") - .def("get_plugin", &PluginManager::getPlugin, py::arg("name"), py::return_value_policy::reference) - .def_property_readonly("plugins", &PluginManager::getPlugins) - .def("is_plugin_enabled", py::overload_cast(&PluginManager::isPluginEnabled, py::const_), - py::arg("plugin")) - .def("is_plugin_enabled", py::overload_cast(&PluginManager::isPluginEnabled, py::const_), - py::arg("plugin")) - .def("load_plugins", &PluginManager::loadPlugins, py::arg("directory")) - .def("enable_plugin", &PluginManager::enablePlugin, py::arg("plugin")) - .def("enable_plugins", &PluginManager::enablePlugins) - .def("disable_plugin", &PluginManager::disablePlugin, py::arg("plugin")) - .def("disable_plugins", &PluginManager::disablePlugins) - .def("clear_plugins", &PluginManager::clearPlugins); -} - -} // namespace endstone::detail diff --git a/src/endstone_python/server.cpp b/src/endstone_python/server.cpp index 69940d585..20e3ecee2 100644 --- a/src/endstone_python/server.cpp +++ b/src/endstone_python/server.cpp @@ -17,19 +17,22 @@ #include #include -#include "endstone/detail/python.h" #include "endstone/logger.h" #include "endstone/plugin/plugin_manager.h" +#include "forward.h" namespace py = pybind11; namespace endstone::detail { -void def_server(py::module &m) +void init_server(py::module &m) { + py_class(m, "PluginManager"); + py::class_(m, "Server") .def_property_readonly("logger", &Server::getLogger, py::return_value_policy::reference) .def_property_readonly("plugin_manager", &Server::getPluginManager, py::return_value_policy::reference) + .def_property_readonly("name", &Server::getVersion) .def_property_readonly("version", &Server::getVersion) .def_property_readonly("minecraft_version", &Server::getMinecraftVersion); } diff --git a/src/endstone_python/color_format.cpp b/src/endstone_python/util.cpp similarity index 96% rename from src/endstone_python/color_format.cpp rename to src/endstone_python/util.cpp index ff13ce1ff..5a2c05492 100644 --- a/src/endstone_python/color_format.cpp +++ b/src/endstone_python/util.cpp @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "endstone/util/color_format.h" +#include -#include "endstone/detail/python.h" +#include "endstone/util/color_format.h" namespace py = pybind11; @@ -22,7 +22,7 @@ namespace py = pybind11; namespace endstone::detail { -void def_color_format(py::module &m) +void init_util(py::module &m) { py::class_(m, "ColorFormat") .ADD_COLOR_FORMAT(BLACK)