mirror of
https://github.com/ammaraskar/pyCraft.git
synced 2024-11-21 17:56:30 +01:00
Add support for Minecraft 1.18 and 1.18.1.
This commit is contained in:
parent
1ae9a2b48a
commit
bf49006553
16
.travis.yml
16
.travis.yml
@ -1,11 +1,13 @@
|
|||||||
language: python
|
language: python
|
||||||
dist: focal
|
dist: focal
|
||||||
python: 3.8
|
python: 3.9
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- python: pypy3
|
- python: pypy3
|
||||||
env: TOX_ENV=pypy
|
env: TOX_ENV=pypy
|
||||||
|
before_install:
|
||||||
|
- python -m pip install --upgrade virtualenv
|
||||||
- python: 3.5
|
- python: 3.5
|
||||||
env: TOX_ENV=py35
|
env: TOX_ENV=py35
|
||||||
- python: 3.6
|
- python: 3.6
|
||||||
@ -14,13 +16,15 @@ matrix:
|
|||||||
env: TOX_ENV=py37
|
env: TOX_ENV=py37
|
||||||
- python: 3.8
|
- python: 3.8
|
||||||
env: TOX_ENV=py38
|
env: TOX_ENV=py38
|
||||||
- python: 3.8
|
- python: 3.9
|
||||||
|
env: TOX_ENV=py39
|
||||||
|
- python: 3.9
|
||||||
env: TOX_ENV=flake8
|
env: TOX_ENV=flake8
|
||||||
- python: 3.8
|
- python: 3.9
|
||||||
env: TOX_ENV=pylint-errors
|
env: TOX_ENV=pylint-errors
|
||||||
- python: 3.8
|
- python: 3.9
|
||||||
env: TOX_ENV=pylint-full
|
env: TOX_ENV=pylint-full
|
||||||
- python: 3.8
|
- python: 3.9
|
||||||
env: TOX_ENV=verify-manifest
|
env: TOX_ENV=verify-manifest
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
@ -32,6 +36,6 @@ install:
|
|||||||
script:
|
script:
|
||||||
- tox -e $TOX_ENV
|
- tox -e $TOX_ENV
|
||||||
after_script:
|
after_script:
|
||||||
- if [ "$TOX_ENV" = "py38" ]; then tox -e coveralls; fi
|
- if [ "$TOX_ENV" = "py39" ]; then tox -e coveralls; fi
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
pyCraft
|
pyCraft
|
||||||
=======
|
=======
|
||||||
.. image:: https://travis-ci.org/ammaraskar/pyCraft.svg?branch=master
|
.. image:: https://app.travis-ci.com/ammaraskar/pyCraft.svg?branch=master
|
||||||
:target: https://travis-ci.org/ammaraskar/pyCraft
|
:target: https://app.travis-ci.com/github/ammaraskar/pyCraft
|
||||||
.. image:: https://readthedocs.org/projects/pycraft/badge/?version=latest
|
.. image:: https://readthedocs.org/projects/pycraft/badge/?version=latest
|
||||||
:target: https://pycraft.readthedocs.org/en/latest
|
:target: https://pycraft.readthedocs.org/en/latest
|
||||||
.. image:: https://coveralls.io/repos/ammaraskar/pyCraft/badge.svg?branch=master
|
.. 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.13, 1.13.1, 1.13.2
|
||||||
* 1.14, 1.14.1, 1.14.2, 1.14.3, 1.14.4
|
* 1.14, 1.14.1, 1.14.2, 1.14.3, 1.14.4
|
||||||
* 1.15, 1.15.1, 1.15.2
|
* 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:
|
In addition, some development snapshots and pre-release versions are supported:
|
||||||
`<minecraft/__init__.py>`_ contains a full list of supported Minecraft versions
|
`<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.6
|
||||||
* Python 3.7
|
* Python 3.7
|
||||||
* Python 3.8
|
* Python 3.8
|
||||||
|
* Python 3.9
|
||||||
* PyPy
|
* PyPy
|
||||||
|
|
||||||
Requirements
|
Requirements
|
||||||
|
@ -460,8 +460,21 @@ KNOWN_MINECRAFT_VERSION_RECORDS = [
|
|||||||
Version('20w45a', PRE | 5, True),
|
Version('20w45a', PRE | 5, True),
|
||||||
Version('20w46a', PRE | 6, True),
|
Version('20w46a', PRE | 6, True),
|
||||||
Version('20w48a', PRE | 7, 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', 755, True),
|
||||||
Version('1.17.1', 756, 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
|
# An OrderedDict mapping the id string of each known Minecraft version to its
|
||||||
|
@ -53,6 +53,13 @@ class ConnectionContext(object):
|
|||||||
later than, or is equal to, 'other_pv', or else False."""
|
later than, or is equal to, 'other_pv', or else False."""
|
||||||
return utility.protocol_earlier_eq(other_pv, self.protocol_version)
|
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):
|
class _ConnectionOptions(object):
|
||||||
def __init__(self, address=None, port=None, compression_threshold=-1,
|
def __init__(self, address=None, port=None, compression_threshold=-1,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from minecraft import PRE
|
||||||
from minecraft.networking.packets import (
|
from minecraft.networking.packets import (
|
||||||
Packet, AbstractKeepAlivePacket, AbstractPluginMessagePacket
|
Packet, AbstractKeepAlivePacket, AbstractPluginMessagePacket
|
||||||
)
|
)
|
||||||
@ -8,7 +9,10 @@ from minecraft.networking.types import (
|
|||||||
PositionAndLook, multi_attribute_alias, attribute_transform,
|
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 .map_packet import MapPacket
|
||||||
from .player_list_item_packet import PlayerListItemPacket
|
from .player_list_item_packet import PlayerListItemPacket
|
||||||
from .player_position_and_look_packet import PlayerPositionAndLookPacket
|
from .player_position_and_look_packet import PlayerPositionAndLookPacket
|
||||||
@ -36,7 +40,6 @@ def get_packets(context):
|
|||||||
EntityPositionDeltaPacket,
|
EntityPositionDeltaPacket,
|
||||||
TimeUpdatePacket,
|
TimeUpdatePacket,
|
||||||
UpdateHealthPacket,
|
UpdateHealthPacket,
|
||||||
CombatEventPacket,
|
|
||||||
ExplosionPacket,
|
ExplosionPacket,
|
||||||
SpawnObjectPacket,
|
SpawnObjectPacket,
|
||||||
BlockChangePacket,
|
BlockChangePacket,
|
||||||
@ -47,18 +50,33 @@ def get_packets(context):
|
|||||||
EntityLookPacket,
|
EntityLookPacket,
|
||||||
ResourcePackSendPacket
|
ResourcePackSendPacket
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.protocol_earlier_eq(47):
|
if context.protocol_earlier_eq(47):
|
||||||
packets |= {
|
packets |= {
|
||||||
SetCompressionPacket,
|
SetCompressionPacket,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if context.protocol_earlier(PRE | 15):
|
||||||
|
packets |= {
|
||||||
|
CombatEventPacket,
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
packets |= {
|
||||||
|
EnterCombatEventPacket,
|
||||||
|
EndCombatEventPacket,
|
||||||
|
DeathCombatEventPacket,
|
||||||
|
}
|
||||||
|
|
||||||
if context.protocol_later_eq(94):
|
if context.protocol_later_eq(94):
|
||||||
packets |= {
|
packets |= {
|
||||||
SoundEffectPacket,
|
SoundEffectPacket,
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.protocol_later_eq(352):
|
if context.protocol_later_eq(352):
|
||||||
packets |= {
|
packets |= {
|
||||||
FacePlayerPacket
|
FacePlayerPacket
|
||||||
}
|
}
|
||||||
|
|
||||||
return packets
|
return packets
|
||||||
|
|
||||||
|
|
||||||
@ -264,7 +282,8 @@ class EntityPositionDeltaPacket(Packet):
|
|||||||
class TimeUpdatePacket(Packet):
|
class TimeUpdatePacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
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 \
|
0x4E if context.protocol_later_eq(721) else \
|
||||||
0x4F if context.protocol_later_eq(550) else \
|
0x4F if context.protocol_later_eq(550) else \
|
||||||
0x4E if context.protocol_later_eq(471) else \
|
0x4E if context.protocol_later_eq(471) else \
|
||||||
@ -332,7 +351,8 @@ class PluginMessagePacket(AbstractPluginMessagePacket):
|
|||||||
class PlayerListHeaderAndFooterPacket(Packet):
|
class PlayerListHeaderAndFooterPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
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 \
|
0x53 if context.protocol_later_eq(721) else \
|
||||||
0x54 if context.protocol_later_eq(550) else \
|
0x54 if context.protocol_later_eq(550) else \
|
||||||
0x53 if context.protocol_later_eq(471) else \
|
0x53 if context.protocol_later_eq(471) else \
|
||||||
@ -374,16 +394,35 @@ class EntityLookPacket(Packet):
|
|||||||
{'on_ground': Boolean}
|
{'on_ground': Boolean}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ResourcePackSendPacket(Packet):
|
class ResourcePackSendPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x3C if context.protocol_later_eq(755) else \
|
return 0x3C if context.protocol_later_eq(PRE | 15) else \
|
||||||
0x38
|
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"
|
packet_name = "resource pack send"
|
||||||
definition = [
|
|
||||||
|
@staticmethod
|
||||||
|
def get_definition(context):
|
||||||
|
return [
|
||||||
{"url": String},
|
{"url": String},
|
||||||
{"hash": String},
|
{"hash": String},
|
||||||
{"forced": Boolean},
|
{"forced": Boolean} if context.protocol_later_eq(PRE | 5) else {},
|
||||||
{"forced_message": String}
|
{"forced_message": String}
|
||||||
|
if context.protocol_later_eq(PRE | 15) else {},
|
||||||
]
|
]
|
@ -1,3 +1,6 @@
|
|||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
|
from minecraft import PRE
|
||||||
from minecraft.networking.packets import Packet
|
from minecraft.networking.packets import Packet
|
||||||
|
|
||||||
from minecraft.networking.types import (
|
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):
|
class CombatEventPacket(Packet):
|
||||||
@staticmethod
|
@classmethod
|
||||||
def get_id(context):
|
def get_id(cls, context):
|
||||||
return 0x31 if context.protocol_later_eq(741) else \
|
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 \
|
0x32 if context.protocol_later_eq(721) else \
|
||||||
0x33 if context.protocol_later_eq(550) else \
|
0x33 if context.protocol_later_eq(550) else \
|
||||||
0x32 if context.protocol_later_eq(471) else \
|
0x32 if context.protocol_later_eq(471) else \
|
||||||
@ -28,19 +37,19 @@ class CombatEventPacket(Packet):
|
|||||||
fields = 'event',
|
fields = 'event',
|
||||||
|
|
||||||
# The abstract type of the 'event' field of this packet.
|
# The abstract type of the 'event' field of this packet.
|
||||||
class EventType(MutableRecord):
|
class EventType(MutableRecord, metaclass=ABCMeta):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
type_from_id_dict = {}
|
type_from_id_dict = {}
|
||||||
|
|
||||||
# Read the fields of the event (not including the ID) from the file.
|
# Read the fields of the event (not including the ID) from the file.
|
||||||
|
@abstractmethod
|
||||||
def read(self, file_object):
|
def read(self, file_object):
|
||||||
raise NotImplementedError(
|
pass
|
||||||
'This abstract method must be overridden in a subclass.')
|
|
||||||
|
|
||||||
# Write the fields of the event (not including the ID) to the buffer.
|
# Write the fields of the event (not including the ID) to the buffer.
|
||||||
|
@abstractmethod
|
||||||
def write(self, packet_buffer):
|
def write(self, packet_buffer):
|
||||||
raise NotImplementedError(
|
pass
|
||||||
'This abstract method must be overridden in a subclass.')
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def type_from_id(cls, event_id):
|
def type_from_id(cls, event_id):
|
||||||
@ -89,10 +98,72 @@ class CombatEventPacket(Packet):
|
|||||||
EventType.type_from_id_dict[EntityDeadEvent.id] = EntityDeadEvent
|
EventType.type_from_id_dict[EntityDeadEvent.id] = EntityDeadEvent
|
||||||
|
|
||||||
def read(self, file_object):
|
def read(self, file_object):
|
||||||
|
if self.context and self.context.protocol_later_eq(PRE | 15):
|
||||||
|
self.deprecated()
|
||||||
event_id = VarInt.read(file_object)
|
event_id = VarInt.read(file_object)
|
||||||
self.event = CombatEventPacket.EventType.type_from_id(event_id)()
|
self.event = CombatEventPacket.EventType.type_from_id(event_id)()
|
||||||
self.event.read(file_object)
|
self.event.read(file_object)
|
||||||
|
|
||||||
def write_fields(self, packet_buffer):
|
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)
|
VarInt.send(self.event.id, packet_buffer)
|
||||||
self.event.write(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}]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
Vector, Float, Byte, Integer, PrefixedArray, multi_attribute_alias, Type,
|
Vector, Float, Byte, Integer, PrefixedArray, multi_attribute_alias, Type,
|
||||||
|
VarInt,
|
||||||
)
|
)
|
||||||
from minecraft.networking.packets import Packet
|
from minecraft.networking.packets import Packet
|
||||||
|
|
||||||
@ -34,15 +35,22 @@ class ExplosionPacket(Packet):
|
|||||||
for coord in record:
|
for coord in record:
|
||||||
Byte.send(coord, socket)
|
Byte.send(coord, socket)
|
||||||
|
|
||||||
definition = [
|
@staticmethod
|
||||||
|
def get_definition(context):
|
||||||
|
return [
|
||||||
{'x': Float},
|
{'x': Float},
|
||||||
{'y': Float},
|
{'y': Float},
|
||||||
{'z': Float},
|
{'z': Float},
|
||||||
{'radius': 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_x': Float},
|
||||||
{'player_motion_y': Float},
|
{'player_motion_y': Float},
|
||||||
{'player_motion_z': Float}]
|
{'player_motion_z': Float},
|
||||||
|
]
|
||||||
|
|
||||||
# Access the 'x', 'y', 'z' fields as a Vector tuple.
|
# Access the 'x', 'y', 'z' fields as a Vector tuple.
|
||||||
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
||||||
|
@ -92,6 +92,8 @@ class JoinGamePacket(AbstractDimensionPacket):
|
|||||||
VarInt if context.protocol_later_eq(749) else UnsignedByte},
|
VarInt if context.protocol_later_eq(749) else UnsignedByte},
|
||||||
{'level_type': String} if context.protocol_earlier(716) else {},
|
{'level_type': String} if context.protocol_earlier(716) else {},
|
||||||
{'render_distance': VarInt} if context.protocol_later_eq(468) 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},
|
{'reduced_debug_info': Boolean},
|
||||||
{'respawn_screen': Boolean} if context.protocol_later_eq(571) else {},
|
{'respawn_screen': Boolean} if context.protocol_later_eq(571) else {},
|
||||||
{'is_debug': Boolean} if context.protocol_later_eq(716) else {},
|
{'is_debug': Boolean} if context.protocol_later_eq(716) else {},
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from minecraft import PRE
|
||||||
from minecraft.networking.packets import Packet
|
from minecraft.networking.packets import Packet
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
VarInt, Byte, Boolean, UnsignedByte, VarIntPrefixedByteArray, String,
|
VarInt, Byte, Boolean, UnsignedByte, VarIntPrefixedByteArray, String,
|
||||||
@ -72,9 +73,9 @@ class MapPacket(Packet):
|
|||||||
self.map_id = VarInt.read(file_object)
|
self.map_id = VarInt.read(file_object)
|
||||||
self.scale = Byte.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)
|
self.is_tracking_position = Boolean.read(file_object)
|
||||||
else:
|
elif self.context.protocol_earlier(107):
|
||||||
self.is_tracking_position = True
|
self.is_tracking_position = True
|
||||||
|
|
||||||
if self.context.protocol_later_eq(452):
|
if self.context.protocol_later_eq(452):
|
||||||
@ -82,6 +83,9 @@ class MapPacket(Packet):
|
|||||||
else:
|
else:
|
||||||
self.is_locked = False
|
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)
|
icon_count = VarInt.read(file_object)
|
||||||
self.icons = []
|
self.icons = []
|
||||||
for i in range(icon_count):
|
for i in range(icon_count):
|
||||||
@ -101,10 +105,7 @@ class MapPacket(Packet):
|
|||||||
icon = MapPacket.MapIcon(type, direction, (x, z), display_name)
|
icon = MapPacket.MapIcon(type, direction, (x, z), display_name)
|
||||||
self.icons.append(icon)
|
self.icons.append(icon)
|
||||||
|
|
||||||
try:
|
|
||||||
self.width = UnsignedByte.read(file_object)
|
self.width = UnsignedByte.read(file_object)
|
||||||
except:
|
|
||||||
self.width = None
|
|
||||||
if self.width:
|
if self.width:
|
||||||
self.height = UnsignedByte.read(file_object)
|
self.height = UnsignedByte.read(file_object)
|
||||||
x = Byte.read(file_object)
|
x = Byte.read(file_object)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
from minecraft import PRE
|
||||||
from minecraft.networking.packets import Packet
|
from minecraft.networking.packets import Packet
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
VarInt, String, Float, Byte, Type, Integer, Vector, Enum,
|
VarInt, String, Float, Byte, Type, Integer, Vector, Enum,
|
||||||
@ -9,7 +10,8 @@ __all__ = 'SoundEffectPacket',
|
|||||||
class SoundEffectPacket(Packet):
|
class SoundEffectPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
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 \
|
0x51 if context.protocol_later_eq(721) else \
|
||||||
0x52 if context.protocol_later_eq(550) else \
|
0x52 if context.protocol_later_eq(550) else \
|
||||||
0x51 if context.protocol_later_eq(471) else \
|
0x51 if context.protocol_later_eq(471) else \
|
||||||
|
@ -268,11 +268,12 @@ class UseItemPacket(Packet):
|
|||||||
|
|
||||||
Hand = RelativeHand
|
Hand = RelativeHand
|
||||||
|
|
||||||
|
|
||||||
class ResourcePackStatusPacket(Packet):
|
class ResourcePackStatusPacket(Packet):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_id(context):
|
def get_id(context):
|
||||||
return 0x21
|
return 0x21
|
||||||
packet_name = "resource pask status"
|
packet_name = "resource pack status"
|
||||||
definition = [
|
definition = [
|
||||||
{"result": VarInt}
|
{"result": VarInt}
|
||||||
]
|
]
|
@ -1,8 +1,11 @@
|
|||||||
|
import operator
|
||||||
|
|
||||||
from minecraft.networking.packets import Packet
|
from minecraft.networking.packets import Packet
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
String, Byte, VarInt, Boolean, UnsignedByte, Enum, BitFieldEnum,
|
String, Byte, VarInt, Boolean, UnsignedByte, Enum, BitFieldEnum,
|
||||||
AbsoluteHand
|
AbsoluteHand
|
||||||
)
|
)
|
||||||
|
from minecraft.utility import attribute_transform
|
||||||
|
|
||||||
|
|
||||||
class ClientSettingsPacket(Packet):
|
class ClientSettingsPacket(Packet):
|
||||||
@ -18,13 +21,40 @@ class ClientSettingsPacket(Packet):
|
|||||||
|
|
||||||
packet_name = 'client settings'
|
packet_name = 'client settings'
|
||||||
|
|
||||||
get_definition = staticmethod(lambda context: [
|
@staticmethod
|
||||||
|
def get_definition(context):
|
||||||
|
return [
|
||||||
{'locale': String},
|
{'locale': String},
|
||||||
{'view_distance': Byte},
|
{'view_distance': Byte},
|
||||||
{'chat_mode': VarInt if context.protocol_later(47) else Byte},
|
{'chat_mode': VarInt if context.protocol_later(47) else Byte},
|
||||||
{'chat_colors': Boolean},
|
{'chat_colors': Boolean},
|
||||||
{'displayed_skin_parts': UnsignedByte},
|
{'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(
|
field_enum = classmethod(
|
||||||
lambda cls, field, context: {
|
lambda cls, field, context: {
|
||||||
|
@ -33,7 +33,20 @@ def attribute_alias(name):
|
|||||||
"""An attribute descriptor that redirects access to a different attribute
|
"""An attribute descriptor that redirects access to a different attribute
|
||||||
with a given name.
|
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):
|
def multi_attribute_alias(container, *arg_names, **kwd_names):
|
||||||
|
@ -115,7 +115,8 @@ class FakeClientHandler(object):
|
|||||||
world_name='minecraft:overworld',
|
world_name='minecraft:overworld',
|
||||||
hashed_seed=12345, difficulty=2, max_players=1,
|
hashed_seed=12345, difficulty=2, max_players=1,
|
||||||
level_type='default', reduced_debug_info=False, render_distance=9,
|
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):
|
if self.server.context.protocol_later_eq(748):
|
||||||
packet.dimension = pynbt.TAG_Compound({
|
packet.dimension = pynbt.TAG_Compound({
|
||||||
|
@ -6,7 +6,10 @@ import struct
|
|||||||
from zlib import decompress
|
from zlib import decompress
|
||||||
from random import choice
|
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.connection import ConnectionContext
|
||||||
from minecraft.networking.types import (
|
from minecraft.networking.types import (
|
||||||
VarInt, Enum, Vector, PositionAndLook, OriginPoint,
|
VarInt, Enum, Vector, PositionAndLook, OriginPoint,
|
||||||
@ -157,19 +160,21 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
maxDiff = None
|
maxDiff = None
|
||||||
|
|
||||||
def test_explosion_packet(self):
|
def test_explosion_packet(self):
|
||||||
|
context = ConnectionContext(protocol_version=TEST_VERSIONS[-1])
|
||||||
Record = clientbound.play.ExplosionPacket.Record
|
Record = clientbound.play.ExplosionPacket.Record
|
||||||
packet = clientbound.play.ExplosionPacket(
|
packet = clientbound.play.ExplosionPacket(
|
||||||
position=Vector(787, -37, 0), radius=15,
|
position=Vector(787, -37, 0), radius=15,
|
||||||
records=[Record(-14, -116, -5), Record(-77, 34, -36),
|
records=[Record(-14, -116, -5), Record(-77, 34, -36),
|
||||||
Record(-35, -127, 95), Record(11, 113, -8)],
|
Record(-35, -127, 95), Record(11, 113, -8)],
|
||||||
player_motion=Vector(4, 5, 0))
|
player_motion=Vector(4, 5, 0), context=context)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
str(packet),
|
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(-14, -116, -5), Record(-77, 34, -36), '
|
||||||
'Record(-35, -127, 95), Record(11, 113, -8)], '
|
'Record(-35, -127, 95), Record(11, 113, -8)], '
|
||||||
'player_motion_x=4, player_motion_y=5, player_motion_z=0)'
|
'player_motion_x=4, player_motion_y=5, player_motion_z=0)'
|
||||||
|
% packet.id
|
||||||
)
|
)
|
||||||
|
|
||||||
self._test_read_write_packet(packet)
|
self._test_read_write_packet(packet)
|
||||||
@ -181,7 +186,16 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
str(packet),
|
str(packet),
|
||||||
'CombatEventPacket(event=EnterCombatEvent())'
|
'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(
|
packet = clientbound.play.CombatEventPacket(
|
||||||
event=clientbound.play.CombatEventPacket.EndCombatEvent(
|
event=clientbound.play.CombatEventPacket.EndCombatEvent(
|
||||||
@ -189,7 +203,16 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
self.assertEqual(str(packet),
|
self.assertEqual(str(packet),
|
||||||
'CombatEventPacket(event=EndCombatEvent('
|
'CombatEventPacket(event=EndCombatEvent('
|
||||||
'duration=415, entity_id=91063502))')
|
'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(
|
packet = clientbound.play.CombatEventPacket(
|
||||||
event=clientbound.play.CombatEventPacket.EntityDeadEvent(
|
event=clientbound.play.CombatEventPacket.EntityDeadEvent(
|
||||||
@ -199,7 +222,16 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
"CombatEventPacket(event=EntityDeadEvent("
|
"CombatEventPacket(event=EntityDeadEvent("
|
||||||
"player_id=178, entity_id=36, message='RIP'))"
|
"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):
|
def test_multi_block_change_packet(self):
|
||||||
Record = clientbound.play.MultiBlockChangePacket.Record
|
Record = clientbound.play.MultiBlockChangePacket.Record
|
||||||
@ -348,16 +380,22 @@ class TestReadWritePackets(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
self._test_read_write_packet(packet, context)
|
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
|
If kwargs are specified, the key will be tested against the
|
||||||
respective delta value. Useful for testing FixedPointNumbers
|
respective delta value. Useful for testing FixedPointNumbers
|
||||||
where there is precision lost in the resulting value.
|
where there is precision lost in the resulting value.
|
||||||
"""
|
"""
|
||||||
if context is None:
|
if context is None:
|
||||||
for protocol_version in TEST_VERSIONS:
|
for pv in TEST_VERSIONS:
|
||||||
logging.debug('protocol_version = %r' % protocol_version)
|
if vmin is not None and protocol_earlier(pv, vmin):
|
||||||
context = ConnectionContext(protocol_version=protocol_version)
|
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)
|
self._test_read_write_packet(packet_in, context)
|
||||||
else:
|
else:
|
||||||
packet_in.context = context
|
packet_in.context = context
|
||||||
|
@ -4,6 +4,7 @@ from minecraft.networking.packets import PacketBuffer
|
|||||||
from minecraft.networking.packets.clientbound.play import (
|
from minecraft.networking.packets.clientbound.play import (
|
||||||
PlayerPositionAndLookPacket, PlayerListItemPacket, MapPacket
|
PlayerPositionAndLookPacket, PlayerListItemPacket, MapPacket
|
||||||
)
|
)
|
||||||
|
from minecraft.networking.packets import serverbound
|
||||||
from minecraft.networking.connection import ConnectionContext
|
from minecraft.networking.connection import ConnectionContext
|
||||||
|
|
||||||
from tests.test_packets import TEST_VERSIONS
|
from tests.test_packets import TEST_VERSIONS
|
||||||
@ -306,3 +307,16 @@ class PlayerListItemTest(unittest.TestCase):
|
|||||||
packet.write_fields(packet_buffer)
|
packet.write_fields(packet_buffer)
|
||||||
self.read_and_apply(context, packet_buffer, player_list)
|
self.read_and_apply(context, packet_buffer, player_list)
|
||||||
self.assertNotIn(fake_uuid, player_list.players_by_uuid)
|
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
14
tox.ini
@ -4,7 +4,7 @@
|
|||||||
# and then run "tox" from this directory.
|
# and then run "tox" from this directory.
|
||||||
|
|
||||||
[tox]
|
[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]
|
[testenv]
|
||||||
commands = nosetests --with-timer
|
commands = nosetests --with-timer
|
||||||
@ -27,7 +27,7 @@ commands =
|
|||||||
deps =
|
deps =
|
||||||
coveralls
|
coveralls
|
||||||
|
|
||||||
[testenv:py38]
|
[testenv:py39]
|
||||||
setenv =
|
setenv =
|
||||||
PYCRAFT_RUN_INTERNET_TESTS=1
|
PYCRAFT_RUN_INTERNET_TESTS=1
|
||||||
commands =
|
commands =
|
||||||
@ -41,7 +41,7 @@ deps =
|
|||||||
mock
|
mock
|
||||||
|
|
||||||
[testenv:flake8]
|
[testenv:flake8]
|
||||||
basepython = python3.8
|
basepython = python3.9
|
||||||
commands =
|
commands =
|
||||||
flake8 minecraft tests setup.py start.py bin/generate_travis_yml.py
|
flake8 minecraft tests setup.py start.py bin/generate_travis_yml.py
|
||||||
deps =
|
deps =
|
||||||
@ -54,14 +54,14 @@ per-file-ignores =
|
|||||||
minecraft/networking/packets/__init__.py:F401
|
minecraft/networking/packets/__init__.py:F401
|
||||||
|
|
||||||
[testenv:pylint-errors]
|
[testenv:pylint-errors]
|
||||||
basepython = python3.8
|
basepython = python3.9
|
||||||
deps =
|
deps =
|
||||||
{[testenv]deps}
|
{[testenv]deps}
|
||||||
pylint
|
pylint
|
||||||
commands = pylint minecraft -E
|
commands = pylint minecraft -E
|
||||||
|
|
||||||
[testenv:pylint-full]
|
[testenv:pylint-full]
|
||||||
basepython = python3.8
|
basepython = python3.9
|
||||||
deps =
|
deps =
|
||||||
{[testenv]deps}
|
{[testenv]deps}
|
||||||
pylint
|
pylint
|
||||||
@ -69,7 +69,7 @@ commands =
|
|||||||
- pylint minecraft --disable=E
|
- pylint minecraft --disable=E
|
||||||
|
|
||||||
[testenv:docs]
|
[testenv:docs]
|
||||||
basepython = python3.8
|
basepython = python3.9
|
||||||
deps =
|
deps =
|
||||||
{[testenv:cover]deps}
|
{[testenv:cover]deps}
|
||||||
sphinx
|
sphinx
|
||||||
@ -78,7 +78,7 @@ commands =
|
|||||||
{toxinidir}/bin/build_docs
|
{toxinidir}/bin/build_docs
|
||||||
|
|
||||||
[testenv:verify-manifest]
|
[testenv:verify-manifest]
|
||||||
basepython = python3.8
|
basepython = python3.9
|
||||||
deps =
|
deps =
|
||||||
check-manifest
|
check-manifest
|
||||||
commands =
|
commands =
|
||||||
|
Loading…
Reference in New Issue
Block a user