From 969419da3f29a6540a17b416f18e7bb89dac58c5 Mon Sep 17 00:00:00 2001 From: joo Date: Wed, 2 Dec 2020 14:30:40 +0100 Subject: [PATCH] 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`. --- minecraft/__init__.py | 795 +++++++++++------- minecraft/networking/connection.py | 44 +- minecraft/networking/packets/__init__.py | 31 +- .../packets/clientbound/login/__init__.py | 22 +- .../packets/clientbound/play/__init__.py | 230 ++--- .../clientbound/play/block_change_packet.py | 42 +- .../clientbound/play/combat_event_packet.py | 26 +- .../clientbound/play/explosion_packet.py | 20 +- .../clientbound/play/face_player_packet.py | 18 +- .../play/join_game_and_respawn_packets.py | 104 +-- .../packets/clientbound/play/map_packet.py | 38 +- .../play/player_list_item_packet.py | 22 +- .../play/player_position_and_look_packet.py | 26 +- .../clientbound/play/sound_effect_packet.py | 43 +- .../clientbound/play/spawn_object_packet.py | 81 +- .../networking/packets/keep_alive_packet.py | 2 +- .../packets/serverbound/login/__init__.py | 12 +- .../packets/serverbound/play/__init__.py | 174 ++-- .../play/client_settings_packet.py | 16 +- minecraft/networking/types/basic.py | 4 +- minecraft/networking/types/utility.py | 167 +--- minecraft/utility.py | 175 ++++ tests/fake_server.py | 20 +- tests/test_backward_compatible.py | 7 + tests/test_connection.py | 64 +- tests/test_packets.py | 30 +- tests/test_packets_with_logic.py | 2 +- tests/test_reactors.py | 6 +- tox.ini | 3 +- 29 files changed, 1241 insertions(+), 983 deletions(-) create mode 100644 minecraft/utility.py diff --git a/minecraft/__init__.py b/minecraft/__init__.py index 0be9a9e..d834d4a 100644 --- a/minecraft/__init__.py +++ b/minecraft/__init__.py @@ -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 # , 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) diff --git a/minecraft/networking/connection.py b/minecraft/networking/connection.py index 5713295..c2c40ee 100644 --- a/minecraft/networking/connection.py +++ b/minecraft/networking/connection.py @@ -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) diff --git a/minecraft/networking/packets/__init__.py b/minecraft/networking/packets/__init__.py index 149887c..e1028ff 100644 --- a/minecraft/networking/packets/__init__.py +++ b/minecraft/networking/packets/__init__.py @@ -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, -) diff --git a/minecraft/networking/packets/clientbound/login/__init__.py b/minecraft/networking/packets/clientbound/login/__init__.py index 38e0d8b..8b08dc0 100644 --- a/minecraft/networking/packets/clientbound/login/__init__.py +++ b/minecraft/networking/packets/clientbound/login/__init__.py @@ -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' diff --git a/minecraft/networking/packets/clientbound/play/__init__.py b/minecraft/networking/packets/clientbound/play/__init__.py index 1afe9f6..b1c13f8 100644 --- a/minecraft/networking/packets/clientbound/play/__init__.py +++ b/minecraft/networking/packets/clientbound/play/__init__.py @@ -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' diff --git a/minecraft/networking/packets/clientbound/play/block_change_packet.py b/minecraft/networking/packets/clientbound/play/block_change_packet.py index ca9c73f..a7c7403 100644 --- a/minecraft/networking/packets/clientbound/play/block_change_packet.py +++ b/minecraft/networking/packets/clientbound/play/block_change_packet.py @@ -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)}, diff --git a/minecraft/networking/packets/clientbound/play/combat_event_packet.py b/minecraft/networking/packets/clientbound/play/combat_event_packet.py index db53734..b06fc0c 100644 --- a/minecraft/networking/packets/clientbound/play/combat_event_packet.py +++ b/minecraft/networking/packets/clientbound/play/combat_event_packet.py @@ -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' diff --git a/minecraft/networking/packets/clientbound/play/explosion_packet.py b/minecraft/networking/packets/clientbound/play/explosion_packet.py index 71a4af2..fe2745e 100644 --- a/minecraft/networking/packets/clientbound/play/explosion_packet.py +++ b/minecraft/networking/packets/clientbound/play/explosion_packet.py @@ -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' diff --git a/minecraft/networking/packets/clientbound/play/face_player_packet.py b/minecraft/networking/packets/clientbound/play/face_player_packet.py index 4aca89d..4af3b76 100644 --- a/minecraft/networking/packets/clientbound/play/face_player_packet.py +++ b/minecraft/networking/packets/clientbound/play/face_player_packet.py @@ -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) diff --git a/minecraft/networking/packets/clientbound/play/join_game_and_respawn_packets.py b/minecraft/networking/packets/clientbound/play/join_game_and_respawn_packets.py index f94a07b..109d27d 100644 --- a/minecraft/networking/packets/clientbound/play/join_game_and_respawn_packets.py +++ b/minecraft/networking/packets/clientbound/play/join_game_and_respawn_packets.py @@ -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: diff --git a/minecraft/networking/packets/clientbound/play/map_packet.py b/minecraft/networking/packets/clientbound/play/map_packet.py index 3de187b..490cf87 100644 --- a/minecraft/networking/packets/clientbound/play/map_packet.py +++ b/minecraft/networking/packets/clientbound/play/map_packet.py @@ -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) diff --git a/minecraft/networking/packets/clientbound/play/player_list_item_packet.py b/minecraft/networking/packets/clientbound/play/player_list_item_packet.py index 6f85fd0..74880e7 100644 --- a/minecraft/networking/packets/clientbound/play/player_list_item_packet.py +++ b/minecraft/networking/packets/clientbound/play/player_list_item_packet.py @@ -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" diff --git a/minecraft/networking/packets/clientbound/play/player_position_and_look_packet.py b/minecraft/networking/packets/clientbound/play/player_position_and_look_packet.py index 5169366..59574e5 100644 --- a/minecraft/networking/packets/clientbound/play/player_position_and_look_packet.py +++ b/minecraft/networking/packets/clientbound/play/player_position_and_look_packet.py @@ -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. diff --git a/minecraft/networking/packets/clientbound/play/sound_effect_packet.py b/minecraft/networking/packets/clientbound/play/sound_effect_packet.py index da56748..697e2d1 100644 --- a/minecraft/networking/packets/clientbound/play/sound_effect_packet.py +++ b/minecraft/networking/packets/clientbound/play/sound_effect_packet.py @@ -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) diff --git a/minecraft/networking/packets/clientbound/play/spawn_object_packet.py b/minecraft/networking/packets/clientbound/play/spawn_object_packet.py index c129b72..9756e4f 100644 --- a/minecraft/networking/packets/clientbound/play/spawn_object_packet.py +++ b/minecraft/networking/packets/clientbound/play/spawn_object_packet.py @@ -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) diff --git a/minecraft/networking/packets/keep_alive_packet.py b/minecraft/networking/packets/keep_alive_packet.py index ac6d237..fd8d484 100644 --- a/minecraft/networking/packets/keep_alive_packet.py +++ b/minecraft/networking/packets/keep_alive_packet.py @@ -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} ]) diff --git a/minecraft/networking/packets/serverbound/login/__init__.py b/minecraft/networking/packets/serverbound/login/__init__.py index 3254d24..dc6fba3 100644 --- a/minecraft/networking/packets/serverbound/login/__init__.py +++ b/minecraft/networking/packets/serverbound/login/__init__.py @@ -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' diff --git a/minecraft/networking/packets/serverbound/play/__init__.py b/minecraft/networking/packets/serverbound/play/__init__.py index 477cdcd..011100b 100644 --- a/minecraft/networking/packets/serverbound/play/__init__.py +++ b/minecraft/networking/packets/serverbound/play/__init__.py @@ -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" diff --git a/minecraft/networking/packets/serverbound/play/client_settings_packet.py b/minecraft/networking/packets/serverbound/play/client_settings_packet.py index f5e84e7..a826f09 100644 --- a/minecraft/networking/packets/serverbound/play/client_settings_packet.py +++ b/minecraft/networking/packets/serverbound/play/client_settings_packet.py @@ -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: { diff --git a/minecraft/networking/types/basic.py b/minecraft/networking/types/basic.py index 0a02ecd..52c4f27 100644 --- a/minecraft/networking/types/basic.py +++ b/minecraft/networking/types/basic.py @@ -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) diff --git a/minecraft/networking/types/utility.py b/minecraft/networking/types/utility.py index bf3fa04..ac3cb83 100644 --- a/minecraft/networking/types/utility.py +++ b/minecraft/networking/types/utility.py @@ -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')) diff --git a/minecraft/utility.py b/minecraft/utility.py new file mode 100644 index 0000000..2ab8d1d --- /dev/null +++ b/minecraft/utility.py @@ -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) diff --git a/tests/fake_server.py b/tests/fake_server.py index 2bc24ec..9d5251b 100644 --- a/tests/fake_server.py +++ b/tests/fake_server.py @@ -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 diff --git a/tests/test_backward_compatible.py b/tests/test_backward_compatible.py index c9cc66c..d929eac 100644 --- a/tests/test_backward_compatible.py +++ b/tests/test_backward_compatible.py @@ -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): diff --git a/tests/test_connection.py b/tests/test_connection.py index 1f52f29..c55a1e4 100644 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -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) diff --git a/tests/test_packets.py b/tests/test_packets.py index 57efeb9..44b775c 100644 --- a/tests/test_packets.py +++ b/tests/test_packets.py @@ -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) diff --git a/tests/test_packets_with_logic.py b/tests/test_packets_with_logic.py index e9a202e..c10a74b 100644 --- a/tests/test_packets_with_logic.py +++ b/tests/test_packets_with_logic.py @@ -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 )) diff --git a/tests/test_reactors.py b/tests/test_reactors.py index 9d0de52..206a35b 100644 --- a/tests/test_reactors.py +++ b/tests/test_reactors.py @@ -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() diff --git a/tox.ini b/tox.ini index a465d4b..488032d 100644 --- a/tox.ini +++ b/tox.ini @@ -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