Various improvements to utility types:

- Add operations for Vector.
- Move some tests into test_utility_types.py.
- Add tests for PositionAndLook and Vector.
This commit is contained in:
joo 2018-06-21 05:13:48 +01:00
parent 61598eba75
commit 4b6feda1cb
4 changed files with 116 additions and 44 deletions

View File

@ -259,6 +259,7 @@ class UUID(Type):
class Position(Type, Vector):
"""3D position vectors with a specific, compact network representation."""
__slots__ = ()
@staticmethod

View File

@ -1,6 +1,7 @@
"""Minecraft data types that are used by packets, but don't have a specific
network representation.
"""
from __future__ import division
from collections import namedtuple
@ -9,11 +10,43 @@ __all__ = (
)
"""An immutable type usually used to represent 3D spatial coordinates.
NOTE: subclasses of 'Vector' should have '__slots__ = ()' to avoid the
creation of a '__dict__' attribute, which would waste space.
"""
Vector = namedtuple('Vector', ('x', 'y', 'z'))
class Vector(namedtuple('BaseVector', ('x', 'y', 'z'))):
"""An immutable type usually used to represent 3D spatial coordinates,
supporting elementwise vector addition, subtraction, and negation; and
scalar multiplication and (right) division.
NOTE: subclasses of 'Vector' should have '__slots__ = ()' to avoid the
creation of a '__dict__' attribute, which would waste space.
"""
__slots__ = ()
def __add__(self, other):
return NotImplemented if not isinstance(other, Vector) else \
type(self)(self.x + other.x, self.y + other.y, self.z + other.z)
def __sub__(self, other):
return NotImplemented if not isinstance(other, Vector) else \
type(self)(self.x - other.x, self.y - other.y, self.z - other.z)
def __neg__(self):
return type(self)(-self.x, -self.y, -self.z)
def __mul__(self, other):
return type(self)(self.x*other, self.y*other, self.z*other)
def __rmul__(self, other):
return type(self)(other*self.x, other*self.y, other*self.z)
def __truediv__(self, other):
return type(self)(self.x/other, self.y/other, self.z/other)
def __floordiv__(self, other):
return type(self)(self.x//other, self.y//other, self.z//other)
__div__ = __floordiv__
def __repr__(self):
return '%s(%r, %r, %r)' % (type(self).__name__, self.x, self.y, self.z)
class MutableRecord(object):
@ -34,8 +67,12 @@ class MutableRecord(object):
return type(self) is type(other) and \
all(getattr(self, a) == getattr(other, a) for a in self.__slots__)
def __neq__(self, other):
return not (self == other)
def __hash__(self):
return hash(getattr(self, a) for a in self.__slots__)
values = tuple(getattr(self, a, None) for a in self.__slots__)
return hash((type(self), values))
class PositionAndLook(MutableRecord):

View File

@ -7,7 +7,7 @@ from random import choice
from minecraft import SUPPORTED_PROTOCOL_VERSIONS
from minecraft.networking.connection import ConnectionContext
from minecraft.networking.types import (
VarInt, Enum, BitFieldEnum, Vector, PositionAndLook
VarInt, Enum, Vector, PositionAndLook
)
from minecraft.networking.packets import (
Packet, PacketBuffer, PacketListener, KeepAlivePacket, serverbound,
@ -142,43 +142,6 @@ class PacketEnumTest(unittest.TestCase):
'0x00 ExamplePacket(alpha=ZERO, beta=0, gamma=0)')
class EnumTest(unittest.TestCase):
def test_enum(self):
class Example(Enum):
ONE = 1
TWO = 2
THREE = 3
self.assertEqual(
list(map(Example.name_from_value, range(5))),
[None, 'ONE', 'TWO', 'THREE', None])
class BitFieldEnumTest(unittest.TestCase):
def test_name_from_value(self):
class Example1(BitFieldEnum):
ONE = 1
TWO = 2
FOUR = 4
ALL = 7
NONE = 0
self.assertEqual(
list(map(Example1.name_from_value, range(9))),
['NONE', 'ONE', 'TWO', 'ONE|TWO', 'FOUR',
'ONE|FOUR', 'TWO|FOUR', 'ALL', None])
class Example2(BitFieldEnum):
ONE = 1
TWO = 2
FOUR = 4
self.assertEqual(
list(map(Example2.name_from_value, range(9))),
['0', 'ONE', 'TWO', 'ONE|TWO', 'FOUR',
'ONE|FOUR', 'TWO|FOUR', 'ONE|TWO|FOUR', None])
class TestReadWritePackets(unittest.TestCase):
maxDiff = None

View File

@ -0,0 +1,71 @@
import unittest
from minecraft.networking.types import (
Enum, BitFieldEnum, Vector, Position, PositionAndLook
)
class EnumTest(unittest.TestCase):
def test_enum(self):
class Example(Enum):
ONE = 1
TWO = 2
THREE = 3
self.assertEqual(
list(map(Example.name_from_value, range(5))),
[None, 'ONE', 'TWO', 'THREE', None])
class BitFieldEnumTest(unittest.TestCase):
def test_name_from_value(self):
class Example1(BitFieldEnum):
ONE = 1
TWO = 2
FOUR = 4
ALL = 7
NONE = 0
self.assertEqual(
list(map(Example1.name_from_value, range(9))),
['NONE', 'ONE', 'TWO', 'ONE|TWO', 'FOUR',
'ONE|FOUR', 'TWO|FOUR', 'ALL', None])
class Example2(BitFieldEnum):
ONE = 1
TWO = 2
FOUR = 4
self.assertEqual(
list(map(Example2.name_from_value, range(9))),
['0', 'ONE', 'TWO', 'ONE|TWO', 'FOUR',
'ONE|FOUR', 'TWO|FOUR', 'ONE|TWO|FOUR', None])
class VectorTest(unittest.TestCase):
def test_operators(self):
self.assertEqual(Vector(1, -2, 0) + Vector(0, 1, 2), Vector(1, -1, 2))
self.assertEqual(Vector(1, -2, 0) - Vector(0, 1, 2), Vector(1, -3, -2))
self.assertEqual(-Vector(1, -2, 0), Vector(-1, 2, 0))
self.assertEqual(Vector(1, -2, 0) * 2, Vector(2, -4, 0))
self.assertEqual(2 * Vector(1, -2, 0), Vector(2, -4, 0))
self.assertEqual(Vector(1, -2, 0) / 2, Vector(1/2, -2/2, 0/2))
self.assertEqual(Vector(1, -2, 0) // 2, Vector(0, -1, 0))
def test_repr(self):
self.assertEqual(str(Vector(1, 2, 3)), 'Vector(1, 2, 3)')
self.assertEqual(str(Position(1, 2, 3)), 'Position(1, 2, 3)')
class PositionAndLookTest(unittest.TestCase):
""" This also tests the MutableRecord base type. """
def test_properties(self):
pos_look_1 = PositionAndLook(position=(1, 2, 3), look=(4, 5))
pos_look_2 = PositionAndLook(x=1, y=2, z=3, yaw=4, pitch=5)
string_repr = 'PositionAndLook(x=1, y=2, z=3, yaw=4, pitch=5)'
self.assertEqual(pos_look_1, pos_look_2)
self.assertEqual(pos_look_1.position, pos_look_1.position)
self.assertEqual(pos_look_1.look, pos_look_2.look)
self.assertEqual(hash(pos_look_1), hash(pos_look_2))
self.assertEqual(str(pos_look_1), string_repr)