Add support for Minecraft 1.18 and 1.18.1.

This commit is contained in:
joodicator 2021-12-16 19:39:13 +01:00
parent 1ae9a2b48a
commit bf49006553
17 changed files with 322 additions and 75 deletions

View File

@ -1,11 +1,13 @@
language: python
dist: focal
python: 3.8
python: 3.9
matrix:
include:
- python: pypy3
env: TOX_ENV=pypy
before_install:
- python -m pip install --upgrade virtualenv
- python: 3.5
env: TOX_ENV=py35
- python: 3.6
@ -14,13 +16,15 @@ matrix:
env: TOX_ENV=py37
- python: 3.8
env: TOX_ENV=py38
- python: 3.8
- python: 3.9
env: TOX_ENV=py39
- python: 3.9
env: TOX_ENV=flake8
- python: 3.8
- python: 3.9
env: TOX_ENV=pylint-errors
- python: 3.8
- python: 3.9
env: TOX_ENV=pylint-full
- python: 3.8
- python: 3.9
env: TOX_ENV=verify-manifest
before_install:
@ -32,6 +36,6 @@ install:
script:
- tox -e $TOX_ENV
after_script:
- if [ "$TOX_ENV" = "py38" ]; then tox -e coveralls; fi
- if [ "$TOX_ENV" = "py39" ]; then tox -e coveralls; fi
notifications:
email: false

View File

@ -1,7 +1,7 @@
pyCraft
=======
.. image:: https://travis-ci.org/ammaraskar/pyCraft.svg?branch=master
:target: https://travis-ci.org/ammaraskar/pyCraft
.. image:: https://app.travis-ci.com/ammaraskar/pyCraft.svg?branch=master
:target: https://app.travis-ci.com/github/ammaraskar/pyCraft
.. image:: https://readthedocs.org/projects/pycraft/badge/?version=latest
:target: https://pycraft.readthedocs.org/en/latest
.. image:: https://coveralls.io/repos/ammaraskar/pyCraft/badge.svg?branch=master
@ -31,7 +31,9 @@ pyCraft is compatible with the following Minecraft releases:
* 1.13, 1.13.1, 1.13.2
* 1.14, 1.14.1, 1.14.2, 1.14.3, 1.14.4
* 1.15, 1.15.1, 1.15.2
* 1.16, 1.16.1, 1.16.2, 1.16.3, 1.16.4
* 1.16, 1.16.1, 1.16.2, 1.16.3, 1.16.4, 1.16.5
* 1.17, 1.17.1
* 1.18, 1.18.1
In addition, some development snapshots and pre-release versions are supported:
`<minecraft/__init__.py>`_ contains a full list of supported Minecraft versions
@ -55,6 +57,7 @@ pyCraft is compatible with (at least) the following Python implementations:
* Python 3.6
* Python 3.7
* Python 3.8
* Python 3.9
* PyPy
Requirements

View File

@ -460,8 +460,21 @@ KNOWN_MINECRAFT_VERSION_RECORDS = [
Version('20w45a', PRE | 5, True),
Version('20w46a', PRE | 6, True),
Version('20w48a', PRE | 7, True),
Version('20w49a', PRE | 8, False),
Version('20w51a', PRE | 9, False),
Version('1.16.5', 754, False),
Version('21w03a', PRE | 11, False),
Version('21w05a', PRE | 12, False),
Version('21w05b', PRE | 13, False),
Version('21w06a', PRE | 14, False),
Version('21w07a', PRE | 15, False),
Version('1.17-rc2', PRE | 35, False),
Version('1.17', 755, True),
Version('1.17.1', 756, True),
Version('21w44a', PRE | 48, False),
Version('1.18-rc4', PRE | 60, False),
Version('1.18', 757, True),
Version('1.18.1', 757, True),
]
# An OrderedDict mapping the id string of each known Minecraft version to its

View File

@ -53,6 +53,13 @@ class ConnectionContext(object):
later than, or is equal to, 'other_pv', or else False."""
return utility.protocol_earlier_eq(other_pv, self.protocol_version)
def protocol_in_range(self, start_pv, end_pv):
"""Returns True if the protocol version of this context was published
later than, or is equal to, 'start_pv' and was published earlier
than 'end_pv' (analogously to Python's 'range' function)."""
return (utility.protocol_earlier(self.protocol_version, end_pv) and
utility.protocol_earlier_eq(start_pv, self.protocol_version))
class _ConnectionOptions(object):
def __init__(self, address=None, port=None, compression_threshold=-1,

View File

@ -1,3 +1,4 @@
from minecraft import PRE
from minecraft.networking.packets import (
Packet, AbstractKeepAlivePacket, AbstractPluginMessagePacket
)
@ -8,7 +9,10 @@ from minecraft.networking.types import (
PositionAndLook, multi_attribute_alias, attribute_transform,
)
from .combat_event_packet import CombatEventPacket
from .combat_event_packet import (
CombatEventPacket, EnterCombatEventPacket, EndCombatEventPacket,
DeathCombatEventPacket,
)
from .map_packet import MapPacket
from .player_list_item_packet import PlayerListItemPacket
from .player_position_and_look_packet import PlayerPositionAndLookPacket
@ -36,7 +40,6 @@ def get_packets(context):
EntityPositionDeltaPacket,
TimeUpdatePacket,
UpdateHealthPacket,
CombatEventPacket,
ExplosionPacket,
SpawnObjectPacket,
BlockChangePacket,
@ -47,18 +50,33 @@ def get_packets(context):
EntityLookPacket,
ResourcePackSendPacket
}
if context.protocol_earlier_eq(47):
packets |= {
SetCompressionPacket,
}
if context.protocol_earlier(PRE | 15):
packets |= {
CombatEventPacket,
}
else:
packets |= {
EnterCombatEventPacket,
EndCombatEventPacket,
DeathCombatEventPacket,
}
if context.protocol_later_eq(94):
packets |= {
SoundEffectPacket,
}
if context.protocol_later_eq(352):
packets |= {
FacePlayerPacket
}
return packets
@ -264,7 +282,8 @@ class EntityPositionDeltaPacket(Packet):
class TimeUpdatePacket(Packet):
@staticmethod
def get_id(context):
return 0x58 if context.protocol_later_eq(755) else \
return 0x59 if context.protocol_later_eq(PRE | 48) else \
0x58 if context.protocol_later_eq(755) else \
0x4E if context.protocol_later_eq(721) else \
0x4F if context.protocol_later_eq(550) else \
0x4E if context.protocol_later_eq(471) else \
@ -332,7 +351,8 @@ class PluginMessagePacket(AbstractPluginMessagePacket):
class PlayerListHeaderAndFooterPacket(Packet):
@staticmethod
def get_id(context):
return 0x5E if context.protocol_later_eq(755) else \
return 0x5F if context.protocol_later_eq(PRE | 48) else \
0x5E if context.protocol_later_eq(755) else \
0x53 if context.protocol_later_eq(721) else \
0x54 if context.protocol_later_eq(550) else \
0x53 if context.protocol_later_eq(471) else \
@ -374,16 +394,35 @@ class EntityLookPacket(Packet):
{'on_ground': Boolean}
]
class ResourcePackSendPacket(Packet):
@staticmethod
def get_id(context):
return 0x3C if context.protocol_later_eq(755) else \
0x38
return 0x3C if context.protocol_later_eq(PRE | 15) else \
0x39 if context.protocol_later_eq(PRE | 8) else \
0x38 if context.protocol_later_eq(741) else \
0x39 if context.protocol_later_eq(721) else \
0x3A if context.protocol_later_eq(550) else \
0x39 if context.protocol_later_eq(471) else \
0x37 if context.protocol_later_eq(461) else \
0x38 if context.protocol_later_eq(451) else \
0x37 if context.protocol_later_eq(389) else \
0x36 if context.protocol_later_eq(352) else \
0x35 if context.protocol_later_eq(345) else \
0x34 if context.protocol_later_eq(336) else \
0x33 if context.protocol_later_eq(332) else \
0x34 if context.protocol_later_eq(318) else \
0x32 if context.protocol_later_eq(70) else \
0x48
packet_name = "resource pack send"
definition = [
@staticmethod
def get_definition(context):
return [
{"url": String},
{"hash": String},
{"forced": Boolean},
{"forced": Boolean} if context.protocol_later_eq(PRE | 5) else {},
{"forced_message": String}
if context.protocol_later_eq(PRE | 15) else {},
]

View File

@ -1,3 +1,6 @@
from abc import ABCMeta, abstractmethod
from minecraft import PRE
from minecraft.networking.packets import Packet
from minecraft.networking.types import (
@ -5,10 +8,16 @@ from minecraft.networking.types import (
)
# Note: this packet was removed in Minecraft 21w07a (protocol PRE|15)
# and replaced with the separate EnterCombatEvent, EndCombatEvent, and
# DeathCombatEvent packets. These are subclasses of CombatEventPacket, so
# that code written to listen for CombatEventPacket instances should in most
# cases continue to work without modification.
class CombatEventPacket(Packet):
@staticmethod
def get_id(context):
return 0x31 if context.protocol_later_eq(741) else \
@classmethod
def get_id(cls, context):
return cls.deprecated() if context.protocol_later_eq(PRE | 15) else \
0x31 if context.protocol_later_eq(741) else \
0x32 if context.protocol_later_eq(721) else \
0x33 if context.protocol_later_eq(550) else \
0x32 if context.protocol_later_eq(471) else \
@ -28,19 +37,19 @@ class CombatEventPacket(Packet):
fields = 'event',
# The abstract type of the 'event' field of this packet.
class EventType(MutableRecord):
class EventType(MutableRecord, metaclass=ABCMeta):
__slots__ = ()
type_from_id_dict = {}
# Read the fields of the event (not including the ID) from the file.
@abstractmethod
def read(self, file_object):
raise NotImplementedError(
'This abstract method must be overridden in a subclass.')
pass
# Write the fields of the event (not including the ID) to the buffer.
@abstractmethod
def write(self, packet_buffer):
raise NotImplementedError(
'This abstract method must be overridden in a subclass.')
pass
@classmethod
def type_from_id(cls, event_id):
@ -89,10 +98,72 @@ class CombatEventPacket(Packet):
EventType.type_from_id_dict[EntityDeadEvent.id] = EntityDeadEvent
def read(self, file_object):
if self.context and self.context.protocol_later_eq(PRE | 15):
self.deprecated()
event_id = VarInt.read(file_object)
self.event = CombatEventPacket.EventType.type_from_id(event_id)()
self.event.read(file_object)
def write_fields(self, packet_buffer):
if self.context and self.context.protocol_later_eq(PRE | 15):
self.deprecated()
VarInt.send(self.event.id, packet_buffer)
self.event.write(packet_buffer)
@staticmethod
def deprecated():
raise NotImplementedError(
'`CombatEventPacket` was removed in Minecraft snapshot 21w07a '
'(protocol version 2**30 + 15). In this and later versions, one '
'of the subclasses '
+ repr(SpecialisedCombatEventPacket.__subclasses__()) + ' must be '
'used directly for usage like that which generates this message.')
# Contains the behaviour common to all concrete CombatEventPacket subclasses
class SpecialisedCombatEventPacket(CombatEventPacket):
def __init__(self, *args, **kwds):
super(SpecialisedCombatEventPacket, self).__init__(*args, **kwds)
# Prior to Minecraft 21w07a, instances of CombatEventPacket had a
# single 'event' field giving a 'MutableRecord' of one of three types
# corresponding to the type of combat event represented. For backward
# compatibility, we here present a similar interface, giving the packet
# object itself as the 'event', which should work identically in most
# use cases, since it is a virtual subclass of, and has attributes of
# the same names and contents as those of, the previous event records.
self.event = self
# The 'get_id', 'fields', 'read', and 'write_fields' attributes of the
# 'Packet' base class are all overridden in 'CombatEventPacket'. We desire
# the default behaviour of these attributes, so we restore them here:
get_id = Packet.__dict__['get_id']
fields = Packet.__dict__['fields']
read = Packet.__dict__['read']
write_fields = Packet.__dict__['write_fields']
@CombatEventPacket.EnterCombatEvent.register # virtual subclass
class EnterCombatEventPacket(SpecialisedCombatEventPacket):
packet_name = 'enter combat event'
id = 0x34
definition = []
@CombatEventPacket.EndCombatEvent.register # virtual subclass
class EndCombatEventPacket(SpecialisedCombatEventPacket):
packet_name = 'end combat event'
id = 0x33
definition = [
{'duration': VarInt},
{'entity_id': Integer}]
@CombatEventPacket.EntityDeadEvent.register # virtual subclass
class DeathCombatEventPacket(SpecialisedCombatEventPacket):
packet_name = 'death combat event'
id = 0x35
definition = [
{'player_id': VarInt},
{'entity_id': Integer},
{'message': String}]

View File

@ -1,5 +1,6 @@
from minecraft.networking.types import (
Vector, Float, Byte, Integer, PrefixedArray, multi_attribute_alias, Type,
VarInt,
)
from minecraft.networking.packets import Packet
@ -34,15 +35,22 @@ class ExplosionPacket(Packet):
for coord in record:
Byte.send(coord, socket)
definition = [
@staticmethod
def get_definition(context):
return [
{'x': Float},
{'y': Float},
{'z': Float},
{'radius': Float},
{'records': PrefixedArray(Integer, Record)},
{'records': PrefixedArray(VarInt, ExplosionPacket.Record)}
if context.protocol_later_eq(755) else
{'records': PrefixedArray(Integer, ExplosionPacket.Record)},
{'player_motion_x': Float},
{'player_motion_y': Float},
{'player_motion_z': Float}]
{'player_motion_z': Float},
]
# Access the 'x', 'y', 'z' fields as a Vector tuple.
position = multi_attribute_alias(Vector, 'x', 'y', 'z')

View File

@ -92,6 +92,8 @@ class JoinGamePacket(AbstractDimensionPacket):
VarInt if context.protocol_later_eq(749) else UnsignedByte},
{'level_type': String} if context.protocol_earlier(716) else {},
{'render_distance': VarInt} if context.protocol_later_eq(468) else {},
{'simulation_distance': VarInt}
if context.protocol_later_eq(757) else {},
{'reduced_debug_info': Boolean},
{'respawn_screen': Boolean} if context.protocol_later_eq(571) else {},
{'is_debug': Boolean} if context.protocol_later_eq(716) else {},

View File

@ -1,3 +1,4 @@
from minecraft import PRE
from minecraft.networking.packets import Packet
from minecraft.networking.types import (
VarInt, Byte, Boolean, UnsignedByte, VarIntPrefixedByteArray, String,
@ -72,9 +73,9 @@ class MapPacket(Packet):
self.map_id = VarInt.read(file_object)
self.scale = Byte.read(file_object)
if self.context.protocol_later_eq(107):
if self.context.protocol_in_range(107, PRE | 6):
self.is_tracking_position = Boolean.read(file_object)
else:
elif self.context.protocol_earlier(107):
self.is_tracking_position = True
if self.context.protocol_later_eq(452):
@ -82,6 +83,9 @@ class MapPacket(Packet):
else:
self.is_locked = False
if self.context.protocol_later_eq(PRE | 6):
self.is_tracking_position = Boolean.read(file_object)
icon_count = VarInt.read(file_object)
self.icons = []
for i in range(icon_count):
@ -101,10 +105,7 @@ class MapPacket(Packet):
icon = MapPacket.MapIcon(type, direction, (x, z), display_name)
self.icons.append(icon)
try:
self.width = UnsignedByte.read(file_object)
except:
self.width = None
if self.width:
self.height = UnsignedByte.read(file_object)
x = Byte.read(file_object)

View File

@ -1,3 +1,4 @@
from minecraft import PRE
from minecraft.networking.packets import Packet
from minecraft.networking.types import (
VarInt, String, Float, Byte, Type, Integer, Vector, Enum,
@ -9,7 +10,8 @@ __all__ = 'SoundEffectPacket',
class SoundEffectPacket(Packet):
@staticmethod
def get_id(context):
return 0x5C if context.protocol_later_eq(755) else \
return 0x5D if context.protocol_later_eq(PRE | 48) else \
0x5C if context.protocol_later_eq(755) else \
0x51 if context.protocol_later_eq(721) else \
0x52 if context.protocol_later_eq(550) else \
0x51 if context.protocol_later_eq(471) else \

View File

@ -268,11 +268,12 @@ class UseItemPacket(Packet):
Hand = RelativeHand
class ResourcePackStatusPacket(Packet):
@staticmethod
def get_id(context):
return 0x21
packet_name = "resource pask status"
packet_name = "resource pack status"
definition = [
{"result": VarInt}
]

View File

@ -1,8 +1,11 @@
import operator
from minecraft.networking.packets import Packet
from minecraft.networking.types import (
String, Byte, VarInt, Boolean, UnsignedByte, Enum, BitFieldEnum,
AbsoluteHand
)
from minecraft.utility import attribute_transform
class ClientSettingsPacket(Packet):
@ -18,13 +21,40 @@ class ClientSettingsPacket(Packet):
packet_name = 'client settings'
get_definition = staticmethod(lambda context: [
@staticmethod
def get_definition(context):
return [
{'locale': String},
{'view_distance': Byte},
{'chat_mode': VarInt if context.protocol_later(47) else Byte},
{'chat_colors': Boolean},
{'displayed_skin_parts': UnsignedByte},
{'main_hand': VarInt} if context.protocol_later(49) else {}])
{'main_hand': VarInt} if context.protocol_later(49) else {},
{'enable_text_filtering': Boolean}
if context.protocol_later_eq(757) else
{'disable_text_filtering': Boolean}
if context.protocol_later_eq(755) else {},
{'allow_server_listings': Boolean}
if context.protocol_later_eq(755) else {},
]
# Set a default value for 'enable_text_filtering', because most clients
# will probably want this value, and to avoid breaking old code.
enable_text_filtering = False
# To support the possibility of both 'enable_text_filtering' and
# 'disable_text_filtering' fields existing, make 'disable_text_filtering'
# (which is the less likely to be used of the two) into a property that
# stores the negation of its value in the ordinary attribute
# 'enable_text_filtering'.
disable_text_filtering = attribute_transform(
'enable_text_filtering', operator.not_, operator.not_)
# Set a default value for 'allow_server_listings', because most clients
# will probably want this value, and to avoid breaking old code.
allow_server_listings = False
field_enum = classmethod(
lambda cls, field, context: {

View File

@ -33,7 +33,20 @@ def attribute_alias(name):
"""An attribute descriptor that redirects access to a different attribute
with a given name.
"""
return attribute_transform(name, lambda x: x, lambda x: x)
return property(
fget=(lambda self: getattr(self, name)),
fset=(lambda self, value: setattr(self, name, value)),
fdel=(lambda self: delattr(self, name)))
def partial_attribute_alias(name, part):
"""An attribute descriptor that redirects access to a particular named
attribute, 'part', on a different attribute with a given name.
"""
return property(
fget=(lambda self: getattr(getattr(self, name), part)),
fset=(lambda self, value: setattr(getattr(self, name), part, value)),
fdel=(lambda self: delattr(getattr(self, name), part)))
def multi_attribute_alias(container, *arg_names, **kwd_names):

View File

@ -115,7 +115,8 @@ class FakeClientHandler(object):
world_name='minecraft:overworld',
hashed_seed=12345, difficulty=2, max_players=1,
level_type='default', reduced_debug_info=False, render_distance=9,
respawn_screen=False, is_debug=False, is_flat=False)
simulation_distance=9, respawn_screen=False, is_debug=False,
is_flat=False)
if self.server.context.protocol_later_eq(748):
packet.dimension = pynbt.TAG_Compound({

View File

@ -6,7 +6,10 @@ import struct
from zlib import decompress
from random import choice
from minecraft import SUPPORTED_PROTOCOL_VERSIONS, RELEASE_PROTOCOL_VERSIONS
from minecraft.utility import protocol_earlier
from minecraft import (
PRE, SUPPORTED_PROTOCOL_VERSIONS, RELEASE_PROTOCOL_VERSIONS,
)
from minecraft.networking.connection import ConnectionContext
from minecraft.networking.types import (
VarInt, Enum, Vector, PositionAndLook, OriginPoint,
@ -157,19 +160,21 @@ class TestReadWritePackets(unittest.TestCase):
maxDiff = None
def test_explosion_packet(self):
context = ConnectionContext(protocol_version=TEST_VERSIONS[-1])
Record = clientbound.play.ExplosionPacket.Record
packet = clientbound.play.ExplosionPacket(
position=Vector(787, -37, 0), radius=15,
records=[Record(-14, -116, -5), Record(-77, 34, -36),
Record(-35, -127, 95), Record(11, 113, -8)],
player_motion=Vector(4, 5, 0))
player_motion=Vector(4, 5, 0), context=context)
self.assertEqual(
str(packet),
'ExplosionPacket(x=787, y=-37, z=0, radius=15, records=['
'0x%02X ExplosionPacket(x=787, y=-37, z=0, radius=15, records=['
'Record(-14, -116, -5), Record(-77, 34, -36), '
'Record(-35, -127, 95), Record(11, 113, -8)], '
'player_motion_x=4, player_motion_y=5, player_motion_z=0)'
% packet.id
)
self._test_read_write_packet(packet)
@ -181,7 +186,16 @@ class TestReadWritePackets(unittest.TestCase):
str(packet),
'CombatEventPacket(event=EnterCombatEvent())'
)
self._test_read_write_packet(packet)
self._test_read_write_packet(packet, vmax=PRE | 14)
with self.assertRaises(NotImplementedError):
self._test_read_write_packet(packet, vmin=PRE | 15)
specialised_packet = clientbound.play.EnterCombatEventPacket()
self.assertIsInstance(specialised_packet.event, type(packet.event))
for field in specialised_packet.fields:
value = getattr(packet.event, field)
setattr(specialised_packet, field, value)
self.assertEqual(getattr(specialised_packet.event, field), value)
packet = clientbound.play.CombatEventPacket(
event=clientbound.play.CombatEventPacket.EndCombatEvent(
@ -189,7 +203,16 @@ class TestReadWritePackets(unittest.TestCase):
self.assertEqual(str(packet),
'CombatEventPacket(event=EndCombatEvent('
'duration=415, entity_id=91063502))')
self._test_read_write_packet(packet)
self._test_read_write_packet(packet, vmax=PRE | 14)
with self.assertRaises(NotImplementedError):
self._test_read_write_packet(packet, vmin=PRE | 15)
specialised_packet = clientbound.play.EndCombatEventPacket()
self.assertIsInstance(specialised_packet.event, type(packet.event))
for field in specialised_packet.fields:
value = getattr(packet.event, field)
setattr(specialised_packet, field, value)
self.assertEqual(getattr(specialised_packet.event, field), value)
packet = clientbound.play.CombatEventPacket(
event=clientbound.play.CombatEventPacket.EntityDeadEvent(
@ -199,7 +222,16 @@ class TestReadWritePackets(unittest.TestCase):
"CombatEventPacket(event=EntityDeadEvent("
"player_id=178, entity_id=36, message='RIP'))"
)
self._test_read_write_packet(packet)
self._test_read_write_packet(packet, vmax=PRE | 14)
with self.assertRaises(NotImplementedError):
self._test_read_write_packet(packet, vmin=PRE | 15)
specialised_packet = clientbound.play.DeathCombatEventPacket()
self.assertIsInstance(specialised_packet.event, type(packet.event))
for field in specialised_packet.fields:
value = getattr(packet.event, field)
setattr(specialised_packet, field, value)
self.assertEqual(getattr(specialised_packet.event, field), value)
def test_multi_block_change_packet(self):
Record = clientbound.play.MultiBlockChangePacket.Record
@ -348,16 +380,22 @@ class TestReadWritePackets(unittest.TestCase):
)
self._test_read_write_packet(packet, context)
def _test_read_write_packet(self, packet_in, context=None, **kwargs):
def _test_read_write_packet(
self, packet_in, context=None, vmin=None, vmax=None, **kwargs
):
"""
If kwargs are specified, the key will be tested against the
respective delta value. Useful for testing FixedPointNumbers
where there is precision lost in the resulting value.
"""
if context is None:
for protocol_version in TEST_VERSIONS:
logging.debug('protocol_version = %r' % protocol_version)
context = ConnectionContext(protocol_version=protocol_version)
for pv in TEST_VERSIONS:
if vmin is not None and protocol_earlier(pv, vmin):
continue
if vmax is not None and protocol_earlier(vmax, pv):
continue
logging.debug('protocol_version = %r' % pv)
context = ConnectionContext(protocol_version=pv)
self._test_read_write_packet(packet_in, context)
else:
packet_in.context = context

View File

@ -4,6 +4,7 @@ from minecraft.networking.packets import PacketBuffer
from minecraft.networking.packets.clientbound.play import (
PlayerPositionAndLookPacket, PlayerListItemPacket, MapPacket
)
from minecraft.networking.packets import serverbound
from minecraft.networking.connection import ConnectionContext
from tests.test_packets import TEST_VERSIONS
@ -306,3 +307,16 @@ class PlayerListItemTest(unittest.TestCase):
packet.write_fields(packet_buffer)
self.read_and_apply(context, packet_buffer, player_list)
self.assertNotIn(fake_uuid, player_list.players_by_uuid)
class ClientSettingsTest(unittest.TestCase):
def test_enable_disable_text_filtering(self):
packet = serverbound.play.ClientSettingsPacket()
self.assertEqual(packet.enable_text_filtering, False)
self.assertEqual(packet.disable_text_filtering, True)
packet.enable_text_filtering = True
self.assertEqual(packet.enable_text_filtering, True)
self.assertEqual(packet.disable_text_filtering, False)
packet.disable_text_filtering = True
self.assertEqual(packet.enable_text_filtering, False)
self.assertEqual(packet.disable_text_filtering, True)

14
tox.ini
View File

@ -4,7 +4,7 @@
# and then run "tox" from this directory.
[tox]
envlist = py35, py36, py37, py38, pypy, flake8, pylint-errors, pylint-full, verify-manifest
envlist = py35, py36, py37, py38, py39, pypy, flake8, pylint-errors, pylint-full, verify-manifest
[testenv]
commands = nosetests --with-timer
@ -27,7 +27,7 @@ commands =
deps =
coveralls
[testenv:py38]
[testenv:py39]
setenv =
PYCRAFT_RUN_INTERNET_TESTS=1
commands =
@ -41,7 +41,7 @@ deps =
mock
[testenv:flake8]
basepython = python3.8
basepython = python3.9
commands =
flake8 minecraft tests setup.py start.py bin/generate_travis_yml.py
deps =
@ -54,14 +54,14 @@ per-file-ignores =
minecraft/networking/packets/__init__.py:F401
[testenv:pylint-errors]
basepython = python3.8
basepython = python3.9
deps =
{[testenv]deps}
pylint
commands = pylint minecraft -E
[testenv:pylint-full]
basepython = python3.8
basepython = python3.9
deps =
{[testenv]deps}
pylint
@ -69,7 +69,7 @@ commands =
- pylint minecraft --disable=E
[testenv:docs]
basepython = python3.8
basepython = python3.9
deps =
{[testenv:cover]deps}
sphinx
@ -78,7 +78,7 @@ commands =
{toxinidir}/bin/build_docs
[testenv:verify-manifest]
basepython = python3.8
basepython = python3.9
deps =
check-manifest
commands =