Fix: non-monotonic protocol versions are not correctly handled

After 1.16.3, Mojang started publishing snapshot, pre-release and release
candidate versions of Minecraft with protocol version numbers of the form
`(1 << 30) | n' where 'n' is a small non-negative integer increasing with each
such version; the release versions continued to use the old format. For
example, these are the last 8 published Minecraft versions as of this commit:

release           1.16.3      uses protocol version 753
pre-release       1.16.4-pre1 uses protocol version 1073741825 == (1 << 30) | 1
pre-release       1.16.4-pre2 uses protocol version 1073741826 == (1 << 30) | 2
release candidate 1.16.4-rc1  uses protocol version 1073741827 == (1 << 30) | 3
release           1.16.4      uses protocol version 754
snapshot          20w45a      uses protocol version 1073741829 == (1 << 30) | 5
snapshot          20w46a      uses protocol version 1073741830 == (1 << 30) | 6
snapshot          20w48a      uses protocol version 1073741831 == (1 << 30) | 7

This means that protocol versions no longer increase monotonically with respect
to publication history, a property that was assumed to hold in much of
pyCraft's code relating to support of multiple protocol versions. This commit
rectifies the issue by replacing any comparison of protocol versions by their
numerical value with a comparison based on their publication time.

Newly defined is the dictionary `minecraft.PROTOCOL_VERSION_INDICES', which
maps each known protocol version to its index in the protocol chronology. As
such, the bound method `minecraft.PROTOCOL_VERSION_INDICES.get` can be used as
a key function for the built-in `sorted`, `min` and `max` functions to collate
protocol versions chronologically.

Two utility functions are provided for direct comparison of protocol versions:
    `minecraft.utility.protocol_earlier` and
    `minecraft.utility.protocol_earlier_eq`.

Additionally, four methods are added to the `ConnectionContext` type to ease
the most common cases where the protocol of a given context must be compared to
a given version number:
    `minecraft.connection.ConnectionContext.protocol_earlier`,
    `minecraft.connection.ConnectionContext.protocol_earlier_eq`,
    `minecraft.connection.ConnectionContext.protocol_later` and
    `minecraft.connection.ConnectionContext.protocol_later_eq`.
This commit is contained in:
joo 2020-12-02 14:30:40 +01:00
parent 4052136d30
commit 969419da3f
29 changed files with 1241 additions and 983 deletions

View File

@ -3,312 +3,531 @@ A modern, Python3-compatible, well-documented library for communicating
with a MineCraft server. with a MineCraft server.
""" """
from collections import OrderedDict, namedtuple
import re
# The version number of the most recent pyCraft release. # The version number of the most recent pyCraft release.
__version__ = "0.7.0" __version__ = "0.7.0"
# A dict mapping the ID string of each Minecraft version supported by pyCraft # This bit occurs in the protocol numbers of pre-release versions after 1.16.3.
# to the corresponding protocol version number. The ID string of a version is PRE = 1 << 30
# the key used to identify it in
# A record representing a Minecraft version in the following list.
Version = namedtuple('Version', ('id', 'protocol', 'supported'))
# A list of Minecraft versions known to pyCraft, including all supported
# versions as well as some unsupported versions (used by certain forward-
# compatible code: e.g. when comparing the current protocol version with that
# of an unsupported version), in chronological order of publication.
#
# The ID string of a version is the key used to identify it in
# <https://launchermeta.mojang.com/mc/game/version_manifest.json>, or the 'id' # <https://launchermeta.mojang.com/mc/game/version_manifest.json>, or the 'id'
# key in "version.json" in the corresponding ".jar" file distributed by Mojang. # key in "version.json" in the corresponding ".jar" file distributed by Mojang.
SUPPORTED_MINECRAFT_VERSIONS = { KNOWN_MINECRAFT_VERSION_RECORDS = [
'1.8': 47, # id protocol supported
'1.8.1': 47, Version('13w41a', 0, False),
'1.8.2': 47, Version('13w41b', 0, False),
'1.8.3': 47, Version('13w42a', 1, False),
'1.8.4': 47, Version('13w42b', 1, False),
'1.8.5': 47, Version('13w43a', 2, False),
'1.8.6': 47, Version('1.7-pre', 3, False),
'1.8.7': 47, Version('1.7.1-pre', 3, False),
'1.8.8': 47, Version('1.7.2', 4, True),
'1.8.9': 47, Version('13w47a', 4, False),
'1.9': 107, Version('13w47b', 4, False),
'1.9.1': 108, Version('13w47c', 4, False),
'1.9.2': 109, Version('13w47d', 4, False),
'1.9.3': 110, Version('13w47e', 4, False),
'1.9.4': 110, Version('13w48a', 4, False),
'1.10': 210, Version('13w48b', 4, False),
'1.10.1': 210, Version('13w49a', 4, False),
'1.10.2': 210, Version('1.7.3-pre', 4, False),
'16w32a': 301, Version('1.7.4', 4, True),
'16w32b': 302, Version('1.7.5', 4, True),
'16w33a': 303, Version('1.7.6-pre1', 5, False),
'16w35a': 304, Version('1.7.6-pre2', 5, False),
'16w36a': 305, Version('1.7.6', 5, True),
'16w38a': 306, Version('1.7.7', 5, True),
'16w39a': 307, Version('1.7.8', 5, True),
'16w39b': 308, Version('1.7.9', 5, True),
'16w39c': 309, Version('1.7.10-pre1', 5, False),
'16w40a': 310, Version('1.7.10-pre2', 5, False),
'16w41a': 311, Version('1.7.10-pre3', 5, False),
'16w42a': 312, Version('1.7.10-pre4', 5, False),
'16w43a': 313, Version('1.7.10', 5, True),
'16w44a': 313, Version('14w02a', 5, False),
'1.11-pre1': 314, Version('14w02b', 5, False),
'1.11': 315, Version('14w02c', 5, False),
'16w50a': 316, Version('14w03a', 6, False),
'1.11.1': 316, Version('14w03b', 6, False),
'1.11.2': 316, Version('14w04a', 7, False),
'17w06a': 317, Version('14w04b', 8, False),
'17w13a': 318, Version('14w05a', 9, False),
'17w13b': 319, Version('14w05b', 9, False),
'17w14a': 320, Version('14w06a', 10, False),
'17w15a': 321, Version('14w06b', 10, False),
'17w16a': 322, Version('14w07a', 11, False),
'17w16b': 323, Version('14w08a', 12, False),
'17w17a': 324, Version('14w10a', 13, False),
'17w17b': 325, Version('14w10b', 13, False),
'17w18a': 326, Version('14w10c', 13, False),
'17w18b': 327, Version('14w11a', 14, False),
'1.12-pre1': 328, Version('14w11b', 14, False),
'1.12-pre2': 329, Version('14w17a', 15, False),
'1.12-pre3': 330, Version('14w18a', 16, False),
'1.12-pre4': 331, Version('14w18b', 16, False),
'1.12-pre5': 332, Version('14w19a', 17, False),
'1.12-pre6': 333, Version('14w20a', 18, False),
'1.12-pre7': 334, Version('14w20b', 18, False),
'1.12': 335, Version('14w21a', 19, False),
'17w31a': 336, Version('14w21b', 20, False),
'1.12.1-pre1': 337, Version('14w25a', 21, False),
'1.12.1': 338, Version('14w25b', 22, False),
'1.12.2-pre1': 339, Version('14w26a', 23, False),
'1.12.2-pre2': 339, Version('14w26b', 24, False),
'1.12.2': 340, Version('14w26c', 25, False),
'17w43a': 341, Version('14w27a', 26, False),
'17w43b': 342, Version('14w27b', 26, False),
'17w45a': 343, Version('14w28a', 27, False),
'17w45b': 344, Version('14w28b', 28, False),
'17w46a': 345, Version('14w29a', 29, False),
'17w47a': 346, Version('14w29a', 29, False),
'17w47b': 347, Version('14w30a', 30, False),
'17w48a': 348, Version('14w30b', 30, False),
'17w49a': 349, Version('14w30c', 31, False),
'17w49b': 350, Version('14w31a', 32, False),
'17w50a': 351, Version('14w32a', 33, False),
'18w01a': 352, Version('14w32b', 34, False),
'18w02a': 353, Version('14w32c', 35, False),
'18w03a': 354, Version('14w32d', 36, False),
'18w03b': 355, Version('14w33a', 37, False),
'18w05a': 356, Version('14w33b', 38, False),
'18w06a': 357, Version('14w33c', 39, False),
'18w07a': 358, Version('14w34a', 40, False),
'18w07b': 359, Version('14w34b', 41, False),
'18w07c': 360, Version('14w34c', 42, False),
'18w08a': 361, Version('14w34d', 43, False),
'18w08b': 362, Version('1.8-pre1', 44, False),
'18w09a': 363, Version('1.8-pre2', 45, False),
'18w10a': 364, Version('1.8-pre3', 46, False),
'18w10b': 365, Version('1.8', 47, True),
'18w10c': 366, Version('1.8.1-pre1', 47, False),
'18w10d': 367, Version('1.8.1-pre2', 47, False),
'18w11a': 368, Version('1.8.1-pre3', 47, False),
'18w14a': 369, Version('1.8.1-pre4', 47, False),
'18w14b': 370, Version('1.8.1-pre5', 47, False),
'18w15a': 371, Version('1.8.1', 47, True),
'18w16a': 372, Version('1.8.2-pre1', 47, False),
'18w19a': 373, Version('1.8.2-pre2', 47, False),
'18w19b': 374, Version('1.8.2-pre3', 47, False),
'18w20a': 375, Version('1.8.2-pre4', 47, False),
'18w20b': 376, Version('1.8.2-pre5', 47, False),
'18w20c': 377, Version('1.8.2-pre6', 47, False),
'18w21a': 378, Version('1.8.2-pre7', 47, False),
'18w21b': 379, Version('1.8.2', 47, True),
'18w22a': 380, Version('1.8.3', 47, True),
'18w22b': 381, Version('1.8.4', 47, True),
'18w22c': 382, Version('1.8.5', 47, True),
'1.13-pre1': 383, Version('1.8.6', 47, True),
'1.13-pre2': 384, Version('1.8.7', 47, True),
'1.13-pre3': 385, Version('1.8.8', 47, True),
'1.13-pre4': 386, Version('1.8.9', 47, True),
'1.13-pre5': 387, Version('15w14a', 48, False),
'1.13-pre6': 388, Version('15w31a', 49, False),
'1.13-pre7': 389, Version('15w31b', 50, False),
'1.13-pre8': 390, Version('15w31c', 51, False),
'1.13-pre9': 391, Version('15w32a', 52, False),
'1.13-pre10': 392, Version('15w32b', 53, False),
'1.13': 393, Version('15w32c', 54, False),
'18w30a': 394, Version('15w33a', 55, False),
'18w30b': 395, Version('15w33b', 56, False),
'18w31a': 396, Version('15w33c', 57, False),
'18w32a': 397, Version('15w34a', 58, False),
'18w33a': 398, Version('15w34b', 59, False),
'1.13.1-pre1': 399, Version('15w34c', 60, False),
'1.13.1-pre2': 400, Version('15w34d', 61, False),
'1.13.1': 401, Version('15w35a', 62, False),
'1.13.2-pre1': 402, Version('15w35b', 63, False),
'1.13.2-pre2': 403, Version('15w35c', 64, False),
'1.13.2': 404, Version('15w35d', 65, False),
'18w43a': 441, Version('15w35e', 66, False),
'18w43b': 441, Version('15w36a', 67, False),
'18w43c': 442, Version('15w36b', 68, False),
'18w44a': 443, Version('15w36c', 69, False),
'18w45a': 444, Version('15w36d', 70, False),
'18w46a': 445, Version('15w37a', 71, False),
'18w47a': 446, Version('15w38a', 72, False),
'18w47b': 447, Version('15w38b', 73, False),
'18w48a': 448, Version('15w39a', 74, False),
'18w48b': 449, Version('15w39b', 74, False),
'18w49a': 450, Version('15w39c', 74, False),
'18w50a': 451, Version('15w40a', 75, False),
'19w02a': 452, Version('15w40b', 76, False),
'19w03a': 453, Version('15w41a', 77, False),
'19w03b': 454, Version('15w41b', 78, False),
'19w03c': 455, Version('15w42a', 79, False),
'19w04a': 456, Version('15w43a', 80, False),
'19w04b': 457, Version('15w43b', 81, False),
'19w05a': 458, Version('15w43c', 82, False),
'19w06a': 459, Version('15w44a', 83, False),
'19w07a': 460, Version('15w44b', 84, False),
'19w08a': 461, Version('15w45a', 85, False),
'19w08b': 462, Version('15w46a', 86, False),
'19w09a': 463, Version('15w47a', 87, False),
'19w11a': 464, Version('15w47b', 88, False),
'19w11b': 465, Version('15w47c', 89, False),
'19w12a': 466, Version('15w49a', 90, False),
'19w12b': 467, Version('15w49b', 91, False),
'19w13a': 468, Version('15w50a', 92, False),
'19w13b': 469, Version('15w51a', 93, False),
'19w14a': 470, Version('15w51b', 94, False),
'19w14b': 471, Version('16w02a', 95, False),
'1.14 Pre-Release 1': 472, Version('16w03a', 96, False),
'1.14 Pre-Release 2': 473, Version('16w04a', 97, False),
'1.14 Pre-Release 3': 474, Version('16w05a', 98, False),
'1.14 Pre-Release 4': 475, Version('16w05b', 99, False),
'1.14 Pre-Release 5': 476, Version('16w06a', 100, False),
'1.14': 477, Version('16w07a', 101, False),
'1.14.1 Pre-Release 1': 478, Version('16w07b', 102, False),
'1.14.1 Pre-Release 2': 479, Version('1.9-pre1', 103, False),
'1.14.1': 480, Version('1.9-pre2', 104, False),
'1.14.2 Pre-Release 1': 481, Version('1.9-pre3', 105, False),
'1.14.2 Pre-Release 2': 482, Version('1.9-pre4', 106, False),
'1.14.2 Pre-Release 3': 483, Version('1.9', 107, True),
'1.14.2 Pre-Release 4': 484, Version('1.9.1-pre1', 107, False),
'1.14.2': 485, Version('1.9.1-pre2', 108, False),
'1.14.3-pre1': 486, Version('1.9.1-pre3', 108, False),
'1.14.3-pre2': 487, Version('1.9.1', 108, True),
'1.14.3-pre3': 488, Version('1.RV-Pre1', 108, False),
'1.14.3-pre4': 489, Version('1.9.2', 109, True),
'1.14.3': 490, Version('16w14a', 109, False),
'1.14.4-pre1': 491, Version('16w15a', 109, False),
'1.14.4-pre2': 492, Version('16w15b', 109, False),
'1.14.4-pre3': 493, Version('1.9.3-pre1', 109, False),
'1.14.4-pre4': 494, Version('1.9.3-pre2', 110, False),
'1.14.4-pre5': 495, Version('1.9.3-pre3', 110, False),
'1.14.4-pre6': 496, Version('1.9.3', 110, True),
'1.14.4-pre7': 497, Version('1.9.4', 110, True),
'1.14.4': 498, Version('16w20a', 201, False),
'19w34a': 550, Version('16w21a', 202, False),
'19w35a': 551, Version('16w21b', 203, False),
'19w36a': 552, Version('1.10-pre1', 204, False),
'19w37a': 553, Version('1.10-pre2', 205, False),
'19w38a': 554, Version('1.10', 210, True),
'19w38b': 555, Version('1.10.1', 210, True),
'19w39a': 556, Version('1.10.2', 210, True),
'19w40a': 557, Version('16w32a', 301, True),
'19w41a': 558, Version('16w32b', 302, True),
'19w42a': 559, Version('16w33a', 303, True),
'19w44a': 560, Version('16w35a', 304, True),
'19w45a': 561, Version('16w36a', 305, True),
'19w45b': 562, Version('16w38a', 306, True),
'19w46a': 563, Version('16w39a', 307, True),
'19w46b': 564, Version('16w39b', 308, True),
'1.15-pre1': 565, Version('16w39c', 309, True),
'1.15-pre2': 566, Version('16w40a', 310, True),
'1.15-pre3': 567, Version('16w41a', 311, True),
'1.15-pre4': 569, Version('16w42a', 312, True),
'1.15-pre5': 570, Version('16w43a', 313, True),
'1.15-pre6': 571, Version('16w44a', 313, True),
'1.15-pre7': 572, Version('1.11-pre1', 314, True),
'1.15': 573, Version('1.11', 315, True),
'1.15.1-pre1': 574, Version('16w50a', 316, True),
'1.15.1': 575, Version('1.11.1', 316, True),
'1.15.2-pre1': 576, Version('1.11.2', 316, True),
'1.15.2-pre2': 577, Version('17w06a', 317, True),
'1.15.2': 578, Version('17w13a', 318, True),
'20w06a': 701, Version('17w13b', 319, True),
'20w07a': 702, Version('17w14a', 320, True),
'20w08a': 703, Version('17w15a', 321, True),
'20w09a': 704, Version('17w16a', 322, True),
'20w10a': 705, Version('17w16b', 323, True),
'20w11a': 706, Version('17w17a', 324, True),
'20w12a': 707, Version('17w17b', 325, True),
'20w13a': 708, Version('17w18a', 326, True),
'20w13b': 709, Version('17w18b', 327, True),
'20w14a': 710, Version('1.12-pre1', 328, True),
'20w15a': 711, Version('1.12-pre2', 329, True),
'20w16a': 712, Version('1.12-pre3', 330, True),
'20w17a': 713, Version('1.12-pre4', 331, True),
'20w18a': 714, Version('1.12-pre5', 332, True),
'20w19a': 715, Version('1.12-pre6', 333, True),
'20w20a': 716, Version('1.12-pre7', 334, True),
'20w20b': 717, Version('1.12', 335, True),
'20w21a': 718, Version('17w31a', 336, True),
'20w22a': 719, Version('1.12.1-pre1', 337, True),
'1.16-pre1': 721, Version('1.12.1', 338, True),
'1.16-pre2': 722, Version('1.12.2-pre1', 339, True),
'1.16-pre3': 725, Version('1.12.2-pre2', 339, True),
'1.16-pre4': 727, Version('1.12.2', 340, True),
'1.16-pre5': 729, Version('17w43a', 341, True),
'1.16-pre6': 730, Version('17w43b', 342, True),
'1.16-pre7': 732, Version('17w45a', 343, True),
'1.16-pre8': 733, Version('17w45b', 344, True),
'1.16-rc1': 734, Version('17w46a', 345, True),
'1.16': 735, Version('17w47a', 346, True),
'1.16.1': 736, Version('17w47b', 347, True),
'20w27a': 738, Version('17w48a', 348, True),
'20w28a': 740, Version('17w49a', 349, True),
'20w29a': 741, Version('17w49b', 350, True),
'20w30a': 743, Version('17w50a', 351, True),
'1.16.2-pre1': 744, Version('18w01a', 352, True),
'1.16.2-pre2': 746, Version('18w02a', 353, True),
'1.16.2-pre3': 748, Version('18w03a', 354, True),
'1.16.2-rc1': 749, Version('18w03b', 355, True),
'1.16.2-rc2': 750, Version('18w05a', 356, True),
'1.16.2': 751, Version('18w06a', 357, True),
'1.16.3-rc1': 752, Version('18w07a', 358, True),
'1.16.3': 753, Version('18w07b', 359, True),
'1.16.4-pre1': 1073741825, Version('18w07c', 360, True),
'1.16.4-pre2': 1073741826, Version('18w08a', 361, True),
'1.16.4-rc1': 1073741827, Version('18w08b', 362, True),
'1.16.4': 754, Version('18w09a', 363, True),
} Version('18w10a', 364, True),
Version('18w10b', 365, True),
Version('18w10c', 366, True),
Version('18w10d', 367, True),
Version('18w11a', 368, True),
Version('18w14a', 369, True),
Version('18w14b', 370, True),
Version('18w15a', 371, True),
Version('18w16a', 372, True),
Version('18w19a', 373, True),
Version('18w19b', 374, True),
Version('18w20a', 375, True),
Version('18w20b', 376, True),
Version('18w20c', 377, True),
Version('18w21a', 378, True),
Version('18w21b', 379, True),
Version('18w22a', 380, True),
Version('18w22b', 381, True),
Version('18w22c', 382, True),
Version('1.13-pre1', 383, True),
Version('1.13-pre2', 384, True),
Version('1.13-pre3', 385, True),
Version('1.13-pre4', 386, True),
Version('1.13-pre5', 387, True),
Version('1.13-pre6', 388, True),
Version('1.13-pre7', 389, True),
Version('1.13-pre8', 390, True),
Version('1.13-pre9', 391, True),
Version('1.13-pre10', 392, True),
Version('1.13', 393, True),
Version('18w30a', 394, True),
Version('18w30b', 395, True),
Version('18w31a', 396, True),
Version('18w32a', 397, True),
Version('18w33a', 398, True),
Version('1.13.1-pre1', 399, True),
Version('1.13.1-pre2', 400, True),
Version('1.13.1', 401, True),
Version('1.13.2-pre1', 402, True),
Version('1.13.2-pre2', 403, True),
Version('1.13.2', 404, True),
Version('18w43a', 441, True),
Version('18w43b', 441, True),
Version('18w43c', 442, True),
Version('18w44a', 443, True),
Version('18w45a', 444, True),
Version('18w46a', 445, True),
Version('18w47a', 446, True),
Version('18w47b', 447, True),
Version('18w48a', 448, True),
Version('18w48b', 449, True),
Version('18w49a', 450, True),
Version('18w50a', 451, True),
Version('19w02a', 452, True),
Version('19w03a', 453, True),
Version('19w03b', 454, True),
Version('19w03c', 455, True),
Version('19w04a', 456, True),
Version('19w04b', 457, True),
Version('19w05a', 458, True),
Version('19w06a', 459, True),
Version('19w07a', 460, True),
Version('19w08a', 461, True),
Version('19w08b', 462, True),
Version('19w09a', 463, True),
Version('19w11a', 464, True),
Version('19w11b', 465, True),
Version('19w12a', 466, True),
Version('19w12b', 467, True),
Version('19w13a', 468, True),
Version('19w13b', 469, True),
Version('19w14a', 470, True),
Version('19w14b', 471, True),
Version('1.14 Pre-Release 1', 472, True),
Version('1.14 Pre-Release 2', 473, True),
Version('1.14 Pre-Release 3', 474, True),
Version('1.14 Pre-Release 4', 475, True),
Version('1.14 Pre-Release 5', 476, True),
Version('1.14', 477, True),
Version('1.14.1 Pre-Release 1', 478, True),
Version('1.14.1 Pre-Release 2', 479, True),
Version('1.14.1', 480, True),
Version('1.14.2 Pre-Release 1', 481, True),
Version('1.14.2 Pre-Release 2', 482, True),
Version('1.14.2 Pre-Release 3', 483, True),
Version('1.14.2 Pre-Release 4', 484, True),
Version('1.14.2', 485, True),
Version('1.14.3-pre1', 486, True),
Version('1.14.3-pre2', 487, True),
Version('1.14.3-pre3', 488, True),
Version('1.14.3-pre4', 489, True),
Version('1.14.3', 490, True),
Version('1.14.4-pre1', 491, True),
Version('1.14.4-pre2', 492, True),
Version('1.14.4-pre3', 493, True),
Version('1.14.4-pre4', 494, True),
Version('1.14.4-pre5', 495, True),
Version('1.14.4-pre6', 496, True),
Version('1.14.4-pre7', 497, True),
Version('1.14.4', 498, True),
Version('19w34a', 550, True),
Version('19w35a', 551, True),
Version('19w36a', 552, True),
Version('19w37a', 553, True),
Version('19w38a', 554, True),
Version('19w38b', 555, True),
Version('19w39a', 556, True),
Version('19w40a', 557, True),
Version('19w41a', 558, True),
Version('19w42a', 559, True),
Version('19w44a', 560, True),
Version('19w45a', 561, True),
Version('19w45b', 562, True),
Version('19w46a', 563, True),
Version('19w46b', 564, True),
Version('1.15-pre1', 565, True),
Version('1.15-pre2', 566, True),
Version('1.15-pre3', 567, True),
Version('1.15-pre4', 569, True),
Version('1.15-pre5', 570, True),
Version('1.15-pre6', 571, True),
Version('1.15-pre7', 572, True),
Version('1.15', 573, True),
Version('1.15.1-pre1', 574, True),
Version('1.15.1', 575, True),
Version('1.15.2-pre1', 576, True),
Version('1.15.2-pre2', 577, True),
Version('1.15.2', 578, True),
Version('20w06a', 701, True),
Version('20w07a', 702, True),
Version('20w08a', 703, True),
Version('20w09a', 704, True),
Version('20w10a', 705, True),
Version('20w11a', 706, True),
Version('20w12a', 707, True),
Version('20w13a', 708, True),
Version('20w13b', 709, True),
Version('20w14a', 710, True),
Version('20w15a', 711, True),
Version('20w16a', 712, True),
Version('20w17a', 713, True),
Version('20w18a', 714, True),
Version('20w19a', 715, True),
Version('20w20a', 716, True),
Version('20w20b', 717, True),
Version('20w21a', 718, True),
Version('20w22a', 719, True),
Version('1.16-pre1', 721, True),
Version('1.16-pre2', 722, True),
Version('1.16-pre3', 725, True),
Version('1.16-pre4', 727, True),
Version('1.16-pre5', 729, True),
Version('1.16-pre6', 730, True),
Version('1.16-pre7', 732, True),
Version('1.16-pre8', 733, True),
Version('1.16-rc1', 734, True),
Version('1.16', 735, True),
Version('1.16.1', 736, True),
Version('20w27a', 738, True),
Version('20w28a', 740, True),
Version('20w29a', 741, True),
Version('20w30a', 743, True),
Version('1.16.2-pre1', 744, True),
Version('1.16.2-pre2', 746, True),
Version('1.16.2-pre3', 748, True),
Version('1.16.2-rc1', 749, True),
Version('1.16.2-rc2', 750, True),
Version('1.16.2', 751, True),
Version('1.16.3-rc1', 752, True),
Version('1.16.3', 753, True),
Version('1.16.4-pre1', PRE | 1, True),
Version('1.16.4-pre2', PRE | 2, True),
Version('1.16.4-rc1', PRE | 3, True),
Version('1.16.4', 754, True),
Version('20w45a', PRE | 5, False),
Version('20w46a', PRE | 6, False),
Version('20w48a', PRE | 7, False),
]
# Those Minecraft versions supported by pyCraft which are "release" versions, # An OrderedDict mapping the id string of each known Minecraft version to its
# i.e. not development snapshots or pre-release versions. # protocol version number, in chronological order of release.
RELEASE_MINECRAFT_VERSIONS = {} KNOWN_MINECRAFT_VERSIONS = OrderedDict()
# The protocol versions of SUPPORTED_MINECRAFT_VERSIONS, without duplicates, # As KNOWN_MINECRAFT_VERSIONS, but only contains versions supported by pyCraft.
# in ascending numerical (and hence chronological) order. SUPPORTED_MINECRAFT_VERSIONS = OrderedDict()
# As SUPPORTED_MINECRAFT_VERSIONS, but only contains release versions.
RELEASE_MINECRAFT_VERSIONS = OrderedDict()
# A list of the protocol version numbers in KNOWN_MINECRAFT_VERSIONS
# in the same order (chronological) but without duplicates.
KNOWN_PROTOCOL_VERSIONS = []
# A list of the protocol version numbers in SUPPORTED_MINECRAFT_VERSIONS
# in the same order (chronological) but without duplicates.
SUPPORTED_PROTOCOL_VERSIONS = [] SUPPORTED_PROTOCOL_VERSIONS = []
# The protocol versions of RELEASE_MINECRAFT_VERSIONS, without duplicates, # A list of the protocol version numbers in RELEASE_MINECRAFT_VERSIONS
# in ascending numerical (and hence chronological) order. # in the same order (chronological) but without duplicates.
RELEASE_PROTOCOL_VERSIONS = [] RELEASE_PROTOCOL_VERSIONS = []
# A dict mapping each protocol version number in KNOWN_PROTOCOL_VERSIONS to
# its index within this list (used for efficient comparison of protocol
# versions according to chronological release order).
PROTOCOL_VERSION_INDICES = {}
def initglobals():
'''Init the globals from the SUPPORTED_MINECRAFT_VERSIONS dict
This allows the SUPPORTED_MINECRAFT_VERSIONS dict to be updated, then the def initglobals(use_known_records=False):
other globals can be updated as well, to allow for dynamic version support. '''Initialise the above global variables, using
All updates are done by reference to allow this to work else where in the 'SUPPORTED_MINECRAFT_VERSIONS' as the source if 'use_known_records' is
code. False (for backward compatibility, this is the default behaviour), or
otherwise using 'KNOWN_MINECRAFT_VERSION_RECORDS' as the source.
This allows 'SUPPORTED_MINECRAFT_VERSIONS' or, respectively,
'KNOWN_MINECRAFT_VERSION_RECORDS' to be updated by the library user
during runtime and then the derived data to be updated as well, to allow
for dynamic version support. All updates are done by reference to allow
this to work elsewhere in the code.
''' '''
global RELEASE_MINECRAFT_VERSIONS, SUPPORTED_PROTOCOL_VERSIONS if use_known_records:
global RELEASE_PROTOCOL_VERSIONS # Update the variables that depend on KNOWN_MINECRAFT_VERSION_RECORDS.
KNOWN_MINECRAFT_VERSIONS.clear()
KNOWN_PROTOCOL_VERSIONS.clear()
SUPPORTED_MINECRAFT_VERSIONS.clear()
PROTOCOL_VERSION_INDICES.clear()
for version in KNOWN_MINECRAFT_VERSION_RECORDS:
KNOWN_MINECRAFT_VERSIONS[version.id] = version.protocol
if version.protocol not in KNOWN_PROTOCOL_VERSIONS:
PROTOCOL_VERSION_INDICES[version.protocol] \
= len(KNOWN_PROTOCOL_VERSIONS)
KNOWN_PROTOCOL_VERSIONS.append(version.protocol)
if version.supported:
SUPPORTED_MINECRAFT_VERSIONS[version.id] = version.protocol
import re # Update the variables that depend on SUPPORTED_MINECRAFT_VERSIONS.
SUPPORTED_PROTOCOL_VERSIONS.clear()
for (vid, protocol) in SUPPORTED_MINECRAFT_VERSIONS.items(): RELEASE_MINECRAFT_VERSIONS.clear()
if re.match(r"\d+(\.\d+)+$", vid): RELEASE_PROTOCOL_VERSIONS.clear()
RELEASE_MINECRAFT_VERSIONS[vid] = protocol for (version_id, protocol) in SUPPORTED_MINECRAFT_VERSIONS.items():
if re.match(r'\d+(\.\d+)+$', version_id):
RELEASE_MINECRAFT_VERSIONS[version_id] = protocol
if protocol not in RELEASE_PROTOCOL_VERSIONS: if protocol not in RELEASE_PROTOCOL_VERSIONS:
RELEASE_PROTOCOL_VERSIONS.append(protocol) RELEASE_PROTOCOL_VERSIONS.append(protocol)
if protocol not in SUPPORTED_PROTOCOL_VERSIONS: if protocol not in SUPPORTED_PROTOCOL_VERSIONS:
SUPPORTED_PROTOCOL_VERSIONS.append(protocol) SUPPORTED_PROTOCOL_VERSIONS.append(protocol)
SUPPORTED_PROTOCOL_VERSIONS.sort()
RELEASE_PROTOCOL_VERSIONS.sort()
initglobals(use_known_records=True)
initglobals()

View File

@ -11,9 +11,11 @@ import re
from .types import VarInt from .types import VarInt
from .packets import clientbound, serverbound from .packets import clientbound, serverbound
from . import packets from . import packets, encryption
from . import encryption from .. import (
from .. import SUPPORTED_PROTOCOL_VERSIONS, SUPPORTED_MINECRAFT_VERSIONS utility, KNOWN_MINECRAFT_VERSIONS, SUPPORTED_MINECRAFT_VERSIONS,
SUPPORTED_PROTOCOL_VERSIONS, PROTOCOL_VERSION_INDICES
)
from ..exceptions import ( from ..exceptions import (
VersionMismatch, LoginDisconnect, IgnorePacket, InvalidState VersionMismatch, LoginDisconnect, IgnorePacket, InvalidState
) )
@ -31,6 +33,26 @@ class ConnectionContext(object):
def __init__(self, **kwds): def __init__(self, **kwds):
self.protocol_version = kwds.get('protocol_version') self.protocol_version = kwds.get('protocol_version')
def protocol_earlier(self, other_pv):
"""Returns True if the protocol version of this context was published
earlier than 'other_pv', or else False."""
return utility.protocol_earlier(self.protocol_version, other_pv)
def protocol_earlier_eq(self, other_pv):
"""Returns True if the protocol version of this context was published
earlier than, or is equal to, 'other_pv', or else False."""
return utility.protocol_earlier_eq(self.protocol_version, other_pv)
def protocol_later(self, other_pv):
"""Returns True if the protocol version of this context was published
later than 'other_pv', or else False."""
return utility.protocol_earlier(other_pv, self.protocol_version)
def protocol_later_eq(self, other_pv):
"""Returns True if the protocol version of this context was published
later than, or is equal to, 'other_pv', or else False."""
return utility.protocol_earlier_eq(other_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,
@ -129,13 +151,15 @@ class Connection(object):
allowed_versions = set(map(proto_version, allowed_versions)) allowed_versions = set(map(proto_version, allowed_versions))
self.allowed_proto_versions = allowed_versions self.allowed_proto_versions = allowed_versions
latest_allowed_proto = max(self.allowed_proto_versions,
key=PROTOCOL_VERSION_INDICES.get)
if initial_version is None: if initial_version is None:
self.default_proto_version = max(self.allowed_proto_versions) self.default_proto_version = latest_allowed_proto
else: else:
self.default_proto_version = proto_version(initial_version) self.default_proto_version = proto_version(initial_version)
self.context = ConnectionContext( self.context = ConnectionContext(protocol_version=latest_allowed_proto)
protocol_version=max(self.allowed_proto_versions))
self.options = _ConnectionOptions() self.options = _ConnectionOptions()
self.options.address = address self.options.address = address
@ -362,7 +386,9 @@ class Connection(object):
# It is important that this is set correctly even when connecting # It is important that this is set correctly even when connecting
# in status mode, as some servers, e.g. SpigotMC with the # in status mode, as some servers, e.g. SpigotMC with the
# ProtocolSupport plugin, use it to determine the correct response. # ProtocolSupport plugin, use it to determine the correct response.
self.context.protocol_version = max(self.allowed_proto_versions) self.context.protocol_version \
= max(self.allowed_proto_versions,
key=PROTOCOL_VERSION_INDICES.get)
self.spawned = False self.spawned = False
self._connect() self._connect()
@ -496,7 +522,7 @@ class Connection(object):
def _version_mismatch(self, server_protocol=None, server_version=None): def _version_mismatch(self, server_protocol=None, server_version=None):
if server_protocol is None: if server_protocol is None:
server_protocol = SUPPORTED_MINECRAFT_VERSIONS.get(server_version) server_protocol = KNOWN_MINECRAFT_VERSIONS.get(server_version)
if server_protocol is None: if server_protocol is None:
vs = 'version' if server_version is None else \ vs = 'version' if server_version is None else \
@ -751,7 +777,7 @@ class PlayingReactor(PacketReactor):
self.connection.write_packet(keep_alive_packet) self.connection.write_packet(keep_alive_packet)
elif packet.packet_name == "player position and look": elif packet.packet_name == "player position and look":
if self.connection.context.protocol_version >= 107: if self.connection.context.protocol_later_eq(107):
teleport_confirm = serverbound.play.TeleportConfirmPacket() teleport_confirm = serverbound.play.TeleportConfirmPacket()
teleport_confirm.teleport_id = packet.teleport_id teleport_confirm.teleport_id = packet.teleport_id
self.connection.write_packet(teleport_confirm) self.connection.write_packet(teleport_confirm)

View File

@ -1,8 +1,8 @@
''' '''
NOTE: The packet classes in __all_legacy_packets__ exported by this NOTE: The packet classes exported by this module are included only for backward
module are included only for backward compatibility, and should not compatibility, and should not be used in new code, as (1) they do not include
be used in new code, as (1) they do not include all packets present all packets present in pyCraft, and (2) some are named oddly, for historical
in pyCraft, and (2) some are named oddly, for historical reasons. reasons.
Use the packet classes under packets.clientbound.* and Use the packet classes under packets.clientbound.* and
packets.serverbound.* instead. packets.serverbound.* instead.
@ -60,26 +60,3 @@ from .serverbound.play import PositionAndLookPacket
from .serverbound.play import TeleportConfirmPacket from .serverbound.play import TeleportConfirmPacket
from .serverbound.play import AnimationPacket as AnimationPacketServerbound from .serverbound.play import AnimationPacket as AnimationPacketServerbound
from .serverbound.play import get_packets as state_playing_serverbound from .serverbound.play import get_packets as state_playing_serverbound
__all_legacy_packets__ = (
state_handshake_clientbound, HandShakePacket,
state_handshake_serverbound, ResponsePacket,
PingPacketResponse, state_status_clientbound,
RequestPacket, PingPacket, state_status_serverbound,
DisconnectPacket, EncryptionRequestPacket, LoginSuccessPacket,
SetCompressionPacket, state_login_clientbound,
LoginStartPacket, EncryptionResponsePacket,
state_login_serverbound, KeepAlivePacketClientbound,
KeepAlivePacketServerbound, JoinGamePacket, ChatMessagePacket,
PlayerPositionAndLookPacket, DisconnectPacketPlayState,
SetCompressionPacketPlayState, PlayerListItemPacket,
MapPacket, state_playing_clientbound, ChatPacket,
PositionAndLookPacket, TeleportConfirmPacket,
AnimationPacketServerbound, state_playing_serverbound,
KeepAlivePacket,
)
__all_other__ = (
Packet, PacketBuffer, PacketListener,
AbstractKeepAlivePacket, AbstractPluginMessagePacket,
)

View File

@ -13,7 +13,7 @@ def get_packets(context):
LoginSuccessPacket, LoginSuccessPacket,
SetCompressionPacket, SetCompressionPacket,
} }
if context.protocol_version >= 385: if context.protocol_later_eq(385):
packets |= { packets |= {
PluginRequestPacket, PluginRequestPacket,
} }
@ -23,8 +23,8 @@ def get_packets(context):
class DisconnectPacket(Packet): class DisconnectPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x00 if context.protocol_version >= 391 else \ return 0x00 if context.protocol_later_eq(391) else \
0x01 if context.protocol_version >= 385 else \ 0x01 if context.protocol_later_eq(385) else \
0x00 0x00
packet_name = "disconnect" packet_name = "disconnect"
@ -35,8 +35,8 @@ class DisconnectPacket(Packet):
class EncryptionRequestPacket(Packet): class EncryptionRequestPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x01 if context.protocol_version >= 391 else \ return 0x01 if context.protocol_later_eq(391) else \
0x02 if context.protocol_version >= 385 else \ 0x02 if context.protocol_later_eq(385) else \
0x01 0x01
packet_name = "encryption request" packet_name = "encryption request"
@ -49,13 +49,13 @@ class EncryptionRequestPacket(Packet):
class LoginSuccessPacket(Packet): class LoginSuccessPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x02 if context.protocol_version >= 391 else \ return 0x02 if context.protocol_later_eq(391) else \
0x03 if context.protocol_version >= 385 else \ 0x03 if context.protocol_later_eq(385) else \
0x02 0x02
packet_name = "login success" packet_name = "login success"
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'UUID': UUID if context.protocol_version >= 707 else String}, {'UUID': UUID if context.protocol_later_eq(707) else String},
{'Username': String} {'Username': String}
]) ])
@ -63,8 +63,8 @@ class LoginSuccessPacket(Packet):
class SetCompressionPacket(Packet): class SetCompressionPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x03 if context.protocol_version >= 391 else \ return 0x03 if context.protocol_later_eq(391) else \
0x04 if context.protocol_version >= 385 else \ 0x04 if context.protocol_later_eq(385) else \
0x03 0x03
packet_name = "set compression" packet_name = "set compression"
@ -89,7 +89,7 @@ class PluginRequestPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x04 if context.protocol_version >= 391 else \ return 0x04 if context.protocol_later_eq(391) else \
0x00 0x00
packet_name = 'login plugin request' packet_name = 'login plugin request'

View File

@ -46,15 +46,15 @@ def get_packets(context):
PlayerListHeaderAndFooterPacket, PlayerListHeaderAndFooterPacket,
EntityLookPacket EntityLookPacket
} }
if context.protocol_version <= 47: if context.protocol_earlier_eq(47):
packets |= { packets |= {
SetCompressionPacket, SetCompressionPacket,
} }
if context.protocol_version >= 94: if context.protocol_later_eq(94):
packets |= { packets |= {
SoundEffectPacket, SoundEffectPacket,
} }
if context.protocol_version >= 352: if context.protocol_later_eq(352):
packets |= { packets |= {
FacePlayerPacket FacePlayerPacket
} }
@ -64,32 +64,32 @@ def get_packets(context):
class KeepAlivePacket(AbstractKeepAlivePacket): class KeepAlivePacket(AbstractKeepAlivePacket):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x1F if context.protocol_version >= 741 else \ return 0x1F if context.protocol_later_eq(741) else \
0x20 if context.protocol_version >= 721 else \ 0x20 if context.protocol_later_eq(721) else \
0x21 if context.protocol_version >= 550 else \ 0x21 if context.protocol_later_eq(550) else \
0x20 if context.protocol_version >= 471 else \ 0x20 if context.protocol_later_eq(471) else \
0x21 if context.protocol_version >= 389 else \ 0x21 if context.protocol_later_eq(389) else \
0x20 if context.protocol_version >= 345 else \ 0x20 if context.protocol_later_eq(345) else \
0x1F if context.protocol_version >= 332 else \ 0x1F if context.protocol_later_eq(332) else \
0x20 if context.protocol_version >= 318 else \ 0x20 if context.protocol_later_eq(318) else \
0x1F if context.protocol_version >= 107 else \ 0x1F if context.protocol_later_eq(107) else \
0x00 0x00
class ServerDifficultyPacket(Packet): class ServerDifficultyPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x0D if context.protocol_version >= 721 else \ return 0x0D if context.protocol_later_eq(721) else \
0x0E if context.protocol_version >= 550 else \ 0x0E if context.protocol_later_eq(550) else \
0x0D if context.protocol_version >= 332 else \ 0x0D if context.protocol_later_eq(332) else \
0x0E if context.protocol_version >= 318 else \ 0x0E if context.protocol_later_eq(318) else \
0x0D if context.protocol_version >= 70 else \ 0x0D if context.protocol_later_eq(70) else \
0x41 0x41
packet_name = 'server difficulty' packet_name = 'server difficulty'
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'difficulty': UnsignedByte}, {'difficulty': UnsignedByte},
{'is_locked': Boolean} if context.protocol_version >= 464 else {}, {'is_locked': Boolean} if context.protocol_later_eq(464) else {},
]) ])
# These aliases declare the Enum type corresponding to each field: # These aliases declare the Enum type corresponding to each field:
@ -99,19 +99,19 @@ class ServerDifficultyPacket(Packet):
class ChatMessagePacket(Packet): class ChatMessagePacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x0E if context.protocol_version >= 721 else \ return 0x0E if context.protocol_later_eq(721) else \
0x0F if context.protocol_version >= 550 else \ 0x0F if context.protocol_later_eq(550) else \
0x0E if context.protocol_version >= 343 else \ 0x0E if context.protocol_later_eq(343) else \
0x0F if context.protocol_version >= 332 else \ 0x0F if context.protocol_later_eq(332) else \
0x10 if context.protocol_version >= 317 else \ 0x10 if context.protocol_later_eq(317) else \
0x0F if context.protocol_version >= 107 else \ 0x0F if context.protocol_later_eq(107) else \
0x02 0x02
packet_name = "chat message" packet_name = "chat message"
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'json_data': String}, {'json_data': String},
{'position': Byte}, {'position': Byte},
{'sender': UUID} if context.protocol_version >= 718 else {}, {'sender': UUID} if context.protocol_later_eq(718) else {},
]) ])
class Position(Enum): class Position(Enum):
@ -123,14 +123,14 @@ class ChatMessagePacket(Packet):
class DisconnectPacket(Packet): class DisconnectPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x19 if context.protocol_version >= 741 else \ return 0x19 if context.protocol_later_eq(741) else \
0x1A if context.protocol_version >= 721 else \ 0x1A if context.protocol_later_eq(721) else \
0x1B if context.protocol_version >= 550 else \ 0x1B if context.protocol_later_eq(550) else \
0x1A if context.protocol_version >= 471 else \ 0x1A if context.protocol_later_eq(471) else \
0x1B if context.protocol_version >= 345 else \ 0x1B if context.protocol_later_eq(345) else \
0x1A if context.protocol_version >= 332 else \ 0x1A if context.protocol_later_eq(332) else \
0x1B if context.protocol_version >= 318 else \ 0x1B if context.protocol_later_eq(318) else \
0x1A if context.protocol_version >= 107 else \ 0x1A if context.protocol_later_eq(107) else \
0x40 0x40
packet_name = "disconnect" packet_name = "disconnect"
@ -150,23 +150,23 @@ class SetCompressionPacket(Packet):
class SpawnPlayerPacket(Packet): class SpawnPlayerPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x04 if context.protocol_version >= 721 else \ return 0x04 if context.protocol_later_eq(721) else \
0x05 if context.protocol_version >= 67 else \ 0x05 if context.protocol_later_eq(67) else \
0x0C 0x0C
packet_name = 'spawn player' packet_name = 'spawn player'
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'entity_id': VarInt}, {'entity_id': VarInt},
{'player_UUID': UUID}, {'player_UUID': UUID},
{'x': Double} if context.protocol_version >= 100 {'x': Double} if context.protocol_later_eq(100)
else {'x': FixedPoint(Integer)}, else {'x': FixedPoint(Integer)},
{'y': Double} if context.protocol_version >= 100 {'y': Double} if context.protocol_later_eq(100)
else {'y': FixedPoint(Integer)}, else {'y': FixedPoint(Integer)},
{'z': Double} if context.protocol_version >= 100 {'z': Double} if context.protocol_later_eq(100)
else {'z': FixedPoint(Integer)}, else {'z': FixedPoint(Integer)},
{'yaw': Angle}, {'yaw': Angle},
{'pitch': Angle}, {'pitch': Angle},
{'current_item': Short} if context.protocol_version <= 49 else {}, {'current_item': Short} if context.protocol_earlier_eq(49) else {},
# TODO: read entity metadata (protocol < 550) # TODO: read entity metadata (protocol < 550)
]) ])
@ -186,20 +186,20 @@ class SpawnPlayerPacket(Packet):
class EntityVelocityPacket(Packet): class EntityVelocityPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x46 if context.protocol_version >= 721 else \ return 0x46 if context.protocol_later_eq(721) else \
0x47 if context.protocol_version >= 707 else \ 0x47 if context.protocol_later_eq(707) else \
0x46 if context.protocol_version >= 550 else \ 0x46 if context.protocol_later_eq(550) else \
0x45 if context.protocol_version >= 471 else \ 0x45 if context.protocol_later_eq(471) else \
0x41 if context.protocol_version >= 461 else \ 0x41 if context.protocol_later_eq(461) else \
0x42 if context.protocol_version >= 451 else \ 0x42 if context.protocol_later_eq(451) else \
0x41 if context.protocol_version >= 389 else \ 0x41 if context.protocol_later_eq(389) else \
0x40 if context.protocol_version >= 352 else \ 0x40 if context.protocol_later_eq(352) else \
0x3F if context.protocol_version >= 345 else \ 0x3F if context.protocol_later_eq(345) else \
0x3E if context.protocol_version >= 336 else \ 0x3E if context.protocol_later_eq(336) else \
0x3D if context.protocol_version >= 332 else \ 0x3D if context.protocol_later_eq(332) else \
0x3B if context.protocol_version >= 86 else \ 0x3B if context.protocol_later_eq(86) else \
0x3C if context.protocol_version >= 77 else \ 0x3C if context.protocol_later_eq(77) else \
0x3B if context.protocol_version >= 67 else \ 0x3B if context.protocol_later_eq(67) else \
0x12 0x12
packet_name = 'entity velocity' packet_name = 'entity velocity'
@ -214,14 +214,14 @@ class EntityVelocityPacket(Packet):
class EntityPositionDeltaPacket(Packet): class EntityPositionDeltaPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x27 if context.protocol_version >= 741 else \ return 0x27 if context.protocol_later_eq(741) else \
0x28 if context.protocol_version >= 721 else \ 0x28 if context.protocol_later_eq(721) else \
0x29 if context.protocol_version >= 550 else \ 0x29 if context.protocol_later_eq(550) else \
0x28 if context.protocol_version >= 389 else \ 0x28 if context.protocol_later_eq(389) else \
0x27 if context.protocol_version >= 345 else \ 0x27 if context.protocol_later_eq(345) else \
0x26 if context.protocol_version >= 318 else \ 0x26 if context.protocol_later_eq(318) else \
0x25 if context.protocol_version >= 94 else \ 0x25 if context.protocol_later_eq(94) else \
0x26 if context.protocol_version >= 70 else \ 0x26 if context.protocol_later_eq(70) else \
0x15 0x15
packet_name = "entity position delta" packet_name = "entity position delta"
@ -229,7 +229,7 @@ class EntityPositionDeltaPacket(Packet):
@staticmethod @staticmethod
def get_definition(context): def get_definition(context):
delta_type = FixedPoint(Short, 12) \ delta_type = FixedPoint(Short, 12) \
if context.protocol_version >= 106 else \ if context.protocol_later_eq(106) else \
FixedPoint(Byte) FixedPoint(Byte)
return [ return [
{'entity_id': VarInt}, {'entity_id': VarInt},
@ -253,18 +253,18 @@ class EntityPositionDeltaPacket(Packet):
class TimeUpdatePacket(Packet): class TimeUpdatePacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x4E if context.protocol_version >= 721 else \ return 0x4E if context.protocol_later_eq(721) else \
0x4F if context.protocol_version >= 550 else \ 0x4F if context.protocol_later_eq(550) else \
0x4E if context.protocol_version >= 471 else \ 0x4E if context.protocol_later_eq(471) else \
0x4A if context.protocol_version >= 461 else \ 0x4A if context.protocol_later_eq(461) else \
0x4B if context.protocol_version >= 451 else \ 0x4B if context.protocol_later_eq(451) else \
0x4A if context.protocol_version >= 389 else \ 0x4A if context.protocol_later_eq(389) else \
0x49 if context.protocol_version >= 352 else \ 0x49 if context.protocol_later_eq(352) else \
0x48 if context.protocol_version >= 345 else \ 0x48 if context.protocol_later_eq(345) else \
0x47 if context.protocol_version >= 336 else \ 0x47 if context.protocol_later_eq(336) else \
0x46 if context.protocol_version >= 318 else \ 0x46 if context.protocol_later_eq(318) else \
0x44 if context.protocol_version >= 94 else \ 0x44 if context.protocol_later_eq(94) else \
0x43 if context.protocol_version >= 70 else \ 0x43 if context.protocol_later_eq(70) else \
0x03 0x03
packet_name = "time update" packet_name = "time update"
@ -277,20 +277,20 @@ class TimeUpdatePacket(Packet):
class UpdateHealthPacket(Packet): class UpdateHealthPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x49 if context.protocol_version >= 721 else \ return 0x49 if context.protocol_later_eq(721) else \
0x4A if context.protocol_version >= 707 else \ 0x4A if context.protocol_later_eq(707) else \
0x49 if context.protocol_version >= 550 else \ 0x49 if context.protocol_later_eq(550) else \
0x48 if context.protocol_version >= 471 else \ 0x48 if context.protocol_later_eq(471) else \
0x44 if context.protocol_version >= 461 else \ 0x44 if context.protocol_later_eq(461) else \
0x45 if context.protocol_version >= 451 else \ 0x45 if context.protocol_later_eq(451) else \
0x44 if context.protocol_version >= 389 else \ 0x44 if context.protocol_later_eq(389) else \
0x43 if context.protocol_version >= 352 else \ 0x43 if context.protocol_later_eq(352) else \
0x42 if context.protocol_version >= 345 else \ 0x42 if context.protocol_later_eq(345) else \
0x41 if context.protocol_version >= 336 else \ 0x41 if context.protocol_later_eq(336) else \
0x40 if context.protocol_version >= 318 else \ 0x40 if context.protocol_later_eq(318) else \
0x3E if context.protocol_version >= 86 else \ 0x3E if context.protocol_later_eq(86) else \
0x3F if context.protocol_version >= 77 else \ 0x3F if context.protocol_later_eq(77) else \
0x3E if context.protocol_version >= 67 else \ 0x3E if context.protocol_later_eq(67) else \
0x06 0x06
packet_name = 'update health' packet_name = 'update health'
@ -304,31 +304,31 @@ class UpdateHealthPacket(Packet):
class PluginMessagePacket(AbstractPluginMessagePacket): class PluginMessagePacket(AbstractPluginMessagePacket):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x17 if context.protocol_version >= 741 else \ return 0x17 if context.protocol_later_eq(741) else \
0x18 if context.protocol_version >= 721 else \ 0x18 if context.protocol_later_eq(721) else \
0x19 if context.protocol_version >= 550 else \ 0x19 if context.protocol_later_eq(550) else \
0x18 if context.protocol_version >= 471 else \ 0x18 if context.protocol_later_eq(471) else \
0x19 if context.protocol_version >= 345 else \ 0x19 if context.protocol_later_eq(345) else \
0x18 if context.protocol_version >= 332 else \ 0x18 if context.protocol_later_eq(332) else \
0x19 if context.protocol_version >= 318 else \ 0x19 if context.protocol_later_eq(318) else \
0x18 if context.protocol_version >= 70 else \ 0x18 if context.protocol_later_eq(70) else \
0x3F 0x3F
class PlayerListHeaderAndFooterPacket(Packet): class PlayerListHeaderAndFooterPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x53 if context.protocol_version >= 721 else \ return 0x53 if context.protocol_later_eq(721) else \
0x54 if context.protocol_version >= 550 else \ 0x54 if context.protocol_later_eq(550) else \
0x53 if context.protocol_version >= 471 else \ 0x53 if context.protocol_later_eq(471) else \
0x5F if context.protocol_version >= 461 else \ 0x5F if context.protocol_later_eq(461) else \
0x50 if context.protocol_version >= 451 else \ 0x50 if context.protocol_later_eq(451) else \
0x4F if context.protocol_version >= 441 else \ 0x4F if context.protocol_later_eq(441) else \
0x4E if context.protocol_version >= 393 else \ 0x4E if context.protocol_later_eq(393) else \
0x4A if context.protocol_version >= 338 else \ 0x4A if context.protocol_later_eq(338) else \
0x49 if context.protocol_version >= 335 else \ 0x49 if context.protocol_later_eq(335) else \
0x47 if context.protocol_version >= 110 else \ 0x47 if context.protocol_later_eq(110) else \
0x48 if context.protocol_version >= 107 else \ 0x48 if context.protocol_later_eq(107) else \
0x47 0x47
packet_name = 'player list header and footer' packet_name = 'player list header and footer'
@ -340,14 +340,14 @@ class PlayerListHeaderAndFooterPacket(Packet):
class EntityLookPacket(Packet): class EntityLookPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x29 if context.protocol_version >= 741 else \ return 0x29 if context.protocol_later_eq(741) else \
0x2A if context.protocol_version >= 721 else \ 0x2A if context.protocol_later_eq(721) else \
0x2B if context.protocol_version >= 550 else \ 0x2B if context.protocol_later_eq(550) else \
0x2A if context.protocol_version >= 389 else \ 0x2A if context.protocol_later_eq(389) else \
0x29 if context.protocol_version >= 345 else \ 0x29 if context.protocol_later_eq(345) else \
0x28 if context.protocol_version >= 318 else \ 0x28 if context.protocol_later_eq(318) else \
0x27 if context.protocol_version >= 94 else \ 0x27 if context.protocol_later_eq(94) else \
0x28 if context.protocol_version >= 70 else \ 0x28 if context.protocol_later_eq(70) else \
0x16 0x16
packet_name = 'entity look' packet_name = 'entity look'

View File

@ -9,12 +9,12 @@ from minecraft.networking.types import (
class BlockChangePacket(Packet): class BlockChangePacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x0B if context.protocol_version >= 721 else \ return 0x0B if context.protocol_later_eq(721) else \
0x0C if context.protocol_version >= 550 else \ 0x0C if context.protocol_later_eq(550) else \
0x0B if context.protocol_version >= 332 else \ 0x0B if context.protocol_later_eq(332) else \
0x0C if context.protocol_version >= 318 else \ 0x0C if context.protocol_later_eq(318) else \
0x0B if context.protocol_version >= 67 else \ 0x0B if context.protocol_later_eq(67) else \
0x24 if context.protocol_version >= 62 else \ 0x24 if context.protocol_later_eq(62) else \
0x23 0x23
packet_name = 'block change' packet_name = 'block change'
@ -23,7 +23,7 @@ class BlockChangePacket(Packet):
{'block_state_id': VarInt}] {'block_state_id': VarInt}]
block_state_id = 0 block_state_id = 0
# For protocols < 347: an accessor for (block_state_id >> 4). # For protocols before 347: an accessor for (block_state_id >> 4).
@property @property
def blockId(self): def blockId(self):
return self.block_state_id >> 4 return self.block_state_id >> 4
@ -32,7 +32,7 @@ class BlockChangePacket(Packet):
def blockId(self, block_id): def blockId(self, block_id):
self.block_state_id = (self.block_state_id & 0xF) | (block_id << 4) self.block_state_id = (self.block_state_id & 0xF) | (block_id << 4)
# For protocols < 347: an accessor for (block_state_id & 0xF). # For protocols before 347: an accessor for (block_state_id & 0xF).
@property @property
def blockMeta(self): def blockMeta(self):
return self.block_state_id & 0xF return self.block_state_id & 0xF
@ -48,13 +48,13 @@ class BlockChangePacket(Packet):
class MultiBlockChangePacket(Packet): class MultiBlockChangePacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x3B if context.protocol_version >= 741 else \ return 0x3B if context.protocol_later_eq(741) else \
0x0F if context.protocol_version >= 721 else \ 0x0F if context.protocol_later_eq(721) else \
0x10 if context.protocol_version >= 550 else \ 0x10 if context.protocol_later_eq(550) else \
0x0F if context.protocol_version >= 343 else \ 0x0F if context.protocol_later_eq(343) else \
0x10 if context.protocol_version >= 332 else \ 0x10 if context.protocol_later_eq(332) else \
0x11 if context.protocol_version >= 318 else \ 0x11 if context.protocol_later_eq(318) else \
0x10 if context.protocol_version >= 67 else \ 0x10 if context.protocol_later_eq(67) else \
0x22 0x22
packet_name = 'multi block change' packet_name = 'multi block change'
@ -87,7 +87,7 @@ class MultiBlockChangePacket(Packet):
# Access the 'x', 'y', 'z' fields as a Vector of ints. # Access the 'x', 'y', 'z' fields as a Vector of ints.
position = multi_attribute_alias(Vector, 'x', 'y', 'z') position = multi_attribute_alias(Vector, 'x', 'y', 'z')
# For protocols < 347: an accessor for (block_state_id >> 4). # For protocols before 347: an accessor for (block_state_id >> 4).
@property @property
def blockId(self): def blockId(self):
return self.block_state_id >> 4 return self.block_state_id >> 4
@ -96,7 +96,7 @@ class MultiBlockChangePacket(Packet):
def blockId(self, block_id): def blockId(self, block_id):
self.block_state_id = self.block_state_id & 0xF | block_id << 4 self.block_state_id = self.block_state_id & 0xF | block_id << 4
# For protocols < 347: an accessor for (block_state_id & 0xF). # For protocols before 347: an accessor for (block_state_id & 0xF).
@property @property
def blockMeta(self): def blockMeta(self):
return self.block_state_id & 0xF return self.block_state_id & 0xF
@ -111,7 +111,7 @@ class MultiBlockChangePacket(Packet):
@classmethod @classmethod
def read_with_context(cls, file_object, context): def read_with_context(cls, file_object, context):
record = cls() record = cls()
if context.protocol_version >= 741: if context.protocol_later_eq(741):
value = VarLong.read(file_object) value = VarLong.read(file_object)
record.block_state_id = value >> 12 record.block_state_id = value >> 12
record.x = (value >> 8) & 0xF record.x = (value >> 8) & 0xF
@ -127,7 +127,7 @@ class MultiBlockChangePacket(Packet):
@classmethod @classmethod
def send_with_context(self, record, socket, context): def send_with_context(self, record, socket, context):
if context.protocol_version >= 741: if context.protocol_later_eq(741):
value = record.block_state_id << 12 | \ value = record.block_state_id << 12 | \
(record.x & 0xF) << 8 | \ (record.x & 0xF) << 8 | \
(record.z & 0xF) << 4 | \ (record.z & 0xF) << 4 | \
@ -141,9 +141,9 @@ class MultiBlockChangePacket(Packet):
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'chunk_section_pos': MultiBlockChangePacket.ChunkSectionPos}, {'chunk_section_pos': MultiBlockChangePacket.ChunkSectionPos},
{'invert_trust_edges': Boolean} {'invert_trust_edges': Boolean}
if context.protocol_version >= 748 else {}, # Provisional field name. if context.protocol_later_eq(748) else {}, # Provisional field name.
{'records': PrefixedArray(VarInt, MultiBlockChangePacket.Record)}, {'records': PrefixedArray(VarInt, MultiBlockChangePacket.Record)},
] if context.protocol_version >= 741 else [ ] if context.protocol_later_eq(741) else [
{'chunk_x': Integer}, {'chunk_x': Integer},
{'chunk_z': Integer}, {'chunk_z': Integer},
{'records': PrefixedArray(VarInt, MultiBlockChangePacket.Record)}, {'records': PrefixedArray(VarInt, MultiBlockChangePacket.Record)},

View File

@ -8,19 +8,19 @@ from minecraft.networking.types import (
class CombatEventPacket(Packet): class CombatEventPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x31 if context.protocol_version >= 741 else \ return 0x31 if context.protocol_later_eq(741) else \
0x32 if context.protocol_version >= 721 else \ 0x32 if context.protocol_later_eq(721) else \
0x33 if context.protocol_version >= 550 else \ 0x33 if context.protocol_later_eq(550) else \
0x32 if context.protocol_version >= 471 else \ 0x32 if context.protocol_later_eq(471) else \
0x30 if context.protocol_version >= 451 else \ 0x30 if context.protocol_later_eq(451) else \
0x2F if context.protocol_version >= 389 else \ 0x2F if context.protocol_later_eq(389) else \
0x2E if context.protocol_version >= 345 else \ 0x2E if context.protocol_later_eq(345) else \
0x2D if context.protocol_version >= 336 else \ 0x2D if context.protocol_later_eq(336) else \
0x2C if context.protocol_version >= 332 else \ 0x2C if context.protocol_later_eq(332) else \
0x2D if context.protocol_version >= 318 else \ 0x2D if context.protocol_later_eq(318) else \
0x2C if context.protocol_version >= 86 else \ 0x2C if context.protocol_later_eq(86) else \
0x2D if context.protocol_version >= 80 else \ 0x2D if context.protocol_later_eq(80) else \
0x2C if context.protocol_version >= 67 else \ 0x2C if context.protocol_later_eq(67) else \
0x42 0x42
packet_name = 'combat event' packet_name = 'combat event'

View File

@ -7,16 +7,16 @@ from minecraft.networking.packets import Packet
class ExplosionPacket(Packet): class ExplosionPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x1B if context.protocol_version >= 741 else \ return 0x1B if context.protocol_later_eq(741) else \
0x1C if context.protocol_version >= 721 else \ 0x1C if context.protocol_later_eq(721) else \
0x1D if context.protocol_version >= 550 else \ 0x1D if context.protocol_later_eq(550) else \
0x1C if context.protocol_version >= 471 else \ 0x1C if context.protocol_later_eq(471) else \
0x1E if context.protocol_version >= 389 else \ 0x1E if context.protocol_later_eq(389) else \
0x1D if context.protocol_version >= 345 else \ 0x1D if context.protocol_later_eq(345) else \
0x1C if context.protocol_version >= 332 else \ 0x1C if context.protocol_later_eq(332) else \
0x1D if context.protocol_version >= 318 else \ 0x1D if context.protocol_later_eq(318) else \
0x1C if context.protocol_version >= 80 else \ 0x1C if context.protocol_later_eq(80) else \
0x1B if context.protocol_version >= 67 else \ 0x1B if context.protocol_later_eq(67) else \
0x27 0x27
packet_name = 'explosion' packet_name = 'explosion'

View File

@ -8,12 +8,12 @@ from minecraft.networking.packets import Packet
class FacePlayerPacket(Packet): class FacePlayerPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x33 if context.protocol_version >= 741 else \ return 0x33 if context.protocol_later_eq(741) else \
0x34 if context.protocol_version >= 721 else \ 0x34 if context.protocol_later_eq(721) else \
0x35 if context.protocol_version >= 550 else \ 0x35 if context.protocol_later_eq(550) else \
0x34 if context.protocol_version >= 471 else \ 0x34 if context.protocol_later_eq(471) else \
0x32 if context.protocol_version >= 451 else \ 0x32 if context.protocol_later_eq(451) else \
0x31 if context.protocol_version >= 389 else \ 0x31 if context.protocol_later_eq(389) else \
0x30 0x30
packet_name = 'face player' packet_name = 'face player'
@ -21,14 +21,14 @@ class FacePlayerPacket(Packet):
@property @property
def fields(self): def fields(self):
return ('origin', 'x', 'y', 'z', 'entity_id', 'entity_origin') \ return ('origin', 'x', 'y', 'z', 'entity_id', 'entity_origin') \
if self.context.protocol_version >= 353 else \ if self.context.protocol_later_eq(353) else \
('entity_id', 'x', 'y', 'z') ('entity_id', 'x', 'y', 'z')
# Access the 'x', 'y', 'z' fields as a Vector tuple. # Access the 'x', 'y', 'z' fields as a Vector tuple.
target = multi_attribute_alias(Vector, 'x', 'y', 'z') target = multi_attribute_alias(Vector, 'x', 'y', 'z')
def read(self, file_object): def read(self, file_object):
if self.context.protocol_version >= 353: if self.context.protocol_later_eq(353):
self.origin = VarInt.read(file_object) self.origin = VarInt.read(file_object)
self.x = Double.read(file_object) self.x = Double.read(file_object)
self.y = Double.read(file_object) self.y = Double.read(file_object)
@ -51,7 +51,7 @@ class FacePlayerPacket(Packet):
self.z = Double.read(file_object) self.z = Double.read(file_object)
def write_fields(self, packet_buffer): def write_fields(self, packet_buffer):
if self.context.protocol_version >= 353: if self.context.protocol_later_eq(353):
VarInt.send(self.origin, packet_buffer) VarInt.send(self.origin, packet_buffer)
Double.send(self.x, packet_buffer) Double.send(self.x, packet_buffer)
Double.send(self.y, packet_buffer) Double.send(self.y, packet_buffer)

View File

@ -48,9 +48,9 @@ class AbstractDimensionPacket(Packet):
''' '''
def field_string(self, field): def field_string(self, field):
# pylint: disable=no-member # pylint: disable=no-member
if self.context.protocol_version >= 748 and field == 'dimension': if self.context.protocol_later_eq(748) and field == 'dimension':
return nbt_to_snbt(self.dimension) return nbt_to_snbt(self.dimension)
elif self.context.protocol_version < 718 and field == 'dimension': elif self.context.protocol_earlier(718) and field == 'dimension':
return Dimension.name_from_value(self.dimension) return Dimension.name_from_value(self.dimension)
return super(AbstractDimensionPacket, self).field_string(field) return super(AbstractDimensionPacket, self).field_string(field)
@ -58,43 +58,43 @@ class AbstractDimensionPacket(Packet):
class JoinGamePacket(AbstractDimensionPacket): class JoinGamePacket(AbstractDimensionPacket):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x24 if context.protocol_version >= 741 else \ return 0x24 if context.protocol_later_eq(741) else \
0x25 if context.protocol_version >= 721 else \ 0x25 if context.protocol_later_eq(721) else \
0x26 if context.protocol_version >= 550 else \ 0x26 if context.protocol_later_eq(550) else \
0x25 if context.protocol_version >= 389 else \ 0x25 if context.protocol_later_eq(389) else \
0x24 if context.protocol_version >= 345 else \ 0x24 if context.protocol_later_eq(345) else \
0x23 if context.protocol_version >= 332 else \ 0x23 if context.protocol_later_eq(332) else \
0x24 if context.protocol_version >= 318 else \ 0x24 if context.protocol_later_eq(318) else \
0x23 if context.protocol_version >= 107 else \ 0x23 if context.protocol_later_eq(107) else \
0x01 0x01
packet_name = "join game" packet_name = "join game"
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'entity_id': Integer}, {'entity_id': Integer},
{'is_hardcore': Boolean} if context.protocol_version >= 738 else {}, {'is_hardcore': Boolean} if context.protocol_later_eq(738) else {},
{'game_mode': UnsignedByte}, {'game_mode': UnsignedByte},
{'previous_game_mode': UnsignedByte} {'previous_game_mode': UnsignedByte}
if context.protocol_version >= 730 else {}, if context.protocol_later_eq(730) else {},
{'world_names': PrefixedArray(VarInt, String)} {'world_names': PrefixedArray(VarInt, String)}
if context.protocol_version >= 722 else {}, if context.protocol_later_eq(722) else {},
{'dimension_codec': NBT} {'dimension_codec': NBT}
if context.protocol_version >= 718 else {}, if context.protocol_later_eq(718) else {},
{'dimension': {'dimension':
NBT if context.protocol_version >= 748 else NBT if context.protocol_later_eq(748) else
String if context.protocol_version >= 718 else String if context.protocol_later_eq(718) else
Integer if context.protocol_version >= 108 else Integer if context.protocol_later_eq(108) else
Byte}, Byte},
{'world_name': String} if context.protocol_version >= 722 else {}, {'world_name': String} if context.protocol_later_eq(722) else {},
{'hashed_seed': Long} if context.protocol_version >= 552 else {}, {'hashed_seed': Long} if context.protocol_later_eq(552) else {},
{'difficulty': UnsignedByte} if context.protocol_version < 464 else {}, {'difficulty': UnsignedByte} if context.protocol_earlier(464) else {},
{'max_players': {'max_players':
VarInt if context.protocol_version >= 749 else UnsignedByte}, VarInt if context.protocol_later_eq(749) else UnsignedByte},
{'level_type': String} if context.protocol_version < 716 else {}, {'level_type': String} if context.protocol_earlier(716) else {},
{'render_distance': VarInt} if context.protocol_version >= 468 else {}, {'render_distance': VarInt} if context.protocol_later_eq(468) else {},
{'reduced_debug_info': Boolean}, {'reduced_debug_info': Boolean},
{'respawn_screen': Boolean} if context.protocol_version >= 571 else {}, {'respawn_screen': Boolean} if context.protocol_later_eq(571) else {},
{'is_debug': Boolean} if context.protocol_version >= 716 else {}, {'is_debug': Boolean} if context.protocol_later_eq(716) else {},
{'is_flat': Boolean} if context.protocol_version >= 716 else {}, {'is_flat': Boolean} if context.protocol_later_eq(716) else {},
]) ])
# These aliases declare the Enum type corresponding to each field: # These aliases declare the Enum type corresponding to each field:
@ -105,7 +105,7 @@ class JoinGamePacket(AbstractDimensionPacket):
# Can be set or deleted when 'context' is undefined. # Can be set or deleted when 'context' is undefined.
@property @property
def game_mode(self): def game_mode(self):
if self.context.protocol_version >= 738: if self.context.protocol_later_eq(738):
return self._game_mode_738 return self._game_mode_738
else: else:
return self._game_mode_0 return self._game_mode_0
@ -124,7 +124,7 @@ class JoinGamePacket(AbstractDimensionPacket):
# Can be set or deleted when 'context' is undefined. # Can be set or deleted when 'context' is undefined.
@property @property
def is_hardcore(self): def is_hardcore(self):
if self.context.protocol_version >= 738: if self.context.protocol_later_eq(738):
return self._is_hardcore return self._is_hardcore
else: else:
return bool(self._game_mode_0 & GameMode.HARDCORE) return bool(self._game_mode_0 & GameMode.HARDCORE)
@ -148,7 +148,7 @@ class JoinGamePacket(AbstractDimensionPacket):
# version-independently. Can be set or deleted when 'context' is undefined. # version-independently. Can be set or deleted when 'context' is undefined.
@property @property
def pure_game_mode(self): def pure_game_mode(self):
if self.context.protocol_version >= 738: if self.context.protocol_later_eq(738):
return self._game_mode_738 return self._game_mode_738
else: else:
return self._game_mode_0 & ~GameMode.HARDCORE return self._game_mode_0 & ~GameMode.HARDCORE
@ -170,37 +170,37 @@ class JoinGamePacket(AbstractDimensionPacket):
class RespawnPacket(AbstractDimensionPacket): class RespawnPacket(AbstractDimensionPacket):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x39 if context.protocol_version >= 741 else \ return 0x39 if context.protocol_later_eq(741) else \
0x3A if context.protocol_version >= 721 else \ 0x3A if context.protocol_later_eq(721) else \
0x3B if context.protocol_version >= 550 else \ 0x3B if context.protocol_later_eq(550) else \
0x3A if context.protocol_version >= 471 else \ 0x3A if context.protocol_later_eq(471) else \
0x38 if context.protocol_version >= 461 else \ 0x38 if context.protocol_later_eq(461) else \
0x39 if context.protocol_version >= 451 else \ 0x39 if context.protocol_later_eq(451) else \
0x38 if context.protocol_version >= 389 else \ 0x38 if context.protocol_later_eq(389) else \
0x37 if context.protocol_version >= 352 else \ 0x37 if context.protocol_later_eq(352) else \
0x36 if context.protocol_version >= 345 else \ 0x36 if context.protocol_later_eq(345) else \
0x35 if context.protocol_version >= 336 else \ 0x35 if context.protocol_later_eq(336) else \
0x34 if context.protocol_version >= 332 else \ 0x34 if context.protocol_later_eq(332) else \
0x35 if context.protocol_version >= 318 else \ 0x35 if context.protocol_later_eq(318) else \
0x33 if context.protocol_version >= 70 else \ 0x33 if context.protocol_later_eq(70) else \
0x07 0x07
packet_name = 'respawn' packet_name = 'respawn'
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'dimension': {'dimension':
NBT if context.protocol_version >= 748 else NBT if context.protocol_later_eq(748) else
String if context.protocol_version >= 718 else String if context.protocol_later_eq(718) else
Integer}, Integer},
{'world_name': String} if context.protocol_version >= 719 else {}, {'world_name': String} if context.protocol_later_eq(719) else {},
{'difficulty': UnsignedByte} if context.protocol_version < 464 else {}, {'difficulty': UnsignedByte} if context.protocol_earlier(464) else {},
{'hashed_seed': Long} if context.protocol_version >= 552 else {}, {'hashed_seed': Long} if context.protocol_later_eq(552) else {},
{'game_mode': UnsignedByte}, {'game_mode': UnsignedByte},
{'previous_game_mode': UnsignedByte} {'previous_game_mode': UnsignedByte}
if context.protocol_version >= 730 else {}, if context.protocol_later_eq(730) else {},
{'level_type': String} if context.protocol_version < 716 else {}, {'level_type': String} if context.protocol_earlier(716) else {},
{'is_debug': Boolean} if context.protocol_version >= 716 else {}, {'is_debug': Boolean} if context.protocol_later_eq(716) else {},
{'is_flat': Boolean} if context.protocol_version >= 716 else {}, {'is_flat': Boolean} if context.protocol_later_eq(716) else {},
{'copy_metadata': Boolean} if context.protocol_version >= 714 else {}, {'copy_metadata': Boolean} if context.protocol_later_eq(714) else {},
]) ])
# These aliases declare the Enum type corresponding to each field: # These aliases declare the Enum type corresponding to each field:

View File

@ -8,14 +8,14 @@ from minecraft.networking.types import (
class MapPacket(Packet): class MapPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x25 if context.protocol_version >= 741 else \ return 0x25 if context.protocol_later_eq(741) else \
0x26 if context.protocol_version >= 721 else \ 0x26 if context.protocol_later_eq(721) else \
0x27 if context.protocol_version >= 550 else \ 0x27 if context.protocol_later_eq(550) else \
0x26 if context.protocol_version >= 389 else \ 0x26 if context.protocol_later_eq(389) else \
0x25 if context.protocol_version >= 345 else \ 0x25 if context.protocol_later_eq(345) else \
0x24 if context.protocol_version >= 334 else \ 0x24 if context.protocol_later_eq(334) else \
0x25 if context.protocol_version >= 318 else \ 0x25 if context.protocol_later_eq(318) else \
0x24 if context.protocol_version >= 107 else \ 0x24 if context.protocol_later_eq(107) else \
0x34 0x34
packet_name = 'map' packet_name = 'map'
@ -23,9 +23,9 @@ class MapPacket(Packet):
@property @property
def fields(self): def fields(self):
fields = 'id', 'scale', 'icons', 'width', 'height', 'pixels' fields = 'id', 'scale', 'icons', 'width', 'height', 'pixels'
if self.context.protocol_version >= 107: if self.context.protocol_later_eq(107):
fields += 'is_tracking_position', fields += 'is_tracking_position',
if self.context.protocol_version >= 452: if self.context.protocol_later_eq(452):
fields += 'is_locked', fields += 'is_locked',
return fields return fields
@ -71,12 +71,12 @@ 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_version >= 107: if self.context.protocol_later_eq(107):
self.is_tracking_position = Boolean.read(file_object) self.is_tracking_position = Boolean.read(file_object)
else: else:
self.is_tracking_position = True self.is_tracking_position = True
if self.context.protocol_version >= 452: if self.context.protocol_later_eq(452):
self.is_locked = Boolean.read(file_object) self.is_locked = Boolean.read(file_object)
else: else:
self.is_locked = False self.is_locked = False
@ -84,15 +84,15 @@ class MapPacket(Packet):
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):
if self.context.protocol_version >= 373: if self.context.protocol_later_eq(373):
type = VarInt.read(file_object) type = VarInt.read(file_object)
else: else:
type, direction = divmod(UnsignedByte.read(file_object), 16) type, direction = divmod(UnsignedByte.read(file_object), 16)
x = Byte.read(file_object) x = Byte.read(file_object)
z = Byte.read(file_object) z = Byte.read(file_object)
if self.context.protocol_version >= 373: if self.context.protocol_later_eq(373):
direction = UnsignedByte.read(file_object) direction = UnsignedByte.read(file_object)
if self.context.protocol_version >= 364: if self.context.protocol_later_eq(364):
has_name = Boolean.read(file_object) has_name = Boolean.read(file_object)
display_name = String.read(file_object) if has_name else None display_name = String.read(file_object) if has_name else None
else: else:
@ -134,12 +134,12 @@ class MapPacket(Packet):
def write_fields(self, packet_buffer): def write_fields(self, packet_buffer):
VarInt.send(self.map_id, packet_buffer) VarInt.send(self.map_id, packet_buffer)
Byte.send(self.scale, packet_buffer) Byte.send(self.scale, packet_buffer)
if self.context.protocol_version >= 107: if self.context.protocol_later_eq(107):
Boolean.send(self.is_tracking_position, packet_buffer) Boolean.send(self.is_tracking_position, packet_buffer)
VarInt.send(len(self.icons), packet_buffer) VarInt.send(len(self.icons), packet_buffer)
for icon in self.icons: for icon in self.icons:
if self.context.protocol_version >= 373: if self.context.protocol_later_eq(373):
VarInt.send(icon.type, packet_buffer) VarInt.send(icon.type, packet_buffer)
else: else:
type_and_direction = (icon.type << 4) & 0xF0 type_and_direction = (icon.type << 4) & 0xF0
@ -147,9 +147,9 @@ class MapPacket(Packet):
UnsignedByte.send(type_and_direction, packet_buffer) UnsignedByte.send(type_and_direction, packet_buffer)
Byte.send(icon.location[0], packet_buffer) Byte.send(icon.location[0], packet_buffer)
Byte.send(icon.location[1], packet_buffer) Byte.send(icon.location[1], packet_buffer)
if self.context.protocol_version >= 373: if self.context.protocol_later_eq(373):
UnsignedByte.send(icon.direction, packet_buffer) UnsignedByte.send(icon.direction, packet_buffer)
if self.context.protocol_version >= 364: if self.context.protocol_later_eq(364):
Boolean.send(icon.display_name is not None, packet_buffer) Boolean.send(icon.display_name is not None, packet_buffer)
if icon.display_name is not None: if icon.display_name is not None:
String.send(icon.display_name, packet_buffer) String.send(icon.display_name, packet_buffer)

View File

@ -9,17 +9,17 @@ from minecraft.networking.types import (
class PlayerListItemPacket(Packet): class PlayerListItemPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x32 if context.protocol_version >= 741 else \ return 0x32 if context.protocol_later_eq(741) else \
0x33 if context.protocol_version >= 721 else \ 0x33 if context.protocol_later_eq(721) else \
0x34 if context.protocol_version >= 550 else \ 0x34 if context.protocol_later_eq(550) else \
0x33 if context.protocol_version >= 471 else \ 0x33 if context.protocol_later_eq(471) else \
0x31 if context.protocol_version >= 451 else \ 0x31 if context.protocol_later_eq(451) else \
0x30 if context.protocol_version >= 389 else \ 0x30 if context.protocol_later_eq(389) else \
0x2F if context.protocol_version >= 345 else \ 0x2F if context.protocol_later_eq(345) else \
0x2E if context.protocol_version >= 336 else \ 0x2E if context.protocol_later_eq(336) else \
0x2D if context.protocol_version >= 332 else \ 0x2D if context.protocol_later_eq(332) else \
0x2E if context.protocol_version >= 318 else \ 0x2E if context.protocol_later_eq(318) else \
0x2D if context.protocol_version >= 107 else \ 0x2D if context.protocol_later_eq(107) else \
0x38 0x38
packet_name = "player list item" packet_name = "player list item"

View File

@ -9,18 +9,18 @@ from minecraft.networking.types import (
class PlayerPositionAndLookPacket(Packet, BitFieldEnum): class PlayerPositionAndLookPacket(Packet, BitFieldEnum):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x34 if context.protocol_version >= 741 else \ return 0x34 if context.protocol_later_eq(741) else \
0x35 if context.protocol_version >= 721 else \ 0x35 if context.protocol_later_eq(721) else \
0x36 if context.protocol_version >= 550 else \ 0x36 if context.protocol_later_eq(550) else \
0x35 if context.protocol_version >= 471 else \ 0x35 if context.protocol_later_eq(471) else \
0x33 if context.protocol_version >= 451 else \ 0x33 if context.protocol_later_eq(451) else \
0x32 if context.protocol_version >= 389 else \ 0x32 if context.protocol_later_eq(389) else \
0x31 if context.protocol_version >= 352 else \ 0x31 if context.protocol_later_eq(352) else \
0x30 if context.protocol_version >= 345 else \ 0x30 if context.protocol_later_eq(345) else \
0x2F if context.protocol_version >= 336 else \ 0x2F if context.protocol_later_eq(336) else \
0x2E if context.protocol_version >= 332 else \ 0x2E if context.protocol_later_eq(332) else \
0x2F if context.protocol_version >= 318 else \ 0x2F if context.protocol_later_eq(318) else \
0x2E if context.protocol_version >= 70 else \ 0x2E if context.protocol_later_eq(70) else \
0x08 0x08
packet_name = "player position and look" packet_name = "player position and look"
@ -31,7 +31,7 @@ class PlayerPositionAndLookPacket(Packet, BitFieldEnum):
{'yaw': Float}, {'yaw': Float},
{'pitch': Float}, {'pitch': Float},
{'flags': Byte}, {'flags': Byte},
{'teleport_id': VarInt} if context.protocol_version >= 107 else {}, {'teleport_id': VarInt} if context.protocol_later_eq(107) else {},
]) ])
# Access the 'x', 'y', 'z' fields as a Vector tuple. # Access the 'x', 'y', 'z' fields as a Vector tuple.

View File

@ -9,18 +9,18 @@ __all__ = 'SoundEffectPacket',
class SoundEffectPacket(Packet): class SoundEffectPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x51 if context.protocol_version >= 721 else \ return 0x51 if context.protocol_later_eq(721) else \
0x52 if context.protocol_version >= 550 else \ 0x52 if context.protocol_later_eq(550) else \
0x51 if context.protocol_version >= 471 else \ 0x51 if context.protocol_later_eq(471) else \
0x4D if context.protocol_version >= 461 else \ 0x4D if context.protocol_later_eq(461) else \
0x4E if context.protocol_version >= 451 else \ 0x4E if context.protocol_later_eq(451) else \
0x4D if context.protocol_version >= 389 else \ 0x4D if context.protocol_later_eq(389) else \
0x4C if context.protocol_version >= 352 else \ 0x4C if context.protocol_later_eq(352) else \
0x4B if context.protocol_version >= 345 else \ 0x4B if context.protocol_later_eq(345) else \
0x4A if context.protocol_version >= 343 else \ 0x4A if context.protocol_later_eq(343) else \
0x49 if context.protocol_version >= 336 else \ 0x49 if context.protocol_later_eq(336) else \
0x48 if context.protocol_version >= 318 else \ 0x48 if context.protocol_later_eq(318) else \
0x46 if context.protocol_version >= 110 else \ 0x46 if context.protocol_later_eq(110) else \
0x47 0x47
packet_name = 'sound effect' packet_name = 'sound effect'
@ -29,13 +29,16 @@ class SoundEffectPacket(Packet):
def get_definition(context): def get_definition(context):
return [ return [
({'sound_category': VarInt} ({'sound_category': VarInt}
if 326 > context.protocol_version >= 321 else {}), if context.protocol_later_eq(321)
and context.protocol_earlier(326) else {}),
{'sound_id': VarInt}, {'sound_id': VarInt},
({'sound_category': VarInt} ({'sound_category': VarInt}
if 95 <= context.protocol_version < 321 if context.protocol_later_eq(95)
or context.protocol_version >= 326 else {}), and context.protocol_earlier(321)
or context.protocol_later_eq(326) else {}),
({'parroted_entity_type': String} ({'parroted_entity_type': String}
if 326 > context.protocol_version >= 321 else {}), if context.protocol_later_eq(321)
and context.protocol_earlier(326) else {}),
{'effect_position': SoundEffectPacket.EffectPosition}, {'effect_position': SoundEffectPacket.EffectPosition},
{'volume': Float}, {'volume': Float},
{'pitch': SoundEffectPacket.Pitch}, {'pitch': SoundEffectPacket.Pitch},
@ -66,19 +69,19 @@ class SoundEffectPacket(Packet):
class Pitch(Type): class Pitch(Type):
@staticmethod @staticmethod
def read_with_context(file_object, context): def read_with_context(file_object, context):
if context.protocol_version >= 201: if context.protocol_later_eq(201):
value = Float.read(file_object) value = Float.read(file_object)
else: else:
value = Byte.read(file_object) value = Byte.read(file_object)
if context.protocol_version < 204: if context.protocol_earlier(204):
value /= 63.5 value /= 63.5
return value return value
@staticmethod @staticmethod
def send_with_context(value, socket, context): def send_with_context(value, socket, context):
if context.protocol_version < 204: if context.protocol_earlier(204):
value *= 63.5 value *= 63.5
if context.protocol_version >= 201: if context.protocol_later_eq(201):
Float.send(value, socket) Float.send(value, socket)
else: else:
Byte.send(int(value), socket) Byte.send(int(value), socket)

View File

@ -10,7 +10,7 @@ from minecraft.networking.types import (
class SpawnObjectPacket(Packet): class SpawnObjectPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x00 if context.protocol_version >= 67 else \ return 0x00 if context.protocol_later_eq(67) else \
0x0E 0x0E
packet_name = 'spawn object' packet_name = 'spawn object'
@ -43,43 +43,44 @@ class SpawnObjectPacket(Packet):
if field != 'type_id' or context is None: if field != 'type_id' or context is None:
return return
pv = context.protocol_version name = 'EntityType_%d' % context.protocol_version
name = 'EntityType_%d' % pv
if hasattr(cls, name): if hasattr(cls, name):
return getattr(cls, name) return getattr(cls, name)
era = 0 if context.protocol_earlier(458) else 1
class EntityType(Enum): class EntityType(Enum):
# XXX This has not been updated for >= v1.15 # XXX This has not been updated for >= v1.15
ACTIVATED_TNT = 50 if pv < 458 else 55 # PrimedTnt ACTIVATED_TNT = (50, 55)[era] # PrimedTnt
AREA_EFFECT_CLOUD = 3 if pv < 458 else 0 AREA_EFFECT_CLOUD = ( 3, 0)[era]
ARMORSTAND = 78 if pv < 458 else 1 ARMORSTAND = (78, 1)[era]
ARROW = 60 if pv < 458 else 2 ARROW = (60, 2)[era]
BOAT = 1 if pv < 458 else 5 BOAT = ( 1, 5)[era]
DRAGON_FIREBALL = 93 if pv < 458 else 13 DRAGON_FIREBALL = (93, 13)[era]
EGG = 62 if pv < 458 else 74 # ThrownEgg EGG = (62, 74)[era] # ThrownEgg
ENDERCRYSTAL = 51 if pv < 458 else 16 ENDERCRYSTAL = (51, 16)[era]
ENDERPEARL = 65 if pv < 458 else 75 # ThrownEnderpearl ENDERPEARL = (65, 75)[era] # ThrownEnderpearl
EVOCATION_FANGS = 79 if pv < 458 else 20 EVOCATION_FANGS = (79, 20)[era]
EXP_BOTTLE = 75 if pv < 458 else 76 # ThrownExpBottle EXP_BOTTLE = (75, 76)[era] # ThrownExpBottle
EYE_OF_ENDER = 72 if pv < 458 else 23 # EyeOfEnderSignal EYE_OF_ENDER = (72, 23)[era] # EyeOfEnderSignal
FALLING_OBJECT = 70 if pv < 458 else 24 # FallingSand FALLING_OBJECT = (70, 24)[era] # FallingSand
FIREBALL = 63 if pv < 458 else 34 # Fireball (ghast) FIREBALL = (63, 34)[era] # Fireball (ghast)
FIRECHARGE = 64 if pv < 458 else 65 # SmallFireball (blaze) FIRECHARGE = (64, 65)[era] # SmallFireball (blaze)
FIREWORK_ROCKET = 76 if pv < 458 else 25 # FireworksRocketEntity FIREWORK_ROCKET = (76, 25)[era] # FireworksRocketEntity
FISHING_HOOK = 90 if pv < 458 else 93 # Fishing bobber FISHING_HOOK = (90, 93)[era] # Fishing bobber
ITEM_FRAMES = 71 if pv < 458 else 33 # ItemFrame ITEM_FRAMES = (71, 33)[era] # ItemFrame
ITEM_STACK = 2 if pv < 458 else 32 # Item ITEM_STACK = ( 2, 32)[era] # Item
LEASH_KNOT = 77 if pv < 458 else 35 LEASH_KNOT = (77, 35)[era]
LLAMA_SPIT = 68 if pv < 458 else 37 LLAMA_SPIT = (68, 37)[era]
MINECART = 10 if pv < 458 else 39 # MinecartRideable MINECART = (10, 39)[era] # MinecartRideable
POTION = 73 if pv < 458 else 77 # ThrownPotion POTION = (73, 77)[era] # ThrownPotion
SHULKER_BULLET = 67 if pv < 458 else 60 SHULKER_BULLET = (67, 60)[era]
SNOWBALL = 61 if pv < 458 else 67 SNOWBALL = (61, 67)[era]
SPECTRAL_ARROW = 91 if pv < 458 else 68 SPECTRAL_ARROW = (91, 68)[era]
WITHER_SKULL = 66 if pv < 458 else 85 WITHER_SKULL = (66, 85)[era]
if pv >= 393: if context.protocol_later_eq(393):
TRIDENT = 94 TRIDENT = 94
if pv >= 458: if context.protocol_later_eq(458):
MINECART_CHEST = 40 MINECART_CHEST = 40
MINECART_COMMAND_BLOCK = 41 MINECART_COMMAND_BLOCK = 41
MINECART_FURNACE = 42 MINECART_FURNACE = 42
@ -92,44 +93,44 @@ class SpawnObjectPacket(Packet):
def read(self, file_object): def read(self, file_object):
self.entity_id = VarInt.read(file_object) self.entity_id = VarInt.read(file_object)
if self.context.protocol_version >= 49: if self.context.protocol_later_eq(49):
self.object_uuid = UUID.read(file_object) self.object_uuid = UUID.read(file_object)
if self.context.protocol_version >= 458: if self.context.protocol_later_eq(458):
self.type_id = VarInt.read(file_object) self.type_id = VarInt.read(file_object)
else: else:
self.type_id = Byte.read(file_object) self.type_id = Byte.read(file_object)
xyz_type = Double if self.context.protocol_version >= 100 else Integer xyz_type = Double if self.context.protocol_later_eq(100) else Integer
for attr in 'x', 'y', 'z': for attr in 'x', 'y', 'z':
setattr(self, attr, xyz_type.read(file_object)) setattr(self, attr, xyz_type.read(file_object))
for attr in 'pitch', 'yaw': for attr in 'pitch', 'yaw':
setattr(self, attr, Angle.read(file_object)) setattr(self, attr, Angle.read(file_object))
self.data = Integer.read(file_object) self.data = Integer.read(file_object)
if self.context.protocol_version >= 49 or self.data > 0: if self.context.protocol_later_eq(49) or self.data > 0:
for attr in 'velocity_x', 'velocity_y', 'velocity_z': for attr in 'velocity_x', 'velocity_y', 'velocity_z':
setattr(self, attr, Short.read(file_object)) setattr(self, attr, Short.read(file_object))
def write_fields(self, packet_buffer): def write_fields(self, packet_buffer):
VarInt.send(self.entity_id, packet_buffer) VarInt.send(self.entity_id, packet_buffer)
if self.context.protocol_version >= 49: if self.context.protocol_later_eq(49):
UUID.send(self.object_uuid, packet_buffer) UUID.send(self.object_uuid, packet_buffer)
if self.context.protocol_version >= 458: if self.context.protocol_later_eq(458):
VarInt.send(self.type_id, packet_buffer) VarInt.send(self.type_id, packet_buffer)
else: else:
Byte.send(self.type_id, packet_buffer) Byte.send(self.type_id, packet_buffer)
# pylint: disable=no-member # pylint: disable=no-member
xyz_type = Double if self.context.protocol_version >= 100 else Integer xyz_type = Double if self.context.protocol_later_eq(100) else Integer
for coord in self.x, self.y, self.z: for coord in self.x, self.y, self.z:
xyz_type.send(coord, packet_buffer) xyz_type.send(coord, packet_buffer)
for coord in self.pitch, self.yaw: for coord in self.pitch, self.yaw:
Angle.send(coord, packet_buffer) Angle.send(coord, packet_buffer)
Integer.send(self.data, packet_buffer) Integer.send(self.data, packet_buffer)
if self.context.protocol_version >= 49 or self.data > 0: if self.context.protocol_later_eq(49) or self.data > 0:
for coord in self.velocity_x, self.velocity_y, self.velocity_z: for coord in self.velocity_x, self.velocity_y, self.velocity_z:
Short.send(coord, packet_buffer) Short.send(coord, packet_buffer)

View File

@ -8,7 +8,7 @@ from minecraft.networking.types import (
class AbstractKeepAlivePacket(Packet): class AbstractKeepAlivePacket(Packet):
packet_name = "keep alive" packet_name = "keep alive"
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'keep_alive_id': Long} if context.protocol_version >= 339 {'keep_alive_id': Long} if context.protocol_later_eq(339)
else {'keep_alive_id': VarInt} else {'keep_alive_id': VarInt}
]) ])

View File

@ -11,7 +11,7 @@ def get_packets(context):
LoginStartPacket, LoginStartPacket,
EncryptionResponsePacket EncryptionResponsePacket
} }
if context.protocol_version >= 385: if context.protocol_later_eq(385):
packets |= { packets |= {
PluginResponsePacket PluginResponsePacket
} }
@ -21,8 +21,8 @@ def get_packets(context):
class LoginStartPacket(Packet): class LoginStartPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x00 if context.protocol_version >= 391 else \ return 0x00 if context.protocol_later_eq(391) else \
0x01 if context.protocol_version >= 385 else \ 0x01 if context.protocol_later_eq(385) else \
0x00 0x00
packet_name = "login start" packet_name = "login start"
@ -33,8 +33,8 @@ class LoginStartPacket(Packet):
class EncryptionResponsePacket(Packet): class EncryptionResponsePacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x01 if context.protocol_version >= 391 else \ return 0x01 if context.protocol_later_eq(391) else \
0x02 if context.protocol_version >= 385 else \ 0x02 if context.protocol_later_eq(385) else \
0x01 0x01
packet_name = "encryption response" packet_name = "encryption response"
@ -50,7 +50,7 @@ class PluginResponsePacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x02 if context.protocol_version >= 391 else \ return 0x02 if context.protocol_later_eq(391) else \
0x00 0x00
packet_name = 'login plugin response' packet_name = 'login plugin response'

View File

@ -23,11 +23,11 @@ def get_packets(context):
PluginMessagePacket, PluginMessagePacket,
PlayerBlockPlacementPacket, PlayerBlockPlacementPacket,
} }
if context.protocol_version >= 69: if context.protocol_later_eq(69):
packets |= { packets |= {
UseItemPacket, UseItemPacket,
} }
if context.protocol_version >= 107: if context.protocol_later_eq(107):
packets |= { packets |= {
TeleportConfirmPacket, TeleportConfirmPacket,
} }
@ -37,33 +37,33 @@ def get_packets(context):
class KeepAlivePacket(AbstractKeepAlivePacket): class KeepAlivePacket(AbstractKeepAlivePacket):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x10 if context.protocol_version >= 712 else \ return 0x10 if context.protocol_later_eq(712) else \
0x0F if context.protocol_version >= 471 else \ 0x0F if context.protocol_later_eq(471) else \
0x10 if context.protocol_version >= 464 else \ 0x10 if context.protocol_later_eq(464) else \
0x0E if context.protocol_version >= 389 else \ 0x0E if context.protocol_later_eq(389) else \
0x0C if context.protocol_version >= 386 else \ 0x0C if context.protocol_later_eq(386) else \
0x0B if context.protocol_version >= 345 else \ 0x0B if context.protocol_later_eq(345) else \
0x0A if context.protocol_version >= 343 else \ 0x0A if context.protocol_later_eq(343) else \
0x0B if context.protocol_version >= 336 else \ 0x0B if context.protocol_later_eq(336) else \
0x0C if context.protocol_version >= 318 else \ 0x0C if context.protocol_later_eq(318) else \
0x0B if context.protocol_version >= 107 else \ 0x0B if context.protocol_later_eq(107) else \
0x00 0x00
class ChatPacket(Packet): class ChatPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x03 if context.protocol_version >= 464 else \ return 0x03 if context.protocol_later_eq(464) else \
0x02 if context.protocol_version >= 389 else \ 0x02 if context.protocol_later_eq(389) else \
0x01 if context.protocol_version >= 343 else \ 0x01 if context.protocol_later_eq(343) else \
0x02 if context.protocol_version >= 336 else \ 0x02 if context.protocol_later_eq(336) else \
0x03 if context.protocol_version >= 318 else \ 0x03 if context.protocol_later_eq(318) else \
0x02 if context.protocol_version >= 107 else \ 0x02 if context.protocol_later_eq(107) else \
0x01 0x01
@staticmethod @staticmethod
def get_max_length(context): def get_max_length(context):
return 256 if context.protocol_version >= 306 else \ return 256 if context.protocol_later_eq(306) else \
100 100
@property @property
@ -79,17 +79,17 @@ class ChatPacket(Packet):
class PositionAndLookPacket(Packet): class PositionAndLookPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x13 if context.protocol_version >= 712 else \ return 0x13 if context.protocol_later_eq(712) else \
0x12 if context.protocol_version >= 471 else \ 0x12 if context.protocol_later_eq(471) else \
0x13 if context.protocol_version >= 464 else \ 0x13 if context.protocol_later_eq(464) else \
0x11 if context.protocol_version >= 389 else \ 0x11 if context.protocol_later_eq(389) else \
0x0F if context.protocol_version >= 386 else \ 0x0F if context.protocol_later_eq(386) else \
0x0E if context.protocol_version >= 345 else \ 0x0E if context.protocol_later_eq(345) else \
0x0D if context.protocol_version >= 343 else \ 0x0D if context.protocol_later_eq(343) else \
0x0E if context.protocol_version >= 336 else \ 0x0E if context.protocol_later_eq(336) else \
0x0F if context.protocol_version >= 332 else \ 0x0F if context.protocol_later_eq(332) else \
0x0E if context.protocol_version >= 318 else \ 0x0E if context.protocol_later_eq(318) else \
0x0D if context.protocol_version >= 107 else \ 0x0D if context.protocol_later_eq(107) else \
0x06 0x06
packet_name = "position and look" packet_name = "position and look"
@ -126,22 +126,22 @@ class TeleportConfirmPacket(Packet):
class AnimationPacket(Packet): class AnimationPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x2C if context.protocol_version >= 738 else \ return 0x2C if context.protocol_later_eq(738) else \
0x2B if context.protocol_version >= 712 else \ 0x2B if context.protocol_later_eq(712) else \
0x2A if context.protocol_version >= 468 else \ 0x2A if context.protocol_later_eq(468) else \
0x29 if context.protocol_version >= 464 else \ 0x29 if context.protocol_later_eq(464) else \
0x27 if context.protocol_version >= 389 else \ 0x27 if context.protocol_later_eq(389) else \
0x25 if context.protocol_version >= 386 else \ 0x25 if context.protocol_later_eq(386) else \
0x1D if context.protocol_version >= 345 else \ 0x1D if context.protocol_later_eq(345) else \
0x1C if context.protocol_version >= 343 else \ 0x1C if context.protocol_later_eq(343) else \
0x1D if context.protocol_version >= 332 else \ 0x1D if context.protocol_later_eq(332) else \
0x1C if context.protocol_version >= 318 else \ 0x1C if context.protocol_later_eq(318) else \
0x1A if context.protocol_version >= 107 else \ 0x1A if context.protocol_later_eq(107) else \
0x0A 0x0A
packet_name = "animation" packet_name = "animation"
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'hand': VarInt} if context.protocol_version >= 107 else {}]) {'hand': VarInt} if context.protocol_later_eq(107) else {}])
Hand = RelativeHand Hand = RelativeHand
HAND_MAIN, HAND_OFF = Hand.MAIN, Hand.OFF # For backward compatibility. HAND_MAIN, HAND_OFF = Hand.MAIN, Hand.OFF # For backward compatibility.
@ -150,14 +150,14 @@ class AnimationPacket(Packet):
class ClientStatusPacket(Packet, Enum): class ClientStatusPacket(Packet, Enum):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x04 if context.protocol_version >= 464 else \ return 0x04 if context.protocol_later_eq(464) else \
0x03 if context.protocol_version >= 389 else \ 0x03 if context.protocol_later_eq(389) else \
0x02 if context.protocol_version >= 343 else \ 0x02 if context.protocol_later_eq(343) else \
0x03 if context.protocol_version >= 336 else \ 0x03 if context.protocol_later_eq(336) else \
0x04 if context.protocol_version >= 318 else \ 0x04 if context.protocol_later_eq(318) else \
0x03 if context.protocol_version >= 80 else \ 0x03 if context.protocol_later_eq(80) else \
0x02 if context.protocol_version >= 67 else \ 0x02 if context.protocol_later_eq(67) else \
0x17 if context.protocol_version >= 49 else \ 0x17 if context.protocol_later_eq(49) else \
0x16 0x16
packet_name = "client status" packet_name = "client status"
@ -175,13 +175,13 @@ class ClientStatusPacket(Packet, Enum):
class PluginMessagePacket(AbstractPluginMessagePacket): class PluginMessagePacket(AbstractPluginMessagePacket):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x0B if context.protocol_version >= 464 else \ return 0x0B if context.protocol_later_eq(464) else \
0x0A if context.protocol_version >= 389 else \ 0x0A if context.protocol_later_eq(389) else \
0x09 if context.protocol_version >= 345 else \ 0x09 if context.protocol_later_eq(345) else \
0x08 if context.protocol_version >= 343 else \ 0x08 if context.protocol_later_eq(343) else \
0x09 if context.protocol_version >= 336 else \ 0x09 if context.protocol_later_eq(336) else \
0x0A if context.protocol_version >= 317 else \ 0x0A if context.protocol_later_eq(317) else \
0x09 if context.protocol_version >= 94 else \ 0x09 if context.protocol_later_eq(94) else \
0x17 0x17
@ -201,17 +201,17 @@ class PlayerBlockPlacementPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x2E if context.protocol_version >= 738 else \ return 0x2E if context.protocol_later_eq(738) else \
0x2D if context.protocol_version >= 712 else \ 0x2D if context.protocol_later_eq(712) else \
0x2C if context.protocol_version >= 468 else \ 0x2C if context.protocol_later_eq(468) else \
0x2B if context.protocol_version >= 464 else \ 0x2B if context.protocol_later_eq(464) else \
0x29 if context.protocol_version >= 389 else \ 0x29 if context.protocol_later_eq(389) else \
0x27 if context.protocol_version >= 386 else \ 0x27 if context.protocol_later_eq(386) else \
0x1F if context.protocol_version >= 345 else \ 0x1F if context.protocol_later_eq(345) else \
0x1E if context.protocol_version >= 343 else \ 0x1E if context.protocol_later_eq(343) else \
0x1F if context.protocol_version >= 332 else \ 0x1F if context.protocol_later_eq(332) else \
0x1E if context.protocol_version >= 318 else \ 0x1E if context.protocol_later_eq(318) else \
0x1C if context.protocol_version >= 94 else \ 0x1C if context.protocol_later_eq(94) else \
0x08 0x08
packet_name = 'player block placement' packet_name = 'player block placement'
@ -219,15 +219,15 @@ class PlayerBlockPlacementPacket(Packet):
@staticmethod @staticmethod
def get_definition(context): def get_definition(context):
return [ return [
{'hand': VarInt} if context.protocol_version >= 453 else {}, {'hand': VarInt} if context.protocol_later_eq(453) else {},
{'location': Position}, {'location': Position},
{'face': VarInt if context.protocol_version >= 69 else Byte}, {'face': VarInt if context.protocol_later_eq(69) else Byte},
{'hand': VarInt} if context.protocol_version < 453 else {}, {'hand': VarInt} if context.protocol_earlier(453) else {},
{'x': Float if context.protocol_version >= 309 else Byte}, {'x': Float if context.protocol_later_eq(309) else Byte},
{'y': Float if context.protocol_version >= 309 else Byte}, {'y': Float if context.protocol_later_eq(309) else Byte},
{'z': Float if context.protocol_version >= 309 else Byte}, {'z': Float if context.protocol_later_eq(309) else Byte},
({'inside_block': Boolean} ({'inside_block': Boolean}
if context.protocol_version >= 453 else {}), if context.protocol_later_eq(453) else {}),
] ]
# PlayerBlockPlacementPacket.Hand is an alias for RelativeHand. # PlayerBlockPlacementPacket.Hand is an alias for RelativeHand.
@ -240,18 +240,18 @@ class PlayerBlockPlacementPacket(Packet):
class UseItemPacket(Packet): class UseItemPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x2F if context.protocol_version >= 738 else \ return 0x2F if context.protocol_later_eq(738) else \
0x2E if context.protocol_version >= 712 else \ 0x2E if context.protocol_later_eq(712) else \
0x2D if context.protocol_version >= 468 else \ 0x2D if context.protocol_later_eq(468) else \
0x2C if context.protocol_version >= 464 else \ 0x2C if context.protocol_later_eq(464) else \
0x2A if context.protocol_version >= 389 else \ 0x2A if context.protocol_later_eq(389) else \
0x28 if context.protocol_version >= 386 else \ 0x28 if context.protocol_later_eq(386) else \
0x20 if context.protocol_version >= 345 else \ 0x20 if context.protocol_later_eq(345) else \
0x1F if context.protocol_version >= 343 else \ 0x1F if context.protocol_later_eq(343) else \
0x20 if context.protocol_version >= 332 else \ 0x20 if context.protocol_later_eq(332) else \
0x1F if context.protocol_version >= 318 else \ 0x1F if context.protocol_later_eq(318) else \
0x1D if context.protocol_version >= 94 else \ 0x1D if context.protocol_later_eq(94) else \
0x1A if context.protocol_version >= 70 else \ 0x1A if context.protocol_later_eq(70) else \
0x08 0x08
packet_name = "use item" packet_name = "use item"

View File

@ -8,12 +8,12 @@ from minecraft.networking.types import (
class ClientSettingsPacket(Packet): class ClientSettingsPacket(Packet):
@staticmethod @staticmethod
def get_id(context): def get_id(context):
return 0x05 if context.protocol_version >= 464 else \ return 0x05 if context.protocol_later_eq(464) else \
0x04 if context.protocol_version >= 389 else \ 0x04 if context.protocol_later_eq(389) else \
0x03 if context.protocol_version >= 343 else \ 0x03 if context.protocol_later_eq(343) else \
0x04 if context.protocol_version >= 336 else \ 0x04 if context.protocol_later_eq(336) else \
0x05 if context.protocol_version >= 318 else \ 0x05 if context.protocol_later_eq(318) else \
0x04 if context.protocol_version >= 94 else \ 0x04 if context.protocol_later_eq(94) else \
0x15 0x15
packet_name = 'client settings' packet_name = 'client settings'
@ -21,10 +21,10 @@ class ClientSettingsPacket(Packet):
get_definition = staticmethod(lambda context: [ get_definition = staticmethod(lambda context: [
{'locale': String}, {'locale': String},
{'view_distance': Byte}, {'view_distance': Byte},
{'chat_mode': VarInt if context.protocol_version > 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_version > 49 else {}]) {'main_hand': VarInt} if context.protocol_later(49) else {}])
field_enum = classmethod( field_enum = classmethod(
lambda cls, field, context: { lambda cls, field, context: {

View File

@ -314,7 +314,7 @@ class Position(Type, Vector):
location = UnsignedLong.read(file_object) location = UnsignedLong.read(file_object)
x = int(location >> 38) # 26 most significant bits x = int(location >> 38) # 26 most significant bits
if context.protocol_version >= 443: if context.protocol_later_eq(443):
z = int((location >> 12) & 0x3FFFFFF) # 26 intermediate bits z = int((location >> 12) & 0x3FFFFFF) # 26 intermediate bits
y = int(location & 0xFFF) # 12 least signficant bits y = int(location & 0xFFF) # 12 least signficant bits
else: else:
@ -337,7 +337,7 @@ class Position(Type, Vector):
# 'position' can be either a tuple or Position object. # 'position' can be either a tuple or Position object.
x, y, z = position x, y, z = position
value = ((x & 0x3FFFFFF) << 38 | (z & 0x3FFFFFF) << 12 | (y & 0xFFF) value = ((x & 0x3FFFFFF) << 38 | (z & 0x3FFFFFF) << 12 | (y & 0xFFF)
if context.protocol_version >= 443 else if context.protocol_later_eq(443) else
(x & 0x3FFFFFF) << 38 | (y & 0xFFF) << 26 | (z & 0x3FFFFFF)) (x & 0x3FFFFFF) << 38 | (y & 0xFFF) << 26 | (z & 0x3FFFFFF))
UnsignedLong.send(value, socket) UnsignedLong.send(value, socket)

View File

@ -1,16 +1,12 @@
"""Minecraft data types that are used by packets, but don't have a specific """Minecraft data types that are used by packets, but don't have a specific
network representation. network representation.
""" """
import types
from collections import namedtuple from collections import namedtuple
from itertools import chain
# These aliases are retained for backward compatibility
__all__ = ( from minecraft.utility import ( # noqa: F401
'Vector', 'MutableRecord', 'Direction', 'PositionAndLook', 'descriptor', descriptor, overridable_descriptor, overridable_property, attribute_alias,
'overridable_descriptor', 'overridable_property', 'attribute_alias', multi_attribute_alias, attribute_transform, class_and_instancemethod,
'multi_attribute_alias', 'attribute_transform',
) )
@ -92,161 +88,6 @@ class MutableRecord(object):
yield slot yield slot
def attribute_transform(name, from_orig, to_orig):
"""An attribute descriptor that provides a view of a different attribute
with a given name via a given transformation and its given inverse."""
return property(
fget=(lambda self: from_orig(getattr(self, name))),
fset=(lambda self, value: setattr(self, name, to_orig(value))),
fdel=(lambda self: delattr(self, name)))
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)
def multi_attribute_alias(container, *arg_names, **kwd_names):
"""A descriptor for an attribute whose value is a container of a given type
with several fields, each of which is aliased to a different attribute
of the parent object.
The 'n'th name in 'arg_names' is the parent attribute that will be
aliased to the field of 'container' settable by the 'n'th positional
argument to its constructor, and accessible as its 'n'th iterable
element.
As a special case, 'tuple' may be given as the 'container' when there
are positional arguments, and (even though the tuple constructor does
not take positional arguments), the arguments will be aliased to the
corresponding positions in a tuple.
The name in 'kwd_names' mapped to by the key 'k' is the parent attribute
that will be aliased to the field of 'container' settable by the keyword
argument 'k' to its constructor, and accessible as its 'k' attribute.
"""
if container is tuple:
container = lambda *args: args # noqa: E731
@property
def alias(self):
return container(
*(getattr(self, name) for name in arg_names),
**{kwd: getattr(self, name) for (kwd, name) in kwd_names.items()})
@alias.setter
def alias(self, values):
if arg_names:
for name, value in zip(arg_names, values):
setattr(self, name, value)
for kwd, name in kwd_names.items():
setattr(self, name, getattr(values, kwd))
@alias.deleter
def alias(self):
for name in chain(arg_names, kwd_names.values()):
delattr(self, name)
return alias
class overridable_descriptor:
"""As 'descriptor' (defined below), except that only a getter can be
defined, and the resulting descriptor has no '__set__' or '__delete__'
methods defined; hence, attributes defined via this class can be
overridden by attributes of instances of the class in which it occurs.
"""
__slots__ = '_fget',
def __init__(self, fget=None):
self._fget = fget if fget is not None else self._default_get
def getter(self, fget):
self._fget = fget
return self
@staticmethod
def _default_get(instance, owner):
raise AttributeError('unreadable attribute')
def __get__(self, instance, owner):
return self._fget(self, instance, owner)
class overridable_property(overridable_descriptor):
"""As the builtin 'property' decorator of Python, except that only
a getter is defined and the resulting descriptor is a non-data
descriptor, overridable by attributes of instances of the class
in which the property occurs. See also 'overridable_descriptor' above.
"""
def __get__(self, instance, _owner):
return self._fget(instance)
class descriptor(overridable_descriptor):
"""Behaves identically to the builtin 'property' decorator of Python,
except that the getter, setter and deleter functions given by the
user are used as the raw __get__, __set__ and __delete__ functions
as defined in Python's descriptor protocol.
Since an instance of this class always havs '__set__' and '__delete__'
defined, it is a "data descriptor", so its binding behaviour cannot be
overridden in instances of the class in which it occurs. See
https://docs.python.org/3/reference/datamodel.html#descriptor-invocation
for more information. See also 'overridable_descriptor' above.
"""
__slots__ = '_fset', '_fdel'
def __init__(self, fget=None, fset=None, fdel=None):
super(descriptor, self).__init__(fget=fget)
self._fset = fset if fset is not None else self._default_set
self._fdel = fdel if fdel is not None else self._default_del
def setter(self, fset):
self._fset = fset
return self
def deleter(self, fdel):
self._fdel = fdel
return self
@staticmethod
def _default_set(instance, value):
raise AttributeError("can't set attribute")
@staticmethod
def _default_del(instance):
raise AttributeError("can't delete attribute")
def __set__(self, instance, value):
return self._fset(self, instance, value)
def __delete__(self, instance):
return self._fdel(self, instance)
class class_and_instancemethod:
""" A decorator for functions defined in a class namespace which are to be
accessed as both class and instance methods: retrieving the method from
a class will return a bound class method (like the built-in
'classmethod' decorator), but retrieving the method from an instance
will return a bound instance method (as if the function were not
decorated). Therefore, the first argument of the decorated function may
be either a class or an instance, depending on how it was called.
"""
__slots__ = '_func',
def __init__(self, func):
self._func = func
def __get__(self, inst, owner=None):
bind_to = owner if inst is None else inst
return types.MethodType(self._func, bind_to)
Direction = namedtuple('Direction', ('yaw', 'pitch')) Direction = namedtuple('Direction', ('yaw', 'pitch'))

175
minecraft/utility.py Normal file
View File

@ -0,0 +1,175 @@
""" Miscellaneous general utilities.
"""
import types
from itertools import chain
from . import PROTOCOL_VERSION_INDICES
def protocol_earlier(pv1, pv2):
""" Returns True if protocol version 'pv1' was published before 'pv2',
or else returns False.
"""
return PROTOCOL_VERSION_INDICES[pv1] < PROTOCOL_VERSION_INDICES[pv2]
def protocol_earlier_eq(pv1, pv2):
""" Returns True if protocol versions 'pv1' and 'pv2' are the same or if
'pv1' was published before 'pv2', or else returns False.
"""
return PROTOCOL_VERSION_INDICES[pv1] <= PROTOCOL_VERSION_INDICES[pv2]
def attribute_transform(name, from_orig, to_orig):
"""An attribute descriptor that provides a view of a different attribute
with a given name via a given transformation and its given inverse."""
return property(
fget=(lambda self: from_orig(getattr(self, name))),
fset=(lambda self, value: setattr(self, name, to_orig(value))),
fdel=(lambda self: delattr(self, name)))
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)
def multi_attribute_alias(container, *arg_names, **kwd_names):
"""A descriptor for an attribute whose value is a container of a given type
with several fields, each of which is aliased to a different attribute
of the parent object.
The 'n'th name in 'arg_names' is the parent attribute that will be
aliased to the field of 'container' settable by the 'n'th positional
argument to its constructor, and accessible as its 'n'th iterable
element.
As a special case, 'tuple' may be given as the 'container' when there
are positional arguments, and (even though the tuple constructor does
not take positional arguments), the arguments will be aliased to the
corresponding positions in a tuple.
The name in 'kwd_names' mapped to by the key 'k' is the parent attribute
that will be aliased to the field of 'container' settable by the keyword
argument 'k' to its constructor, and accessible as its 'k' attribute.
"""
if container is tuple:
container = lambda *args: args # noqa: E731
@property
def alias(self):
return container(
*(getattr(self, name) for name in arg_names),
**{kwd: getattr(self, name) for (kwd, name) in kwd_names.items()})
@alias.setter
def alias(self, values):
if arg_names:
for name, value in zip(arg_names, values):
setattr(self, name, value)
for kwd, name in kwd_names.items():
setattr(self, name, getattr(values, kwd))
@alias.deleter
def alias(self):
for name in chain(arg_names, kwd_names.values()):
delattr(self, name)
return alias
class overridable_descriptor:
"""As 'descriptor' (defined below), except that only a getter can be
defined, and the resulting descriptor has no '__set__' or '__delete__'
methods defined; hence, attributes defined via this class can be
overridden by attributes of instances of the class in which it occurs.
"""
__slots__ = '_fget',
def __init__(self, fget=None):
self._fget = fget if fget is not None else self._default_get
def getter(self, fget):
self._fget = fget
return self
@staticmethod
def _default_get(instance, owner):
raise AttributeError('unreadable attribute')
def __get__(self, instance, owner):
return self._fget(self, instance, owner)
class overridable_property(overridable_descriptor):
"""As the builtin 'property' decorator of Python, except that only
a getter is defined and the resulting descriptor is a non-data
descriptor, overridable by attributes of instances of the class
in which the property occurs. See also 'overridable_descriptor' above.
"""
def __get__(self, instance, _owner):
return self._fget(instance)
class descriptor(overridable_descriptor):
"""Behaves identically to the builtin 'property' decorator of Python,
except that the getter, setter and deleter functions given by the
user are used as the raw __get__, __set__ and __delete__ functions
as defined in Python's descriptor protocol.
Since an instance of this class always havs '__set__' and '__delete__'
defined, it is a "data descriptor", so its binding behaviour cannot be
overridden in instances of the class in which it occurs. See
https://docs.python.org/3/reference/datamodel.html#descriptor-invocation
for more information. See also 'overridable_descriptor' above.
"""
__slots__ = '_fset', '_fdel'
def __init__(self, fget=None, fset=None, fdel=None):
super(descriptor, self).__init__(fget=fget)
self._fset = fset if fset is not None else self._default_set
self._fdel = fdel if fdel is not None else self._default_del
def setter(self, fset):
self._fset = fset
return self
def deleter(self, fdel):
self._fdel = fdel
return self
@staticmethod
def _default_set(instance, value):
raise AttributeError("can't set attribute")
@staticmethod
def _default_del(instance):
raise AttributeError("can't delete attribute")
def __set__(self, instance, value):
return self._fset(self, instance, value)
def __delete__(self, instance):
return self._fdel(self, instance)
class class_and_instancemethod:
""" A decorator for functions defined in a class namespace which are to be
accessed as both class and instance methods: retrieving the method from
a class will return a bound class method (like the built-in
'classmethod' decorator), but retrieving the method from an instance
will return a bound instance method (as if the function were not
decorated). Therefore, the first argument of the decorated function may
be either a class or an instance, depending on how it was called.
"""
__slots__ = '_func',
def __init__(self, func):
self._func = func
def __get__(self, inst, owner=None):
bind_to = owner if inst is None else inst
return types.MethodType(self._func, bind_to)

View File

@ -24,9 +24,6 @@ import hashlib
import uuid import uuid
VERSIONS = sorted(SUPPORTED_MINECRAFT_VERSIONS.items(), key=lambda i: i[1])
VERSIONS = [v for (v, p) in VERSIONS]
THREAD_TIMEOUT_S = 2 THREAD_TIMEOUT_S = 2
@ -108,7 +105,7 @@ class FakeClientHandler(object):
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) respawn_screen=False, is_debug=False, is_flat=False)
if self.server.context.protocol_version >= 748: if self.server.context.protocol_later_eq(748):
packet.dimension = pynbt.TAG_Compound({ packet.dimension = pynbt.TAG_Compound({
'natural': pynbt.TAG_Byte(1), 'natural': pynbt.TAG_Byte(1),
'effects': pynbt.TAG_String('minecraft:overworld'), 'effects': pynbt.TAG_String('minecraft:overworld'),
@ -134,7 +131,7 @@ class FakeClientHandler(object):
]), ]),
}), }),
}, '') }, '')
elif self.server.context.protocol_version >= 718: elif self.server.context.protocol_later_eq(718):
packet.dimension = 'minecraft:overworld' packet.dimension = 'minecraft:overworld'
else: else:
packet.dimension = types.Dimension.OVERWORLD packet.dimension = types.Dimension.OVERWORLD
@ -232,13 +229,13 @@ class FakeClientHandler(object):
# Prepare to transition from handshaking to play state (via login), # Prepare to transition from handshaking to play state (via login),
# using the given serverbound HandShakePacket to perform play-specific # using the given serverbound HandShakePacket to perform play-specific
# processing. # processing.
if packet.protocol_version == self.server.context.protocol_version: if self.server.context.protocol_version == packet.protocol_version:
return self._run_login() return self._run_login()
if packet.protocol_version < self.server.context.protocol_version: elif self.server.context.protocol_earlier(packet.protocol_version):
msg = 'Outdated client! Please use %s' \ msg = "Outdated server! I'm still on %s" \
% self.server.minecraft_version % self.server.minecraft_version
else: else:
msg = "Outdated server! I'm still on %s" \ msg = 'Outdated client! Please use %s' \
% self.server.minecraft_version % self.server.minecraft_version
self.handle_login_server_disconnect(msg) self.handle_login_server_disconnect(msg)
@ -372,7 +369,7 @@ class FakeServer(object):
client_handler_type=FakeClientHandler, private_key=None, client_handler_type=FakeClientHandler, private_key=None,
public_key_bytes=None, test_case=None): public_key_bytes=None, test_case=None):
if minecraft_version is None: if minecraft_version is None:
minecraft_version = VERSIONS[-1][0] minecraft_version = list(SUPPORTED_MINECRAFT_VERSIONS.keys())[-1]
if isinstance(minecraft_version, Integral): if isinstance(minecraft_version, Integral):
proto = minecraft_version proto = minecraft_version
@ -458,8 +455,9 @@ class _FakeServerTest(unittest.TestCase):
must raise a 'FakeServerTestSuccess' exception. must raise a 'FakeServerTestSuccess' exception.
""" """
server_version = VERSIONS[-1] server_version = None
# The Minecraft version ID that the server will support. # The Minecraft version ID that the server will support.
# If None, the latest supported version will be used.
client_versions = None client_versions = None
# The set of Minecraft version IDs or protocol version numbers that the # The set of Minecraft version IDs or protocol version numbers that the

View File

@ -1,6 +1,7 @@
import unittest import unittest
from minecraft import SUPPORTED_PROTOCOL_VERSIONS from minecraft import SUPPORTED_PROTOCOL_VERSIONS
from minecraft import utility
from minecraft.networking.connection import ConnectionContext from minecraft.networking.connection import ConnectionContext
from minecraft.networking import packets from minecraft.networking import packets
from minecraft.networking import types from minecraft.networking import types
@ -87,6 +88,12 @@ class LegacyTypesTest(unittest.TestCase):
self.assertIsInstance(types.FixedPointInteger, types.FixedPoint) self.assertIsInstance(types.FixedPointInteger, types.FixedPoint)
self.assertEqual(types.FixedPointInteger.denominator, 32) self.assertEqual(types.FixedPointInteger.denominator, 32)
for attr in ('descriptor', 'overridable_descriptor',
'overridable_property', 'attribute_alias',
'multi_attribute_alias', 'attribute_transform',
'class_and_instancemethod'):
self.assertEqual(getattr(types, attr), getattr(utility, attr))
class ClassMemberAliasesTest(unittest.TestCase): class ClassMemberAliasesTest(unittest.TestCase):
def test_alias_values(self): def test_alias_values(self):

View File

@ -1,5 +1,7 @@
from minecraft import SUPPORTED_MINECRAFT_VERSIONS from minecraft import (
from minecraft import SUPPORTED_PROTOCOL_VERSIONS SUPPORTED_MINECRAFT_VERSIONS, SUPPORTED_PROTOCOL_VERSIONS,
PROTOCOL_VERSION_INDICES,
)
from minecraft.networking.packets import clientbound, serverbound from minecraft.networking.packets import clientbound, serverbound
from minecraft.networking.connection import Connection from minecraft.networking.connection import Connection
from minecraft.exceptions import ( from minecraft.exceptions import (
@ -119,26 +121,22 @@ class ConnectCompressionHighTest(ConnectTest):
class AllowedVersionsTest(fake_server._FakeServerTest): class AllowedVersionsTest(fake_server._FakeServerTest):
versions = sorted(SUPPORTED_MINECRAFT_VERSIONS.items(), key=lambda p: p[1]) versions = list(SUPPORTED_MINECRAFT_VERSIONS.items())
versions = dict((versions[0], versions[len(versions)//2], versions[-1])) test_indices = (0, len(versions) // 2, len(versions) - 1)
client_handler_type = ConnectTest.client_handler_type client_handler_type = ConnectTest.client_handler_type
def test_with_version_names(self): def test_with_version_names(self):
for version, proto in AllowedVersionsTest.versions.items(): for index in self.test_indices:
client_versions = {
v for (v, p) in SUPPORTED_MINECRAFT_VERSIONS.items()
if p <= proto}
self._test_connect( self._test_connect(
server_version=version, client_versions=client_versions) server_version=self.versions[index][0],
client_versions={v[0] for v in self.versions[:index+1]})
def test_with_protocol_numbers(self): def test_with_protocol_numbers(self):
for version, proto in AllowedVersionsTest.versions.items(): for index in self.test_indices:
client_versions = {
p for (v, p) in SUPPORTED_MINECRAFT_VERSIONS.items()
if p <= proto}
self._test_connect( self._test_connect(
server_version=version, client_versions=client_versions) server_version=self.versions[index][0],
client_versions={v[1] for v in self.versions[:index+1]})
class LoginDisconnectTest(fake_server._FakeServerTest): class LoginDisconnectTest(fake_server._FakeServerTest):
@ -363,12 +361,22 @@ class HandleExceptionTest(ConnectTest):
class VersionNegotiationEdgeCases(fake_server._FakeServerTest): class VersionNegotiationEdgeCases(fake_server._FakeServerTest):
lowest_version = min(SUPPORTED_PROTOCOL_VERSIONS) earliest_version = SUPPORTED_PROTOCOL_VERSIONS[0]
highest_version = max(SUPPORTED_PROTOCOL_VERSIONS) latest_version = SUPPORTED_PROTOCOL_VERSIONS[-1]
impossible_version = highest_version + 1
fake_version = max(PROTOCOL_VERSION_INDICES.keys()) + 1
fake_version_index = max(PROTOCOL_VERSION_INDICES.values()) + 1
def setUp(self):
PROTOCOL_VERSION_INDICES[self.fake_version] = self.fake_version_index
super(VersionNegotiationEdgeCases, self).setUp()
def tearDown(self):
super(VersionNegotiationEdgeCases, self).tearDown()
del PROTOCOL_VERSION_INDICES[self.fake_version]
def test_client_protocol_unsupported(self): def test_client_protocol_unsupported(self):
self._test_client_protocol(version=self.impossible_version) self._test_client_protocol(version=self.fake_version)
def test_client_protocol_unknown(self): def test_client_protocol_unknown(self):
self._test_client_protocol(version='surprise me!') self._test_client_protocol(version='surprise me!')
@ -383,21 +391,21 @@ class VersionNegotiationEdgeCases(fake_server._FakeServerTest):
def test_server_protocol_unsupported(self, client_versions=None): def test_server_protocol_unsupported(self, client_versions=None):
with self.assertRaisesRegexp(VersionMismatch, 'not supported'): with self.assertRaisesRegexp(VersionMismatch, 'not supported'):
self._test_connect(client_versions=client_versions, self._test_connect(client_versions=client_versions,
server_version=self.impossible_version) server_version=self.fake_version)
def test_server_protocol_unsupported_direct(self): def test_server_protocol_unsupported_direct(self):
self.test_server_protocol_unsupported({self.highest_version}) self.test_server_protocol_unsupported({self.latest_version})
def test_server_protocol_disallowed(self, client_versions=None): def test_server_protocol_disallowed(self, client_versions=None):
if client_versions is None: if client_versions is None:
client_versions = set(SUPPORTED_PROTOCOL_VERSIONS) \ client_versions = set(SUPPORTED_PROTOCOL_VERSIONS) \
- {self.highest_version} - {self.latest_version}
with self.assertRaisesRegexp(VersionMismatch, 'not allowed'): with self.assertRaisesRegexp(VersionMismatch, 'not allowed'):
self._test_connect(client_versions={self.lowest_version}, self._test_connect(client_versions={self.earliest_version},
server_version=self.highest_version) server_version=self.latest_version)
def test_server_protocol_disallowed_direct(self): def test_server_protocol_disallowed_direct(self):
self.test_server_protocol_disallowed({self.lowest_version}) self.test_server_protocol_disallowed({self.earliest_version})
def test_default_protocol_version(self, status_response=None): def test_default_protocol_version(self, status_response=None):
if status_response is None: if status_response is None:
@ -414,10 +422,10 @@ class VersionNegotiationEdgeCases(fake_server._FakeServerTest):
raise fake_server.FakeServerDisconnect('Test complete.') raise fake_server.FakeServerDisconnect('Test complete.')
def make_connection(*args, **kwds): def make_connection(*args, **kwds):
kwds['initial_version'] = self.lowest_version kwds['initial_version'] = self.earliest_version
return Connection(*args, **kwds) return Connection(*args, **kwds)
self._test_connect(server_version=self.lowest_version, self._test_connect(server_version=self.earliest_version,
client_handler_type=ClientHandler, client_handler_type=ClientHandler,
connection_type=make_connection) connection_type=make_connection)
@ -436,9 +444,9 @@ class VersionNegotiationEdgeCases(fake_server._FakeServerTest):
raise fake_server.FakeServerDisconnect('Test complete.') raise fake_server.FakeServerDisconnect('Test complete.')
def make_connection(*args, **kwds): def make_connection(*args, **kwds):
kwds['initial_version'] = self.lowest_version kwds['initial_version'] = self.earliest_version
return Connection(*args, **kwds) return Connection(*args, **kwds)
self._test_connect(server_version=self.lowest_version, self._test_connect(server_version=self.earliest_version,
client_handler_type=ClientHandler, client_handler_type=ClientHandler,
connection_type=make_connection) connection_type=make_connection)

View File

@ -209,7 +209,7 @@ class TestReadWritePackets(unittest.TestCase):
context.protocol_version = protocol_version context.protocol_version = protocol_version
packet = clientbound.play.MultiBlockChangePacket(context) packet = clientbound.play.MultiBlockChangePacket(context)
if protocol_version >= 741: if context.protocol_later_eq(741):
packet.chunk_section_pos = Vector(167, 17, 33) packet.chunk_section_pos = Vector(167, 17, 33)
packet.invert_trust_edges = False packet.invert_trust_edges = False
else: else:
@ -239,8 +239,9 @@ class TestReadWritePackets(unittest.TestCase):
'type_id', context) 'type_id', context)
pos_look = PositionAndLook( pos_look = PositionAndLook(
position=(Vector(68.0, 38.0, 76.0) if protocol_version >= 100 position=(Vector(68.0, 38.0, 76.0)
else Vector(68, 38, 76)), if context.protocol_later_eq(100) else
Vector(68, 38, 76)),
yaw=263.494, pitch=180) yaw=263.494, pitch=180)
velocity = Vector(21, 55, 41) velocity = Vector(21, 55, 41)
entity_id, type_name, type_id = 49846, 'EGG', EntityType.EGG entity_id, type_name, type_id = 49846, 'EGG', EntityType.EGG
@ -252,7 +253,7 @@ class TestReadWritePackets(unittest.TestCase):
velocity_x=velocity.x, velocity_y=velocity.y, velocity_x=velocity.x, velocity_y=velocity.y,
velocity_z=velocity.z, velocity_z=velocity.z,
entity_id=entity_id, type_id=type_id, data=1) entity_id=entity_id, type_id=type_id, data=1)
if protocol_version >= 49: if context.protocol_later_eq(49):
object_uuid = 'd9568851-85bc-4a10-8d6a-261d130626fa' object_uuid = 'd9568851-85bc-4a10-8d6a-261d130626fa'
packet.object_uuid = object_uuid packet.object_uuid = object_uuid
self.assertEqual(packet.objectUUID, object_uuid) self.assertEqual(packet.objectUUID, object_uuid)
@ -267,12 +268,12 @@ class TestReadWritePackets(unittest.TestCase):
"object_uuid='d9568851-85bc-4a10-8d6a-261d130626fa', " "object_uuid='d9568851-85bc-4a10-8d6a-261d130626fa', "
"type_id=EGG, x=68.0, y=38.0, z=76.0, pitch=180, yaw=263.494, " "type_id=EGG, x=68.0, y=38.0, z=76.0, pitch=180, yaw=263.494, "
"data=1, velocity_x=21, velocity_y=55, velocity_z=41)" "data=1, velocity_x=21, velocity_y=55, velocity_z=41)"
% packet.id if protocol_version >= 100 else % packet.id if context.protocol_later_eq(100) else
"0x%02X SpawnObjectPacket(entity_id=49846, " "0x%02X SpawnObjectPacket(entity_id=49846, "
"object_uuid='d9568851-85bc-4a10-8d6a-261d130626fa', " "object_uuid='d9568851-85bc-4a10-8d6a-261d130626fa', "
"type_id=EGG, x=68, y=38, z=76, pitch=180, yaw=263.494, " "type_id=EGG, x=68, y=38, z=76, pitch=180, yaw=263.494, "
"data=1, velocity_x=21, velocity_y=55, velocity_z=41)" "data=1, velocity_x=21, velocity_y=55, velocity_z=41)"
% packet.id if protocol_version >= 49 else % packet.id if context.protocol_later_eq(49) else
"0x%02X SpawnObjectPacket(entity_id=49846, type_id=EGG, " "0x%02X SpawnObjectPacket(entity_id=49846, type_id=EGG, "
"x=68, y=38, z=76, pitch=180, yaw=263.494, data=1, " "x=68, y=38, z=76, pitch=180, yaw=263.494, data=1, "
"velocity_x=21, velocity_y=55, velocity_z=41)" % packet.id "velocity_x=21, velocity_y=55, velocity_z=41)" % packet.id
@ -282,7 +283,7 @@ class TestReadWritePackets(unittest.TestCase):
context=context, position_and_look=pos_look, context=context, position_and_look=pos_look,
velocity=velocity, type=type_name, velocity=velocity, type=type_name,
entity_id=entity_id, data=1) entity_id=entity_id, data=1)
if protocol_version >= 49: if context.protocol_later_eq(49):
packet2.object_uuid = object_uuid packet2.object_uuid = object_uuid
self.assertEqual(packet.__dict__, packet2.__dict__) self.assertEqual(packet.__dict__, packet2.__dict__)
@ -290,7 +291,7 @@ class TestReadWritePackets(unittest.TestCase):
self.assertEqual(packet.position, packet2.position) self.assertEqual(packet.position, packet2.position)
packet2.data = 0 packet2.data = 0
if protocol_version < 49: if context.protocol_earlier(49):
del packet2.velocity del packet2.velocity
self._test_read_write_packet(packet, context, self._test_read_write_packet(packet, context,
yaw=360/256, pitch=360/256) yaw=360/256, pitch=360/256)
@ -304,11 +305,11 @@ class TestReadWritePackets(unittest.TestCase):
packet = clientbound.play.SoundEffectPacket( packet = clientbound.play.SoundEffectPacket(
sound_id=545, effect_position=Vector(0.125, 300.0, 50.5), sound_id=545, effect_position=Vector(0.125, 300.0, 50.5),
volume=0.75) volume=0.75)
if protocol_version >= 201: if context.protocol_later_eq(201):
packet.pitch = struct.unpack('f', struct.pack('f', 1.5))[0] packet.pitch = struct.unpack('f', struct.pack('f', 1.5))[0]
else: else:
packet.pitch = int(1.5 / 63.5) * 63.5 packet.pitch = int(1.5 / 63.5) * 63.5
if context.protocol_version >= 95: if context.protocol_later_eq(95):
packet.sound_category = \ packet.sound_category = \
clientbound.play.SoundEffectPacket.SoundCategory.NEUTRAL clientbound.play.SoundEffectPacket.SoundCategory.NEUTRAL
@ -321,19 +322,20 @@ class TestReadWritePackets(unittest.TestCase):
packet = clientbound.play.FacePlayerPacket(context) packet = clientbound.play.FacePlayerPacket(context)
packet.target = 1.0, -2.0, 3.5 packet.target = 1.0, -2.0, 3.5
packet.entity_id = None packet.entity_id = None
if protocol_version >= 353: if context.protocol_later_eq(353):
packet.origin = OriginPoint.EYES packet.origin = OriginPoint.EYES
self.assertEqual( self.assertEqual(
str(packet), str(packet),
"0x%02X FacePlayerPacket(origin=EYES, x=1.0, y=-2.0, z=3.5, " "0x%02X FacePlayerPacket(origin=EYES, x=1.0, y=-2.0, z=3.5, "
"entity_id=None)" % packet.id if protocol_version >= 353 else "entity_id=None)" % packet.id
if context.protocol_later_eq(353) else
"0x%02X FacePlayerPacket(entity_id=None, x=1.0, y=-2.0, z=3.5)" "0x%02X FacePlayerPacket(entity_id=None, x=1.0, y=-2.0, z=3.5)"
% packet.id % packet.id
) )
self._test_read_write_packet(packet, context) self._test_read_write_packet(packet, context)
packet.entity_id = 123 packet.entity_id = 123
if protocol_version >= 353: if context.protocol_later_eq(353):
packet.entity_origin = OriginPoint.FEET packet.entity_origin = OriginPoint.FEET
else: else:
del packet.target del packet.target
@ -341,7 +343,7 @@ class TestReadWritePackets(unittest.TestCase):
str(packet), str(packet),
"0x%02X FacePlayerPacket(origin=EYES, x=1.0, y=-2.0, z=3.5, " "0x%02X FacePlayerPacket(origin=EYES, x=1.0, y=-2.0, z=3.5, "
"entity_id=123, entity_origin=FEET)" % packet.id "entity_id=123, entity_origin=FEET)" % packet.id
if protocol_version >= 353 else if context.protocol_later_eq(353) else
"0x%02X FacePlayerPacket(entity_id=123)" % packet.id "0x%02X FacePlayerPacket(entity_id=123)" % packet.id
) )
self._test_read_write_packet(packet, context) self._test_read_write_packet(packet, context)

View File

@ -55,7 +55,7 @@ class MapPacketTest(unittest.TestCase):
packet.is_tracking_position = True packet.is_tracking_position = True
packet.is_locked = False packet.is_locked = False
packet.icons = [] packet.icons = []
d_name = u'Marshmallow' if context.protocol_version >= 364 else None d_name = u'Marshmallow' if context.protocol_later_eq(364) else None
packet.icons.append(MapPacket.MapIcon( packet.icons.append(MapPacket.MapIcon(
type=2, direction=2, location=(1, 1), display_name=d_name type=2, direction=2, location=(1, 1), display_name=d_name
)) ))

View File

@ -11,7 +11,7 @@ from minecraft.networking.connection import (
from minecraft.networking.packets import clientbound from minecraft.networking.packets import clientbound
max_proto_ver = max(SUPPORTED_PROTOCOL_VERSIONS) latest_proto = SUPPORTED_PROTOCOL_VERSIONS[-1]
class LoginReactorTest(unittest.TestCase): class LoginReactorTest(unittest.TestCase):
@ -19,7 +19,7 @@ class LoginReactorTest(unittest.TestCase):
@mock.patch('minecraft.networking.connection.encryption') @mock.patch('minecraft.networking.connection.encryption')
def test_encryption_online_server(self, encrypt): def test_encryption_online_server(self, encrypt):
connection = mock.MagicMock() connection = mock.MagicMock()
connection.context = ConnectionContext(protocol_version=max_proto_ver) connection.context = ConnectionContext(protocol_version=latest_proto)
reactor = LoginReactor(connection) reactor = LoginReactor(connection)
packet = clientbound.login.EncryptionRequestPacket() packet = clientbound.login.EncryptionRequestPacket()
@ -43,7 +43,7 @@ class LoginReactorTest(unittest.TestCase):
@mock.patch('minecraft.networking.connection.encryption') @mock.patch('minecraft.networking.connection.encryption')
def test_encryption_offline_server(self, encrypt): def test_encryption_offline_server(self, encrypt):
connection = mock.MagicMock() connection = mock.MagicMock()
connection.context = ConnectionContext(protocol_version=max_proto_ver) connection.context = ConnectionContext(protocol_version=latest_proto)
reactor = LoginReactor(connection) reactor = LoginReactor(connection)
packet = clientbound.login.EncryptionRequestPacket() packet = clientbound.login.EncryptionRequestPacket()

View File

@ -50,7 +50,8 @@ deps =
[flake8] [flake8]
per-file-ignores = per-file-ignores =
*/clientbound/play/spawn_object_packet.py:E221,E222,E271,E272 */clientbound/play/spawn_object_packet.py:E221,E222,E271,E272,E201
minecraft/networking/packets/__init__.py:F401
[testenv:pylint-errors] [testenv:pylint-errors]
basepython = python3.8 basepython = python3.8