From 1070147872c4b9489175d0617fe1c629f97b27eb Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 10 Apr 2026 00:10:38 +0100 Subject: [PATCH 1/6] feat: add PyIdentifier type for Identifier Python bindings Replace transparent str conversion with a proper Identifier Python object that exposes namespace and key properties. The type caster accepts both str and Identifier inputs and returns Identifier objects. --- endstone/__init__.pyi | 25 +++++++++++ scripts/stubgen.py | 8 ++-- src/endstone/python/block.cpp | 2 +- src/endstone/python/enchantments.cpp | 2 +- src/endstone/python/endstone_python.cpp | 39 ++++++++++++++++ src/endstone/python/inventory.cpp | 2 +- src/endstone/python/registry.h | 40 +++++++++++++++++ src/endstone/python/type_caster.h | 45 +++++++++++++------ .../src/endstone_test/tests/test_registry.py | 3 +- 9 files changed, 143 insertions(+), 23 deletions(-) diff --git a/endstone/__init__.pyi b/endstone/__init__.pyi index 5c0a1fb8ba..c58e38b349 100644 --- a/endstone/__init__.pyi +++ b/endstone/__init__.pyi @@ -50,6 +50,7 @@ __all__ = [ "__version__", "ColorFormat", "GameMode", + "Identifier", "Logger", "OfflinePlayer", "Player", @@ -780,6 +781,30 @@ class Skin: __minecraft_version__ = "26.12" +class Identifier(typing.Generic[_T]): + """ + Represents a namespaced identifier consisting of a namespace and a key. + """ + def __init__(self, id: str) -> None: ... + @property + def namespace(self) -> str: + """ + The namespace component of this identifier. + """ + ... + @property + def key(self) -> str: + """ + The key component of this identifier. + """ + ... + def __str__(self) -> str: ... + def __repr__(self) -> str: ... + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... + + class Registry(typing.Generic[_T]): """ Presents a registry diff --git a/scripts/stubgen.py b/scripts/stubgen.py index 76104b73db..339e570e28 100644 --- a/scripts/stubgen.py +++ b/scripts/stubgen.py @@ -19,16 +19,14 @@ def main() -> None: module = load(module_name) package = module.package + # endstone module.members = {"_T": package.get_member("_T"), **module.members} - for member in list(module.members.keys()): - if member.endswith("Registry"): - module.members.pop(member) - module.exports.remove(member) module.set_member("Registry", package.get_member("Registry")) module.exports.append("Registry") + module.set_member("Identifier", package.get_member("Identifier")) + module.exports.append("Identifier") module.exports = sorted(module.exports) module["Server.get_registry"] = package["Server.get_registry"] - module.set_member("__version__", package.get_member("__version__")) module.imports.setdefault("__version__", package.imports.get("__version__")) module.exports = ["__version__"] + module.exports diff --git a/src/endstone/python/block.cpp b/src/endstone/python/block.cpp index 03e6e51af7..685ad24716 100644 --- a/src/endstone/python/block.cpp +++ b/src/endstone/python/block.cpp @@ -50,7 +50,7 @@ void init_block(py::module_ &m, py::class_ &block) "Creates a new BlockData instance for this block type, with all properties initialized to defaults.") .def_static("get", &BlockType::get, py::arg("name"), "Attempts to get the BlockType with the given name.", py::return_value_policy::reference) - .def("__str__", &BlockType::getId) + .def("__str__", [](const BlockType &self) { return std::string(self.getId()); }) .def("__repr__", [](const BlockType &self) { return fmt::format("BlockType({})", self.getId()); }); py::class_(m, "BlockState", diff --git a/src/endstone/python/enchantments.cpp b/src/endstone/python/enchantments.cpp index bf154b61d8..57d005b45b 100644 --- a/src/endstone/python/enchantments.cpp +++ b/src/endstone/python/enchantments.cpp @@ -84,7 +84,7 @@ void init_enchantments(py::module_ &m) "with any enchantments already applied to the item.") .def_static("get", &Enchantment::get, py::arg("name"), "Attempts to get the Enchantment with the given name.", py::return_value_policy::reference) - .def("__str__", &Enchantment::getId) + .def("__str__", [](const Enchantment &self) { return std::string(self.getId()); }) .def("__hash__", [](const Enchantment &self) { return std::hash{}(self.getId()); }) .def(py::self == py::self) .def(py::self != py::self) diff --git a/src/endstone/python/endstone_python.cpp b/src/endstone/python/endstone_python.cpp index aaa2f85139..ba12863def 100644 --- a/src/endstone/python/endstone_python.cpp +++ b/src/endstone/python/endstone_python.cpp @@ -58,6 +58,45 @@ PYBIND11_MODULE(_python, m) // NOLINT(*-use-anonymous-namespace) py::options options; options.disable_enum_members_docstring(); + // Identifier (registered early, before classes that use it via type caster) + py::class_(m, "Identifier", + "Represents a namespaced identifier consisting of a namespace and a key.") + .def(py::init(), py::arg("id"), "Create an Identifier from a string like 'namespace:key'.") + .def(py::init(), py::arg("namespace_"), py::arg("key"), + "Create an Identifier from separate namespace and key.") + .def_property_readonly( + "namespace", [](const PyIdentifier &self) { return self.namespace_; }, + "The namespace component of this identifier.") + .def_property_readonly( + "key", [](const PyIdentifier &self) { return self.key_; }, "The key component of this identifier.") + .def("__str__", &PyIdentifier::str) + .def("__repr__", + [](const PyIdentifier &self) { return "Identifier(" + self.str() + ")"; }) + .def("__hash__", + [](const PyIdentifier &self) { return py::hash(py::str(self.str())); }) + .def("__eq__", + [](const PyIdentifier &self, const py::object &other) { + if (py::isinstance(other)) { + return self == other.cast(); + } + if (py::isinstance(other)) { + return self.str() == other.cast(); + } + return false; + }) + .def("__ne__", + [](const PyIdentifier &self, const py::object &other) { + if (py::isinstance(other)) { + return !(self == other.cast()); + } + if (py::isinstance(other)) { + return self.str() != other.cast(); + } + return true; + }) + .def_static( + "__class_getitem__", [](const py::object &) { return py::type::of(); }, py::arg("item")); + // Submodules auto m_actor = m.def_submodule("actor", "Classes relating to actors (entities) that can exist in a world, including all " diff --git a/src/endstone/python/inventory.cpp b/src/endstone/python/inventory.cpp index 92e66caa2f..5237d4daff 100644 --- a/src/endstone/python/inventory.cpp +++ b/src/endstone/python/inventory.cpp @@ -44,7 +44,7 @@ void init_inventory(py::module_ &m, py::class_ &item_stack) "Constructs a new ItemStack with this item type.") .def_static("get", &ItemType::get, py::arg("name"), "Attempts to get the ItemType with the given name.", py::return_value_policy::reference) - .def("__str__", &ItemType::getId) + .def("__str__", [](const ItemType &self) { return std::string(self.getId()); }) .def(py::self == py::self) .def(py::self != py::self) .def(py::self == std::string_view()) diff --git a/src/endstone/python/registry.h b/src/endstone/python/registry.h index 49c2d52152..b4b5225926 100644 --- a/src/endstone/python/registry.h +++ b/src/endstone/python/registry.h @@ -24,6 +24,46 @@ namespace py = pybind11; namespace endstone::python { +struct PyIdentifier { + std::string namespace_; + std::string key_; + + PyIdentifier() = default; + + PyIdentifier(std::string ns, std::string key) : namespace_(std::move(ns)), key_(std::move(key)) + { + if (namespace_.empty() || key_.empty()) { + throw std::invalid_argument("Identifier namespace and key must not be empty."); + } + } + + explicit PyIdentifier(const std::string &full) + { + if (full.empty()) { + throw std::invalid_argument("Identifier string must not be empty."); + } + const auto pos = full.rfind(':'); + if (pos == std::string::npos) { + namespace_ = "minecraft"; + key_ = full; + } + else { + namespace_ = full.substr(0, pos); + key_ = full.substr(pos + 1); + } + if (namespace_.empty() || key_.empty()) { + throw std::invalid_argument("Identifier namespace and key must not be empty."); + } + } + + [[nodiscard]] std::string str() const { return namespace_ + ":" + key_; } + + bool operator==(const PyIdentifier &other) const + { + return namespace_ == other.namespace_ && key_ == other.key_; + } +}; + class PyRegistry { public: explicit PyRegistry(const IRegistry ®istry) : registry_(registry) {} diff --git a/src/endstone/python/type_caster.h b/src/endstone/python/type_caster.h index 9efc056a01..85116a03db 100644 --- a/src/endstone/python/type_caster.h +++ b/src/endstone/python/type_caster.h @@ -18,6 +18,7 @@ #include #include "endstone/endstone.hpp" +#include "registry.h" namespace pybind11::detail { template <> @@ -266,32 +267,48 @@ class type_caster { template class type_caster> { public: + using value_conv = make_caster; explicit type_caster() : value("") {} // Python -> C++ bool load(handle src, bool convert) { - make_caster str_caster; - if (!str_caster.load(src, convert)) { - return false; - } - try { - value = static_cast(str_caster); + // Accept PyIdentifier objects + if (isinstance(src)) { + auto &py_id = src.cast(); + storage_ = py_id.str(); + value = endstone::Identifier(std::string_view(storage_)); return true; } - catch (const std::exception &e) { - PyErr_SetString(PyExc_ValueError, e.what()); - return false; + // Accept strings + make_caster str_caster; + if (str_caster.load(src, convert)) { + try { + storage_ = cast_op(std::move(str_caster)); + value = endstone::Identifier(std::string_view(storage_)); + return true; + } + catch (const std::exception &e) { + PyErr_SetString(PyExc_ValueError, e.what()); + return false; + } } + return false; } - // C++ -> Python - static handle cast(endstone::Identifier src, return_value_policy policy, handle parent) + // C++ -> Python: return PyIdentifier object + static handle cast(endstone::Identifier src, return_value_policy /*policy*/, handle /*parent*/) { - make_caster str_caster; - return str_caster.cast(src, policy, parent); + endstone::python::PyIdentifier id(std::string(src.getNamespace()), std::string(src.getKey())); + return pybind11::cast(std::move(id)).release(); } - PYBIND11_TYPE_CASTER(endstone::Identifier, const_name(PYBIND11_STRING_NAME)); + // PYBIND11_TYPE_CASTER(endstone::Identifier, const_name("@Identifier[") + value_conv::name + + // const_name("] | str@Identifier[") + value_conv::name + + // const_name("]@")); + PYBIND11_TYPE_CASTER(endstone::Identifier, const_name("endstone.Identifier[") + value_conv::name + const_name("]")); + +private: + std::string storage_; }; template <> diff --git a/tests/endstone_test/src/endstone_test/tests/test_registry.py b/tests/endstone_test/src/endstone_test/tests/test_registry.py index 6fb26d5f55..a7ad0370b7 100644 --- a/tests/endstone_test/src/endstone_test/tests/test_registry.py +++ b/tests/endstone_test/src/endstone_test/tests/test_registry.py @@ -1,5 +1,6 @@ import pytest from endstone import Server +from endstone._python import Identifier from endstone.actor import ActorType from endstone.enchantments import Enchantment from endstone.inventory import ItemType @@ -13,7 +14,7 @@ def _get_enum_constants(cls): return { name: getattr(cls, name) for name in dir(cls) - if not name.startswith("_") and isinstance(getattr(cls, name), str) + if not name.startswith("_") and isinstance(getattr(cls, name), Identifier) } From cbcd3c07077ae9ecf5b748c4dc8735fb0b307679 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 10 Apr 2026 00:20:47 +0100 Subject: [PATCH 2/6] feat: accept Identifier in PyRegistry methods Change PyRegistry::get, getOrThrow, and contains to accept PyIdentifier instead of raw std::string. Update Registry stubs to accept Identifier[_T] | str. --- endstone/__init__.pyi | 9 ++++----- src/endstone/python/registry.h | 12 ++++++------ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/endstone/__init__.pyi b/endstone/__init__.pyi index c58e38b349..fdd1e04192 100644 --- a/endstone/__init__.pyi +++ b/endstone/__init__.pyi @@ -804,22 +804,21 @@ class Identifier(typing.Generic[_T]): def __eq__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... - class Registry(typing.Generic[_T]): """ Presents a registry """ - def get(self, key: str) -> _T | None: + def get(self, id: Identifier[_T] | str) -> _T | None: """ Get the object by its key. """ ... - def get_or_throw(self, key: str) -> _T: + def get_or_throw(self, id: Identifier[_T] | str) -> _T: """ Get the object by its key or throw if missing. """ ... - def __getitem__(self, key: str) -> _T: ... + def __getitem__(self, id: Identifier[_T] | str) -> _T: ... def __iter__(self) -> list: ... - def __contains__(self, key: str) -> bool: ... + def __contains__(self, id: Identifier[_T] | str) -> bool: ... def __len__(self) -> int: ... diff --git a/src/endstone/python/registry.h b/src/endstone/python/registry.h index b4b5225926..7788cc20f3 100644 --- a/src/endstone/python/registry.h +++ b/src/endstone/python/registry.h @@ -68,20 +68,20 @@ class PyRegistry { public: explicit PyRegistry(const IRegistry ®istry) : registry_(registry) {} - [[nodiscard]] py::object get(const std::string &id) const + [[nodiscard]] py::object get(const PyIdentifier &id) const { - if (const auto *p = registry_.get0(id)) { + if (const auto *p = registry_.get0(id.str())) { return cast(p); } return py::none(); } - [[nodiscard]] py::object getOrThrow(const std::string &id) const + [[nodiscard]] py::object getOrThrow(const PyIdentifier &id) const { - if (const auto *p = registry_.get0(id)) { + if (const auto *p = registry_.get0(id.str())) { return cast(p); } - throw py::key_error(fmt::format("No registry entry found for identifier: {}", id)); + throw py::key_error(fmt::format("No registry entry found for identifier: {}", id.str())); } [[nodiscard]] py::iterator iter() const @@ -94,7 +94,7 @@ class PyRegistry { return py::iter(items); } - [[nodiscard]] bool contains(const std::string &id) const { return registry_.get0(id) != nullptr; } + [[nodiscard]] bool contains(const PyIdentifier &id) const { return registry_.get0(id.str()) != nullptr; } [[nodiscard]] std::size_t size() const { return registry_.size(); } From c351a9bc029c0f553cbb377d7f82afe86ada6f55 Mon Sep 17 00:00:00 2001 From: Vincent Date: Fri, 10 Apr 2026 00:25:15 +0100 Subject: [PATCH 3/6] fix: register implicit conversion from str to PyIdentifier Without this, passing str to PyRegistry methods (get, contains, etc.) would raise TypeError since pybind11 doesn't auto-convert. --- src/endstone/python/endstone_python.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/endstone/python/endstone_python.cpp b/src/endstone/python/endstone_python.cpp index ba12863def..a468e62526 100644 --- a/src/endstone/python/endstone_python.cpp +++ b/src/endstone/python/endstone_python.cpp @@ -96,6 +96,7 @@ PYBIND11_MODULE(_python, m) // NOLINT(*-use-anonymous-namespace) }) .def_static( "__class_getitem__", [](const py::object &) { return py::type::of(); }, py::arg("item")); + py::implicitly_convertible(); // Submodules auto m_actor = From a875f7320c2e31d9c825a5d3cc50e0367616d22a Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 28 May 2026 13:16:57 +0100 Subject: [PATCH 4/6] feat(python): tighten Identifier bindings and split caster signature - Use pybind11's io_name so Identifier casts to endstone.Identifier[T] on return but accepts endstone.Identifier[T] | str on input, reflecting the runtime behaviour without forcing every call site through the Identifier constructor. - Mirror __str__ in __repr__ on PyIdentifier. Users never construct Identifier directly (it's a facade for typed ids surfaced by the registry API), so the eval(repr(x)) convention buys nothing and the shorter form reads better in logs. - Add __eq__/__hash__/string-comparison overloads on BlockType and the missing __hash__ on ItemType so all four registry types (ActorType, BlockType, Enchantment, ItemType) are consistently hashable and string-comparable. --- src/endstone/python/block.cpp | 7 ++++++- src/endstone/python/endstone_python.cpp | 3 +-- src/endstone/python/inventory.cpp | 1 + src/endstone/python/type_caster.h | 8 ++++---- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/endstone/python/block.cpp b/src/endstone/python/block.cpp index 685ad24716..213a261a79 100644 --- a/src/endstone/python/block.cpp +++ b/src/endstone/python/block.cpp @@ -51,7 +51,12 @@ void init_block(py::module_ &m, py::class_ &block) .def_static("get", &BlockType::get, py::arg("name"), "Attempts to get the BlockType with the given name.", py::return_value_policy::reference) .def("__str__", [](const BlockType &self) { return std::string(self.getId()); }) - .def("__repr__", [](const BlockType &self) { return fmt::format("BlockType({})", self.getId()); }); + .def("__repr__", [](const BlockType &self) { return fmt::format("BlockType({})", self.getId()); }) + .def("__hash__", [](const BlockType &self) { return std::hash{}(self.getId()); }) + .def(py::self == py::self) + .def(py::self != py::self) + .def(py::self == std::string_view()) + .def(py::self != std::string_view()); py::class_(m, "BlockState", "Represents a captured state of a block, which will not update automatically.") diff --git a/src/endstone/python/endstone_python.cpp b/src/endstone/python/endstone_python.cpp index a468e62526..e725a2cc1e 100644 --- a/src/endstone/python/endstone_python.cpp +++ b/src/endstone/python/endstone_python.cpp @@ -70,8 +70,7 @@ PYBIND11_MODULE(_python, m) // NOLINT(*-use-anonymous-namespace) .def_property_readonly( "key", [](const PyIdentifier &self) { return self.key_; }, "The key component of this identifier.") .def("__str__", &PyIdentifier::str) - .def("__repr__", - [](const PyIdentifier &self) { return "Identifier(" + self.str() + ")"; }) + .def("__repr__", &PyIdentifier::str) .def("__hash__", [](const PyIdentifier &self) { return py::hash(py::str(self.str())); }) .def("__eq__", diff --git a/src/endstone/python/inventory.cpp b/src/endstone/python/inventory.cpp index 5237d4daff..29aa43a813 100644 --- a/src/endstone/python/inventory.cpp +++ b/src/endstone/python/inventory.cpp @@ -45,6 +45,7 @@ void init_inventory(py::module_ &m, py::class_ &item_stack) .def_static("get", &ItemType::get, py::arg("name"), "Attempts to get the ItemType with the given name.", py::return_value_policy::reference) .def("__str__", [](const ItemType &self) { return std::string(self.getId()); }) + .def("__hash__", [](const ItemType &self) { return std::hash{}(self.getId()); }) .def(py::self == py::self) .def(py::self != py::self) .def(py::self == std::string_view()) diff --git a/src/endstone/python/type_caster.h b/src/endstone/python/type_caster.h index 85116a03db..692da98c0b 100644 --- a/src/endstone/python/type_caster.h +++ b/src/endstone/python/type_caster.h @@ -302,10 +302,10 @@ class type_caster> { return pybind11::cast(std::move(id)).release(); } - // PYBIND11_TYPE_CASTER(endstone::Identifier, const_name("@Identifier[") + value_conv::name + - // const_name("] | str@Identifier[") + value_conv::name + - // const_name("]@")); - PYBIND11_TYPE_CASTER(endstone::Identifier, const_name("endstone.Identifier[") + value_conv::name + const_name("]")); + // The caster accepts a str at runtime, so parameter annotations are widened + // to `endstone.Identifier[T] | str` while return annotations stay narrow. + PYBIND11_TYPE_CASTER(endstone::Identifier, + const_name("endstone.Identifier[") + value_conv::name + io_name("] | str", "]")); private: std::string storage_; From a4b5297302975264790985c023d2a54ccac2ba91 Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 28 May 2026 13:17:06 +0100 Subject: [PATCH 5/6] build(stubgen): self-contained script with Identifier post-processing - Declare endstone-stubgen (from the EndstoneMC/stubgen GitHub source, which handles std::function callable annotations and equality overloads cleanly) and ruff as inline PEP 723 dependencies so `uv run scripts/stubgen.py` resolves them automatically. - Strip the bogus `from . import endstone` that Pybind11ImportFix injects alongside the absolute import. - Retype `NAME = Identifier(...)` class constants to `NAME: Identifier[] = ""`, tracking the enclosing class by indentation so the generic type parameter survives and the bare string remains visible as documentation. - Rewrite `endstone.Identifier` references in submodules to bare `Identifier` plus a `from endstone import Identifier` so the constant blocks read cleanly. - Deduplicate exports so Identifier/Registry don't appear twice in `__all__`. --- scripts/stubgen.py | 75 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 6 deletions(-) diff --git a/scripts/stubgen.py b/scripts/stubgen.py index 339e570e28..903cac8d59 100644 --- a/scripts/stubgen.py +++ b/scripts/stubgen.py @@ -1,3 +1,11 @@ +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "endstone-stubgen @ git+https://github.com/EndstoneMC/stubgen", +# "ruff", +# ] +# /// +import re import subprocess import sys from pathlib import Path @@ -7,12 +15,51 @@ sys.path = [p for p in sys.path if Path(p).resolve() != _project_root] +_CLASS_RE = re.compile(r"^(?P *)class (?P\w+)(?:\([^)]*\))?\s*:") +# Matches either `NAME = Identifier(...)` (older repr) or the bare `NAME = ns:key` +# (current repr, which mirrors __str__). The bare form is recognised by the colon +# between two identifier-like halves. +_IDENTIFIER_CONST_RE = re.compile( + r"^(?P +)(?P\w+)\s*=\s*" + r"(?:(?:endstone\.)?Identifier\((?P[^)]*)\)|(?P[\w.]+:[\w.]+))\s*$" +) + + +def _retype_identifier_constants(text: str) -> str: + """Rewrite Identifier class constants to typed assignments + `NAME: Identifier[] = ""`. The value is the bare string literal + for documentation; the runtime object is an `Identifier` constructed by the + bindings, not what the stub literally shows. Class scope is tracked by + indentation so nested classes pick up the innermost owner. + """ + out: list[str] = [] + stack: list[tuple[int, str]] = [] # (indent_width, class_name) + for line in text.splitlines(keepends=True): + cls = _CLASS_RE.match(line) + if cls: + indent = len(cls.group("indent")) + while stack and stack[-1][0] >= indent: + stack.pop() + stack.append((indent, cls.group("name"))) + out.append(line) + continue + const = _IDENTIFIER_CONST_RE.match(line) + if const and stack: + indent = len(const.group("indent")) + owner = next((name for w, name in reversed(stack) if w < indent), None) + if owner: + key = (const.group("wrapped") or const.group("bare")).strip().strip("'\"") + out.append( + f'{const.group("indent")}{const.group("name")}: ' + f'Identifier[{owner}] = "{key}"\n' + ) + continue + out.append(line) + return "".join(out) + + def main() -> None: - try: - from endstone_stubgen import load, render - except ImportError: - print("Error: endstone-stubgen not installed. Install with: pip install endstone-stubgen") - sys.exit(1) + from endstone_stubgen import load, render module_name = "endstone._python" stubs_path = Path.cwd() / "stubs" @@ -25,7 +72,7 @@ def main() -> None: module.exports.append("Registry") module.set_member("Identifier", package.get_member("Identifier")) module.exports.append("Identifier") - module.exports = sorted(module.exports) + module.exports = sorted(set(module.exports)) module["Server.get_registry"] = package["Server.get_registry"] module.set_member("__version__", package.get_member("__version__")) module.imports.setdefault("__version__", package.imports.get("__version__")) @@ -56,8 +103,24 @@ def main() -> None: text = text.replace("collections.abc.Sequence", "list") text = text.replace("typing.SupportsFloat", "float") text = text.replace("typing.SupportsInt", "int") + # Pybind11ImportFix injects a bogus `from . import endstone` in submodules + # alongside the correct `import endstone`. Strip the relative-import form. + text = text.replace("from . import endstone\n", "") if relative_path == Path(".") / "__init__.pyi": text = text.replace("from endstone._version import __version__", "from ._version import __version__") + else: + # Retype `NAME = Identifier(...)` class constants to `NAME: Identifier[]` + # so the generic parameter survives. Tracks the enclosing class by indentation. + text = _retype_identifier_constants(text) + # Rewrite submodule refs to `endstone.Identifier` to the bare `Identifier` + # name, importing it explicitly. + if "endstone.Identifier" in text or re.search(r": Identifier\[", text): + text = text.replace("endstone.Identifier", "Identifier") + text = text.replace("import endstone\n", "from endstone import Identifier\n") + if not re.search(r"^from endstone import Identifier$", text, flags=re.MULTILINE): + m = re.match(r'^"""[\s\S]*?"""\n', text) + insert_at = m.end() if m else 0 + text = text[:insert_at] + "from endstone import Identifier\n" + text[insert_at:] if relative_path == Path("nbt") / "__init__.pyi": text = text.replace("endstone.nbt.", "") From 8f54f64cd5d83718265d03a11c22ac455c520fca Mon Sep 17 00:00:00 2001 From: Vincent Date: Thu, 28 May 2026 13:17:15 +0100 Subject: [PATCH 6/6] feat(python): regenerate stubs with Identifier[T] discrimination - Class constants like Dimension.OVERWORLD, ActorType.ZOMBIE, Enchantment.PROTECTION are now typed as `Identifier[Owner]` rather than plain `str`. Static type checkers reject passing `Dimension.OVERWORLD` where `Identifier[ActorType]` is expected. - Registry lookups and `Identifier`-typed parameters accept either an Identifier or a str at runtime; stubs show this as `Identifier[T] | str` via the io_name split. - Expose Identifier, Registry, and BlockType through the lazy_loader forwarding in the relevant package __init__.py files so the runtime matches the stubs. --- CHANGELOG.md | 2 + endstone/__init__.py | 2 + endstone/__init__.pyi | 50 +++--- endstone/actor/__init__.pyi | 275 +++++++++++++++-------------- endstone/attribute/__init__.pyi | 36 ++-- endstone/block/__init__.py | 2 +- endstone/block/__init__.pyi | 8 +- endstone/effect/__init__.pyi | 76 ++++---- endstone/enchantments/__init__.pyi | 87 ++++----- endstone/inventory/__init__.pyi | 22 +-- endstone/level/__init__.pyi | 15 +- endstone/potion/__init__.pyi | 96 +++++----- 12 files changed, 345 insertions(+), 326 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec914e3d96..2dacb729b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `/restart` command (console-only) that gracefully restarts the server without manually relaunching. - Added support for custom Python events with optional cancellation. - Added `ActorType` to the registry API. +- Added the `endstone.Identifier` type for namespaced ids: `dim.id.namespace == "minecraft"`, `dim.id.key == "overworld"`, and type checkers can now tell `Identifier[Dimension]` apart from `Identifier[ActorType]`. Plain strings are still accepted where an `Identifier` is required (e.g. `level.get_dimension("overworld")`). - Added `BlockData.translation_key` for retrieving the translation key of a block. - Added `WritableBookMeta`, `BookMeta`, and `CrossbowMeta` item meta types. - Added binary NBT serialization (`dump`/`load`) with support for multiple formats. @@ -34,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING**: The Docker image now stores server data in `/data` instead of `/home/endstone/bedrock_server`. Update your volume mount accordingly (e.g. `-v ./data:/data`). - **BREAKING**: Standalone bundles now ship for both Windows (`endstone--windows-x86_64.zip`) and Linux (`endstone--linux-x86_64.zip`), and use [uv](https://docs.astral.sh/uv/) to provision Python on demand instead of shipping a Python interpreter. The included `start.cmd` / `start.sh` installs uv on first run if it isn't already on `PATH`, then launches the server via `uv run` against the bundled wheel. The old `bin/python/` directory is gone; the server folder (`./bedrock_server/`) is unchanged. +- `str()` on `BlockType`, `Enchantment`, and `ItemType` now returns a plain `"namespace:key"` string instead of the underlying `Identifier` repr. ### Removed diff --git a/endstone/__init__.py b/endstone/__init__.py index 4af7cd2971..43d9907353 100644 --- a/endstone/__init__.py +++ b/endstone/__init__.py @@ -9,10 +9,12 @@ "ColorFormat", "EnchantmentRegistry", "GameMode", + "Identifier", "ItemRegistry", "Logger", "OfflinePlayer", "Player", + "Registry", "Server", "Skin", "actor", diff --git a/endstone/__init__.pyi b/endstone/__init__.pyi index fdd1e04192..3308ed97d9 100644 --- a/endstone/__init__.pyi +++ b/endstone/__init__.pyi @@ -84,6 +84,29 @@ __all__ = [ _T = typing.TypeVar("_T") +class Identifier(typing.Generic[_T]): + """ + Represents a namespaced identifier consisting of a namespace and a key. + """ + def __init__(self, id: str) -> None: ... + @property + def namespace(self) -> str: + """ + The namespace component of this identifier. + """ + ... + @property + def key(self) -> str: + """ + The key component of this identifier. + """ + ... + def __str__(self) -> str: ... + def __repr__(self) -> str: ... + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... + class Server: """ Represents a server implementation. @@ -779,31 +802,6 @@ class Skin: """ ... -__minecraft_version__ = "26.12" - -class Identifier(typing.Generic[_T]): - """ - Represents a namespaced identifier consisting of a namespace and a key. - """ - def __init__(self, id: str) -> None: ... - @property - def namespace(self) -> str: - """ - The namespace component of this identifier. - """ - ... - @property - def key(self) -> str: - """ - The key component of this identifier. - """ - ... - def __str__(self) -> str: ... - def __repr__(self) -> str: ... - def __hash__(self) -> int: ... - def __eq__(self, other: object) -> bool: ... - def __ne__(self, other: object) -> bool: ... - class Registry(typing.Generic[_T]): """ Presents a registry @@ -822,3 +820,5 @@ class Registry(typing.Generic[_T]): def __iter__(self) -> list: ... def __contains__(self, id: Identifier[_T] | str) -> bool: ... def __len__(self) -> int: ... + +__minecraft_version__ = "26.20" diff --git a/endstone/actor/__init__.pyi b/endstone/actor/__init__.pyi index 5b15fcbe35..8785743a88 100644 --- a/endstone/actor/__init__.pyi +++ b/endstone/actor/__init__.pyi @@ -4,6 +4,7 @@ Classes relating to actors (entities) that can exist in a world, including all p import typing +from endstone import Identifier from endstone.command import CommandSender from endstone.inventory import ItemStack from endstone.level import Dimension, Level, Location @@ -190,143 +191,143 @@ class ActorType: Represents an actor type. """ - AGENT = "minecraft:agent" - ALLAY = "minecraft:allay" - AREA_EFFECT_CLOUD = "minecraft:area_effect_cloud" - ARMADILLO = "minecraft:armadillo" - ARMOR_STAND = "minecraft:armor_stand" - ARROW = "minecraft:arrow" - AXOLOTL = "minecraft:axolotl" - BALLOON = "minecraft:balloon" - BAT = "minecraft:bat" - BEE = "minecraft:bee" - BLAZE = "minecraft:blaze" - BOAT = "minecraft:boat" - BOGGED = "minecraft:bogged" - BREEZE = "minecraft:breeze" - BREEZE_WIND_CHARGE_PROJECTILE = "minecraft:breeze_wind_charge_projectile" - CAMEL = "minecraft:camel" - CAMEL_HUSK = "minecraft:camel_husk" - CAT = "minecraft:cat" - CAVE_SPIDER = "minecraft:cave_spider" - CHEST_BOAT = "minecraft:chest_boat" - CHEST_MINECART = "minecraft:chest_minecart" - CHICKEN = "minecraft:chicken" - COD = "minecraft:cod" - COMMAND_BLOCK_MINECART = "minecraft:command_block_minecart" - COPPER_GOLEM = "minecraft:copper_golem" - COW = "minecraft:cow" - CREAKING = "minecraft:creaking" - CREEPER = "minecraft:creeper" - DOLPHIN = "minecraft:dolphin" - DONKEY = "minecraft:donkey" - DRAGON_FIREBALL = "minecraft:dragon_fireball" - DROWNED = "minecraft:drowned" - EGG = "minecraft:egg" - ELDER_GUARDIAN = "minecraft:elder_guardian" - ELDER_GUARDIAN_GHOST = "minecraft:elder_guardian_ghost" - ENDER_CRYSTAL = "minecraft:ender_crystal" - ENDER_DRAGON = "minecraft:ender_dragon" - ENDER_PEARL = "minecraft:ender_pearl" - ENDERMAN = "minecraft:enderman" - ENDERMITE = "minecraft:endermite" - EVOCATION_FANG = "minecraft:evocation_fang" - EVOCATION_ILLAGER = "minecraft:evocation_illager" - EYE_OF_ENDER_SIGNAL = "minecraft:eye_of_ender_signal" - FALLING_BLOCK = "minecraft:falling_block" - FIREBALL = "minecraft:fireball" - FIREWORKS_ROCKET = "minecraft:fireworks_rocket" - FISHING_HOOK = "minecraft:fishing_hook" - FOX = "minecraft:fox" - FROG = "minecraft:frog" - GHAST = "minecraft:ghast" - GLOW_SQUID = "minecraft:glow_squid" - GOAT = "minecraft:goat" - GUARDIAN = "minecraft:guardian" - HAPPY_GHAST = "minecraft:happy_ghast" - HOGLIN = "minecraft:hoglin" - HOPPER_MINECART = "minecraft:hopper_minecart" - HORSE = "minecraft:horse" - HUSK = "minecraft:husk" - ICE_BOMB = "minecraft:ice_bomb" - IRON_GOLEM = "minecraft:iron_golem" - ITEM = "minecraft:item" - LEASH_KNOT = "minecraft:leash_knot" - LIGHTNING_BOLT = "minecraft:lightning_bolt" - LINGERING_POTION = "minecraft:lingering_potion" - LLAMA = "minecraft:llama" - LLAMA_SPIT = "minecraft:llama_spit" - MAGMA_CUBE = "minecraft:magma_cube" - MINECART = "minecraft:minecart" - MOOSHROOM = "minecraft:mooshroom" - MULE = "minecraft:mule" - NAUTILUS = "minecraft:nautilus" - NPC = "minecraft:npc" - OCELOT = "minecraft:ocelot" - OMINOUS_ITEM_SPAWNER = "minecraft:ominous_item_spawner" - PAINTING = "minecraft:painting" - PANDA = "minecraft:panda" - PARCHED = "minecraft:parched" - PARROT = "minecraft:parrot" - PHANTOM = "minecraft:phantom" - PIG = "minecraft:pig" - PIGLIN = "minecraft:piglin" - PIGLIN_BRUTE = "minecraft:piglin_brute" - PILLAGER = "minecraft:pillager" - PLAYER = "minecraft:player" - POLAR_BEAR = "minecraft:polar_bear" - PUFFERFISH = "minecraft:pufferfish" - RABBIT = "minecraft:rabbit" - RAVAGER = "minecraft:ravager" - SALMON = "minecraft:salmon" - SHEEP = "minecraft:sheep" - SHULKER = "minecraft:shulker" - SHULKER_BULLET = "minecraft:shulker_bullet" - SILVERFISH = "minecraft:silverfish" - SKELETON = "minecraft:skeleton" - SKELETON_HORSE = "minecraft:skeleton_horse" - SLIME = "minecraft:slime" - SMALL_FIREBALL = "minecraft:small_fireball" - SNIFFER = "minecraft:sniffer" - SNOW_GOLEM = "minecraft:snow_golem" - SNOWBALL = "minecraft:snowball" - SPIDER = "minecraft:spider" - SPLASH_POTION = "minecraft:splash_potion" - SQUID = "minecraft:squid" - STRAY = "minecraft:stray" - STRIDER = "minecraft:strider" - TADPOLE = "minecraft:tadpole" - THROWN_TRIDENT = "minecraft:thrown_trident" - TNT = "minecraft:tnt" - TNT_MINECART = "minecraft:tnt_minecart" - TRADER_LLAMA = "minecraft:trader_llama" - TRIPOD_CAMERA = "minecraft:tripod_camera" - TROPICALFISH = "minecraft:tropicalfish" - TURTLE = "minecraft:turtle" - VEX = "minecraft:vex" - VILLAGER = "minecraft:villager" - VILLAGER_V2 = "minecraft:villager_v2" - VINDICATOR = "minecraft:vindicator" - WANDERING_TRADER = "minecraft:wandering_trader" - WARDEN = "minecraft:warden" - WIND_CHARGE_PROJECTILE = "minecraft:wind_charge_projectile" - WITCH = "minecraft:witch" - WITHER = "minecraft:wither" - WITHER_SKELETON = "minecraft:wither_skeleton" - WITHER_SKULL = "minecraft:wither_skull" - WITHER_SKULL_DANGEROUS = "minecraft:wither_skull_dangerous" - WOLF = "minecraft:wolf" - XP_BOTTLE = "minecraft:xp_bottle" - XP_ORB = "minecraft:xp_orb" - ZOGLIN = "minecraft:zoglin" - ZOMBIE = "minecraft:zombie" - ZOMBIE_HORSE = "minecraft:zombie_horse" - ZOMBIE_NAUTILUS = "minecraft:zombie_nautilus" - ZOMBIE_PIGMAN = "minecraft:zombie_pigman" - ZOMBIE_VILLAGER = "minecraft:zombie_villager" - ZOMBIE_VILLAGER_V2 = "minecraft:zombie_villager_v2" + AGENT: Identifier[ActorType] = "minecraft:agent" + ALLAY: Identifier[ActorType] = "minecraft:allay" + AREA_EFFECT_CLOUD: Identifier[ActorType] = "minecraft:area_effect_cloud" + ARMADILLO: Identifier[ActorType] = "minecraft:armadillo" + ARMOR_STAND: Identifier[ActorType] = "minecraft:armor_stand" + ARROW: Identifier[ActorType] = "minecraft:arrow" + AXOLOTL: Identifier[ActorType] = "minecraft:axolotl" + BALLOON: Identifier[ActorType] = "minecraft:balloon" + BAT: Identifier[ActorType] = "minecraft:bat" + BEE: Identifier[ActorType] = "minecraft:bee" + BLAZE: Identifier[ActorType] = "minecraft:blaze" + BOAT: Identifier[ActorType] = "minecraft:boat" + BOGGED: Identifier[ActorType] = "minecraft:bogged" + BREEZE: Identifier[ActorType] = "minecraft:breeze" + BREEZE_WIND_CHARGE_PROJECTILE: Identifier[ActorType] = "minecraft:breeze_wind_charge_projectile" + CAMEL: Identifier[ActorType] = "minecraft:camel" + CAMEL_HUSK: Identifier[ActorType] = "minecraft:camel_husk" + CAT: Identifier[ActorType] = "minecraft:cat" + CAVE_SPIDER: Identifier[ActorType] = "minecraft:cave_spider" + CHEST_BOAT: Identifier[ActorType] = "minecraft:chest_boat" + CHEST_MINECART: Identifier[ActorType] = "minecraft:chest_minecart" + CHICKEN: Identifier[ActorType] = "minecraft:chicken" + COD: Identifier[ActorType] = "minecraft:cod" + COMMAND_BLOCK_MINECART: Identifier[ActorType] = "minecraft:command_block_minecart" + COPPER_GOLEM: Identifier[ActorType] = "minecraft:copper_golem" + COW: Identifier[ActorType] = "minecraft:cow" + CREAKING: Identifier[ActorType] = "minecraft:creaking" + CREEPER: Identifier[ActorType] = "minecraft:creeper" + DOLPHIN: Identifier[ActorType] = "minecraft:dolphin" + DONKEY: Identifier[ActorType] = "minecraft:donkey" + DRAGON_FIREBALL: Identifier[ActorType] = "minecraft:dragon_fireball" + DROWNED: Identifier[ActorType] = "minecraft:drowned" + EGG: Identifier[ActorType] = "minecraft:egg" + ELDER_GUARDIAN: Identifier[ActorType] = "minecraft:elder_guardian" + ELDER_GUARDIAN_GHOST: Identifier[ActorType] = "minecraft:elder_guardian_ghost" + ENDER_CRYSTAL: Identifier[ActorType] = "minecraft:ender_crystal" + ENDER_DRAGON: Identifier[ActorType] = "minecraft:ender_dragon" + ENDER_PEARL: Identifier[ActorType] = "minecraft:ender_pearl" + ENDERMAN: Identifier[ActorType] = "minecraft:enderman" + ENDERMITE: Identifier[ActorType] = "minecraft:endermite" + EVOCATION_FANG: Identifier[ActorType] = "minecraft:evocation_fang" + EVOCATION_ILLAGER: Identifier[ActorType] = "minecraft:evocation_illager" + EYE_OF_ENDER_SIGNAL: Identifier[ActorType] = "minecraft:eye_of_ender_signal" + FALLING_BLOCK: Identifier[ActorType] = "minecraft:falling_block" + FIREBALL: Identifier[ActorType] = "minecraft:fireball" + FIREWORKS_ROCKET: Identifier[ActorType] = "minecraft:fireworks_rocket" + FISHING_HOOK: Identifier[ActorType] = "minecraft:fishing_hook" + FOX: Identifier[ActorType] = "minecraft:fox" + FROG: Identifier[ActorType] = "minecraft:frog" + GHAST: Identifier[ActorType] = "minecraft:ghast" + GLOW_SQUID: Identifier[ActorType] = "minecraft:glow_squid" + GOAT: Identifier[ActorType] = "minecraft:goat" + GUARDIAN: Identifier[ActorType] = "minecraft:guardian" + HAPPY_GHAST: Identifier[ActorType] = "minecraft:happy_ghast" + HOGLIN: Identifier[ActorType] = "minecraft:hoglin" + HOPPER_MINECART: Identifier[ActorType] = "minecraft:hopper_minecart" + HORSE: Identifier[ActorType] = "minecraft:horse" + HUSK: Identifier[ActorType] = "minecraft:husk" + ICE_BOMB: Identifier[ActorType] = "minecraft:ice_bomb" + IRON_GOLEM: Identifier[ActorType] = "minecraft:iron_golem" + ITEM: Identifier[ActorType] = "minecraft:item" + LEASH_KNOT: Identifier[ActorType] = "minecraft:leash_knot" + LIGHTNING_BOLT: Identifier[ActorType] = "minecraft:lightning_bolt" + LINGERING_POTION: Identifier[ActorType] = "minecraft:lingering_potion" + LLAMA: Identifier[ActorType] = "minecraft:llama" + LLAMA_SPIT: Identifier[ActorType] = "minecraft:llama_spit" + MAGMA_CUBE: Identifier[ActorType] = "minecraft:magma_cube" + MINECART: Identifier[ActorType] = "minecraft:minecart" + MOOSHROOM: Identifier[ActorType] = "minecraft:mooshroom" + MULE: Identifier[ActorType] = "minecraft:mule" + NAUTILUS: Identifier[ActorType] = "minecraft:nautilus" + NPC: Identifier[ActorType] = "minecraft:npc" + OCELOT: Identifier[ActorType] = "minecraft:ocelot" + OMINOUS_ITEM_SPAWNER: Identifier[ActorType] = "minecraft:ominous_item_spawner" + PAINTING: Identifier[ActorType] = "minecraft:painting" + PANDA: Identifier[ActorType] = "minecraft:panda" + PARCHED: Identifier[ActorType] = "minecraft:parched" + PARROT: Identifier[ActorType] = "minecraft:parrot" + PHANTOM: Identifier[ActorType] = "minecraft:phantom" + PIG: Identifier[ActorType] = "minecraft:pig" + PIGLIN: Identifier[ActorType] = "minecraft:piglin" + PIGLIN_BRUTE: Identifier[ActorType] = "minecraft:piglin_brute" + PILLAGER: Identifier[ActorType] = "minecraft:pillager" + PLAYER: Identifier[ActorType] = "minecraft:player" + POLAR_BEAR: Identifier[ActorType] = "minecraft:polar_bear" + PUFFERFISH: Identifier[ActorType] = "minecraft:pufferfish" + RABBIT: Identifier[ActorType] = "minecraft:rabbit" + RAVAGER: Identifier[ActorType] = "minecraft:ravager" + SALMON: Identifier[ActorType] = "minecraft:salmon" + SHEEP: Identifier[ActorType] = "minecraft:sheep" + SHULKER: Identifier[ActorType] = "minecraft:shulker" + SHULKER_BULLET: Identifier[ActorType] = "minecraft:shulker_bullet" + SILVERFISH: Identifier[ActorType] = "minecraft:silverfish" + SKELETON: Identifier[ActorType] = "minecraft:skeleton" + SKELETON_HORSE: Identifier[ActorType] = "minecraft:skeleton_horse" + SLIME: Identifier[ActorType] = "minecraft:slime" + SMALL_FIREBALL: Identifier[ActorType] = "minecraft:small_fireball" + SNIFFER: Identifier[ActorType] = "minecraft:sniffer" + SNOW_GOLEM: Identifier[ActorType] = "minecraft:snow_golem" + SNOWBALL: Identifier[ActorType] = "minecraft:snowball" + SPIDER: Identifier[ActorType] = "minecraft:spider" + SPLASH_POTION: Identifier[ActorType] = "minecraft:splash_potion" + SQUID: Identifier[ActorType] = "minecraft:squid" + STRAY: Identifier[ActorType] = "minecraft:stray" + STRIDER: Identifier[ActorType] = "minecraft:strider" + TADPOLE: Identifier[ActorType] = "minecraft:tadpole" + THROWN_TRIDENT: Identifier[ActorType] = "minecraft:thrown_trident" + TNT: Identifier[ActorType] = "minecraft:tnt" + TNT_MINECART: Identifier[ActorType] = "minecraft:tnt_minecart" + TRADER_LLAMA: Identifier[ActorType] = "minecraft:trader_llama" + TRIPOD_CAMERA: Identifier[ActorType] = "minecraft:tripod_camera" + TROPICALFISH: Identifier[ActorType] = "minecraft:tropicalfish" + TURTLE: Identifier[ActorType] = "minecraft:turtle" + VEX: Identifier[ActorType] = "minecraft:vex" + VILLAGER: Identifier[ActorType] = "minecraft:villager" + VILLAGER_V2: Identifier[ActorType] = "minecraft:villager_v2" + VINDICATOR: Identifier[ActorType] = "minecraft:vindicator" + WANDERING_TRADER: Identifier[ActorType] = "minecraft:wandering_trader" + WARDEN: Identifier[ActorType] = "minecraft:warden" + WIND_CHARGE_PROJECTILE: Identifier[ActorType] = "minecraft:wind_charge_projectile" + WITCH: Identifier[ActorType] = "minecraft:witch" + WITHER: Identifier[ActorType] = "minecraft:wither" + WITHER_SKELETON: Identifier[ActorType] = "minecraft:wither_skeleton" + WITHER_SKULL: Identifier[ActorType] = "minecraft:wither_skull" + WITHER_SKULL_DANGEROUS: Identifier[ActorType] = "minecraft:wither_skull_dangerous" + WOLF: Identifier[ActorType] = "minecraft:wolf" + XP_BOTTLE: Identifier[ActorType] = "minecraft:xp_bottle" + XP_ORB: Identifier[ActorType] = "minecraft:xp_orb" + ZOGLIN: Identifier[ActorType] = "minecraft:zoglin" + ZOMBIE: Identifier[ActorType] = "minecraft:zombie" + ZOMBIE_HORSE: Identifier[ActorType] = "minecraft:zombie_horse" + ZOMBIE_NAUTILUS: Identifier[ActorType] = "minecraft:zombie_nautilus" + ZOMBIE_PIGMAN: Identifier[ActorType] = "minecraft:zombie_pigman" + ZOMBIE_VILLAGER: Identifier[ActorType] = "minecraft:zombie_villager" + ZOMBIE_VILLAGER_V2: Identifier[ActorType] = "minecraft:zombie_villager_v2" @property - def id(self) -> str: + def id(self) -> Identifier[ActorType]: """ Return the identifier of this actor type. """ @@ -338,7 +339,7 @@ class ActorType: """ ... @staticmethod - def get(name: str) -> ActorType: + def get(name: Identifier[ActorType] | str) -> ActorType: """ Attempts to get the ActorType with the given name. """ diff --git a/endstone/attribute/__init__.pyi b/endstone/attribute/__init__.pyi index e6c99ae3bb..ffd03fd1ec 100644 --- a/endstone/attribute/__init__.pyi +++ b/endstone/attribute/__init__.pyi @@ -5,6 +5,8 @@ Classes relevant to attributes. import enum import uuid +from endstone import Identifier + __all__ = ["Attribute", "AttributeInstance", "AttributeModifier"] class Attribute: @@ -12,22 +14,22 @@ class Attribute: All attribute types. """ - HEALTH = "minecraft:health" - FOLLOW_RANGE = "minecraft:follow_range" - KNOCKBACK_RESISTANCE = "minecraft:knockback_resistance" - MOVEMENT_SPEED = "minecraft:movement" - UNDERWATER_MOVEMENT_SPEED = "minecraft:underwater_movement" - LAVA_MOVEMENT_SPEED = "minecraft:lava_movement" - ATTACK_DAMAGE = "minecraft:attack_damage" - ABSORPTION = "minecraft:absorption" - LUCK = "minecraft:luck" - JUMP_STRENGTH = "minecraft:jump_strength" - PLAYER_HUNGER = "minecraft:player.hunger" - PLAYER_SATURATION = "minecraft:player.saturation" - PLAYER_EXHAUSTION = "minecraft:player.exhaustion" - PLAYER_LEVEL = "minecraft:player.level" - PLAYER_EXPERIENCE = "minecraft:player.experience" - ZOMBIE_SPAWN_REINFORCEMENTS = "minecraft:zombie.spawn_reinforcements" + HEALTH: Identifier[Attribute] = "minecraft:health" + FOLLOW_RANGE: Identifier[Attribute] = "minecraft:follow_range" + KNOCKBACK_RESISTANCE: Identifier[Attribute] = "minecraft:knockback_resistance" + MOVEMENT_SPEED: Identifier[Attribute] = "minecraft:movement" + UNDERWATER_MOVEMENT_SPEED: Identifier[Attribute] = "minecraft:underwater_movement" + LAVA_MOVEMENT_SPEED: Identifier[Attribute] = "minecraft:lava_movement" + ATTACK_DAMAGE: Identifier[Attribute] = "minecraft:attack_damage" + ABSORPTION: Identifier[Attribute] = "minecraft:absorption" + LUCK: Identifier[Attribute] = "minecraft:luck" + JUMP_STRENGTH: Identifier[Attribute] = "minecraft:jump_strength" + PLAYER_HUNGER: Identifier[Attribute] = "minecraft:player.hunger" + PLAYER_SATURATION: Identifier[Attribute] = "minecraft:player.saturation" + PLAYER_EXHAUSTION: Identifier[Attribute] = "minecraft:player.exhaustion" + PLAYER_LEVEL: Identifier[Attribute] = "minecraft:player.level" + PLAYER_EXPERIENCE: Identifier[Attribute] = "minecraft:player.experience" + ZOMBIE_SPAWN_REINFORCEMENTS: Identifier[Attribute] = "minecraft:zombie.spawn_reinforcements" class AttributeModifier: """ @@ -85,7 +87,7 @@ class AttributeInstance: Represents a mutable instance of an attribute and its associated modifiers and values. """ @property - def type(self) -> str: + def type(self) -> Identifier[Attribute]: """ The attribute type pertaining to this instance. """ diff --git a/endstone/block/__init__.py b/endstone/block/__init__.py index 641d319c85..fa2ff3341b 100644 --- a/endstone/block/__init__.py +++ b/endstone/block/__init__.py @@ -3,6 +3,6 @@ __getattr__, __dir__, __all__ = lazy.attach( "endstone._python", submod_attrs={ - "block": ["Block", "BlockData", "BlockFace", "BlockState"], + "block": ["Block", "BlockData", "BlockFace", "BlockState", "BlockType"], }, ) diff --git a/endstone/block/__init__.pyi b/endstone/block/__init__.pyi index 0c34329982..a50409ecfa 100644 --- a/endstone/block/__init__.pyi +++ b/endstone/block/__init__.pyi @@ -5,6 +5,7 @@ Classes relating to the blocks in a world, including special states. import enum import typing +from endstone import Identifier from endstone.level import Dimension, Location __all__ = ["Block", "BlockData", "BlockFace", "BlockState", "BlockType"] @@ -97,7 +98,7 @@ class BlockType: Represents a block type. """ @property - def id(self) -> str: + def id(self) -> Identifier[BlockType]: """ Return the identifier of this block type. """ @@ -120,13 +121,16 @@ class BlockType: """ ... @staticmethod - def get(name: str) -> BlockType: + def get(name: Identifier[BlockType] | str) -> BlockType: """ Attempts to get the BlockType with the given name. """ ... def __str__(self) -> str: ... def __repr__(self) -> str: ... + def __hash__(self) -> int: ... + def __eq__(self, other: object) -> bool: ... + def __ne__(self, other: object) -> bool: ... class BlockData: """ diff --git a/endstone/effect/__init__.pyi b/endstone/effect/__init__.pyi index 6a75e6f9d3..042885e093 100644 --- a/endstone/effect/__init__.pyi +++ b/endstone/effect/__init__.pyi @@ -2,6 +2,8 @@ Classes relating to the effects that can be applied to entities. """ +from endstone import Identifier + __all__ = ["EffectType"] class EffectType: @@ -9,40 +11,40 @@ class EffectType: All effect types. """ - SPEED = "minecraft:speed" - SLOWNESS = "minecraft:slowness" - HASTE = "minecraft:haste" - MINING_FATIGUE = "minecraft:mining_fatigue" - STRENGTH = "minecraft:strength" - INSTANT_HEALTH = "minecraft:instant_health" - INSTANT_DAMAGE = "minecraft:instant_damage" - JUMP_BOOST = "minecraft:jump_boost" - NAUSEA = "minecraft:nausea" - REGENERATION = "minecraft:regeneration" - RESISTANCE = "minecraft:resistance" - FIRE_RESISTANCE = "minecraft:fire_resistance" - WATER_BREATHING = "minecraft:water_breathing" - INVISIBILITY = "minecraft:invisibility" - BLINDNESS = "minecraft:blindness" - NIGHT_VISION = "minecraft:night_vision" - HUNGER = "minecraft:hunger" - WEAKNESS = "minecraft:weakness" - POISON = "minecraft:poison" - WITHER = "minecraft:wither" - HEALTH_BOOST = "minecraft:health_boost" - ABSORPTION = "minecraft:absorption" - SATURATION = "minecraft:saturation" - LEVITATION = "minecraft:levitation" - FATAL_POISON = "minecraft:fatal_poison" - CONDUIT_POWER = "minecraft:conduit_power" - SLOW_FALLING = "minecraft:slow_falling" - BAD_OMEN = "minecraft:bad_omen" - HERO_OF_THE_VILLAGE = "minecraft:hero_of_the_village" - DARKNESS = "minecraft:darkness" - TRIAL_OMEN = "minecraft:trial_omen" - WIND_CHARGED = "minecraft:wind_charged" - WEAVING = "minecraft:weaving" - OOZING = "minecraft:oozing" - INFESTED = "minecraft:infested" - RAID_OMEN = "minecraft:raid_omen" - BREATH_OF_THE_NAUTILUS = "minecraft:breath_of_the_nautilus" + SPEED: Identifier[EffectType] = "minecraft:speed" + SLOWNESS: Identifier[EffectType] = "minecraft:slowness" + HASTE: Identifier[EffectType] = "minecraft:haste" + MINING_FATIGUE: Identifier[EffectType] = "minecraft:mining_fatigue" + STRENGTH: Identifier[EffectType] = "minecraft:strength" + INSTANT_HEALTH: Identifier[EffectType] = "minecraft:instant_health" + INSTANT_DAMAGE: Identifier[EffectType] = "minecraft:instant_damage" + JUMP_BOOST: Identifier[EffectType] = "minecraft:jump_boost" + NAUSEA: Identifier[EffectType] = "minecraft:nausea" + REGENERATION: Identifier[EffectType] = "minecraft:regeneration" + RESISTANCE: Identifier[EffectType] = "minecraft:resistance" + FIRE_RESISTANCE: Identifier[EffectType] = "minecraft:fire_resistance" + WATER_BREATHING: Identifier[EffectType] = "minecraft:water_breathing" + INVISIBILITY: Identifier[EffectType] = "minecraft:invisibility" + BLINDNESS: Identifier[EffectType] = "minecraft:blindness" + NIGHT_VISION: Identifier[EffectType] = "minecraft:night_vision" + HUNGER: Identifier[EffectType] = "minecraft:hunger" + WEAKNESS: Identifier[EffectType] = "minecraft:weakness" + POISON: Identifier[EffectType] = "minecraft:poison" + WITHER: Identifier[EffectType] = "minecraft:wither" + HEALTH_BOOST: Identifier[EffectType] = "minecraft:health_boost" + ABSORPTION: Identifier[EffectType] = "minecraft:absorption" + SATURATION: Identifier[EffectType] = "minecraft:saturation" + LEVITATION: Identifier[EffectType] = "minecraft:levitation" + FATAL_POISON: Identifier[EffectType] = "minecraft:fatal_poison" + CONDUIT_POWER: Identifier[EffectType] = "minecraft:conduit_power" + SLOW_FALLING: Identifier[EffectType] = "minecraft:slow_falling" + BAD_OMEN: Identifier[EffectType] = "minecraft:bad_omen" + HERO_OF_THE_VILLAGE: Identifier[EffectType] = "minecraft:hero_of_the_village" + DARKNESS: Identifier[EffectType] = "minecraft:darkness" + TRIAL_OMEN: Identifier[EffectType] = "minecraft:trial_omen" + WIND_CHARGED: Identifier[EffectType] = "minecraft:wind_charged" + WEAVING: Identifier[EffectType] = "minecraft:weaving" + OOZING: Identifier[EffectType] = "minecraft:oozing" + INFESTED: Identifier[EffectType] = "minecraft:infested" + RAID_OMEN: Identifier[EffectType] = "minecraft:raid_omen" + BREATH_OF_THE_NAUTILUS: Identifier[EffectType] = "minecraft:breath_of_the_nautilus" diff --git a/endstone/enchantments/__init__.pyi b/endstone/enchantments/__init__.pyi index 783211c77f..ceb817e573 100644 --- a/endstone/enchantments/__init__.pyi +++ b/endstone/enchantments/__init__.pyi @@ -2,54 +2,55 @@ Classes relating to the specialized enhancements to ItemStacks. """ +from endstone import Identifier from endstone.inventory import ItemStack __all__ = ["Enchantment"] class Enchantment: - PROTECTION = "minecraft:protection" - FIRE_PROTECTION = "minecraft:fire_protection" - FEATHER_FALLING = "minecraft:feather_falling" - BLAST_PROTECTION = "minecraft:blast_protection" - PROJECTILE_PROTECTION = "minecraft:projectile_protection" - THORNS = "minecraft:thorns" - RESPIRATION = "minecraft:respiration" - DEPTH_STRIDER = "minecraft:depth_strider" - AQUA_AFFINITY = "minecraft:aqua_affinity" - SHARPNESS = "minecraft:sharpness" - SMITE = "minecraft:smite" - BANE_OF_ARTHROPODS = "minecraft:bane_of_arthropods" - KNOCKBACK = "minecraft:knockback" - FIRE_ASPECT = "minecraft:fire_aspect" - LOOTING = "minecraft:looting" - EFFICIENCY = "minecraft:efficiency" - SILK_TOUCH = "minecraft:silk_touch" - UNBREAKING = "minecraft:unbreaking" - POWER = "minecraft:power" - PUNCH = "minecraft:punch" - FLAME = "minecraft:flame" - INFINITY = "minecraft:infinity" - LUCK_OF_THE_SEA = "minecraft:luck_of_the_sea" - LURE = "minecraft:lure" - FROST_WALKER = "minecraft:frost_walker" - MENDING = "minecraft:mending" - CURSE_OF_BINDING = "minecraft:binding" - CURSE_OF_VANISHING = "minecraft:vanishing" - IMPALING = "minecraft:impaling" - RIPTIDE = "minecraft:riptide" - LOYALTY = "minecraft:loyalty" - CHANNELING = "minecraft:channeling" - MULTISHOT = "minecraft:multishot" - PIERCING = "minecraft:piercing" - QUICK_CHARGE = "minecraft:quick_charge" - SOUL_SPEED = "minecraft:soul_speed" - SWIFT_SNEAK = "minecraft:swift_sneak" - WIND_BURST = "minecraft:wind_burst" - DENSITY = "minecraft:density" - BREACH = "minecraft:breach" - LUNGE = "minecraft:lunge" + PROTECTION: Identifier[Enchantment] = "minecraft:protection" + FIRE_PROTECTION: Identifier[Enchantment] = "minecraft:fire_protection" + FEATHER_FALLING: Identifier[Enchantment] = "minecraft:feather_falling" + BLAST_PROTECTION: Identifier[Enchantment] = "minecraft:blast_protection" + PROJECTILE_PROTECTION: Identifier[Enchantment] = "minecraft:projectile_protection" + THORNS: Identifier[Enchantment] = "minecraft:thorns" + RESPIRATION: Identifier[Enchantment] = "minecraft:respiration" + DEPTH_STRIDER: Identifier[Enchantment] = "minecraft:depth_strider" + AQUA_AFFINITY: Identifier[Enchantment] = "minecraft:aqua_affinity" + SHARPNESS: Identifier[Enchantment] = "minecraft:sharpness" + SMITE: Identifier[Enchantment] = "minecraft:smite" + BANE_OF_ARTHROPODS: Identifier[Enchantment] = "minecraft:bane_of_arthropods" + KNOCKBACK: Identifier[Enchantment] = "minecraft:knockback" + FIRE_ASPECT: Identifier[Enchantment] = "minecraft:fire_aspect" + LOOTING: Identifier[Enchantment] = "minecraft:looting" + EFFICIENCY: Identifier[Enchantment] = "minecraft:efficiency" + SILK_TOUCH: Identifier[Enchantment] = "minecraft:silk_touch" + UNBREAKING: Identifier[Enchantment] = "minecraft:unbreaking" + POWER: Identifier[Enchantment] = "minecraft:power" + PUNCH: Identifier[Enchantment] = "minecraft:punch" + FLAME: Identifier[Enchantment] = "minecraft:flame" + INFINITY: Identifier[Enchantment] = "minecraft:infinity" + LUCK_OF_THE_SEA: Identifier[Enchantment] = "minecraft:luck_of_the_sea" + LURE: Identifier[Enchantment] = "minecraft:lure" + FROST_WALKER: Identifier[Enchantment] = "minecraft:frost_walker" + MENDING: Identifier[Enchantment] = "minecraft:mending" + CURSE_OF_BINDING: Identifier[Enchantment] = "minecraft:binding" + CURSE_OF_VANISHING: Identifier[Enchantment] = "minecraft:vanishing" + IMPALING: Identifier[Enchantment] = "minecraft:impaling" + RIPTIDE: Identifier[Enchantment] = "minecraft:riptide" + LOYALTY: Identifier[Enchantment] = "minecraft:loyalty" + CHANNELING: Identifier[Enchantment] = "minecraft:channeling" + MULTISHOT: Identifier[Enchantment] = "minecraft:multishot" + PIERCING: Identifier[Enchantment] = "minecraft:piercing" + QUICK_CHARGE: Identifier[Enchantment] = "minecraft:quick_charge" + SOUL_SPEED: Identifier[Enchantment] = "minecraft:soul_speed" + SWIFT_SNEAK: Identifier[Enchantment] = "minecraft:swift_sneak" + WIND_BURST: Identifier[Enchantment] = "minecraft:wind_burst" + DENSITY: Identifier[Enchantment] = "minecraft:density" + BREACH: Identifier[Enchantment] = "minecraft:breach" + LUNGE: Identifier[Enchantment] = "minecraft:lunge" @property - def id(self) -> str: + def id(self) -> Identifier[Enchantment]: """ Return the identifier for this enchantment. """ @@ -85,7 +86,7 @@ class Enchantment: """ ... @staticmethod - def get(name: str) -> Enchantment: + def get(name: Identifier[Enchantment] | str) -> Enchantment: """ Attempts to get the Enchantment with the given name. """ diff --git a/endstone/inventory/__init__.pyi b/endstone/inventory/__init__.pyi index 6cff9e1764..a0e5fc8af7 100644 --- a/endstone/inventory/__init__.pyi +++ b/endstone/inventory/__init__.pyi @@ -5,6 +5,7 @@ Classes relating to player inventories and item interactions. import enum import typing +from endstone import Identifier from endstone.enchantments import Enchantment from endstone.map import MapView from endstone.nbt import CompoundTag @@ -28,7 +29,7 @@ class ItemStack: """ Represents a stack of items. """ - def __init__(self, type: str, amount: int = 1, data: int = 0) -> None: ... + def __init__(self, type: Identifier[ItemType] | str, amount: int = 1, data: int = 0) -> None: ... @property def type(self) -> ItemType: """ @@ -110,7 +111,7 @@ class ItemType: Represents an item type. """ @property - def id(self) -> str: + def id(self) -> Identifier[ItemType]: """ Return the identifier of this item type. """ @@ -144,12 +145,13 @@ class ItemType: """ ... @staticmethod - def get(name: str) -> ItemType: + def get(name: Identifier[ItemType] | str) -> ItemType: """ Attempts to get the ItemType with the given name. """ ... def __str__(self) -> str: ... + def __hash__(self) -> int: ... def __eq__(self, other: object) -> bool: ... def __ne__(self, other: object) -> bool: ... @@ -210,12 +212,12 @@ class ItemMeta: Checks for the existence of any enchantments. """ ... - def has_enchant(self, id: str) -> bool: + def has_enchant(self, id: Identifier[Enchantment] | str) -> bool: """ Checks for existence of the specified enchantment. """ ... - def get_enchant_level(self, id: str) -> int: + def get_enchant_level(self, id: Identifier[Enchantment] | str) -> int: """ Checks for the level of the specified enchantment. """ @@ -226,12 +228,12 @@ class ItemMeta: Returns a copy the enchantments in this ItemMeta. """ ... - def add_enchant(self, id: str, level: int, force: bool = False) -> bool: + def add_enchant(self, id: Identifier[Enchantment] | str, level: int, force: bool = False) -> bool: """ Adds the specified enchantment to this item meta. """ ... - def remove_enchant(self, id: str) -> bool: + def remove_enchant(self, id: Identifier[Enchantment] | str) -> bool: """ Removes the specified enchantment from this item meta. """ @@ -409,12 +411,12 @@ class CrossbowMeta(ItemMeta): def charged_projectile(self, arg1: ItemStack | None) -> None: ... class ItemFactory: - def get_item_meta(self, type: str) -> ItemMeta: + def get_item_meta(self, type: Identifier[ItemType] | str) -> ItemMeta: """ This creates a new item meta for the item type. """ ... - def is_applicable(self, meta: ItemMeta, type: str) -> bool: + def is_applicable(self, meta: ItemMeta, type: Identifier[ItemType] | str) -> bool: """ This method checks the item meta to confirm that it is applicable (no data lost if applied) to the specified ItemStack """ @@ -424,7 +426,7 @@ class ItemFactory: This method is used to compare two ItemMeta objects. """ ... - def as_meta_for(self, meta: ItemMeta, type: str) -> ItemMeta: + def as_meta_for(self, meta: ItemMeta, type: Identifier[ItemType] | str) -> ItemMeta: """ Returns an appropriate item meta for the specified item type. """ diff --git a/endstone/level/__init__.pyi b/endstone/level/__init__.pyi index d3a8f7e3cd..6a46aa98fd 100644 --- a/endstone/level/__init__.pyi +++ b/endstone/level/__init__.pyi @@ -1,6 +1,7 @@ import typing -from endstone.actor import Actor, Item +from endstone import Identifier +from endstone.actor import Actor, ActorType, Item from endstone.block import Block from endstone.inventory import ItemStack from endstone.util import Vector @@ -34,7 +35,7 @@ class Level: Gets a list of all dimensions within this level. """ ... - def get_dimension(self, id: str) -> Dimension: + def get_dimension(self, id: Identifier[Dimension] | str) -> Dimension: """ Gets the dimension with the given id. """ @@ -51,11 +52,11 @@ class Dimension: Represents a dimension within a Level. """ - OVERWORLD = "minecraft:overworld" - NETHER = "minecraft:nether" - THE_END = "minecraft:the_end" + OVERWORLD: Identifier[Dimension] = "minecraft:overworld" + NETHER: Identifier[Dimension] = "minecraft:nether" + THE_END: Identifier[Dimension] = "minecraft:the_end" @property - def id(self) -> str: + def id(self) -> Identifier[Dimension]: """ Gets the identifier of this dimension """ @@ -112,7 +113,7 @@ class Dimension: Drops an item at the specified Location """ ... - def spawn_actor(self, location: Location, type: str) -> Actor: + def spawn_actor(self, location: Location, type: Identifier[ActorType] | str) -> Actor: """ Creates an actor at the given Location """ diff --git a/endstone/potion/__init__.pyi b/endstone/potion/__init__.pyi index 3ab0587a3a..14c6726781 100644 --- a/endstone/potion/__init__.pyi +++ b/endstone/potion/__init__.pyi @@ -2,6 +2,8 @@ Classes relating to potion effects. """ +from endstone import Identifier + __all__ = ["PotionType"] class PotionType: @@ -9,50 +11,50 @@ class PotionType: All potion types. """ - WATER = "minecraft:potion_type:water" - MUNDANE = "minecraft:potion_type:mundane" - LONG_MUNDANE = "minecraft:potion_type:long_mundane" - THICK = "minecraft:potion_type:thick" - AWKWARD = "minecraft:potion_type:awkward" - NIGHTVISION = "minecraft:potion_type:nightvision" - LONG_NIGHTVISION = "minecraft:potion_type:long_nightvision" - INVISIBILITY = "minecraft:potion_type:invisibility" - LONG_INVISIBILITY = "minecraft:potion_type:long_invisibility" - LEAPING = "minecraft:potion_type:leaping" - LONG_LEAPING = "minecraft:potion_type:long_leaping" - STRONG_LEAPING = "minecraft:potion_type:strong_leaping" - FIRE_RESISTANCE = "minecraft:potion_type:fire_resistance" - LONG_FIRE_RESISTANCE = "minecraft:potion_type:long_fire_resistance" - SWIFTNESS = "minecraft:potion_type:swiftness" - LONG_SWIFTNESS = "minecraft:potion_type:long_swiftness" - STRONG_SWIFTNESS = "minecraft:potion_type:strong_swiftness" - SLOWNESS = "minecraft:potion_type:slowness" - LONG_SLOWNESS = "minecraft:potion_type:long_slowness" - STRONG_SLOWNESS = "minecraft:potion_type:strong_slowness" - WATER_BREATHING = "minecraft:potion_type:water_breathing" - LONG_WATER_BREATHING = "minecraft:potion_type:long_water_breathing" - HEALING = "minecraft:potion_type:healing" - STRONG_HEALING = "minecraft:potion_type:strong_healing" - HARMING = "minecraft:potion_type:harming" - STRONG_HARMING = "minecraft:potion_type:strong_harming" - POISON = "minecraft:potion_type:poison" - LONG_POISON = "minecraft:potion_type:long_poison" - STRONG_POISON = "minecraft:potion_type:strong_poison" - REGENERATION = "minecraft:potion_type:regeneration" - LONG_REGENERATION = "minecraft:potion_type:long_regeneration" - STRONG_REGENERATION = "minecraft:potion_type:strong_regeneration" - STRENGTH = "minecraft:potion_type:strength" - LONG_STRENGTH = "minecraft:potion_type:long_strength" - STRONG_STRENGTH = "minecraft:potion_type:strong_strength" - WEAKNESS = "minecraft:potion_type:weakness" - LONG_WEAKNESS = "minecraft:potion_type:long_weakness" - WITHER = "minecraft:potion_type:wither" - TURTLE_MASTER = "minecraft:potion_type:turtle_master" - LONG_TURTLE_MASTER = "minecraft:potion_type:long_turtle_master" - STRONG_TURTLE_MASTER = "minecraft:potion_type:strong_turtle_master" - SLOW_FALLING = "minecraft:potion_type:slow_falling" - LONG_SLOW_FALLING = "minecraft:potion_type:long_slow_falling" - WIND_CHARGED = "minecraft:potion_type:wind_charged" - WEAVING = "minecraft:potion_type:weaving" - OOZING = "minecraft:potion_type:oozing" - INFESTED = "minecraft:potion_type:infested" + WATER: Identifier[PotionType] = "minecraft:potion_type:water" + MUNDANE: Identifier[PotionType] = "minecraft:potion_type:mundane" + LONG_MUNDANE: Identifier[PotionType] = "minecraft:potion_type:long_mundane" + THICK: Identifier[PotionType] = "minecraft:potion_type:thick" + AWKWARD: Identifier[PotionType] = "minecraft:potion_type:awkward" + NIGHTVISION: Identifier[PotionType] = "minecraft:potion_type:nightvision" + LONG_NIGHTVISION: Identifier[PotionType] = "minecraft:potion_type:long_nightvision" + INVISIBILITY: Identifier[PotionType] = "minecraft:potion_type:invisibility" + LONG_INVISIBILITY: Identifier[PotionType] = "minecraft:potion_type:long_invisibility" + LEAPING: Identifier[PotionType] = "minecraft:potion_type:leaping" + LONG_LEAPING: Identifier[PotionType] = "minecraft:potion_type:long_leaping" + STRONG_LEAPING: Identifier[PotionType] = "minecraft:potion_type:strong_leaping" + FIRE_RESISTANCE: Identifier[PotionType] = "minecraft:potion_type:fire_resistance" + LONG_FIRE_RESISTANCE: Identifier[PotionType] = "minecraft:potion_type:long_fire_resistance" + SWIFTNESS: Identifier[PotionType] = "minecraft:potion_type:swiftness" + LONG_SWIFTNESS: Identifier[PotionType] = "minecraft:potion_type:long_swiftness" + STRONG_SWIFTNESS: Identifier[PotionType] = "minecraft:potion_type:strong_swiftness" + SLOWNESS: Identifier[PotionType] = "minecraft:potion_type:slowness" + LONG_SLOWNESS: Identifier[PotionType] = "minecraft:potion_type:long_slowness" + STRONG_SLOWNESS: Identifier[PotionType] = "minecraft:potion_type:strong_slowness" + WATER_BREATHING: Identifier[PotionType] = "minecraft:potion_type:water_breathing" + LONG_WATER_BREATHING: Identifier[PotionType] = "minecraft:potion_type:long_water_breathing" + HEALING: Identifier[PotionType] = "minecraft:potion_type:healing" + STRONG_HEALING: Identifier[PotionType] = "minecraft:potion_type:strong_healing" + HARMING: Identifier[PotionType] = "minecraft:potion_type:harming" + STRONG_HARMING: Identifier[PotionType] = "minecraft:potion_type:strong_harming" + POISON: Identifier[PotionType] = "minecraft:potion_type:poison" + LONG_POISON: Identifier[PotionType] = "minecraft:potion_type:long_poison" + STRONG_POISON: Identifier[PotionType] = "minecraft:potion_type:strong_poison" + REGENERATION: Identifier[PotionType] = "minecraft:potion_type:regeneration" + LONG_REGENERATION: Identifier[PotionType] = "minecraft:potion_type:long_regeneration" + STRONG_REGENERATION: Identifier[PotionType] = "minecraft:potion_type:strong_regeneration" + STRENGTH: Identifier[PotionType] = "minecraft:potion_type:strength" + LONG_STRENGTH: Identifier[PotionType] = "minecraft:potion_type:long_strength" + STRONG_STRENGTH: Identifier[PotionType] = "minecraft:potion_type:strong_strength" + WEAKNESS: Identifier[PotionType] = "minecraft:potion_type:weakness" + LONG_WEAKNESS: Identifier[PotionType] = "minecraft:potion_type:long_weakness" + WITHER: Identifier[PotionType] = "minecraft:potion_type:wither" + TURTLE_MASTER: Identifier[PotionType] = "minecraft:potion_type:turtle_master" + LONG_TURTLE_MASTER: Identifier[PotionType] = "minecraft:potion_type:long_turtle_master" + STRONG_TURTLE_MASTER: Identifier[PotionType] = "minecraft:potion_type:strong_turtle_master" + SLOW_FALLING: Identifier[PotionType] = "minecraft:potion_type:slow_falling" + LONG_SLOW_FALLING: Identifier[PotionType] = "minecraft:potion_type:long_slow_falling" + WIND_CHARGED: Identifier[PotionType] = "minecraft:potion_type:wind_charged" + WEAVING: Identifier[PotionType] = "minecraft:potion_type:weaving" + OOZING: Identifier[PotionType] = "minecraft:potion_type:oozing" + INFESTED: Identifier[PotionType] = "minecraft:potion_type:infested"