Skip to content

Commit

Permalink
Tweak the type classes
Browse files Browse the repository at this point in the history
- Added sphinx build for the types
- Use None as the default nbt value for Slot
- Add even more types and tests
  • Loading branch information
LiteApplication committed Jun 17, 2024
1 parent 96d739c commit 37b95e0
Show file tree
Hide file tree
Showing 42 changed files with 3,105 additions and 582 deletions.
8 changes: 8 additions & 0 deletions docs/api/types/general.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
General Types
======================

Here are documented the general types used throughout the Minecraft protocol.

.. automodule:: mcproto.types
:no-undoc-members:
:exclude-members: NBTag, StringNBT, CompoundNBT, EndNBT
1 change: 1 addition & 0 deletions docs/api/types/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ This folder contains the documentation for various types used in the project.
.. toctree::
:maxdepth: 2

general.rst
nbt.rst
entity_metadata.rst
7 changes: 0 additions & 7 deletions mcproto/packets/handshaking/handshake.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,3 @@ def _deserialize(cls, buf: Buffer, /) -> Self:
server_port=buf.read_value(StructFormat.USHORT),
next_state=buf.read_varint(),
)

@override
def validate(self) -> None:
if not isinstance(self.next_state, NextState):
rev_lookup = {x.value: x for x in NextState.__members__.values()}
if self.next_state not in rev_lookup:
raise ValueError("No such next_state.")
103 changes: 103 additions & 0 deletions mcproto/types/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,104 @@
from __future__ import annotations

from mcproto.types.abc import MCType, Serializable
from mcproto.types.advancement import Advancement, AdvancementProgress, AdvancementDisplay, AdvancementCriterion
from mcproto.types.angle import Angle
from mcproto.types.bitset import Bitset, FixedBitset
from mcproto.types.block_entity import BlockEntity
from mcproto.types.chat import JSONTextComponent, TextComponent
from mcproto.types.entity import EntityMetadata
from mcproto.types.identifier import Identifier
from mcproto.types.map_icon import MapIcon, IconType
from mcproto.types.modifier import ModifierData, ModifierOperation
from mcproto.types.nbt import NBTag, CompoundNBT
from mcproto.types.particle_data import ParticleData
from mcproto.types.quaternion import Quaternion
from mcproto.types.recipe import (
Recipe,
ArmorDyeRecipe,
BannerDuplicateRecipe,
BlastingRecipe,
BookCloningRecipe,
ShulkerBoxColoringRecipe,
CampfireRecipe,
ShapedRecipe,
DecoratedPotRecipe,
FireworkRocketRecipe,
MapCloningRecipe,
MapExtendingRecipe,
RepairItemRecipe,
ShapelessRecipe,
ShieldDecorationRecipe,
SmokingRecipe,
FireworkStarRecipe,
FireworkStarFadeRecipe,
Ingredient,
SmeltingRecipe,
SmithingTransformRecipe,
SmithingTrimRecipe,
TippedArrowRecipe,
StoneCuttingRecipe,
SuspiciousStewRecipe,
)
from mcproto.types.slot import Slot
from mcproto.types.registry_tag import RegistryTag
from mcproto.types.trade import Trade
from mcproto.types.uuid import UUID
from mcproto.types.vec3 import Position, Vec3


__all__ = [
"MCType",
"Serializable",
"Angle",
"Bitset",
"FixedBitset",
"JSONTextComponent",
"TextComponent",
"Identifier",
"NBTag",
"CompoundNBT",
"Quaternion",
"Slot",
"RegistryTag",
"UUID",
"Position",
"Vec3",
"ParticleData",
"BlockEntity",
"MapIcon",
"IconType",
"Trade",
"EntityMetadata",
"Advancement",
"AdvancementProgress",
"AdvancementDisplay",
"AdvancementCriterion",
"ModifierData",
"ModifierOperation",
"Recipe",
"ArmorDyeRecipe",
"BannerDuplicateRecipe",
"BlastingRecipe",
"BookCloningRecipe",
"ShulkerBoxColoringRecipe",
"CampfireRecipe",
"ShapedRecipe",
"DecoratedPotRecipe",
"FireworkRocketRecipe",
"MapCloningRecipe",
"MapExtendingRecipe",
"RepairItemRecipe",
"ShapelessRecipe",
"ShieldDecorationRecipe",
"SmokingRecipe",
"FireworkStarRecipe",
"FireworkStarFadeRecipe",
"Ingredient",
"SmeltingRecipe",
"SmithingTransformRecipe",
"SmithingTrimRecipe",
"TippedArrowRecipe",
"StoneCuttingRecipe",
"SuspiciousStewRecipe",
]
184 changes: 184 additions & 0 deletions mcproto/types/advancement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
from __future__ import annotations

from typing import final
from attrs import define

from typing_extensions import override

from mcproto.buffer import Buffer
from mcproto.protocol import StructFormat
from mcproto.types.identifier import Identifier
from mcproto.types.chat import TextComponent
from mcproto.types.slot import Slot
from mcproto.types.abc import MCType


@final
@define
class Advancement(MCType):
"""Represents an advancement in the game.
https://wiki.vg/Protocol#Update_Advancements
:param parent: The parent advancement.
:type parent: :class:`mcproto.types.identifier.Identifier`, optional
:param display: The display information.
:type display: :class:`AdvancementDisplay`, optional
:param requirements: The criteria for this advancement.
:type requirements: list[list[str]]
:param telemetry: Whether to send telemetry data.
:type telemetry: bool
"""

parent: Identifier | None
display: AdvancementDisplay | None
requirements: list[list[str]]
telemetry: bool

@override
def serialize_to(self, buf: Buffer) -> None:
buf.write_optional(self.parent, lambda x: x.serialize_to(buf))
buf.write_optional(self.display, lambda x: x.serialize_to(buf))
buf.write_varint(len(self.requirements))
for requirement in self.requirements:
buf.write_varint(len(requirement))
for criterion in requirement:
buf.write_utf(criterion)
buf.write_value(StructFormat.BOOL, self.telemetry)

@override
@classmethod
def deserialize(cls, buf: Buffer) -> Advancement:
parent = buf.read_optional(lambda: Identifier.deserialize(buf))
display = buf.read_optional(lambda: AdvancementDisplay.deserialize(buf))
requirements = [[buf.read_utf() for _ in range(buf.read_varint())] for _ in range(buf.read_varint())]
telemetry = buf.read_value(StructFormat.BOOL)
return cls(parent=parent, display=display, requirements=requirements, telemetry=telemetry)


@final
@define
class AdvancementDisplay(MCType):
"""Describes how an advancement should look.
:param title: The title of the advancement.
:type title: :class:`mcproto.types.chat.TextComponent`
:param description: The description of the advancement.
:type description: :class:`mcproto.types.chat.TextComponent`
:param icon: The icon of the advancement.
:type icon: :class:`mcproto.types.slot.Slot`
:param frame: The frame of the advancement (0: task, 1: challenge, 2: goal).
:param background: The background texture of the advancement.
:type background: :class:`mcproto.types.identifier.Identifier`, optional
:param show_toast: Whether to show a toast notification.
:type show_toast: bool
:param hidden: Whether the advancement is hidden.
:type hidden: bool
:param x: The x-coordinate of the advancement (in the GUI).
:type x: float
:param y: The y-coordinate of the advancement (in the GUI).
:type y: float
"""

title: TextComponent
description: TextComponent
icon: Slot
frame: int
background: Identifier | None
show_toast: bool
hidden: bool
x: float
y: float

@override
def serialize_to(self, buf: Buffer) -> None:
self.title.serialize_to(buf)
self.description.serialize_to(buf)
self.icon.serialize_to(buf)
buf.write_varint(self.frame)

flags = (self.background is not None) << 0 | self.show_toast << 1 | self.hidden << 2
buf.write_value(StructFormat.BYTE, flags)
if self.background is not None:
self.background.serialize_to(buf)
buf.write_value(StructFormat.FLOAT, self.x)
buf.write_value(StructFormat.FLOAT, self.y)

@override
@classmethod
def deserialize(cls, buf: Buffer) -> AdvancementDisplay:
title = TextComponent.deserialize(buf)
description = TextComponent.deserialize(buf)
icon = Slot.deserialize(buf)
frame = buf.read_varint()
flags = buf.read_value(StructFormat.BYTE)
background = Identifier.deserialize(buf) if flags & 0x1 else None
show_toast = bool(flags & 0x2)
hidden = bool(flags & 0x4)
x = buf.read_value(StructFormat.FLOAT)
y = buf.read_value(StructFormat.FLOAT)
return cls(
title=title,
description=description,
icon=icon,
frame=frame,
background=background,
show_toast=show_toast,
hidden=hidden,
x=x,
y=y,
)


@final
@define
class AdvancementProgress(MCType):
"""Represents the progress of an advancement.
:param criteria: The criteria for this advancement.
:type criteria: dict[:class:`mcproto.types.identifier.Identifier`, :class:`AdvancementCriterion`]
"""

criteria: dict[Identifier, AdvancementCriterion]

@override
def serialize_to(self, buf: Buffer) -> None:
buf.write_varint(len(self.criteria))
for identifier, criterion in self.criteria.items():
identifier.serialize_to(buf)
criterion.serialize_to(buf)

@override
@classmethod
def deserialize(cls, buf: Buffer) -> AdvancementProgress:
criteria = {
Identifier.deserialize(buf): AdvancementCriterion.deserialize(buf) for _ in range(buf.read_varint())
}
return cls(criteria=criteria)


@final
@define
class AdvancementCriterion(MCType):
"""Represents a criterion for an advancement.
:param date: The date the criterion was achieved. (As returned by Date.getTime() in Java), None if not achieved.
:type date: int, optional
"""

date: int | None

@override
def serialize_to(self, buf: Buffer) -> None:
buf.write_optional(self.date, lambda x: buf.write_value(StructFormat.LONGLONG, x))

@override
@classmethod
def deserialize(cls, buf: Buffer) -> AdvancementCriterion:
date = buf.read_optional(lambda: buf.read_value(StructFormat.LONGLONG))
return cls(date=date)

@property
def achieved(self) -> bool:
"""Whether the criterion was achieved."""
return self.date is not None
Loading

0 comments on commit 37b95e0

Please sign in to comment.