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

View File

@ -11,9 +11,11 @@ import re
from .types import VarInt
from .packets import clientbound, serverbound
from . import packets
from . import encryption
from .. import SUPPORTED_PROTOCOL_VERSIONS, SUPPORTED_MINECRAFT_VERSIONS
from . import packets, encryption
from .. import (
utility, KNOWN_MINECRAFT_VERSIONS, SUPPORTED_MINECRAFT_VERSIONS,
SUPPORTED_PROTOCOL_VERSIONS, PROTOCOL_VERSION_INDICES
)
from ..exceptions import (
VersionMismatch, LoginDisconnect, IgnorePacket, InvalidState
)
@ -31,6 +33,26 @@ class ConnectionContext(object):
def __init__(self, **kwds):
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):
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))
self.allowed_proto_versions = allowed_versions
latest_allowed_proto = max(self.allowed_proto_versions,
key=PROTOCOL_VERSION_INDICES.get)
if initial_version is None:
self.default_proto_version = max(self.allowed_proto_versions)
self.default_proto_version = latest_allowed_proto
else:
self.default_proto_version = proto_version(initial_version)
self.context = ConnectionContext(
protocol_version=max(self.allowed_proto_versions))
self.context = ConnectionContext(protocol_version=latest_allowed_proto)
self.options = _ConnectionOptions()
self.options.address = address
@ -362,7 +386,9 @@ class Connection(object):
# It is important that this is set correctly even when connecting
# in status mode, as some servers, e.g. SpigotMC with the
# 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._connect()
@ -496,7 +522,7 @@ class Connection(object):
def _version_mismatch(self, server_protocol=None, server_version=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:
vs = 'version' if server_version is None else \
@ -751,7 +777,7 @@ class PlayingReactor(PacketReactor):
self.connection.write_packet(keep_alive_packet)
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.teleport_id = packet.teleport_id
self.connection.write_packet(teleport_confirm)

View File

@ -1,8 +1,8 @@
'''
NOTE: The packet classes in __all_legacy_packets__ exported by this
module are included only for backward compatibility, and should not
be used in new code, as (1) they do not include all packets present
in pyCraft, and (2) some are named oddly, for historical reasons.
NOTE: The packet classes exported by this module are included only for backward
compatibility, and should not be used in new code, as (1) they do not include
all packets present in pyCraft, and (2) some are named oddly, for historical
reasons.
Use the packet classes under packets.clientbound.* and
packets.serverbound.* instead.
@ -60,26 +60,3 @@ from .serverbound.play import PositionAndLookPacket
from .serverbound.play import TeleportConfirmPacket
from .serverbound.play import AnimationPacket as AnimationPacketServerbound
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,
SetCompressionPacket,
}
if context.protocol_version >= 385:
if context.protocol_later_eq(385):
packets |= {
PluginRequestPacket,
}
@ -23,8 +23,8 @@ def get_packets(context):
class DisconnectPacket(Packet):
@staticmethod
def get_id(context):
return 0x00 if context.protocol_version >= 391 else \
0x01 if context.protocol_version >= 385 else \
return 0x00 if context.protocol_later_eq(391) else \
0x01 if context.protocol_later_eq(385) else \
0x00
packet_name = "disconnect"
@ -35,8 +35,8 @@ class DisconnectPacket(Packet):
class EncryptionRequestPacket(Packet):
@staticmethod
def get_id(context):
return 0x01 if context.protocol_version >= 391 else \
0x02 if context.protocol_version >= 385 else \
return 0x01 if context.protocol_later_eq(391) else \
0x02 if context.protocol_later_eq(385) else \
0x01
packet_name = "encryption request"
@ -49,13 +49,13 @@ class EncryptionRequestPacket(Packet):
class LoginSuccessPacket(Packet):
@staticmethod
def get_id(context):
return 0x02 if context.protocol_version >= 391 else \
0x03 if context.protocol_version >= 385 else \
return 0x02 if context.protocol_later_eq(391) else \
0x03 if context.protocol_later_eq(385) else \
0x02
packet_name = "login success"
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}
])
@ -63,8 +63,8 @@ class LoginSuccessPacket(Packet):
class SetCompressionPacket(Packet):
@staticmethod
def get_id(context):
return 0x03 if context.protocol_version >= 391 else \
0x04 if context.protocol_version >= 385 else \
return 0x03 if context.protocol_later_eq(391) else \
0x04 if context.protocol_later_eq(385) else \
0x03
packet_name = "set compression"
@ -89,7 +89,7 @@ class PluginRequestPacket(Packet):
@staticmethod
def get_id(context):
return 0x04 if context.protocol_version >= 391 else \
return 0x04 if context.protocol_later_eq(391) else \
0x00
packet_name = 'login plugin request'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,18 +9,18 @@ from minecraft.networking.types import (
class PlayerPositionAndLookPacket(Packet, BitFieldEnum):
@staticmethod
def get_id(context):
return 0x34 if context.protocol_version >= 741 else \
0x35 if context.protocol_version >= 721 else \
0x36 if context.protocol_version >= 550 else \
0x35 if context.protocol_version >= 471 else \
0x33 if context.protocol_version >= 451 else \
0x32 if context.protocol_version >= 389 else \
0x31 if context.protocol_version >= 352 else \
0x30 if context.protocol_version >= 345 else \
0x2F if context.protocol_version >= 336 else \
0x2E if context.protocol_version >= 332 else \
0x2F if context.protocol_version >= 318 else \
0x2E if context.protocol_version >= 70 else \
return 0x34 if context.protocol_later_eq(741) else \
0x35 if context.protocol_later_eq(721) else \
0x36 if context.protocol_later_eq(550) else \
0x35 if context.protocol_later_eq(471) else \
0x33 if context.protocol_later_eq(451) else \
0x32 if context.protocol_later_eq(389) else \
0x31 if context.protocol_later_eq(352) else \
0x30 if context.protocol_later_eq(345) else \
0x2F if context.protocol_later_eq(336) else \
0x2E if context.protocol_later_eq(332) else \
0x2F if context.protocol_later_eq(318) else \
0x2E if context.protocol_later_eq(70) else \
0x08
packet_name = "player position and look"
@ -31,7 +31,7 @@ class PlayerPositionAndLookPacket(Packet, BitFieldEnum):
{'yaw': Float},
{'pitch': Float},
{'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.

View File

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

View File

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

View File

@ -8,7 +8,7 @@ from minecraft.networking.types import (
class AbstractKeepAlivePacket(Packet):
packet_name = "keep alive"
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}
])

View File

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

View File

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

View File

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

View File

@ -314,7 +314,7 @@ class Position(Type, Vector):
location = UnsignedLong.read(file_object)
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
y = int(location & 0xFFF) # 12 least signficant bits
else:
@ -337,7 +337,7 @@ class Position(Type, Vector):
# 'position' can be either a tuple or Position object.
x, y, z = position
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))
UnsignedLong.send(value, socket)

View File

@ -1,16 +1,12 @@
"""Minecraft data types that are used by packets, but don't have a specific
network representation.
"""
import types
from collections import namedtuple
from itertools import chain
__all__ = (
'Vector', 'MutableRecord', 'Direction', 'PositionAndLook', 'descriptor',
'overridable_descriptor', 'overridable_property', 'attribute_alias',
'multi_attribute_alias', 'attribute_transform',
# These aliases are retained for backward compatibility
from minecraft.utility import ( # noqa: F401
descriptor, overridable_descriptor, overridable_property, attribute_alias,
multi_attribute_alias, attribute_transform, class_and_instancemethod,
)
@ -92,161 +88,6 @@ class MutableRecord(object):
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'))

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

View File

@ -1,6 +1,7 @@
import unittest
from minecraft import SUPPORTED_PROTOCOL_VERSIONS
from minecraft import utility
from minecraft.networking.connection import ConnectionContext
from minecraft.networking import packets
from minecraft.networking import types
@ -87,6 +88,12 @@ class LegacyTypesTest(unittest.TestCase):
self.assertIsInstance(types.FixedPointInteger, types.FixedPoint)
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):
def test_alias_values(self):

View File

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

View File

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

View File

@ -55,7 +55,7 @@ class MapPacketTest(unittest.TestCase):
packet.is_tracking_position = True
packet.is_locked = False
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(
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
max_proto_ver = max(SUPPORTED_PROTOCOL_VERSIONS)
latest_proto = SUPPORTED_PROTOCOL_VERSIONS[-1]
class LoginReactorTest(unittest.TestCase):
@ -19,7 +19,7 @@ class LoginReactorTest(unittest.TestCase):
@mock.patch('minecraft.networking.connection.encryption')
def test_encryption_online_server(self, encrypt):
connection = mock.MagicMock()
connection.context = ConnectionContext(protocol_version=max_proto_ver)
connection.context = ConnectionContext(protocol_version=latest_proto)
reactor = LoginReactor(connection)
packet = clientbound.login.EncryptionRequestPacket()
@ -43,7 +43,7 @@ class LoginReactorTest(unittest.TestCase):
@mock.patch('minecraft.networking.connection.encryption')
def test_encryption_offline_server(self, encrypt):
connection = mock.MagicMock()
connection.context = ConnectionContext(protocol_version=max_proto_ver)
connection.context = ConnectionContext(protocol_version=latest_proto)
reactor = LoginReactor(connection)
packet = clientbound.login.EncryptionRequestPacket()

View File

@ -50,7 +50,8 @@ deps =
[flake8]
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]
basepython = python3.8