├── .gitignore ├── mcdbot.py ├── mcdbotUtils ├── __init__.py ├── botmanager.py ├── carpetbot.py ├── minecraft │ ├── __init__.py │ ├── authentication.py │ ├── compat.py │ ├── exceptions.py │ └── networking │ │ ├── __init__.py │ │ ├── __init__.pyc │ │ ├── connection.py │ │ ├── connection.pyc │ │ ├── encryption.py │ │ ├── encryption.pyc │ │ ├── packets │ │ ├── __init__.py │ │ ├── __init__.pyc │ │ ├── clientbound │ │ │ ├── __init__.py │ │ │ ├── __init__.pyc │ │ │ ├── handshake │ │ │ │ ├── __init__.py │ │ │ │ └── __init__.pyc │ │ │ ├── login │ │ │ │ ├── __init__.py │ │ │ │ └── __init__.pyc │ │ │ ├── play │ │ │ │ ├── __init__.py │ │ │ │ ├── __init__.pyc │ │ │ │ ├── block_change_packet.py │ │ │ │ ├── block_change_packet.pyc │ │ │ │ ├── combat_event_packet.py │ │ │ │ ├── combat_event_packet.pyc │ │ │ │ ├── explosion_packet.py │ │ │ │ ├── explosion_packet.pyc │ │ │ │ ├── face_player_packet.py │ │ │ │ ├── map_packet.py │ │ │ │ ├── map_packet.pyc │ │ │ │ ├── player_list_item_packet.py │ │ │ │ ├── player_list_item_packet.pyc │ │ │ │ ├── player_position_and_look_packet.py │ │ │ │ ├── player_position_and_look_packet.pyc │ │ │ │ ├── sound_effect_packet.py │ │ │ │ ├── spawn_object_packet.py │ │ │ │ └── spawn_object_packet.pyc │ │ │ └── status │ │ │ │ ├── __init__.py │ │ │ │ └── __init__.pyc │ │ ├── keep_alive_packet.py │ │ ├── keep_alive_packet.pyc │ │ ├── packet.py │ │ ├── packet.pyc │ │ ├── packet_buffer.py │ │ ├── packet_buffer.pyc │ │ ├── packet_listener.py │ │ ├── packet_listener.pyc │ │ ├── plugin_message_packet.py │ │ ├── plugin_message_packet.pyc │ │ └── serverbound │ │ │ ├── __init__.py │ │ │ ├── __init__.pyc │ │ │ ├── handshake │ │ │ ├── __init__.py │ │ │ └── __init__.pyc │ │ │ ├── login │ │ │ ├── __init__.py │ │ │ └── __init__.pyc │ │ │ ├── play │ │ │ ├── __init__.py │ │ │ ├── __init__.pyc │ │ │ ├── client_settings_packet.py │ │ │ └── client_settings_packet.pyc │ │ │ └── status │ │ │ ├── __init__.py │ │ │ └── __init__.pyc │ │ └── types │ │ ├── __init__.py │ │ ├── __init__.pyc │ │ ├── basic.py │ │ ├── basic.pyc │ │ ├── enum.py │ │ ├── enum.pyc │ │ ├── utility.py │ │ └── utility.pyc └── port.ini ├── readme.md └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *.pyc 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | .pytest_cache/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | local_settings.py 58 | db.sqlite3 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | env.bak/ 92 | venv.bak/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | *.stackdump 107 | start.sh 108 | 109 | mcdbot-es.py 110 | .idea/ -------------------------------------------------------------------------------- /mcdbot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import copy 3 | import os 4 | import sys 5 | from time import sleep 6 | import json 7 | 8 | helpmsg = '''=====MCD BOT===== 9 | 命令帮助如下: 10 | !!bot add (注释) [-keep] [-f(force)]:召唤一个bot,使用-keep参数使bot在你下线后不下线,使用-f强制忽略bot前缀 11 | !!bot stop (name):让bot离开游戏 12 | !!bot tp (name):让bot传送到你的地方 13 | !!bot gm (name) (c/s):设置bot的gamemode(Creative / Spectator) 14 | !!bot kickall:!慎用!使所有bot退出游戏,请在重载插件之前使用 15 | ''' 16 | 17 | botlist = [] 18 | namelist = [] 19 | config = {} 20 | default = 'protocol' 21 | 22 | try: 23 | with open('./config/mcdbot.json','r') as handle: 24 | config = json.load(handle) 25 | except: 26 | print('could not open config file, using default(' + default + ')') 27 | config['mode'] = default 28 | if not os.path.isdir('./config'): 29 | os.makedirs('./config') 30 | with open('./config/mcdbot.json','w') as handle: 31 | json.dump(config, handle) 32 | 33 | sys.path.append("plugins/") 34 | if config['mode'] == 'carpet': 35 | from mcdbotUtils.carpetbot import mcbot 36 | mode = 'carpet' 37 | else: 38 | from mcdbotUtils.botmanager import mcbot 39 | mode = 'network' 40 | 41 | def onServerInfo(server, info): 42 | if (info.isPlayer == 1): 43 | if info.content.startswith('!!bot'): 44 | args = info.content.split(' ') 45 | global namelist 46 | global botlist 47 | if args[0] == '!!bot': 48 | if len(args) == 1: 49 | for line in helpmsg.splitlines(): 50 | server.tell(info.player, line) 51 | elif (args[1] == 'mode'): 52 | server.say(config['mode']) 53 | elif (args[1] == 'add') and (len(args)>2): 54 | if ' -f' in info.content: 55 | args = info.content.replace(' -f', '').split(' ') 56 | botname = 'bot_' + args[2] 57 | else: 58 | botname = info.player + '_b_'+ args[2] 59 | if (len(botname)>16): 60 | server.tell(info.player, '[MCD-bot]:名字太长(请控制在16个字符以内)') 61 | if botname in namelist: 62 | server.tell(info.player, 'bot已经存在!') 63 | else: 64 | if (len(args) == 4) and (args[3] == '-keep'): 65 | if config['mode'] == 'carpet': 66 | bot = mcbot(botname, info.player, server, 1) 67 | else: 68 | bot = mcbot(botname, info.player, 1) 69 | else: 70 | if config['mode'] == 'carpet': 71 | bot = mcbot(botname ,info.player, server) 72 | else: 73 | bot = mcbot(botname, info.player) 74 | sleep(0.1) 75 | server.execute('gamemode creative ' + botname) 76 | botlist.append(bot) 77 | namelist.append(botname) 78 | elif (args[1] == 'stop') and (len(args) == 3): 79 | botname = args[2] 80 | if botname in namelist: 81 | namelist.remove(botname) 82 | for bot in botlist: 83 | if bot.name == botname: 84 | if config['mode'] == 'carpet': 85 | bot.stop(server) 86 | else: 87 | bot.stop() 88 | botlist.remove(bot) 89 | elif (args[1] == 'tp') and (len(args) == 3): 90 | if args[2] in namelist: 91 | for bot in botlist: 92 | if bot.name == args[2]: 93 | if bot.owner == info.player: 94 | server.tell(info.player, 'Teleporting...') 95 | server.execute('execute at ' + info.player + ' run tp ' + args[2] + ' ' + info.player) 96 | else: 97 | server.tell(info.player, '你不是这个bot的主人') 98 | elif (args[1] == 'gm') and (len(args) == 4): 99 | if args[2] in namelist: 100 | for bot in botlist: 101 | if bot.name == args[2]: 102 | if bot.owner == info.player: 103 | if args[3] == 'c': 104 | server.execute('gamemode creative ' + args[2]) 105 | if args[3] == 's': 106 | server.execute('gamemode spectator ' + args[2]) 107 | else: 108 | server.tell(info.player, '你不是这个bot的主人') 109 | elif (args[1] == 'kickall') and (len(args) == 2): 110 | clean_bots() 111 | server.say('bot已清空') 112 | else: 113 | server.tell(info.player, '参数格式不正确') 114 | 115 | 116 | def clean_bots(): 117 | global namelist, botlist 118 | namelist = [] 119 | for bot in botlist: 120 | bot.stop() 121 | botlist = [] 122 | 123 | def onPlayerLeave(server, player): 124 | global namelist 125 | global botlist 126 | removelist = [] 127 | for bot in botlist: 128 | if (bot.owner == player) and (bot.keep == 0): 129 | namelist.remove(bot.name) 130 | if config['mode'] == 'carpet': 131 | bot.stop(server) 132 | else: 133 | bot.stop() 134 | removelist.append(bot) 135 | for bot in removelist: 136 | botlist.remove(bot) 137 | 138 | 139 | def on_info(server, info): 140 | info2 = copy.deepcopy(info) 141 | info2.isPlayer = info2.is_player 142 | onServerInfo(server, info2) 143 | 144 | 145 | def on_player_left(server, player): 146 | onPlayerLeave(server, player) 147 | 148 | 149 | def on_load(server, old): 150 | if old is not None: 151 | global namelist, botlist 152 | namelist = old.namelist 153 | botlist = old.botlist 154 | 155 | 156 | def on_server_stop(server, code): 157 | clean_bots() 158 | -------------------------------------------------------------------------------- /mcdbotUtils/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | here comes basic functions 3 | ''' 4 | -------------------------------------------------------------------------------- /mcdbotUtils/botmanager.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .minecraft.networking import connection as conn 4 | import traceback 5 | 6 | try: 7 | with open('./plugins/mcdbotUtils/port.ini','r') as handle: 8 | port = int(handle.read()) 9 | except: 10 | print('[MCD-bot]: failed to read port from config file,using default') 11 | print(traceback.format_exc()) 12 | port = 25565 13 | 14 | class mcbot(object): 15 | def __init__(self, name, owner, keep=0): 16 | self.start(name, owner, keep) 17 | 18 | def start(self, name, owner, keep): 19 | self.bot = conn.Connection('127.0.0.1', port, None, name) 20 | self.bot.connect() 21 | self.owner = owner 22 | self.name = name 23 | self.keep = keep 24 | 25 | def stop(self): 26 | self.bot.disconnect() 27 | -------------------------------------------------------------------------------- /mcdbotUtils/carpetbot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import traceback 4 | 5 | try: 6 | with open('./plugins/mcdbotUtils/port.ini','r') as handle: 7 | port = int(handle.read()) 8 | except: 9 | print('[MCD-bot]: failed to read port from config file,using default') 10 | print(traceback.format_exc()) 11 | port = 25565 12 | 13 | class mcbot(object): 14 | def __init__(self, name, owner, server, keep=0): 15 | self.start(name, owner, keep, server) 16 | 17 | 18 | def start(self, name, owner, keep, server): 19 | server.execute('player ' + name + ' spawn') 20 | self.owner = owner 21 | self.name = name 22 | self.keep = keep 23 | 24 | def stop(self, server): 25 | server.execute('player ' + self.name + ' kill') -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | A modern, Python3-compatible, well-documented library for communicating 3 | with a MineCraft server. 4 | """ 5 | 6 | # The version number of the most recent pyCraft release. 7 | __version__ = "0.6.0" 8 | 9 | # A dict mapping the ID string of each Minecraft version supported by pyCraft 10 | # to the corresponding protocol version number. The ID string of a version is 11 | # the key used to identify it in 12 | # , or the 'id' 13 | # key in "version.json" in the corresponding ".jar" file distributed by Mojang. 14 | SUPPORTED_MINECRAFT_VERSIONS = { 15 | '1.8': 47, 16 | '1.8.1': 47, 17 | '1.8.2': 47, 18 | '1.8.3': 47, 19 | '1.8.4': 47, 20 | '1.8.5': 47, 21 | '1.8.6': 47, 22 | '1.8.7': 47, 23 | '1.8.8': 47, 24 | '1.8.9': 47, 25 | '1.9': 107, 26 | '1.9.1': 108, 27 | '1.9.2': 109, 28 | '1.9.3': 110, 29 | '1.9.4': 110, 30 | '1.10': 210, 31 | '1.10.1': 210, 32 | '1.10.2': 210, 33 | '16w32a': 301, 34 | '16w32b': 302, 35 | '16w33a': 303, 36 | '16w35a': 304, 37 | '16w36a': 305, 38 | '16w38a': 306, 39 | '16w39a': 307, 40 | '16w39b': 308, 41 | '16w39c': 309, 42 | '16w40a': 310, 43 | '16w41a': 311, 44 | '16w42a': 312, 45 | '16w43a': 313, 46 | '16w44a': 313, 47 | '1.11-pre1': 314, 48 | '1.11': 315, 49 | '16w50a': 316, 50 | '1.11.1': 316, 51 | '1.11.2': 316, 52 | '17w06a': 317, 53 | '17w13a': 318, 54 | '17w13b': 319, 55 | '17w14a': 320, 56 | '17w15a': 321, 57 | '17w16a': 322, 58 | '17w16b': 323, 59 | '17w17a': 324, 60 | '17w17b': 325, 61 | '17w18a': 326, 62 | '17w18b': 327, 63 | '1.12-pre1': 328, 64 | '1.12-pre2': 329, 65 | '1.12-pre3': 330, 66 | '1.12-pre4': 331, 67 | '1.12-pre5': 332, 68 | '1.12-pre6': 333, 69 | '1.12-pre7': 334, 70 | '1.12': 335, 71 | '17w31a': 336, 72 | '1.12.1-pre1': 337, 73 | '1.12.1': 338, 74 | '1.12.2-pre1': 339, 75 | '1.12.2-pre2': 339, 76 | '1.12.2': 340, 77 | '17w43a': 341, 78 | '17w43b': 342, 79 | '17w45a': 343, 80 | '17w45b': 344, 81 | '17w46a': 345, 82 | '17w47a': 346, 83 | '17w47b': 347, 84 | '17w48a': 348, 85 | '17w49a': 349, 86 | '17w49b': 350, 87 | '17w50a': 351, 88 | '18w01a': 352, 89 | '18w02a': 353, 90 | '18w03a': 354, 91 | '18w03b': 355, 92 | '18w05a': 356, 93 | '18w06a': 357, 94 | '18w07a': 358, 95 | '18w07b': 359, 96 | '18w07c': 360, 97 | '18w08a': 361, 98 | '18w08b': 362, 99 | '18w09a': 363, 100 | '18w10a': 364, 101 | '18w10b': 365, 102 | '18w10c': 366, 103 | '18w10d': 367, 104 | '18w11a': 368, 105 | '18w14a': 369, 106 | '18w14b': 370, 107 | '18w15a': 371, 108 | '18w16a': 372, 109 | '18w19a': 373, 110 | '18w19b': 374, 111 | '18w20a': 375, 112 | '18w20b': 376, 113 | '18w20c': 377, 114 | '18w21a': 378, 115 | '18w21b': 379, 116 | '18w22a': 380, 117 | '18w22b': 381, 118 | '18w22c': 382, 119 | '1.13-pre1': 383, 120 | '1.13-pre2': 384, 121 | '1.13-pre3': 385, 122 | '1.13-pre4': 386, 123 | '1.13-pre5': 387, 124 | '1.13-pre6': 388, 125 | '1.13-pre7': 389, 126 | '1.13-pre8': 390, 127 | '1.13-pre9': 391, 128 | '1.13-pre10': 392, 129 | '1.13': 393, 130 | '18w30a': 394, 131 | '18w30b': 395, 132 | '18w31a': 396, 133 | '18w32a': 397, 134 | '18w33a': 398, 135 | '1.13.1-pre1': 399, 136 | '1.13.1-pre2': 400, 137 | '1.13.1': 401, 138 | '1.13.2-pre1': 402, 139 | '1.13.2-pre2': 403, 140 | '1.13.2': 404, 141 | '18w43a': 441, 142 | '18w43b': 441, 143 | '18w43c': 442, 144 | '18w44a': 443, 145 | '18w45a': 444, 146 | '18w46a': 445, 147 | '18w47a': 446, 148 | '18w47b': 447, 149 | '18w48a': 448, 150 | '18w48b': 449, 151 | '18w49a': 450, 152 | '18w50a': 451, 153 | '19w02a': 452, 154 | '19w03a': 453, 155 | '19w03b': 454, 156 | '19w03c': 455, 157 | '19w04a': 456, 158 | '19w04b': 457, 159 | '19w05a': 458, 160 | '19w06a': 459, 161 | '19w07a': 460, 162 | '19w08a': 461, 163 | '19w08b': 462, 164 | '19w09a': 463, 165 | '19w11a': 464, 166 | '19w11b': 465, 167 | '19w12a': 466, 168 | '19w12b': 467, 169 | '19w13a': 468, 170 | '19w13b': 469, 171 | '19w14a': 470, 172 | '19w14b': 471, 173 | '1.14 Pre-Release 1': 472, 174 | '1.14 Pre-Release 2': 473, 175 | '1.14 Pre-Release 3': 474, 176 | '1.14 Pre-Release 4': 475, 177 | '1.14 Pre-Release 5': 476, 178 | '1.14': 477, 179 | '1.14.1 Pre-Release 1': 478, 180 | '1.14.1 Pre-Release 2': 479, 181 | '1.14.1': 480, 182 | '1.14.2 Pre-Release 1': 481, 183 | '1.14.2 Pre-Release 2': 482, 184 | '1.14.2 Pre-Release 3': 483, 185 | '1.14.2 Pre-Release 4': 484, 186 | '1.14.2': 485, 187 | '1.14.3-pre1': 486, 188 | '1.14.3-pre2': 487, 189 | '1.14.3-pre3': 488, 190 | '1.14.3-pre4': 489, 191 | '1.14.3': 490, 192 | '1.14.4-pre1': 491, 193 | '1.14.4-pre2': 492, 194 | '1.14.4-pre3': 493, 195 | '1.14.4-pre4': 494, 196 | '1.14.4-pre5': 495, 197 | '1.14.4-pre6': 496, 198 | '1.14.4-pre7': 497, 199 | '1.14.4': 498, 200 | } 201 | 202 | # Those Minecraft versions supported by pyCraft which are "release" versions, 203 | # i.e. not development snapshots or pre-release versions. 204 | RELEASE_MINECRAFT_VERSIONS = { 205 | vid: protocol for (vid, protocol) in SUPPORTED_MINECRAFT_VERSIONS.items() 206 | if __import__('re').match(r'\d+(\.\d+)+$', vid)} 207 | 208 | # The protocol versions of SUPPORTED_MINECRAFT_VERSIONS, without duplicates, 209 | # in ascending numerical (and hence chronological) order. 210 | SUPPORTED_PROTOCOL_VERSIONS = \ 211 | sorted(set(SUPPORTED_MINECRAFT_VERSIONS.values())) 212 | 213 | # The protocol versions of RELEASE_MINECRAFT_VERSIONS, without duplicates, 214 | # in ascending numerical (and hence chronological) order. 215 | RELEASE_PROTOCOL_VERSIONS = \ 216 | sorted(set(RELEASE_MINECRAFT_VERSIONS.values())) 217 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/authentication.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import uuid 4 | from .exceptions import YggdrasilError 5 | 6 | #: The base url for Ygdrassil requests 7 | AUTH_SERVER = "https://authserver.mojang.com" 8 | SESSION_SERVER = "https://sessionserver.mojang.com/session/minecraft" 9 | # Need this content type, or authserver will complain 10 | CONTENT_TYPE = "application/json" 11 | HEADERS = {"content-type": CONTENT_TYPE} 12 | 13 | 14 | class Profile(object): 15 | """ 16 | Container class for a MineCraft Selected profile. 17 | See: ``_ 18 | """ 19 | def __init__(self, id_=None, name=None): 20 | self.id_ = id_ 21 | self.name = name 22 | 23 | def to_dict(self): 24 | """ 25 | Returns ``self`` in dictionary-form, which can be serialized by json. 26 | """ 27 | if self: 28 | return {"id": self.id_, 29 | "name": self.name} 30 | else: 31 | raise AttributeError("Profile is not yet populated.") 32 | 33 | def __bool__(self): 34 | bool_state = self.id_ is not None and self.name is not None 35 | return bool_state 36 | 37 | # Python 2 support 38 | def __nonzero__(self): 39 | return self.__bool__() 40 | 41 | 42 | class AuthenticationToken(object): 43 | """ 44 | Represents an authentication token. 45 | 46 | See http://wiki.vg/Authentication. 47 | """ 48 | AGENT_NAME = "Minecraft" 49 | AGENT_VERSION = 1 50 | 51 | def __init__(self, username=None, access_token=None, client_token=None): 52 | """ 53 | Constructs an `AuthenticationToken` based on `access_token` and 54 | `client_token`. 55 | 56 | Parameters: 57 | access_token - An `str` object containing the `access_token`. 58 | client_token - An `str` object containing the `client_token`. 59 | 60 | Returns: 61 | A `AuthenticationToken` with `access_token` and `client_token` set. 62 | """ 63 | self.username = username 64 | self.access_token = access_token 65 | self.client_token = client_token 66 | self.profile = Profile() 67 | 68 | @property 69 | def authenticated(self): 70 | """ 71 | Attribute which is ``True`` when the token is authenticated and 72 | ``False`` when it isn't. 73 | """ 74 | if not self.username: 75 | return False 76 | 77 | if not self.access_token: 78 | return False 79 | 80 | if not self.client_token: 81 | return False 82 | 83 | if not self.profile: 84 | return False 85 | 86 | return True 87 | 88 | def authenticate(self, username, password, invalidate_previous=False): 89 | """ 90 | Authenticates the user against https://authserver.mojang.com using 91 | `username` and `password` parameters. 92 | 93 | Parameters: 94 | username - An `str` object with the username (unmigrated accounts) 95 | or email address for a Mojang account. 96 | password - An `str` object with the password. 97 | invalidate_previous - A `bool`. When `True`, invalidate 98 | all previously acquired `access_token`s across all clients. 99 | 100 | Returns: 101 | Returns `True` if successful. 102 | Otherwise it will raise an exception. 103 | 104 | Raises: 105 | minecraft.exceptions.YggdrasilError 106 | """ 107 | payload = { 108 | "agent": { 109 | "name": self.AGENT_NAME, 110 | "version": self.AGENT_VERSION 111 | }, 112 | "username": username, 113 | "password": password 114 | } 115 | 116 | if not invalidate_previous: 117 | # Include a `client_token` in the payload to prevent existing 118 | # `access_token`s from being invalidated. If `self.client_token` 119 | # is `None` generate a `client_token` using uuid4 120 | payload["clientToken"] = self.client_token or uuid.uuid4().hex 121 | 122 | res = _make_request(AUTH_SERVER, "authenticate", payload) 123 | 124 | _raise_from_response(res) 125 | 126 | json_resp = res.json() 127 | 128 | self.username = username 129 | self.access_token = json_resp["accessToken"] 130 | self.client_token = json_resp["clientToken"] 131 | self.profile.id_ = json_resp["selectedProfile"]["id"] 132 | self.profile.name = json_resp["selectedProfile"]["name"] 133 | 134 | return True 135 | 136 | def refresh(self): 137 | """ 138 | Refreshes the `AuthenticationToken`. Used to keep a user logged in 139 | between sessions and is preferred over storing a user's password in a 140 | file. 141 | 142 | Returns: 143 | Returns `True` if `AuthenticationToken` was successfully refreshed. 144 | Otherwise it raises an exception. 145 | 146 | Raises: 147 | minecraft.exceptions.YggdrasilError 148 | ValueError - if `AuthenticationToken.access_token` or 149 | `AuthenticationToken.client_token` isn't set. 150 | """ 151 | if self.access_token is None: 152 | raise ValueError("'access_token' not set!'") 153 | 154 | if self.client_token is None: 155 | raise ValueError("'client_token' is not set!") 156 | 157 | res = _make_request(AUTH_SERVER, 158 | "refresh", {"accessToken": self.access_token, 159 | "clientToken": self.client_token}) 160 | 161 | _raise_from_response(res) 162 | 163 | json_resp = res.json() 164 | 165 | self.access_token = json_resp["accessToken"] 166 | self.client_token = json_resp["clientToken"] 167 | self.profile.id_ = json_resp["selectedProfile"]["id"] 168 | self.profile.name = json_resp["selectedProfile"]["name"] 169 | 170 | return True 171 | 172 | def validate(self): 173 | """ 174 | Validates the AuthenticationToken. 175 | 176 | `AuthenticationToken.access_token` must be set! 177 | 178 | Returns: 179 | Returns `True` if `AuthenticationToken` is valid. 180 | Otherwise it will raise an exception. 181 | 182 | Raises: 183 | minecraft.exceptions.YggdrasilError 184 | ValueError - if `AuthenticationToken.access_token` is not set. 185 | """ 186 | if self.access_token is None: 187 | raise ValueError("'access_token' not set!") 188 | 189 | res = _make_request(AUTH_SERVER, "validate", 190 | {"accessToken": self.access_token}) 191 | 192 | # Validate returns 204 to indicate success 193 | # http://wiki.vg/Authentication#Response_3 194 | if res.status_code == 204: 195 | return True 196 | 197 | @staticmethod 198 | def sign_out(username, password): 199 | """ 200 | Invalidates `access_token`s using an account's 201 | `username` and `password`. 202 | 203 | Parameters: 204 | username - ``str`` containing the username 205 | password - ``str`` containing the password 206 | 207 | Returns: 208 | Returns `True` if sign out was successful. 209 | Otherwise it will raise an exception. 210 | 211 | Raises: 212 | minecraft.exceptions.YggdrasilError 213 | """ 214 | res = _make_request(AUTH_SERVER, "signout", 215 | {"username": username, "password": password}) 216 | 217 | if _raise_from_response(res) is None: 218 | return True 219 | 220 | def invalidate(self): 221 | """ 222 | Invalidates `access_token`s using the token pair stored in 223 | the `AuthenticationToken`. 224 | 225 | Returns: 226 | ``True`` if tokens were successfully invalidated. 227 | 228 | Raises: 229 | :class:`minecraft.exceptions.YggdrasilError` 230 | """ 231 | res = _make_request(AUTH_SERVER, "invalidate", 232 | {"accessToken": self.access_token, 233 | "clientToken": self.client_token}) 234 | 235 | if res.status_code != 204: 236 | _raise_from_response(res) 237 | return True 238 | 239 | def join(self, server_id): 240 | """ 241 | Informs the Mojang session-server that we're joining the 242 | MineCraft server with id ``server_id``. 243 | 244 | Parameters: 245 | server_id - ``str`` with the server id 246 | 247 | Returns: 248 | ``True`` if no errors occured 249 | 250 | Raises: 251 | :class:`minecraft.exceptions.YggdrasilError` 252 | 253 | """ 254 | if not self.authenticated: 255 | err = "AuthenticationToken hasn't been authenticated yet!" 256 | raise YggdrasilError(err) 257 | 258 | res = _make_request(SESSION_SERVER, "join", 259 | {"accessToken": self.access_token, 260 | "selectedProfile": self.profile.to_dict(), 261 | "serverId": server_id}) 262 | 263 | if res.status_code != 204: 264 | _raise_from_response(res) 265 | return True 266 | 267 | 268 | def _make_request(server, endpoint, data): 269 | """ 270 | Fires a POST with json-packed data to the given endpoint and returns 271 | response. 272 | 273 | Parameters: 274 | endpoint - An `str` object with the endpoint, e.g. "authenticate" 275 | data - A `dict` containing the payload data. 276 | 277 | Returns: 278 | A `requests.Request` object. 279 | """ 280 | res = requests.post(server + "/" + endpoint, data=json.dumps(data), 281 | headers=HEADERS, timeout=15) 282 | return res 283 | 284 | 285 | def _raise_from_response(res): 286 | """ 287 | Raises an appropriate `YggdrasilError` based on the `status_code` and 288 | `json` of a `requests.Request` object. 289 | """ 290 | if res.status_code == requests.codes['ok']: 291 | return None 292 | 293 | exception = YggdrasilError() 294 | exception.status_code = res.status_code 295 | 296 | try: 297 | json_resp = res.json() 298 | if not ("error" in json_resp and "errorMessage" in json_resp): 299 | raise ValueError 300 | except ValueError: 301 | message = "[{status_code}] Malformed error message: '{response_text}'" 302 | message = message.format(status_code=str(res.status_code), 303 | response_text=res.text) 304 | exception.args = (message,) 305 | else: 306 | message = "[{status_code}] {error}: '{error_message}'" 307 | message = message.format(status_code=str(res.status_code), 308 | error=json_resp["error"], 309 | error_message=json_resp["errorMessage"]) 310 | exception.args = (message,) 311 | exception.yggdrasil_error = json_resp["error"] 312 | exception.yggdrasil_message = json_resp["errorMessage"] 313 | exception.yggdrasil_cause = json_resp.get("cause") 314 | 315 | raise exception 316 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/compat.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module stores code used for making pyCraft compatible with 3 | both Python2 and Python3 while using the same codebase. 4 | """ 5 | 6 | # Raw input -> input shenangians 7 | # example 8 | # > from minecraft.compat import input 9 | # > input("asd") 10 | 11 | # Hi, I'm pylint, and sometimes I act silly, at which point my programmer 12 | # overlords need to correct me. 13 | 14 | # pylint: disable=undefined-variable,redefined-builtin,invalid-name 15 | try: 16 | input = raw_input 17 | except NameError: 18 | input = input 19 | 20 | try: 21 | unicode = unicode 22 | except NameError: 23 | unicode = str 24 | # pylint: enable=undefined-variable,redefined-builtin,invalid-name 25 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains the `Exceptions` used by this library. 3 | """ 4 | 5 | 6 | class YggdrasilError(Exception): 7 | """ 8 | Base `Exception` for the Yggdrasil authentication service. 9 | 10 | :param str message: A human-readable string representation of the error. 11 | :param int status_code: Initial value of :attr:`status_code`. 12 | :param str yggdrasil_error: Initial value of :attr:`yggdrasil_error`. 13 | :param str yggdrasil_message: Initial value of :attr:`yggdrasil_message`. 14 | :param str yggdrasil_cause: Initial value of :attr:`yggdrasil_cause`. 15 | """ 16 | 17 | def __init__( 18 | self, 19 | message=None, 20 | status_code=None, 21 | yggdrasil_error=None, 22 | yggdrasil_message=None, 23 | yggdrasil_cause=None, 24 | ): 25 | super(YggdrasilError, self).__init__(message) 26 | self.status_code = status_code 27 | self.yggdrasil_error = yggdrasil_error 28 | self.yggdrasil_message = yggdrasil_message 29 | self.yggdrasil_cause = yggdrasil_cause 30 | 31 | status_code = None 32 | """`int` or `None`. The associated HTTP status code. May be set.""" 33 | 34 | yggdrasil_error = None 35 | """`str` or `None`. The `"error"` field of the Yggdrasil response: a short 36 | description such as `"Method Not Allowed"` or 37 | `"ForbiddenOperationException"`. May be set. 38 | """ 39 | 40 | yggdrasil_message = None 41 | """`str` or `None`. The `"errorMessage"` field of the Yggdrasil response: 42 | a longer description such as `"Invalid credentials. Invalid username or 43 | password."`. May be set. 44 | """ 45 | 46 | yggdrasil_cause = None 47 | """`str` or `None`. The `"cause"` field of the Yggdrasil response: a string 48 | containing additional information about the error. May be set. 49 | """ 50 | 51 | 52 | class ConnectionFailure(Exception): 53 | """Raised by 'minecraft.networking.Connection' when a connection attempt 54 | fails. 55 | """ 56 | 57 | 58 | class VersionMismatch(ConnectionFailure): 59 | """Raised by 'minecraft.networking.Connection' when connection is not 60 | possible due to a difference between the server's and client's 61 | supported protocol versions. 62 | """ 63 | 64 | 65 | class LoginDisconnect(ConnectionFailure): 66 | """Raised by 'minecraft.networking.Connection' when a connection attempt 67 | is terminated by the server sending a Disconnect packet, during login, 68 | with an unknown message format. 69 | """ 70 | 71 | 72 | class InvalidState(ConnectionFailure): 73 | """Raised by 'minecraft.networking.Connection' when a connection attempt 74 | fails due to to the internal state of the Connection being unsuitable, 75 | for example if there is an existing ongoing connection. 76 | """ 77 | 78 | 79 | class IgnorePacket(Exception): 80 | """This exception may be raised from within a packet handler, such as 81 | `PacketReactor.react' or a packet listener added with 82 | `Connection.register_packet_listener', to stop any subsequent handlers 83 | from being called on that particular packet. 84 | """ 85 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains the networking code for `pyminecraft`. 3 | """ 4 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/connection.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | from collections import deque 4 | from threading import RLock 5 | import zlib 6 | import threading 7 | import socket 8 | import timeit 9 | import select 10 | import sys 11 | import json 12 | import re 13 | 14 | from future.utils import raise_ 15 | 16 | from .types import VarInt 17 | from .packets import clientbound, serverbound 18 | from . import packets 19 | from . import encryption 20 | from .. import SUPPORTED_PROTOCOL_VERSIONS, SUPPORTED_MINECRAFT_VERSIONS 21 | from ..exceptions import ( 22 | VersionMismatch, LoginDisconnect, IgnorePacket, InvalidState 23 | ) 24 | 25 | 26 | STATE_STATUS = 1 27 | STATE_PLAYING = 2 28 | 29 | 30 | class ConnectionContext(object): 31 | """A ConnectionContext encapsulates the static configuration parameters 32 | shared by the Connection class with other classes, such as Packet. 33 | Importantly, it can be used without knowing the interface of Connection. 34 | """ 35 | def __init__(self, **kwds): 36 | self.protocol_version = kwds.get('protocol_version') 37 | 38 | 39 | class _ConnectionOptions(object): 40 | def __init__(self, address=None, port=None, compression_threshold=-1, 41 | compression_enabled=False): 42 | self.address = address 43 | self.port = port 44 | self.compression_threshold = compression_threshold 45 | self.compression_enabled = compression_enabled 46 | 47 | 48 | class Connection(object): 49 | """This class represents a connection to a minecraft 50 | server, it handles everything from connecting, sending packets to 51 | handling default network behaviour 52 | """ 53 | def __init__( 54 | self, 55 | address, 56 | port=25565, 57 | auth_token=None, 58 | username=None, 59 | initial_version=None, 60 | allowed_versions=None, 61 | handle_exception=None, 62 | handle_exit=None, 63 | ): 64 | """Sets up an instance of this object to be able to connect to a 65 | minecraft server. 66 | 67 | The connect method needs to be called in order to actually begin 68 | the connection 69 | 70 | :param address: address of the server to connect to 71 | :param port(int): port of the server to connect to 72 | :param auth_token: :class:`minecraft.authentication.AuthenticationToken` 73 | object. If None, no authentication is attempted and 74 | the server is assumed to be running in offline mode. 75 | :param username: Username string; only applicable in offline mode. 76 | :param initial_version: A Minecraft version ID string or protocol 77 | version number to use if the server's protocol 78 | version cannot be determined. (Although it is 79 | now somewhat inaccurate, this name is retained 80 | for backward compatibility.) 81 | :param allowed_versions: A set of versions, each being a Minecraft 82 | version ID string or protocol version number, 83 | restricting the versions that the client may 84 | use in connecting to the server. 85 | :param handle_exception: The final exception handler. This is triggered 86 | when an exception occurs in the networking 87 | thread that is not caught normally. After 88 | any other user-registered exception handlers 89 | are run, the final exception (which may be the 90 | original exception or one raised by another 91 | handler) is passed, regardless of whether or 92 | not it was caught by another handler, to the 93 | final handler, which may be a function obeying 94 | the protocol of 'register_exception_handler'; 95 | the value 'None', meaning that if the 96 | exception was otherwise uncaught, it is 97 | re-raised from the networking thread after 98 | closing the connection; or the value 'False', 99 | meaning that the exception is never re-raised. 100 | :param handle_exit: A function to be called when a connection to a 101 | server terminates, not caused by an exception, 102 | and not with the intention to automatically 103 | reconnect. Exceptions raised from this function 104 | will be handled by any matching exception handlers. 105 | """ # NOQA 106 | 107 | # This lock is re-entrant because it may be acquired in a re-entrant 108 | # manner from within an outgoing packet listener 109 | self._write_lock = RLock() 110 | 111 | self.networking_thread = None 112 | self.new_networking_thread = None 113 | self.packet_listeners = [] 114 | self.early_packet_listeners = [] 115 | self.outgoing_packet_listeners = [] 116 | self.early_outgoing_packet_listeners = [] 117 | self._exception_handlers = [] 118 | 119 | def proto_version(version): 120 | if isinstance(version, str): 121 | proto_version = SUPPORTED_MINECRAFT_VERSIONS.get(version) 122 | elif isinstance(version, int): 123 | proto_version = version 124 | else: 125 | proto_version = None 126 | if proto_version not in SUPPORTED_PROTOCOL_VERSIONS: 127 | raise ValueError('Unsupported version number: %r.' % version) 128 | return proto_version 129 | 130 | if allowed_versions is None: 131 | self.allowed_proto_versions = set(SUPPORTED_PROTOCOL_VERSIONS) 132 | else: 133 | allowed_versions = set(map(proto_version, allowed_versions)) 134 | self.allowed_proto_versions = allowed_versions 135 | 136 | if initial_version is None: 137 | self.default_proto_version = max(self.allowed_proto_versions) 138 | else: 139 | self.default_proto_version = proto_version(initial_version) 140 | 141 | self.context = ConnectionContext( 142 | protocol_version=max(self.allowed_proto_versions)) 143 | 144 | self.options = _ConnectionOptions() 145 | self.options.address = address 146 | self.options.port = port 147 | self.auth_token = auth_token 148 | self.username = username 149 | self.connected = False 150 | 151 | self.handle_exception = handle_exception 152 | self.exception, self.exc_info = None, None 153 | self.handle_exit = handle_exit 154 | 155 | # The reactor handles all the default responses to packets, 156 | # it should be changed per networking state 157 | self.reactor = PacketReactor(self) 158 | 159 | def _start_network_thread(self): 160 | with self._write_lock: 161 | if self.networking_thread is not None and \ 162 | not self.networking_thread.interrupt or \ 163 | self.new_networking_thread is not None: 164 | raise InvalidState('A networking thread is already running.') 165 | elif self.networking_thread is None: 166 | self.networking_thread = NetworkingThread(self) 167 | self.networking_thread.start() 168 | else: 169 | # This thread will wait until the existing thread exits, and 170 | # then set 'networking_thread' to itself and 171 | # 'new_networking_thread' to None. 172 | self.new_networking_thread \ 173 | = NetworkingThread(self, previous=self.networking_thread) 174 | self.new_networking_thread.start() 175 | 176 | def write_packet(self, packet, force=False): 177 | """Writes a packet to the server. 178 | 179 | If force is set to true, the method attempts to acquire the write lock 180 | and write the packet out immediately, and as such may block. 181 | 182 | If force is false then the packet will be added to the end of the 183 | packet writing queue to be sent 'as soon as possible' 184 | 185 | :param packet: The :class:`network.packets.Packet` to write 186 | :param force(bool): Specifies if the packet write should be immediate 187 | """ 188 | packet.context = self.context 189 | if force: 190 | with self._write_lock: 191 | self._write_packet(packet) 192 | else: 193 | self._outgoing_packet_queue.append(packet) 194 | 195 | def listener(self, *packet_types, **kwds): 196 | """ 197 | Shorthand decorator to register a function as a packet listener. 198 | """ 199 | def listener_decorator(handler_func): 200 | self.register_packet_listener(handler_func, *packet_types, **kwds) 201 | return handler_func 202 | 203 | return listener_decorator 204 | 205 | def exception_handler(self, *exc_types, **kwds): 206 | """ 207 | Shorthand decorator to register a function as an exception handler. 208 | """ 209 | def exception_handler_decorator(handler_func): 210 | self.register_exception_handler(handler_func, *exc_types, **kwds) 211 | return handler_func 212 | 213 | return exception_handler_decorator 214 | 215 | def register_packet_listener(self, method, *packet_types, **kwds): 216 | """ 217 | Registers a listener method which will be notified when a packet of 218 | a selected type is received. 219 | 220 | If :class:`minecraft.networking.connection.IgnorePacket` is raised from 221 | within this method, no subsequent handlers will be called. If 222 | 'early=True', this has the additional effect of preventing the default 223 | in-built action; this could break the internal state of the 224 | 'Connection', so should be done with care. If, in addition, 225 | 'outgoing=True', this will prevent the packet from being written to the 226 | network. 227 | 228 | :param method: The method which will be called back with the packet 229 | :param packet_types: The packets to listen for 230 | :param outgoing: If 'True', this listener will be called on outgoing 231 | packets just after they are sent to the server, rather 232 | than on incoming packets. 233 | :param early: If 'True', this listener will be called before any 234 | built-in default action is carried out, and before any 235 | listeners with 'early=False' are called. If 236 | 'outgoing=True', the listener will be called before the 237 | packet is written to the network, rather than afterwards. 238 | """ 239 | outgoing = kwds.pop('outgoing', False) 240 | early = kwds.pop('early', False) 241 | target = self.packet_listeners if not early and not outgoing \ 242 | else self.early_packet_listeners if early and not outgoing \ 243 | else self.outgoing_packet_listeners if not early \ 244 | else self.early_outgoing_packet_listeners 245 | target.append(packets.PacketListener(method, *packet_types, **kwds)) 246 | 247 | def register_exception_handler(self, handler_func, *exc_types, **kwds): 248 | """ 249 | Register a function to be called when an unhandled exception occurs 250 | in the networking thread. 251 | 252 | When multiple exception handlers are registered, they act like 'except' 253 | clauses in a Python 'try' clause, with the earliest matching handler 254 | catching the exception, and any later handlers catching any uncaught 255 | exception raised from within an earlier handler. 256 | 257 | Regardless of the presence or absence of matching handlers, any such 258 | exception will cause the connection and the networking thread to 259 | terminate, the final exception handler will be called (see the 260 | 'handle_exception' argument of the 'Connection' contructor), and the 261 | original exception - or the last exception raised by a handler - will 262 | be set as the 'exception' and 'exc_info' attributes of the 263 | 'Connection'. 264 | 265 | :param handler_func: A function taking two arguments: the exception 266 | object 'e' as in 'except Exception as e:', and the corresponding 267 | 3-tuple given by 'sys.exc_info()'. The return value of the function is 268 | ignored, but any exception raised in it replaces the original 269 | exception, and may be passed to later exception handlers. 270 | 271 | :param exc_types: The types of exceptions that this handler shall 272 | catch, as in 'except (exc_type_1, exc_type_2, ...) as e:'. If this is 273 | empty, the handler will catch all exceptions. 274 | 275 | :param early: If 'True', the exception handler is registered before 276 | any existing exception handlers in the handling order. 277 | """ 278 | early = kwds.pop('early', False) 279 | assert not kwds, 'Unexpected keyword arguments: %r' % (kwds,) 280 | if early: 281 | self._exception_handlers.insert(0, (handler_func, exc_types)) 282 | else: 283 | self._exception_handlers.append((handler_func, exc_types)) 284 | 285 | def _pop_packet(self): 286 | # Pops the topmost packet off the outgoing queue and writes it out 287 | # through the socket 288 | # 289 | # Mostly an internal convenience function, caller should make sure 290 | # they have the write lock acquired to avoid issues caused by 291 | # asynchronous access to the socket. 292 | # This should be the only method that removes elements from the 293 | # outbound queue 294 | if len(self._outgoing_packet_queue) == 0: 295 | return False 296 | else: 297 | self._write_packet(self._outgoing_packet_queue.popleft()) 298 | return True 299 | 300 | def _write_packet(self, packet): 301 | # Immediately writes the given packet to the network. The caller must 302 | # have the write lock acquired before calling this method. 303 | try: 304 | for listener in self.early_outgoing_packet_listeners: 305 | listener.call_packet(packet) 306 | 307 | if self.options.compression_enabled: 308 | packet.write(self.socket, self.options.compression_threshold) 309 | else: 310 | packet.write(self.socket) 311 | 312 | for listener in self.outgoing_packet_listeners: 313 | listener.call_packet(packet) 314 | except IgnorePacket: 315 | pass 316 | 317 | def status(self, handle_status=None, handle_ping=False): 318 | """Issue a status request to the server and then disconnect. 319 | 320 | :param handle_status: a function to be called with the status 321 | dictionary None for the default behaviour of 322 | printing the dictionary to standard output, or 323 | False to ignore the result. 324 | :param handle_ping: a function to be called with the measured latency 325 | in milliseconds, None for the default handler, 326 | which prints the latency to standard outout, or 327 | False, to prevent measurement of the latency. 328 | """ 329 | with self._write_lock: # pylint: disable=not-context-manager 330 | self._check_connection() 331 | 332 | self._connect() 333 | self._handshake(next_state=STATE_STATUS) 334 | self._start_network_thread() 335 | 336 | do_ping = handle_ping is not False 337 | self.reactor = StatusReactor(self, do_ping=do_ping) 338 | 339 | if handle_status is False: 340 | self.reactor.handle_status = lambda *args, **kwds: None 341 | elif handle_status is not None: 342 | self.reactor.handle_status = handle_status 343 | 344 | if handle_ping is False: 345 | self.reactor.handle_ping = lambda *args, **kwds: None 346 | elif handle_ping is not None: 347 | self.reactor.handle_ping = handle_ping 348 | 349 | request_packet = serverbound.status.RequestPacket() 350 | self.write_packet(request_packet) 351 | 352 | def connect(self): 353 | """ 354 | Attempt to begin connecting to the server. 355 | May safely be called multiple times after the first, i.e. to reconnect. 356 | """ 357 | # Hold the lock throughout, in case connect() is called from the 358 | # networking thread while another connection is in progress. 359 | with self._write_lock: # pylint: disable=not-context-manager 360 | self._check_connection() 361 | 362 | # It is important that this is set correctly even when connecting 363 | # in status mode, as some servers, e.g. SpigotMC with the 364 | # ProtocolSupport plugin, use it to determine the correct response. 365 | self.context.protocol_version = max(self.allowed_proto_versions) 366 | 367 | self.spawned = False 368 | self._connect() 369 | if len(self.allowed_proto_versions) == 1: 370 | # There is exactly one allowed protocol version, so skip the 371 | # process of determining the server's version, and immediately 372 | # connect. 373 | self._handshake(next_state=STATE_PLAYING) 374 | login_start_packet = serverbound.login.LoginStartPacket() 375 | if self.auth_token: 376 | login_start_packet.name = self.auth_token.profile.name 377 | else: 378 | login_start_packet.name = self.username 379 | self.write_packet(login_start_packet) 380 | self.reactor = LoginReactor(self) 381 | else: 382 | # Determine the server's protocol version by first performing a 383 | # status query. 384 | self._handshake(next_state=STATE_STATUS) 385 | self.write_packet(serverbound.status.RequestPacket()) 386 | self.reactor = PlayingStatusReactor(self) 387 | self._start_network_thread() 388 | 389 | def _check_connection(self): 390 | if self.networking_thread is not None and \ 391 | not self.networking_thread.interrupt or \ 392 | self.new_networking_thread is not None: 393 | raise InvalidState('There is an existing connection.') 394 | 395 | def _connect(self): 396 | # Connect a socket to the server and create a file object from the 397 | # socket. 398 | # The file object is used to read any and all data from the socket 399 | # since it's "guaranteed" to read the number of bytes specified, 400 | # the socket itself will mostly be used to write data upstream to 401 | # the server. 402 | self._outgoing_packet_queue = deque() 403 | 404 | info = socket.getaddrinfo(self.options.address, self.options.port, 405 | 0, socket.SOCK_STREAM) 406 | 407 | # Prefer to use IPv4 (for backward compatibility with previous 408 | # versions that always resolved hostnames to IPv4 addresses), 409 | # then IPv6, then other address families. 410 | def key(ai): 411 | return 0 if ai[0] == socket.AF_INET else \ 412 | 1 if ai[0] == socket.AF_INET6 else 2 413 | ai_faml, ai_type, ai_prot, _ai_cnam, ai_addr = min(info, key=key) 414 | 415 | self.socket = socket.socket(ai_faml, ai_type, ai_prot) 416 | self.socket.connect(ai_addr) 417 | self.file_object = self.socket.makefile("rb", 0) 418 | self.options.compression_enabled = False 419 | self.options.compression_threshold = -1 420 | self.connected = True 421 | 422 | def disconnect(self, immediate=False): 423 | """Terminate the existing server connection, if there is one. 424 | If 'immediate' is True, do not attempt to write any packets. 425 | """ 426 | with self._write_lock: # pylint: disable=not-context-manager 427 | self.connected = False 428 | 429 | if not immediate and self.socket is not None: 430 | # Flush any packets remaining in the queue. 431 | while self._pop_packet(): 432 | pass 433 | 434 | if self.networking_thread is not None: 435 | self.networking_thread.interrupt = True 436 | 437 | if self.socket is not None: 438 | try: 439 | self.socket.shutdown(socket.SHUT_RDWR) 440 | except socket.error: 441 | pass 442 | finally: 443 | self.socket.close() 444 | self.socket = None 445 | 446 | def _handshake(self, next_state=STATE_PLAYING): 447 | handshake = serverbound.handshake.HandShakePacket() 448 | handshake.protocol_version = self.context.protocol_version 449 | handshake.server_address = self.options.address 450 | handshake.server_port = self.options.port 451 | handshake.next_state = next_state 452 | 453 | self.write_packet(handshake) 454 | 455 | def _handle_exception(self, exc, exc_info): 456 | # Call the current PacketReactor's exception handler. 457 | try: 458 | if self.reactor.handle_exception(exc, exc_info): 459 | return 460 | except Exception as new_exc: 461 | exc, exc_info = new_exc, sys.exc_info() 462 | 463 | # Call the user-registered exception handlers in order. 464 | for handler, exc_types in self._exception_handlers: 465 | if not exc_types or isinstance(exc, exc_types): 466 | try: 467 | handler(exc, exc_info) 468 | caught = True 469 | break 470 | except Exception as new_exc: 471 | exc, exc_info = new_exc, sys.exc_info() 472 | else: 473 | caught = False 474 | 475 | # Call the user-specified final exception handler. 476 | if self.handle_exception not in (None, False): 477 | try: 478 | self.handle_exception(exc, exc_info) 479 | except Exception as new_exc: 480 | exc, exc_info = new_exc, sys.exc_info() 481 | 482 | # For backward compatibility, try to set the 'exc_info' attribute. 483 | try: 484 | exc.exc_info = exc_info 485 | except (TypeError, AttributeError): 486 | pass 487 | 488 | # Record the exception and cleanly terminate the connection. 489 | self.exception, self.exc_info = exc, exc_info 490 | self.disconnect(immediate=True) 491 | 492 | # If allowed by the final exception handler, re-raise the exception. 493 | if self.handle_exception is None and not caught: 494 | raise_(*exc_info) 495 | 496 | def _version_mismatch(self, server_protocol=None, server_version=None): 497 | if server_protocol is None: 498 | server_protocol = SUPPORTED_MINECRAFT_VERSIONS.get(server_version) 499 | 500 | if server_protocol is None: 501 | vs = 'version' if server_version is None else \ 502 | ('version of %s' % server_version) 503 | else: 504 | vs = ('protocol version of %d' % server_protocol) + \ 505 | ('' if server_version is None else ' (%s)' % server_version) 506 | ss = 'supported, but not allowed for this connection' \ 507 | if server_protocol in SUPPORTED_PROTOCOL_VERSIONS \ 508 | else 'not supported' 509 | raise VersionMismatch("Server's %s is %s." % (vs, ss)) 510 | 511 | def _handle_exit(self): 512 | if not self.connected and self.handle_exit is not None: 513 | self.handle_exit() 514 | 515 | def _react(self, packet): 516 | try: 517 | for listener in self.early_packet_listeners: 518 | listener.call_packet(packet) 519 | self.reactor.react(packet) 520 | for listener in self.packet_listeners: 521 | listener.call_packet(packet) 522 | except IgnorePacket: 523 | pass 524 | 525 | 526 | class NetworkingThread(threading.Thread): 527 | def __init__(self, connection, previous=None): 528 | threading.Thread.__init__(self) 529 | self.interrupt = False 530 | self.connection = connection 531 | self.name = "Networking Thread" 532 | self.daemon = True 533 | 534 | self.previous_thread = previous 535 | 536 | def run(self): 537 | try: 538 | if self.previous_thread is not None: 539 | if self.previous_thread.is_alive(): 540 | self.previous_thread.join() 541 | with self.connection._write_lock: 542 | self.connection.networking_thread = self 543 | self.connection.new_networking_thread = None 544 | self._run() 545 | self.connection._handle_exit() 546 | except Exception as e: 547 | self.interrupt = True 548 | self.connection._handle_exception(e, sys.exc_info()) 549 | finally: 550 | with self.connection._write_lock: 551 | self.connection.networking_thread = None 552 | 553 | def _run(self): 554 | while not self.interrupt: 555 | # Attempt to write out as many as 300 packets. 556 | num_packets = 0 557 | with self.connection._write_lock: 558 | try: 559 | while not self.interrupt and self.connection._pop_packet(): 560 | num_packets += 1 561 | if num_packets >= 300: 562 | break 563 | exc_info = None 564 | except IOError: 565 | exc_info = sys.exc_info() 566 | 567 | # If any packets remain to be written, resume writing as soon 568 | # as possible after reading any available packets; otherwise, 569 | # wait for up to 50ms (1 tick) for new packets to arrive. 570 | if self.connection._outgoing_packet_queue: 571 | read_timeout = 0 572 | else: 573 | read_timeout = 0.05 574 | 575 | # Read and react to as many as 50 packets. 576 | while num_packets < 50 and not self.interrupt: 577 | packet = self.connection.reactor.read_packet( 578 | self.connection.file_object, timeout=read_timeout) 579 | if not packet: 580 | break 581 | num_packets += 1 582 | self.connection._react(packet) 583 | read_timeout = 0 584 | 585 | # Ignore the earlier exception if a disconnect packet is 586 | # received, as it may have been caused by trying to write to 587 | # the closed socket, which does not represent a program error. 588 | if exc_info is not None and packet.packet_name == "disconnect": 589 | exc_info = None 590 | 591 | if exc_info is not None: 592 | raise_(*exc_info) 593 | 594 | 595 | class PacketReactor(object): 596 | """ 597 | Reads and reacts to packets 598 | """ 599 | state_name = None 600 | 601 | # Handshaking is considered the "default" state 602 | get_clientbound_packets = staticmethod(clientbound.handshake.get_packets) 603 | 604 | def __init__(self, connection): 605 | self.connection = connection 606 | context = self.connection.context 607 | self.clientbound_packets = { 608 | packet.get_id(context): packet 609 | for packet in self.__class__.get_clientbound_packets(context)} 610 | 611 | def read_packet(self, stream, timeout=0): 612 | # Block for up to `timeout' seconds waiting for `stream' to become 613 | # readable, returning `None' if the timeout elapses. 614 | ready_to_read = select.select([stream], [], [], timeout)[0] 615 | 616 | if ready_to_read: 617 | length = VarInt.read(stream) 618 | 619 | packet_data = packets.PacketBuffer() 620 | packet_data.send(stream.read(length)) 621 | # Ensure we read all the packet 622 | while len(packet_data.get_writable()) < length: 623 | packet_data.send( 624 | stream.read(length - len(packet_data.get_writable()))) 625 | packet_data.reset_cursor() 626 | 627 | if self.connection.options.compression_enabled: 628 | decompressed_size = VarInt.read(packet_data) 629 | if decompressed_size > 0: 630 | decompressor = zlib.decompressobj() 631 | decompressed_packet = decompressor.decompress( 632 | packet_data.read()) 633 | assert len(decompressed_packet) == decompressed_size, \ 634 | 'decompressed length %d, but expected %d' % \ 635 | (len(decompressed_packet), decompressed_size) 636 | packet_data.reset() 637 | packet_data.send(decompressed_packet) 638 | packet_data.reset_cursor() 639 | 640 | packet_id = VarInt.read(packet_data) 641 | 642 | # If we know the structure of the packet, attempt to parse it 643 | # otherwise just skip it 644 | if packet_id in self.clientbound_packets: 645 | packet = self.clientbound_packets[packet_id]() 646 | packet.context = self.connection.context 647 | packet.read(packet_data) 648 | return packet 649 | else: 650 | return packets.Packet(context=self.connection.context) 651 | else: 652 | return None 653 | 654 | def react(self, packet): 655 | """Called with each incoming packet after early packet listeners are 656 | run (if none of them raise 'IgnorePacket'), but before regular 657 | packet listeners are run. If this method raises 'IgnorePacket', no 658 | subsequent packet listeners will be called for this packet. 659 | """ 660 | raise NotImplementedError("Call to base reactor") 661 | 662 | def handle_exception(self, exc, exc_info): 663 | """Called when an exception is raised in the networking thread. If this 664 | method returns True, the default action will be prevented and the 665 | exception ignored (but the networking thread will still terminate). 666 | """ 667 | return False 668 | 669 | 670 | class LoginReactor(PacketReactor): 671 | get_clientbound_packets = staticmethod(clientbound.login.get_packets) 672 | 673 | def react(self, packet): 674 | if packet.packet_name == "encryption request": 675 | 676 | secret = encryption.generate_shared_secret() 677 | token, encrypted_secret = encryption.encrypt_token_and_secret( 678 | packet.public_key, packet.verify_token, secret) 679 | 680 | # A server id of '-' means the server is in offline mode 681 | if packet.server_id != '-': 682 | server_id = encryption.generate_verification_hash( 683 | packet.server_id, secret, packet.public_key) 684 | if self.connection.auth_token is not None: 685 | self.connection.auth_token.join(server_id) 686 | 687 | encryption_response = serverbound.login.EncryptionResponsePacket() 688 | encryption_response.shared_secret = encrypted_secret 689 | encryption_response.verify_token = token 690 | 691 | # Forced because we'll have encrypted the connection by the time 692 | # it reaches the outgoing queue 693 | self.connection.write_packet(encryption_response, force=True) 694 | 695 | # Enable the encryption 696 | cipher = encryption.create_AES_cipher(secret) 697 | encryptor = cipher.encryptor() 698 | decryptor = cipher.decryptor() 699 | self.connection.socket = encryption.EncryptedSocketWrapper( 700 | self.connection.socket, encryptor, decryptor) 701 | self.connection.file_object = \ 702 | encryption.EncryptedFileObjectWrapper( 703 | self.connection.file_object, decryptor) 704 | 705 | elif packet.packet_name == "disconnect": 706 | # Receiving a disconnect packet in the login state indicates an 707 | # abnormal condition. Raise an exception explaining the situation. 708 | try: 709 | msg = json.loads(packet.json_data)['text'] 710 | except (ValueError, TypeError, KeyError): 711 | msg = packet.json_data 712 | match = re.match(r"Outdated (client! Please use|server!" 713 | r" I'm still on) (?P\S+)$", msg) 714 | if match: 715 | ver = match.group('ver') 716 | self.connection._version_mismatch(server_version=ver) 717 | raise LoginDisconnect('The server rejected our login attempt ' 718 | 'with: "%s".' % msg) 719 | 720 | elif packet.packet_name == "login success": 721 | self.connection.reactor = PlayingReactor(self.connection) 722 | 723 | elif packet.packet_name == "set compression": 724 | self.connection.options.compression_threshold = packet.threshold 725 | self.connection.options.compression_enabled = True 726 | 727 | elif packet.packet_name == "login plugin request": 728 | self.connection.write_packet( 729 | serverbound.login.PluginResponsePacket( 730 | message_id=packet.message_id, successful=False)) 731 | 732 | 733 | class PlayingReactor(PacketReactor): 734 | get_clientbound_packets = staticmethod(clientbound.play.get_packets) 735 | 736 | def react(self, packet): 737 | if packet.packet_name == "set compression": 738 | self.connection.options.compression_threshold = packet.threshold 739 | self.connection.options.compression_enabled = True 740 | 741 | elif packet.packet_name == "keep alive": 742 | keep_alive_packet = serverbound.play.KeepAlivePacket() 743 | keep_alive_packet.keep_alive_id = packet.keep_alive_id 744 | self.connection.write_packet(keep_alive_packet) 745 | 746 | elif packet.packet_name == "player position and look": 747 | if self.connection.context.protocol_version >= 107: 748 | teleport_confirm = serverbound.play.TeleportConfirmPacket() 749 | teleport_confirm.teleport_id = packet.teleport_id 750 | self.connection.write_packet(teleport_confirm) 751 | position_response = serverbound.play.PositionAndLookPacket() 752 | position_response.x = packet.x 753 | position_response.feet_y = packet.y 754 | position_response.z = packet.z 755 | position_response.yaw = packet.yaw 756 | position_response.pitch = packet.pitch 757 | position_response.on_ground = True 758 | self.connection.write_packet(position_response) 759 | self.connection.spawned = True 760 | 761 | elif packet.packet_name == "disconnect": 762 | self.connection.disconnect() 763 | 764 | 765 | class StatusReactor(PacketReactor): 766 | get_clientbound_packets = staticmethod(clientbound.status.get_packets) 767 | 768 | def __init__(self, connection, do_ping=False): 769 | super(StatusReactor, self).__init__(connection) 770 | self.do_ping = do_ping 771 | 772 | def react(self, packet): 773 | if packet.packet_name == "response": 774 | status_dict = json.loads(packet.json_response) 775 | if self.do_ping: 776 | ping_packet = serverbound.status.PingPacket() 777 | # NOTE: it may be better to depend on the `monotonic' package 778 | # or something similar for more accurate time measurement. 779 | ping_packet.time = int(1000 * timeit.default_timer()) 780 | self.connection.write_packet(ping_packet) 781 | else: 782 | self.connection.disconnect() 783 | self.handle_status(status_dict) 784 | 785 | elif packet.packet_name == "ping": 786 | if self.do_ping: 787 | now = int(1000 * timeit.default_timer()) 788 | self.connection.disconnect() 789 | self.handle_ping(now - packet.time) 790 | 791 | def handle_status(self, status_dict): 792 | print(status_dict) 793 | 794 | def handle_ping(self, latency_ms): 795 | print('Ping: %d ms' % latency_ms) 796 | 797 | 798 | class PlayingStatusReactor(StatusReactor): 799 | def __init__(self, connection): 800 | super(PlayingStatusReactor, self).__init__(connection, do_ping=False) 801 | 802 | def handle_status(self, status): 803 | if status == {}: 804 | # This can occur when we connect to a Mojang server while it is 805 | # still initialising, so it must not cause the client to connect 806 | # with the default version. 807 | raise IOError('Invalid server status.') 808 | elif 'version' not in status or 'protocol' not in status['version']: 809 | return self.handle_failure() 810 | 811 | proto = status['version']['protocol'] 812 | if proto not in self.connection.allowed_proto_versions: 813 | self.connection._version_mismatch( 814 | server_protocol=proto, 815 | server_version=status['version'].get('name')) 816 | 817 | self.handle_proto_version(proto) 818 | 819 | def handle_proto_version(self, proto_version): 820 | self.connection.allowed_proto_versions = {proto_version} 821 | self.connection.connect() 822 | 823 | def handle_failure(self): 824 | self.handle_proto_version(self.connection.default_proto_version) 825 | 826 | def handle_exception(self, exc, exc_info): 827 | if isinstance(exc, EOFError): 828 | # An exception of this type may indicate that the server does not 829 | # properly support status queries, so we treat it as non-fatal. 830 | self.connection.disconnect(immediate=True) 831 | self.handle_failure() 832 | return True 833 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/connection.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/connection.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/encryption.py: -------------------------------------------------------------------------------- 1 | import os 2 | from hashlib import sha1 3 | from cryptography.hazmat.backends import default_backend 4 | from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15 5 | from cryptography.hazmat.primitives.serialization import load_der_public_key 6 | from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes 7 | 8 | 9 | def generate_shared_secret(): 10 | return os.urandom(16) 11 | 12 | 13 | def create_AES_cipher(shared_secret): 14 | cipher = Cipher(algorithms.AES(shared_secret), modes.CFB8(shared_secret), 15 | backend=default_backend()) 16 | return cipher 17 | 18 | 19 | def encrypt_token_and_secret(pubkey, verification_token, shared_secret): 20 | """Encrypts the verification token and shared secret 21 | with the server's public key. 22 | 23 | :param pubkey: The RSA public key provided by the server 24 | :param verification_token: The verification token provided by the server 25 | :param shared_secret: The generated shared secret 26 | :return: A tuple containing (encrypted token, encrypted secret) 27 | """ 28 | pubkey = load_der_public_key(pubkey, default_backend()) 29 | 30 | encrypted_token = pubkey.encrypt(verification_token, PKCS1v15()) 31 | encrypted_secret = pubkey.encrypt(shared_secret, PKCS1v15()) 32 | return encrypted_token, encrypted_secret 33 | 34 | 35 | def generate_verification_hash(server_id, shared_secret, public_key): 36 | verification_hash = sha1() 37 | 38 | verification_hash.update(server_id.encode('utf-8')) 39 | verification_hash.update(shared_secret) 40 | verification_hash.update(public_key) 41 | 42 | return minecraft_sha1_hash_digest(verification_hash) 43 | 44 | 45 | def minecraft_sha1_hash_digest(sha1_hash): 46 | # Minecraft first parses the sha1 bytes as a signed number and then 47 | # spits outs its hex representation 48 | number_representation = _number_from_bytes(sha1_hash.digest(), signed=True) 49 | return format(number_representation, 'x') 50 | 51 | 52 | def _number_from_bytes(b, signed=False): 53 | try: 54 | return int.from_bytes(b, byteorder='big', signed=signed) 55 | except AttributeError: # pragma: no cover 56 | # py-2 compatibility 57 | if len(b) == 0: 58 | b = b'\x00' 59 | num = int(str(b).encode('hex'), 16) 60 | if signed and (ord(b[0]) & 0x80): 61 | num -= 2 ** (len(b) * 8) 62 | return num 63 | 64 | 65 | class EncryptedFileObjectWrapper(object): 66 | def __init__(self, file_object, decryptor): 67 | self.actual_file_object = file_object 68 | self.decryptor = decryptor 69 | 70 | def read(self, length): 71 | return self.decryptor.update(self.actual_file_object.read(length)) 72 | 73 | def fileno(self): 74 | return self.actual_file_object.fileno() 75 | 76 | def close(self): 77 | self.actual_file_object.close() 78 | 79 | 80 | class EncryptedSocketWrapper(object): 81 | def __init__(self, socket, encryptor, decryptor): 82 | self.actual_socket = socket 83 | self.encryptor = encryptor 84 | self.decryptor = decryptor 85 | 86 | def recv(self, length): 87 | return self.decryptor.update(self.actual_socket.recv(length)) 88 | 89 | def send(self, data): 90 | self.actual_socket.send(self.encryptor.update(data)) 91 | 92 | def fileno(self): 93 | return self.actual_socket.fileno() 94 | 95 | def close(self): 96 | return self.actual_socket.close() 97 | 98 | def shutdown(self, *args, **kwds): 99 | return self.actual_socket.shutdown(*args, **kwds) 100 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/encryption.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/encryption.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | NOTE: The packet classes in __all_legacy_packets__ exported by this 3 | module are included only for backward compatibility, and should not 4 | be used in new code, as (1) they do not include all packets present 5 | in pyCraft, and (2) some are named oddly, for historical reasons. 6 | 7 | Use the packet classes under packets.clientbound.* and 8 | packets.serverbound.* instead. 9 | ''' 10 | 11 | # Packet-Related Utilities 12 | from .packet_buffer import PacketBuffer 13 | from .packet_listener import PacketListener 14 | 15 | # Abstract Packet Classes 16 | from .packet import Packet 17 | from .keep_alive_packet import AbstractKeepAlivePacket 18 | from .plugin_message_packet import AbstractPluginMessagePacket 19 | 20 | 21 | # Legacy Packets (Handshake State) 22 | from .clientbound.handshake import get_packets as state_handshake_clientbound 23 | from .serverbound.handshake import HandShakePacket 24 | from .serverbound.handshake import get_packets as state_handshake_serverbound 25 | 26 | # Legacy Packets (Status State) 27 | from .clientbound.status import ResponsePacket 28 | from .clientbound.status import PingResponsePacket as PingPacketResponse 29 | from .clientbound.status import get_packets as state_status_clientbound 30 | from .serverbound.status import RequestPacket 31 | from .serverbound.status import PingPacket 32 | from .serverbound.status import get_packets as state_status_serverbound 33 | 34 | # Legacy Packets (Login State) 35 | from .clientbound.login import DisconnectPacket 36 | from .clientbound.login import EncryptionRequestPacket 37 | from .clientbound.login import LoginSuccessPacket 38 | from .clientbound.login import SetCompressionPacket 39 | from .clientbound.login import get_packets as state_login_clientbound 40 | from .serverbound.login import LoginStartPacket 41 | from .serverbound.login import EncryptionResponsePacket 42 | from .serverbound.login import get_packets as state_login_serverbound 43 | 44 | # Legacy Packets (Playing State) 45 | from .keep_alive_packet import KeepAlivePacket 46 | from .clientbound.play import KeepAlivePacket as KeepAlivePacketClientbound 47 | from .serverbound.play import KeepAlivePacket as KeepAlivePacketServerbound 48 | from .clientbound.play import JoinGamePacket 49 | from .clientbound.play import ChatMessagePacket 50 | from .clientbound.play import PlayerPositionAndLookPacket 51 | from .clientbound.play import DisconnectPacket as DisconnectPacketPlayState 52 | from .clientbound.play import ( 53 | SetCompressionPacket as SetCompressionPacketPlayState 54 | ) 55 | from .clientbound.play import PlayerListItemPacket 56 | from .clientbound.play import MapPacket 57 | from .clientbound.play import get_packets as state_playing_clientbound 58 | from .serverbound.play import ChatPacket 59 | from .serverbound.play import PositionAndLookPacket 60 | from .serverbound.play import TeleportConfirmPacket 61 | from .serverbound.play import AnimationPacket as AnimationPacketServerbound 62 | from .serverbound.play import get_packets as state_playing_serverbound 63 | 64 | __all_legacy_packets__ = ( 65 | state_handshake_clientbound, HandShakePacket, 66 | state_handshake_serverbound, ResponsePacket, 67 | PingPacketResponse, state_status_clientbound, 68 | RequestPacket, PingPacket, state_status_serverbound, 69 | DisconnectPacket, EncryptionRequestPacket, LoginSuccessPacket, 70 | SetCompressionPacket, state_login_clientbound, 71 | LoginStartPacket, EncryptionResponsePacket, 72 | state_login_serverbound, KeepAlivePacketClientbound, 73 | KeepAlivePacketServerbound, JoinGamePacket, ChatMessagePacket, 74 | PlayerPositionAndLookPacket, DisconnectPacketPlayState, 75 | SetCompressionPacketPlayState, PlayerListItemPacket, 76 | MapPacket, state_playing_clientbound, ChatPacket, 77 | PositionAndLookPacket, TeleportConfirmPacket, 78 | AnimationPacketServerbound, state_playing_serverbound, 79 | KeepAlivePacket, 80 | ) 81 | 82 | __all_other__ = ( 83 | Packet, PacketBuffer, PacketListener, 84 | AbstractKeepAlivePacket, AbstractPluginMessagePacket, 85 | ) 86 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains the clientbound packets for `pyminecraft`. 3 | """ 4 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/handshake/__init__.py: -------------------------------------------------------------------------------- 1 | # Formerly known as state_handshake_clientbound. 2 | def get_packets(context): 3 | return set() 4 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/handshake/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/handshake/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/login/__init__.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | 3 | from ....types import ( 4 | VarInt, String, VarIntPrefixedByteArray, TrailingByteArray 5 | ) 6 | 7 | 8 | # Formerly known as state_login_clientbound. 9 | def get_packets(context): 10 | packets = { 11 | DisconnectPacket, 12 | EncryptionRequestPacket, 13 | LoginSuccessPacket, 14 | SetCompressionPacket, 15 | } 16 | if context.protocol_version >= 385: 17 | packets |= { 18 | PluginRequestPacket, 19 | } 20 | return packets 21 | 22 | 23 | class DisconnectPacket(Packet): 24 | @staticmethod 25 | def get_id(context): 26 | return 0x00 if context.protocol_version >= 391 else \ 27 | 0x01 if context.protocol_version >= 385 else \ 28 | 0x00 29 | 30 | packet_name = "disconnect" 31 | definition = [ 32 | {'json_data': String}] 33 | 34 | 35 | class EncryptionRequestPacket(Packet): 36 | @staticmethod 37 | def get_id(context): 38 | return 0x01 if context.protocol_version >= 391 else \ 39 | 0x02 if context.protocol_version >= 385 else \ 40 | 0x01 41 | 42 | packet_name = "encryption request" 43 | definition = [ 44 | {'server_id': String}, 45 | {'public_key': VarIntPrefixedByteArray}, 46 | {'verify_token': VarIntPrefixedByteArray}] 47 | 48 | 49 | class LoginSuccessPacket(Packet): 50 | @staticmethod 51 | def get_id(context): 52 | return 0x02 if context.protocol_version >= 391 else \ 53 | 0x03 if context.protocol_version >= 385 else \ 54 | 0x02 55 | 56 | packet_name = "login success" 57 | definition = [ 58 | {'UUID': String}, 59 | {'Username': String}] 60 | 61 | 62 | class SetCompressionPacket(Packet): 63 | @staticmethod 64 | def get_id(context): 65 | return 0x03 if context.protocol_version >= 391 else \ 66 | 0x04 if context.protocol_version >= 385 else \ 67 | 0x03 68 | 69 | packet_name = "set compression" 70 | definition = [ 71 | {'threshold': VarInt}] 72 | 73 | 74 | class PluginRequestPacket(Packet): 75 | """ NOTE: pyCraft's default behaviour on receiving a 'PluginRequestPacket' 76 | is to send a corresponding 'PluginResponsePacket' with 77 | 'successful=False'. To override this, set a packet listener that: 78 | 79 | (1) has the keyword argument 'early=True' set when calling 80 | 'register_packet_listener'; and 81 | 82 | (2) raises 'minecraft.networking.connection.IgnorePacket' after 83 | sending a corresponding 'PluginResponsePacket'. 84 | 85 | Otherwise, one 'PluginRequestPacket' may result in multiple responses, 86 | which contravenes Minecraft's protocol. 87 | """ 88 | 89 | @staticmethod 90 | def get_id(context): 91 | return 0x04 if context.protocol_version >= 391 else \ 92 | 0x00 93 | 94 | packet_name = 'login plugin request' 95 | definition = [ 96 | {'message_id': VarInt}, 97 | {'channel': String}, 98 | {'data': TrailingByteArray}] 99 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/login/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/login/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/__init__.py: -------------------------------------------------------------------------------- 1 | from ....packets import ( 2 | Packet, AbstractKeepAlivePacket, AbstractPluginMessagePacket 3 | ) 4 | 5 | from ....types import ( 6 | Integer, FixedPointInteger, Angle, UnsignedByte, Byte, Boolean, UUID, 7 | Short, VarInt, Double, Float, String, Enum, Difficulty, Dimension, 8 | GameMode, Vector, Direction, PositionAndLook, multi_attribute_alias, 9 | ) 10 | 11 | from .combat_event_packet import CombatEventPacket 12 | from .map_packet import MapPacket 13 | from .player_list_item_packet import PlayerListItemPacket 14 | from .player_position_and_look_packet import PlayerPositionAndLookPacket 15 | from .spawn_object_packet import SpawnObjectPacket 16 | from .block_change_packet import BlockChangePacket, MultiBlockChangePacket 17 | from .explosion_packet import ExplosionPacket 18 | from .sound_effect_packet import SoundEffectPacket 19 | from .face_player_packet import FacePlayerPacket 20 | 21 | 22 | # Formerly known as state_playing_clientbound. 23 | def get_packets(context): 24 | packets = { 25 | KeepAlivePacket, 26 | JoinGamePacket, 27 | ServerDifficultyPacket, 28 | ChatMessagePacket, 29 | PlayerPositionAndLookPacket, 30 | MapPacket, 31 | PlayerListItemPacket, 32 | DisconnectPacket, 33 | SpawnPlayerPacket, 34 | EntityVelocityPacket, 35 | UpdateHealthPacket, 36 | CombatEventPacket, 37 | ExplosionPacket, 38 | SpawnObjectPacket, 39 | BlockChangePacket, 40 | MultiBlockChangePacket, 41 | RespawnPacket, 42 | PluginMessagePacket, 43 | PlayerListHeaderAndFooterPacket, 44 | EntityLookPacket 45 | } 46 | if context.protocol_version <= 47: 47 | packets |= { 48 | SetCompressionPacket, 49 | } 50 | if context.protocol_version >= 94: 51 | packets |= { 52 | SoundEffectPacket, 53 | } 54 | if context.protocol_version >= 352: 55 | packets |= { 56 | FacePlayerPacket 57 | } 58 | return packets 59 | 60 | 61 | class KeepAlivePacket(AbstractKeepAlivePacket): 62 | @staticmethod 63 | def get_id(context): 64 | return 0x20 if context.protocol_version >= 471 else \ 65 | 0x21 if context.protocol_version >= 389 else \ 66 | 0x20 if context.protocol_version >= 345 else \ 67 | 0x1F if context.protocol_version >= 332 else \ 68 | 0x20 if context.protocol_version >= 318 else \ 69 | 0x1F if context.protocol_version >= 107 else \ 70 | 0x00 71 | 72 | 73 | class JoinGamePacket(Packet): 74 | @staticmethod 75 | def get_id(context): 76 | return 0x25 if context.protocol_version >= 389 else \ 77 | 0x24 if context.protocol_version >= 345 else \ 78 | 0x23 if context.protocol_version >= 332 else \ 79 | 0x24 if context.protocol_version >= 318 else \ 80 | 0x23 if context.protocol_version >= 107 else \ 81 | 0x01 82 | 83 | packet_name = "join game" 84 | get_definition = staticmethod(lambda context: [ 85 | {'entity_id': Integer}, 86 | {'game_mode': UnsignedByte}, 87 | {'dimension': Integer if context.protocol_version >= 108 else Byte}, 88 | {'difficulty': UnsignedByte} if context.protocol_version < 464 else {}, 89 | {'max_players': UnsignedByte}, 90 | {'level_type': String}, 91 | {'render_distance': VarInt} if context.protocol_version >= 468 else {}, 92 | {'reduced_debug_info': Boolean}, 93 | ]) 94 | 95 | # These aliases declare the Enum type corresponding to each field: 96 | Difficulty = Difficulty 97 | GameMode = GameMode 98 | Dimension = Dimension 99 | 100 | 101 | class ServerDifficultyPacket(Packet): 102 | @staticmethod 103 | def get_id(context): 104 | return 0x0D if context.protocol_version >= 332 else \ 105 | 0x0E if context.protocol_version >= 318 else \ 106 | 0x0D if context.protocol_version >= 70 else \ 107 | 0x41 108 | 109 | packet_name = 'server difficulty' 110 | get_definition = staticmethod(lambda context: [ 111 | {'difficulty': UnsignedByte}, 112 | {'is_locked': Boolean} if context.protocol_version >= 464 else {}, 113 | ]) 114 | 115 | # These aliases declare the Enum type corresponding to each field: 116 | Difficulty = Difficulty 117 | 118 | 119 | class ChatMessagePacket(Packet): 120 | @staticmethod 121 | def get_id(context): 122 | return 0x0E if context.protocol_version >= 343 else \ 123 | 0x0F if context.protocol_version >= 332 else \ 124 | 0x10 if context.protocol_version >= 317 else \ 125 | 0x0F if context.protocol_version >= 107 else \ 126 | 0x02 127 | 128 | packet_name = "chat message" 129 | definition = [ 130 | {'json_data': String}, 131 | {'position': Byte}] 132 | 133 | class Position(Enum): 134 | CHAT = 0 # A player-initiated chat message. 135 | SYSTEM = 1 # The result of running a command. 136 | GAME_INFO = 2 # Displayed above the hotbar in vanilla clients. 137 | 138 | 139 | class DisconnectPacket(Packet): 140 | @staticmethod 141 | def get_id(context): 142 | return 0x1A if context.protocol_version >= 471 else \ 143 | 0x1B if context.protocol_version >= 345 else \ 144 | 0x1A if context.protocol_version >= 332 else \ 145 | 0x1B if context.protocol_version >= 318 else \ 146 | 0x1A if context.protocol_version >= 107 else \ 147 | 0x40 148 | 149 | packet_name = "disconnect" 150 | 151 | definition = [ 152 | {'json_data': String}] 153 | 154 | 155 | class SetCompressionPacket(Packet): 156 | # Note: removed between protocol versions 47 and 107. 157 | id = 0x46 158 | packet_name = "set compression" 159 | definition = [ 160 | {'threshold': VarInt}] 161 | 162 | 163 | class SpawnPlayerPacket(Packet): 164 | @staticmethod 165 | def get_id(context): 166 | return 0x05 if context.protocol_version >= 67 else \ 167 | 0x0C 168 | 169 | packet_name = 'spawn player' 170 | get_definition = staticmethod(lambda context: [ 171 | {'entity_id': VarInt}, 172 | {'player_UUID': UUID}, 173 | {'x': Double} if context.protocol_version >= 100 174 | else {'x': FixedPointInteger}, 175 | {'y': Double} if context.protocol_version >= 100 176 | else {'y': FixedPointInteger}, 177 | {'z': Double} if context.protocol_version >= 100 178 | else {'z': FixedPointInteger}, 179 | {'yaw': Angle}, 180 | {'pitch': Angle}, 181 | {'current_item': Short} if context.protocol_version <= 49 else {}, 182 | # TODO: read entity metadata 183 | ]) 184 | 185 | # Access the 'x', 'y', 'z' fields as a Vector tuple. 186 | position = multi_attribute_alias(Vector, 'x', 'y', 'z') 187 | 188 | # Access the 'yaw', 'pitch' fields as a Direction tuple. 189 | look = multi_attribute_alias(Direction, 'yaw', 'pitch') 190 | 191 | # Access the 'x', 'y', 'z', 'yaw', 'pitch' fields as a PositionAndLook. 192 | # NOTE: modifying the object retrieved from this property will not change 193 | # the packet; it can only be changed by attribute or property assignment. 194 | position_and_look = multi_attribute_alias( 195 | PositionAndLook, 'x', 'y', 'z', 'yaw', 'pitch') 196 | 197 | 198 | class EntityVelocityPacket(Packet): 199 | @staticmethod 200 | def get_id(context): 201 | return 0x45 if context.protocol_version >= 471 else \ 202 | 0x41 if context.protocol_version >= 461 else \ 203 | 0x42 if context.protocol_version >= 451 else \ 204 | 0x41 if context.protocol_version >= 389 else \ 205 | 0x40 if context.protocol_version >= 352 else \ 206 | 0x3F if context.protocol_version >= 345 else \ 207 | 0x3E if context.protocol_version >= 336 else \ 208 | 0x3D if context.protocol_version >= 332 else \ 209 | 0x3B if context.protocol_version >= 86 else \ 210 | 0x3C if context.protocol_version >= 77 else \ 211 | 0x3B if context.protocol_version >= 67 else \ 212 | 0x12 213 | 214 | packet_name = 'entity velocity' 215 | get_definition = staticmethod(lambda context: [ 216 | {'entity_id': VarInt}, 217 | {'velocity_x': Short}, 218 | {'velocity_y': Short}, 219 | {'velocity_z': Short} 220 | ]) 221 | 222 | 223 | class UpdateHealthPacket(Packet): 224 | @staticmethod 225 | def get_id(context): 226 | return 0x48 if context.protocol_version >= 471 else \ 227 | 0x44 if context.protocol_version >= 461 else \ 228 | 0x45 if context.protocol_version >= 451 else \ 229 | 0x44 if context.protocol_version >= 389 else \ 230 | 0x43 if context.protocol_version >= 352 else \ 231 | 0x42 if context.protocol_version >= 345 else \ 232 | 0x41 if context.protocol_version >= 336 else \ 233 | 0x40 if context.protocol_version >= 318 else \ 234 | 0x3E if context.protocol_version >= 86 else \ 235 | 0x3F if context.protocol_version >= 77 else \ 236 | 0x3E if context.protocol_version >= 67 else \ 237 | 0x06 238 | 239 | packet_name = 'update health' 240 | get_definition = staticmethod(lambda context: [ 241 | {'health': Float}, 242 | {'food': VarInt}, 243 | {'food_saturation': Float} 244 | ]) 245 | 246 | 247 | class RespawnPacket(Packet): 248 | @staticmethod 249 | def get_id(context): 250 | return 0x3A if context.protocol_version >= 471 else \ 251 | 0x38 if context.protocol_version >= 461 else \ 252 | 0x39 if context.protocol_version >= 451 else \ 253 | 0x38 if context.protocol_version >= 389 else \ 254 | 0x37 if context.protocol_version >= 352 else \ 255 | 0x36 if context.protocol_version >= 345 else \ 256 | 0x35 if context.protocol_version >= 336 else \ 257 | 0x34 if context.protocol_version >= 332 else \ 258 | 0x35 if context.protocol_version >= 318 else \ 259 | 0x33 if context.protocol_version >= 70 else \ 260 | 0x07 261 | 262 | packet_name = 'respawn' 263 | get_definition = staticmethod(lambda context: [ 264 | {'dimension': Integer}, 265 | {'difficulty': UnsignedByte} if context.protocol_version < 464 else {}, 266 | {'game_mode': UnsignedByte}, 267 | {'level_type': String}, 268 | ]) 269 | 270 | # These aliases declare the Enum type corresponding to each field: 271 | Difficulty = Difficulty 272 | Dimension = Dimension 273 | GameMode = GameMode 274 | 275 | 276 | class PluginMessagePacket(AbstractPluginMessagePacket): 277 | @staticmethod 278 | def get_id(context): 279 | return 0x18 if context.protocol_version >= 471 else \ 280 | 0x19 if context.protocol_version >= 345 else \ 281 | 0x18 if context.protocol_version >= 332 else \ 282 | 0x19 if context.protocol_version >= 318 else \ 283 | 0x18 if context.protocol_version >= 70 else \ 284 | 0x3F 285 | 286 | 287 | class PlayerListHeaderAndFooterPacket(Packet): 288 | @staticmethod 289 | def get_id(context): 290 | return 0x53 if context.protocol_version >= 471 else \ 291 | 0x5F if context.protocol_version >= 461 else \ 292 | 0x50 if context.protocol_version >= 451 else \ 293 | 0x4F if context.protocol_version >= 441 else \ 294 | 0x4E if context.protocol_version >= 393 else \ 295 | 0x4A if context.protocol_version >= 338 else \ 296 | 0x49 if context.protocol_version >= 335 else \ 297 | 0x47 if context.protocol_version >= 110 else \ 298 | 0x48 if context.protocol_version >= 107 else \ 299 | 0x47 300 | 301 | packet_name = 'player list header and footer' 302 | definition = [ 303 | {'header': String}, 304 | {'footer': String}] 305 | 306 | 307 | class EntityLookPacket(Packet): 308 | @staticmethod 309 | def get_id(context): 310 | return 0x2A if context.protocol_version >= 389 else \ 311 | 0x29 if context.protocol_version >= 345 else \ 312 | 0x28 if context.protocol_version >= 318 else \ 313 | 0x27 if context.protocol_version >= 94 else \ 314 | 0x28 if context.protocol_version >= 70 else \ 315 | 0x16 316 | 317 | packet_name = 'entity look' 318 | definition = [ 319 | {'entity_id': VarInt}, 320 | {'yaw': Angle}, 321 | {'pitch': Angle}, 322 | {'on_ground': Boolean} 323 | ] 324 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/play/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/block_change_packet.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | from ....types import ( 3 | VarInt, Integer, UnsignedByte, Position, Vector, MutableRecord, 4 | attribute_alias, multi_attribute_alias, 5 | ) 6 | 7 | 8 | class BlockChangePacket(Packet): 9 | @staticmethod 10 | def get_id(context): 11 | return 0x0B if context.protocol_version >= 332 else \ 12 | 0x0C if context.protocol_version >= 318 else \ 13 | 0x0B if context.protocol_version >= 67 else \ 14 | 0x24 if context.protocol_version >= 62 else \ 15 | 0x23 16 | 17 | packet_name = 'block change' 18 | definition = [ 19 | {'location': Position}, 20 | {'block_state_id': VarInt}] 21 | block_state_id = 0 22 | 23 | # For protocols < 347: an accessor for (block_state_id >> 4). 24 | @property 25 | def blockId(self): 26 | return self.block_state_id >> 4 27 | 28 | @blockId.setter 29 | def blockId(self, block_id): 30 | self.block_state_id = (self.block_state_id & 0xF) | (block_id << 4) 31 | 32 | # For protocols < 347: an accessor for (block_state_id & 0xF). 33 | @property 34 | def blockMeta(self): 35 | return self.block_state_id & 0xF 36 | 37 | @blockMeta.setter 38 | def blockMeta(self, meta): 39 | self.block_state_id = (self.block_state_id & ~0xF) | (meta & 0xF) 40 | 41 | # This alias is retained for backward compatibility. 42 | blockStateId = attribute_alias('block_state_id') 43 | 44 | 45 | class MultiBlockChangePacket(Packet): 46 | @staticmethod 47 | def get_id(context): 48 | return 0x0F if context.protocol_version >= 343 else \ 49 | 0x10 if context.protocol_version >= 332 else \ 50 | 0x11 if context.protocol_version >= 318 else \ 51 | 0x10 if context.protocol_version >= 67 else \ 52 | 0x22 53 | 54 | packet_name = 'multi block change' 55 | 56 | fields = 'chunk_x', 'chunk_z', 'records' 57 | 58 | # Access the 'chunk_x' and 'chunk_z' fields as a tuple. 59 | chunk_pos = multi_attribute_alias(tuple, 'chunk_x', 'chunk_z') 60 | 61 | class Record(MutableRecord): 62 | __slots__ = 'x', 'y', 'z', 'block_state_id' 63 | 64 | def __init__(self, **kwds): 65 | self.block_state_id = 0 66 | super(MultiBlockChangePacket.Record, self).__init__(**kwds) 67 | 68 | # Access the 'x', 'y', 'z' fields as a Vector of ints. 69 | position = multi_attribute_alias(Vector, 'x', 'y', 'z') 70 | 71 | # For protocols < 347: an accessor for (block_state_id >> 4). 72 | @property 73 | def blockId(self): 74 | return self.block_state_id >> 4 75 | 76 | @blockId.setter 77 | def blockId(self, block_id): 78 | self.block_state_id = self.block_state_id & 0xF | block_id << 4 79 | 80 | # For protocols < 347: an accessor for (block_state_id & 0xF). 81 | @property 82 | def blockMeta(self): 83 | return self.block_state_id & 0xF 84 | 85 | @blockMeta.setter 86 | def blockMeta(self, meta): 87 | self.block_state_id = self.block_state_id & ~0xF | meta & 0xF 88 | 89 | # This alias is retained for backward compatibility. 90 | blockStateId = attribute_alias('block_state_id') 91 | 92 | def read(self, file_object): 93 | h_position = UnsignedByte.read(file_object) 94 | self.x, self.z = h_position >> 4, h_position & 0xF 95 | self.y = UnsignedByte.read(file_object) 96 | self.block_state_id = VarInt.read(file_object) 97 | 98 | def write(self, packet_buffer): 99 | UnsignedByte.send(self.x << 4 | self.z & 0xF, packet_buffer) 100 | UnsignedByte.send(self.y, packet_buffer) 101 | VarInt.send(self.block_state_id, packet_buffer) 102 | 103 | def read(self, file_object): 104 | self.chunk_x = Integer.read(file_object) 105 | self.chunk_z = Integer.read(file_object) 106 | records_count = VarInt.read(file_object) 107 | self.records = [] 108 | for i in range(records_count): 109 | record = self.Record() 110 | record.read(file_object) 111 | self.records.append(record) 112 | 113 | def write_fields(self, packet_buffer): 114 | Integer.send(self.chunk_x, packet_buffer) 115 | Integer.send(self.chunk_z, packet_buffer) 116 | VarInt.send(len(self.records), packet_buffer) 117 | for record in self.records: 118 | record.write(packet_buffer) 119 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/block_change_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/play/block_change_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/combat_event_packet.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | 3 | from ....types import ( 4 | VarInt, Integer, String, MutableRecord 5 | ) 6 | 7 | 8 | class CombatEventPacket(Packet): 9 | @staticmethod 10 | def get_id(context): 11 | return 0x32 if context.protocol_version >= 471 else \ 12 | 0x30 if context.protocol_version >= 451 else \ 13 | 0x2F if context.protocol_version >= 389 else \ 14 | 0x2E if context.protocol_version >= 345 else \ 15 | 0x2D if context.protocol_version >= 336 else \ 16 | 0x2C if context.protocol_version >= 332 else \ 17 | 0x2D if context.protocol_version >= 318 else \ 18 | 0x2C if context.protocol_version >= 86 else \ 19 | 0x2D if context.protocol_version >= 80 else \ 20 | 0x2C if context.protocol_version >= 67 else \ 21 | 0x42 22 | 23 | packet_name = 'combat event' 24 | 25 | fields = 'event', 26 | 27 | # The abstract type of the 'event' field of this packet. 28 | class EventType(MutableRecord): 29 | __slots__ = () 30 | type_from_id_dict = {} 31 | 32 | # Read the fields of the event (not including the ID) from the file. 33 | def read(self, file_object): 34 | raise NotImplementedError( 35 | 'This abstract method must be overridden in a subclass.') 36 | 37 | # Write the fields of the event (not including the ID) to the buffer. 38 | def write(self, packet_buffer): 39 | raise NotImplementedError( 40 | 'This abstract method must be overridden in a subclass.') 41 | 42 | @classmethod 43 | def type_from_id(cls, event_id): 44 | subcls = cls.type_from_id_dict.get(event_id) 45 | if subcls is None: 46 | raise ValueError('Unknown combat event ID: %s.' % event_id) 47 | return subcls 48 | 49 | class EnterCombatEvent(EventType): 50 | __slots__ = () 51 | id = 0 52 | 53 | def read(self, file_object): 54 | pass 55 | 56 | def write(self, packet_buffer): 57 | pass 58 | EventType.type_from_id_dict[EnterCombatEvent.id] = EnterCombatEvent 59 | 60 | class EndCombatEvent(EventType): 61 | __slots__ = 'duration', 'entity_id' 62 | id = 1 63 | 64 | def read(self, file_object): 65 | self.duration = VarInt.read(file_object) 66 | self.entity_id = Integer.read(file_object) 67 | 68 | def write(self, packet_buffer): 69 | VarInt.send(self.duration, packet_buffer) 70 | Integer.send(self.entity_id, packet_buffer) 71 | EventType.type_from_id_dict[EndCombatEvent.id] = EndCombatEvent 72 | 73 | class EntityDeadEvent(EventType): 74 | __slots__ = 'player_id', 'entity_id', 'message' 75 | id = 2 76 | 77 | def read(self, file_object): 78 | self.player_id = VarInt.read(file_object) 79 | self.entity_id = Integer.read(file_object) 80 | self.message = String.read(file_object) 81 | 82 | def write(self, packet_buffer): 83 | VarInt.send(self.player_id, packet_buffer) 84 | Integer.send(self.entity_id, packet_buffer) 85 | String.send(self.message, packet_buffer) 86 | EventType.type_from_id_dict[EntityDeadEvent.id] = EntityDeadEvent 87 | 88 | def read(self, file_object): 89 | event_id = VarInt.read(file_object) 90 | self.event = CombatEventPacket.EventType.type_from_id(event_id)() 91 | self.event.read(file_object) 92 | 93 | def write_fields(self, packet_buffer): 94 | VarInt.send(self.event.id, packet_buffer) 95 | self.event.write(packet_buffer) 96 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/combat_event_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/play/combat_event_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/explosion_packet.py: -------------------------------------------------------------------------------- 1 | from ....types import ( 2 | Vector, Float, Byte, Integer, multi_attribute_alias, 3 | ) 4 | from ....packets import Packet 5 | 6 | 7 | class ExplosionPacket(Packet): 8 | @staticmethod 9 | def get_id(context): 10 | return 0x1C if context.protocol_version >= 471 else \ 11 | 0x1E if context.protocol_version >= 389 else \ 12 | 0x1D if context.protocol_version >= 345 else \ 13 | 0x1C if context.protocol_version >= 332 else \ 14 | 0x1D if context.protocol_version >= 318 else \ 15 | 0x1C if context.protocol_version >= 80 else \ 16 | 0x1B if context.protocol_version >= 67 else \ 17 | 0x27 18 | 19 | packet_name = 'explosion' 20 | 21 | fields = 'x', 'y', 'z', 'radius', 'records', \ 22 | 'player_motion_x', 'player_motion_y', 'player_motion_z' 23 | 24 | # Access the 'x', 'y', 'z' fields as a Vector tuple. 25 | position = multi_attribute_alias(Vector, 'x', 'y', 'z') 26 | 27 | # Access the 'player_motion_{x,y,z}' fields as a Vector tuple. 28 | player_motion = multi_attribute_alias( 29 | Vector, 'player_motion_x', 'player_motion_y', 'player_motion_z') 30 | 31 | class Record(Vector): 32 | __slots__ = () 33 | 34 | def read(self, file_object): 35 | self.x = Float.read(file_object) 36 | self.y = Float.read(file_object) 37 | self.z = Float.read(file_object) 38 | self.radius = Float.read(file_object) 39 | records_count = Integer.read(file_object) 40 | self.records = [] 41 | for i in range(records_count): 42 | rec_x = Byte.read(file_object) 43 | rec_y = Byte.read(file_object) 44 | rec_z = Byte.read(file_object) 45 | record = ExplosionPacket.Record(rec_x, rec_y, rec_z) 46 | self.records.append(record) 47 | self.player_motion_x = Float.read(file_object) 48 | self.player_motion_y = Float.read(file_object) 49 | self.player_motion_z = Float.read(file_object) 50 | 51 | def write_fields(self, packet_buffer): 52 | Float.send(self.x, packet_buffer) 53 | Float.send(self.y, packet_buffer) 54 | Float.send(self.z, packet_buffer) 55 | Float.send(self.radius, packet_buffer) 56 | Integer.send(len(self.records), packet_buffer) 57 | for record in self.records: 58 | Byte.send(record.x, packet_buffer) 59 | Byte.send(record.y, packet_buffer) 60 | Byte.send(record.z, packet_buffer) 61 | Float.send(self.player_motion_x, packet_buffer) 62 | Float.send(self.player_motion_y, packet_buffer) 63 | Float.send(self.player_motion_z, packet_buffer) 64 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/explosion_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/play/explosion_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/face_player_packet.py: -------------------------------------------------------------------------------- 1 | from ....types import ( 2 | VarInt, Double, Boolean, OriginPoint, Vector, multi_attribute_alias 3 | ) 4 | 5 | from ....packets import Packet 6 | 7 | 8 | class FacePlayerPacket(Packet): 9 | @staticmethod 10 | def get_id(context): 11 | return 0x34 if context.protocol_version >= 471 else \ 12 | 0x32 if context.protocol_version >= 451 else \ 13 | 0x31 if context.protocol_version >= 389 else \ 14 | 0x30 15 | 16 | packet_name = 'face player' 17 | 18 | @property 19 | def fields(self): 20 | return ('origin', 'x', 'y', 'z', 'entity_id', 'entity_origin') \ 21 | if self.context.protocol_version >= 353 else \ 22 | ('entity_id', 'x', 'y', 'z') 23 | 24 | # Access the 'x', 'y', 'z' fields as a Vector tuple. 25 | target = multi_attribute_alias(Vector, 'x', 'y', 'z') 26 | 27 | def read(self, file_object): 28 | if self.context.protocol_version >= 353: 29 | self.origin = VarInt.read(file_object) 30 | self.x = Double.read(file_object) 31 | self.y = Double.read(file_object) 32 | self.z = Double.read(file_object) 33 | is_entity = Boolean.read(file_object) 34 | if is_entity: 35 | # If the entity given by entity ID cannot be found, 36 | # this packet should be treated as if is_entity was false. 37 | self.entity_id = VarInt.read(file_object) 38 | self.entity_origin = VarInt.read(file_object) 39 | else: 40 | self.entity_id = None 41 | 42 | else: # Protocol version 352 43 | is_entity = Boolean.read(file_object) 44 | self.entity_id = VarInt.read(file_object) if is_entity else None 45 | if not is_entity: 46 | self.x = Double.read(file_object) 47 | self.y = Double.read(file_object) 48 | self.z = Double.read(file_object) 49 | 50 | def write_fields(self, packet_buffer): 51 | if self.context.protocol_version >= 353: 52 | VarInt.send(self.origin, packet_buffer) 53 | Double.send(self.x, packet_buffer) 54 | Double.send(self.y, packet_buffer) 55 | Double.send(self.z, packet_buffer) 56 | if self.entity_id is not None: 57 | Boolean.send(True, packet_buffer) 58 | VarInt.send(self.entity_id, packet_buffer) 59 | VarInt.send(self.entity_origin, packet_buffer) 60 | else: 61 | Boolean.send(False, packet_buffer) 62 | 63 | else: # Protocol version 352 64 | if self.entity_id is not None: 65 | Boolean.send(True, packet_buffer) 66 | VarInt.send(self.entity_id, packet_buffer) 67 | else: 68 | Boolean.send(False, packet_buffer) 69 | Double.send(self.x, packet_buffer) 70 | Double.send(self.y, packet_buffer) 71 | Double.send(self.z, packet_buffer) 72 | 73 | # These aliases declare the Enum type corresponding to each field: 74 | Origin = OriginPoint 75 | EntityOrigin = OriginPoint 76 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/map_packet.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | from ....types import ( 3 | VarInt, Byte, Boolean, UnsignedByte, VarIntPrefixedByteArray, String, 4 | MutableRecord 5 | ) 6 | 7 | 8 | class MapPacket(Packet): 9 | @staticmethod 10 | def get_id(context): 11 | return 0x26 if context.protocol_version >= 389 else \ 12 | 0x25 if context.protocol_version >= 345 else \ 13 | 0x24 if context.protocol_version >= 334 else \ 14 | 0x25 if context.protocol_version >= 318 else \ 15 | 0x24 if context.protocol_version >= 107 else \ 16 | 0x34 17 | 18 | packet_name = 'map' 19 | 20 | @property 21 | def fields(self): 22 | fields = 'id', 'scale', 'icons', 'width', 'height', 'pixels' 23 | if self.context.protocol_version >= 107: 24 | fields += 'is_tracking_position', 25 | if self.context.protocol_version >= 452: 26 | fields += 'is_locked', 27 | return fields 28 | 29 | def field_string(self, field): 30 | if field == 'pixels' and isinstance(self.pixels, bytearray): 31 | return 'bytearray(...)' 32 | return super(MapPacket, self).field_string(field) 33 | 34 | class MapIcon(MutableRecord): 35 | __slots__ = 'type', 'direction', 'location', 'display_name' 36 | 37 | def __init__(self, type, direction, location, display_name=None): 38 | self.type = type 39 | self.direction = direction 40 | self.location = location 41 | self.display_name = display_name 42 | 43 | class Map(MutableRecord): 44 | __slots__ = ('id', 'scale', 'icons', 'pixels', 'width', 'height', 45 | 'is_tracking_position', 'is_locked') 46 | 47 | def __init__(self, id=None, scale=None, width=128, height=128): 48 | self.id = id 49 | self.scale = scale 50 | self.icons = [] 51 | self.width = width 52 | self.height = height 53 | self.pixels = bytearray(0 for i in range(width*height)) 54 | self.is_tracking_position = True 55 | self.is_locked = False 56 | 57 | class MapSet(object): 58 | __slots__ = 'maps_by_id' 59 | 60 | def __init__(self, *maps): 61 | self.maps_by_id = {map.id: map for map in maps} 62 | 63 | def __repr__(self): 64 | maps = (repr(map) for map in self.maps_by_id.values()) 65 | return 'MapSet(%s)' % ', '.join(maps) 66 | 67 | def read(self, file_object): 68 | self.map_id = VarInt.read(file_object) 69 | self.scale = Byte.read(file_object) 70 | 71 | if self.context.protocol_version >= 107: 72 | self.is_tracking_position = Boolean.read(file_object) 73 | else: 74 | self.is_tracking_position = True 75 | 76 | if self.context.protocol_version >= 452: 77 | self.is_locked = Boolean.read(file_object) 78 | else: 79 | self.is_locked = False 80 | 81 | icon_count = VarInt.read(file_object) 82 | self.icons = [] 83 | for i in range(icon_count): 84 | if self.context.protocol_version >= 373: 85 | type = VarInt.read(file_object) 86 | else: 87 | type, direction = divmod(UnsignedByte.read(file_object), 16) 88 | x = Byte.read(file_object) 89 | z = Byte.read(file_object) 90 | if self.context.protocol_version >= 373: 91 | direction = UnsignedByte.read(file_object) 92 | if self.context.protocol_version >= 364: 93 | has_name = Boolean.read(file_object) 94 | display_name = String.read(file_object) if has_name else None 95 | else: 96 | display_name = None 97 | icon = MapPacket.MapIcon(type, direction, (x, z), display_name) 98 | self.icons.append(icon) 99 | 100 | self.width = UnsignedByte.read(file_object) 101 | if self.width: 102 | self.height = UnsignedByte.read(file_object) 103 | x = Byte.read(file_object) 104 | z = Byte.read(file_object) 105 | self.offset = (x, z) 106 | self.pixels = VarIntPrefixedByteArray.read(file_object) 107 | else: 108 | self.height = 0 109 | self.offset = None 110 | self.pixels = None 111 | 112 | def apply_to_map(self, map): 113 | map.id = self.map_id 114 | map.scale = self.scale 115 | map.icons[:] = self.icons 116 | if self.pixels is not None: 117 | for i in range(len(self.pixels)): 118 | x = self.offset[0] + i % self.width 119 | z = self.offset[1] + i // self.width 120 | map.pixels[x + map.width * z] = self.pixels[i] 121 | map.is_tracking_position = self.is_tracking_position 122 | map.is_locked = self.is_locked 123 | 124 | def apply_to_map_set(self, map_set): 125 | map = map_set.maps_by_id.get(self.map_id) 126 | if map is None: 127 | map = MapPacket.Map(self.map_id) 128 | map_set.maps_by_id[self.map_id] = map 129 | self.apply_to_map(map) 130 | 131 | def write_fields(self, packet_buffer): 132 | VarInt.send(self.map_id, packet_buffer) 133 | Byte.send(self.scale, packet_buffer) 134 | if self.context.protocol_version >= 107: 135 | Boolean.send(self.is_tracking_position, packet_buffer) 136 | 137 | VarInt.send(len(self.icons), packet_buffer) 138 | for icon in self.icons: 139 | if self.context.protocol_version >= 373: 140 | VarInt.send(icon.type, packet_buffer) 141 | else: 142 | type_and_direction = (icon.type << 4) & 0xF0 143 | type_and_direction |= (icon.direction & 0xF) 144 | UnsignedByte.send(type_and_direction, packet_buffer) 145 | Byte.send(icon.location[0], packet_buffer) 146 | Byte.send(icon.location[1], packet_buffer) 147 | if self.context.protocol_version >= 373: 148 | UnsignedByte.send(icon.direction, packet_buffer) 149 | if self.context.protocol_version >= 364: 150 | Boolean.send(icon.display_name is not None, packet_buffer) 151 | if icon.display_name is not None: 152 | String.send(icon.display_name, packet_buffer) 153 | 154 | UnsignedByte.send(self.width, packet_buffer) 155 | if self.width: 156 | UnsignedByte.send(self.height, packet_buffer) 157 | UnsignedByte.send(self.offset[0], packet_buffer) # x 158 | UnsignedByte.send(self.offset[1], packet_buffer) # z 159 | VarIntPrefixedByteArray.send(self.pixels, packet_buffer) 160 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/map_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/play/map_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/player_list_item_packet.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | 3 | from ....types import ( 4 | String, Boolean, UUID, VarInt, MutableRecord, 5 | ) 6 | 7 | 8 | class PlayerListItemPacket(Packet): 9 | @staticmethod 10 | def get_id(context): 11 | return 0x33 if context.protocol_version >= 471 else \ 12 | 0x31 if context.protocol_version >= 451 else \ 13 | 0x30 if context.protocol_version >= 389 else \ 14 | 0x2F if context.protocol_version >= 345 else \ 15 | 0x2E if context.protocol_version >= 336 else \ 16 | 0x2D if context.protocol_version >= 332 else \ 17 | 0x2E if context.protocol_version >= 318 else \ 18 | 0x2D if context.protocol_version >= 107 else \ 19 | 0x38 20 | 21 | packet_name = "player list item" 22 | 23 | fields = 'action_type', 'actions' 24 | 25 | def field_string(self, field): 26 | if field == 'action_type': 27 | return self.action_type.__name__ 28 | return super(PlayerListItemPacket, self).field_string(field) 29 | 30 | class PlayerList(object): 31 | __slots__ = 'players_by_uuid' 32 | 33 | def __init__(self, *items): 34 | self.players_by_uuid = {item.uuid: item for item in items} 35 | 36 | class PlayerListItem(MutableRecord): 37 | __slots__ = ( 38 | 'uuid', 'name', 'properties', 'gamemode', 'ping', 'display_name') 39 | 40 | class PlayerProperty(MutableRecord): 41 | __slots__ = 'name', 'value', 'signature' 42 | 43 | def read(self, file_object): 44 | self.name = String.read(file_object) 45 | self.value = String.read(file_object) 46 | is_signed = Boolean.read(file_object) 47 | if is_signed: 48 | self.signature = String.read(file_object) 49 | else: 50 | self.signature = None 51 | 52 | def send(self, packet_buffer): 53 | String.send(self.name, packet_buffer) 54 | String.send(self.value, packet_buffer) 55 | if self.signature is not None: 56 | Boolean.send(True, packet_buffer) 57 | String.send(self.signature, packet_buffer) 58 | else: 59 | Boolean.send(False, packet_buffer) 60 | 61 | class Action(MutableRecord): 62 | __slots__ = 'uuid', 63 | 64 | def read(self, file_object): 65 | self.uuid = UUID.read(file_object) 66 | self._read(file_object) 67 | 68 | def send(self, packet_buffer): 69 | UUID.send(self.uuid, packet_buffer) 70 | self._send(packet_buffer) 71 | 72 | def _read(self, file_object): 73 | raise NotImplementedError( 74 | 'This abstract method must be overridden in a subclass.') 75 | 76 | def _send(self, packet_buffer): 77 | raise NotImplementedError( 78 | 'This abstract method must be overridden in a subclass.') 79 | 80 | @classmethod 81 | def type_from_id(cls, action_id): 82 | for subcls in cls.__subclasses__(): 83 | if subcls.action_id == action_id: 84 | return subcls 85 | raise ValueError("Unknown player list action ID: %s." % action_id) 86 | 87 | class AddPlayerAction(Action): 88 | __slots__ = 'name', 'properties', 'gamemode', 'ping', 'display_name' 89 | action_id = 0 90 | 91 | def _read(self, file_object): 92 | self.name = String.read(file_object) 93 | prop_count = VarInt.read(file_object) 94 | self.properties = [] 95 | for i in range(prop_count): 96 | property = PlayerListItemPacket.PlayerProperty() 97 | property.read(file_object) 98 | self.properties.append(property) 99 | self.gamemode = VarInt.read(file_object) 100 | self.ping = VarInt.read(file_object) 101 | has_display_name = Boolean.read(file_object) 102 | if has_display_name: 103 | self.display_name = String.read(file_object) 104 | else: 105 | self.display_name = None 106 | 107 | def _send(self, packet_buffer): 108 | String.send(self.name, packet_buffer) 109 | VarInt.send(len(self.properties), packet_buffer) 110 | for property in self.properties: 111 | property.send(packet_buffer) 112 | VarInt.send(self.gamemode, packet_buffer) 113 | VarInt.send(self.ping, packet_buffer) 114 | if self.display_name is not None: 115 | Boolean.send(True, packet_buffer) 116 | String.send(self.display_name, packet_buffer) 117 | else: 118 | Boolean.send(False, packet_buffer) 119 | 120 | def apply(self, player_list): 121 | player = PlayerListItemPacket.PlayerListItem( 122 | uuid=self.uuid, 123 | name=self.name, 124 | properties=self.properties, 125 | gamemode=self.gamemode, 126 | ping=self.ping, 127 | display_name=self.display_name) 128 | player_list.players_by_uuid[self.uuid] = player 129 | 130 | class UpdateGameModeAction(Action): 131 | __slots__ = 'gamemode' 132 | action_id = 1 133 | 134 | def _read(self, file_object): 135 | self.gamemode = VarInt.read(file_object) 136 | 137 | def _send(self, packet_buffer): 138 | VarInt.send(self.gamemode, packet_buffer) 139 | 140 | def apply(self, player_list): 141 | player = player_list.players_by_uuid.get(self.uuid) 142 | if player: 143 | player.gamemode = self.gamemode 144 | 145 | class UpdateLatencyAction(Action): 146 | __slots__ = 'ping' 147 | action_id = 2 148 | 149 | def _read(self, file_object): 150 | self.ping = VarInt.read(file_object) 151 | 152 | def _send(self, packet_buffer): 153 | VarInt.send(self.ping, packet_buffer) 154 | 155 | def apply(self, player_list): 156 | player = player_list.players_by_uuid.get(self.uuid) 157 | if player: 158 | player.ping = self.ping 159 | 160 | class UpdateDisplayNameAction(Action): 161 | __slots__ = 'display_name' 162 | action_id = 3 163 | 164 | def _read(self, file_object): 165 | has_display_name = Boolean.read(file_object) 166 | if has_display_name: 167 | self.display_name = String.read(file_object) 168 | else: 169 | self.display_name = None 170 | 171 | def _send(self, packet_buffer): 172 | if self.display_name is not None: 173 | Boolean.send(True, packet_buffer) 174 | String.send(self.display_name, packet_buffer) 175 | else: 176 | Boolean.send(False, packet_buffer) 177 | 178 | def apply(self, player_list): 179 | player = player_list.players_by_uuid.get(self.uuid) 180 | if player: 181 | player.display_name = self.display_name 182 | 183 | class RemovePlayerAction(Action): 184 | action_id = 4 185 | 186 | def _read(self, file_object): 187 | pass 188 | 189 | def _send(self, packet_buffer): 190 | pass 191 | 192 | def apply(self, player_list): 193 | if self.uuid in player_list.players_by_uuid: 194 | del player_list.players_by_uuid[self.uuid] 195 | 196 | def read(self, file_object): 197 | action_id = VarInt.read(file_object) 198 | self.action_type = PlayerListItemPacket.Action.type_from_id(action_id) 199 | action_count = VarInt.read(file_object) 200 | self.actions = [] 201 | for i in range(action_count): 202 | action = self.action_type() 203 | action.read(file_object) 204 | self.actions.append(action) 205 | 206 | def write_fields(self, packet_buffer): 207 | VarInt.send(self.action_type.action_id, packet_buffer) 208 | VarInt.send(len(self.actions), packet_buffer) 209 | for action in self.actions: 210 | action.send(packet_buffer) 211 | 212 | def apply(self, player_list): 213 | for action in self.actions: 214 | action.apply(player_list) 215 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/player_list_item_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/play/player_list_item_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/player_position_and_look_packet.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | 3 | from ....types import ( 4 | Double, Float, Byte, VarInt, BitFieldEnum, Vector, Direction, 5 | PositionAndLook, multi_attribute_alias, 6 | ) 7 | 8 | 9 | class PlayerPositionAndLookPacket(Packet, BitFieldEnum): 10 | @staticmethod 11 | def get_id(context): 12 | return 0x35 if context.protocol_version >= 471 else \ 13 | 0x33 if context.protocol_version >= 451 else \ 14 | 0x32 if context.protocol_version >= 389 else \ 15 | 0x31 if context.protocol_version >= 352 else \ 16 | 0x30 if context.protocol_version >= 345 else \ 17 | 0x2F if context.protocol_version >= 336 else \ 18 | 0x2E if context.protocol_version >= 332 else \ 19 | 0x2F if context.protocol_version >= 318 else \ 20 | 0x2E if context.protocol_version >= 70 else \ 21 | 0x08 22 | 23 | packet_name = "player position and look" 24 | get_definition = staticmethod(lambda context: [ 25 | {'x': Double}, 26 | {'y': Double}, 27 | {'z': Double}, 28 | {'yaw': Float}, 29 | {'pitch': Float}, 30 | {'flags': Byte}, 31 | {'teleport_id': VarInt} if context.protocol_version >= 107 else {}, 32 | ]) 33 | 34 | # Access the 'x', 'y', 'z' fields as a Vector tuple. 35 | position = multi_attribute_alias(Vector, 'x', 'y', 'z') 36 | 37 | # Access the 'yaw', 'pitch' fields as a Direction tuple. 38 | look = multi_attribute_alias(Direction, 'yaw', 'pitch') 39 | 40 | # Access the 'x', 'y', 'z', 'yaw', 'pitch' fields as a PositionAndLook. 41 | # NOTE: modifying the object retrieved from this property will not change 42 | # the packet; it can only be changed by attribute or property assignment. 43 | position_and_look = multi_attribute_alias( 44 | PositionAndLook, 'x', 'y', 'z', 'yaw', 'pitch') 45 | 46 | field_enum = classmethod( 47 | lambda cls, field, context: cls if field == 'flags' else None) 48 | 49 | FLAG_REL_X = 0x01 50 | FLAG_REL_Y = 0x02 51 | FLAG_REL_Z = 0x04 52 | FLAG_REL_YAW = 0x08 53 | FLAG_REL_PITCH = 0x10 54 | 55 | # This alias is retained for backward compatibility. 56 | PositionAndLook = PositionAndLook 57 | 58 | # Update a PositionAndLook instance using this packet. 59 | def apply(self, target): 60 | # pylint: disable=no-member 61 | if self.flags & self.FLAG_REL_X: 62 | target.x += self.x 63 | else: 64 | target.x = self.x 65 | 66 | if self.flags & self.FLAG_REL_Y: 67 | target.y += self.y 68 | else: 69 | target.y = self.y 70 | 71 | if self.flags & self.FLAG_REL_Z: 72 | target.z += self.z 73 | else: 74 | target.z = self.z 75 | 76 | if self.flags & self.FLAG_REL_YAW: 77 | target.yaw += self.yaw 78 | else: 79 | target.yaw = self.yaw 80 | 81 | if self.flags & self.FLAG_REL_PITCH: 82 | target.pitch += self.pitch 83 | else: 84 | target.pitch = self.pitch 85 | 86 | target.yaw %= 360 87 | target.pitch %= 360 88 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/player_position_and_look_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/play/player_position_and_look_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/sound_effect_packet.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | from ....types import ( 3 | VarInt, String, Float, Byte, Type, Integer, Vector, Enum, 4 | ) 5 | 6 | __all__ = 'SoundEffectPacket', 7 | 8 | 9 | class SoundEffectPacket(Packet): 10 | @staticmethod 11 | def get_id(context): 12 | return 0x51 if context.protocol_version >= 471 else \ 13 | 0x4D if context.protocol_version >= 461 else \ 14 | 0x4E if context.protocol_version >= 451 else \ 15 | 0x4E if context.protocol_version >= 451 else \ 16 | 0x4D if context.protocol_version >= 389 else \ 17 | 0x4C if context.protocol_version >= 352 else \ 18 | 0x4B if context.protocol_version >= 345 else \ 19 | 0x4A if context.protocol_version >= 343 else \ 20 | 0x49 if context.protocol_version >= 336 else \ 21 | 0x48 if context.protocol_version >= 318 else \ 22 | 0x46 if context.protocol_version >= 110 else \ 23 | 0x47 24 | 25 | packet_name = 'sound effect' 26 | 27 | @staticmethod 28 | def get_definition(context): 29 | return [ 30 | ({'sound_category': VarInt} 31 | if 326 > context.protocol_version >= 321 else {}), 32 | {'sound_id': VarInt}, 33 | ({'sound_category': VarInt} 34 | if 95 <= context.protocol_version < 321 35 | or context.protocol_version >= 326 else {}), 36 | ({'parroted_entity_type': String} 37 | if 326 > context.protocol_version >= 321 else {}), 38 | {'effect_position': SoundEffectPacket.EffectPosition}, 39 | {'volume': Float}, 40 | {'pitch': SoundEffectPacket.Pitch}, 41 | ] 42 | 43 | class SoundCategory(Enum): 44 | MASTER = 0 45 | MUSIC = 1 46 | RECORDS = 2 47 | WEATHER = 3 48 | BLOCKS = 4 49 | HOSTILE = 5 50 | NEUTRAL = 6 51 | PLAYERS = 7 52 | AMBIENT = 8 53 | VOICE = 9 54 | 55 | class EffectPosition(Type): 56 | @classmethod 57 | def read(cls, file_object): 58 | return Vector(*(Integer.read(file_object) / 8.0 for i in range(3))) 59 | 60 | @classmethod 61 | def send(cls, value, socket): 62 | for coordinate in value: 63 | Integer.send(int(coordinate * 8), socket) 64 | 65 | class Pitch(Type): 66 | @staticmethod 67 | def read_with_context(file_object, context): 68 | if context.protocol_version >= 201: 69 | value = Float.read(file_object) 70 | else: 71 | value = Byte.read(file_object) 72 | if context.protocol_version < 204: 73 | value /= 63.5 74 | return value 75 | 76 | @staticmethod 77 | def send_with_context(value, socket, context): 78 | if context.protocol_version < 204: 79 | value *= 63.5 80 | if context.protocol_version >= 201: 81 | Float.send(value, socket) 82 | else: 83 | Byte.send(int(value), socket) 84 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/spawn_object_packet.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | from ....types.utility import descriptor 3 | 4 | from ....types import ( 5 | VarInt, UUID, Byte, Double, Integer, Angle, Short, Enum, Vector, 6 | Direction, PositionAndLook, attribute_alias, multi_attribute_alias, 7 | ) 8 | 9 | 10 | class SpawnObjectPacket(Packet): 11 | @staticmethod 12 | def get_id(context): 13 | return 0x00 if context.protocol_version >= 67 else \ 14 | 0x0E 15 | 16 | packet_name = 'spawn object' 17 | 18 | fields = ('entity_id', 'object_uuid', 'type_id', 'x', 'y', 'z', 'pitch', 19 | 'yaw', 'data', 'velocity_x', 'velocity_y', 'velocity_z') 20 | 21 | @descriptor 22 | def EntityType(desc, self, cls): # pylint: disable=no-self-argument 23 | if self is None: 24 | # EntityType is being accessed as a class attribute. 25 | raise AttributeError( 26 | 'This interface is deprecated:\n\n' 27 | 'As of pyCraft\'s support for Minecraft 1.14, the nested ' 28 | 'class "SpawnObjectPacket.EntityType" cannot be accessed as a ' 29 | 'class attribute, because it depends on the protocol version. ' 30 | 'There are two ways to access the correct version of the ' 31 | 'class:\n\n' 32 | '1. Access the "EntityType" attribute of a ' 33 | '"SpawnObjectPacket" instance with its "context" property ' 34 | 'set.\n\n' 35 | '2. Call "SpawnObjectPacket.field_enum(\'type_id\', ' 36 | 'context)".') 37 | else: 38 | # EntityType is being accessed as an instance attribute. 39 | return self.field_enum('type_id', self.context) 40 | 41 | @classmethod 42 | def field_enum(cls, field, context): 43 | if field != 'type_id' or context is None: 44 | return 45 | 46 | pv = context.protocol_version 47 | name = 'EntityType_%d' % pv 48 | if hasattr(cls, name): 49 | return getattr(cls, name) 50 | 51 | class EntityType(Enum): 52 | ACTIVATED_TNT = 50 if pv < 458 else 55 # PrimedTnt 53 | AREA_EFFECT_CLOUD = 3 if pv < 458 else 0 54 | ARMORSTAND = 78 if pv < 458 else 1 55 | ARROW = 60 if pv < 458 else 2 56 | BOAT = 1 if pv < 458 else 5 57 | DRAGON_FIREBALL = 93 if pv < 458 else 13 58 | EGG = 62 if pv < 458 else 74 # ThrownEgg 59 | ENDERCRYSTAL = 51 if pv < 458 else 16 60 | ENDERPEARL = 65 if pv < 458 else 75 # ThrownEnderpearl 61 | EVOCATION_FANGS = 79 if pv < 458 else 20 62 | EXP_BOTTLE = 75 if pv < 458 else 76 # ThrownExpBottle 63 | EYE_OF_ENDER = 72 if pv < 458 else 23 # EyeOfEnderSignal 64 | FALLING_OBJECT = 70 if pv < 458 else 24 # FallingSand 65 | FIREBALL = 63 if pv < 458 else 34 # Fireball (ghast) 66 | FIRECHARGE = 64 if pv < 458 else 65 # SmallFireball (blaze) 67 | FIREWORK_ROCKET = 76 if pv < 458 else 25 # FireworksRocketEntity 68 | FISHING_HOOK = 90 if pv < 458 else 93 # Fishing bobber 69 | ITEM_FRAMES = 71 if pv < 458 else 33 # ItemFrame 70 | ITEM_STACK = 2 if pv < 458 else 32 # Item 71 | LEASH_KNOT = 77 if pv < 458 else 35 72 | LLAMA_SPIT = 68 if pv < 458 else 37 73 | MINECART = 10 if pv < 458 else 39 # MinecartRideable 74 | POTION = 73 if pv < 458 else 77 # ThrownPotion 75 | SHULKER_BULLET = 67 if pv < 458 else 60 76 | SNOWBALL = 61 if pv < 458 else 67 77 | SPECTRAL_ARROW = 91 if pv < 458 else 68 78 | WITHER_SKULL = 66 if pv < 458 else 85 79 | if pv >= 393: 80 | TRIDENT = 94 81 | if pv >= 458: 82 | MINECART_CHEST = 40 83 | MINECART_COMMAND_BLOCK = 41 84 | MINECART_FURNACE = 42 85 | MINECART_HOPPER = 43 86 | MINECART_SPAWNER = 44 87 | MINECART_TNT = 45 88 | 89 | setattr(cls, name, EntityType) 90 | return EntityType 91 | 92 | def read(self, file_object): 93 | self.entity_id = VarInt.read(file_object) 94 | if self.context.protocol_version >= 49: 95 | self.object_uuid = UUID.read(file_object) 96 | 97 | if self.context.protocol_version >= 458: 98 | self.type_id = VarInt.read(file_object) 99 | else: 100 | self.type_id = Byte.read(file_object) 101 | 102 | xyz_type = Double if self.context.protocol_version >= 100 else Integer 103 | for attr in 'x', 'y', 'z': 104 | setattr(self, attr, xyz_type.read(file_object)) 105 | for attr in 'pitch', 'yaw': 106 | setattr(self, attr, Angle.read(file_object)) 107 | 108 | self.data = Integer.read(file_object) 109 | if self.context.protocol_version >= 49 or self.data > 0: 110 | for attr in 'velocity_x', 'velocity_y', 'velocity_z': 111 | setattr(self, attr, Short.read(file_object)) 112 | 113 | def write_fields(self, packet_buffer): 114 | VarInt.send(self.entity_id, packet_buffer) 115 | if self.context.protocol_version >= 49: 116 | UUID.send(self.object_uuid, packet_buffer) 117 | 118 | if self.context.protocol_version >= 458: 119 | VarInt.send(self.type_id, packet_buffer) 120 | else: 121 | Byte.send(self.type_id, packet_buffer) 122 | 123 | # pylint: disable=no-member 124 | xyz_type = Double if self.context.protocol_version >= 100 else Integer 125 | for coord in self.x, self.y, self.z: 126 | xyz_type.send(coord, packet_buffer) 127 | for coord in self.pitch, self.yaw: 128 | Angle.send(coord, packet_buffer) 129 | 130 | Integer.send(self.data, packet_buffer) 131 | if self.context.protocol_version >= 49 or self.data > 0: 132 | for coord in self.velocity_x, self.velocity_y, self.velocity_z: 133 | Short.send(coord, packet_buffer) 134 | 135 | # Access the entity type as a string, according to the EntityType enum. 136 | @property 137 | def type(self): 138 | if self.context is None: 139 | raise ValueError('This packet must have a non-None "context" ' 140 | 'in order to read the "type" property.') 141 | # pylint: disable=no-member 142 | return self.EntityType.name_from_value(self.type_id) 143 | 144 | @type.setter 145 | def type(self, type_name): 146 | if self.context is None: 147 | raise ValueError('This packet must have a non-None "context" ' 148 | 'in order to set the "type" property.') 149 | self.type_id = getattr(self.EntityType, type_name) 150 | 151 | @type.deleter 152 | def type(self): 153 | del self.type_id 154 | 155 | # Access the 'x', 'y', 'z' fields as a Vector. 156 | position = multi_attribute_alias(Vector, 'x', 'y', 'z') 157 | 158 | # Access the 'yaw', 'pitch' fields as a Direction. 159 | look = multi_attribute_alias(Direction, 'yaw', 'pitch') 160 | 161 | # Access the 'x', 'y', 'z', 'pitch', 'yaw' fields as a PositionAndLook. 162 | # NOTE: modifying the object retrieved from this property will not change 163 | # the packet; it can only be changed by attribute or property assignment. 164 | position_and_look = multi_attribute_alias( 165 | PositionAndLook, x='x', y='y', z='z', yaw='yaw', pitch='pitch') 166 | 167 | # Access the 'velocity_{x,y,z}' fields as a Vector. 168 | velocity = multi_attribute_alias( 169 | Vector, 'velocity_x', 'velocity_y', 'velocity_z') 170 | 171 | # This alias is retained for backward compatibility. 172 | objectUUID = attribute_alias('object_uuid') 173 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/play/spawn_object_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/play/spawn_object_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/status/__init__.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | 3 | from ....types import ( 4 | String, Long 5 | ) 6 | 7 | 8 | # Formerly known as state_status_clientbound. 9 | def get_packets(context): 10 | packets = { 11 | ResponsePacket, 12 | PingResponsePacket, 13 | } 14 | return packets 15 | 16 | 17 | class ResponsePacket(Packet): 18 | id = 0x00 19 | packet_name = "response" 20 | definition = [ 21 | {'json_response': String}] 22 | 23 | 24 | class PingResponsePacket(Packet): 25 | id = 0x01 26 | packet_name = "ping" 27 | definition = [ 28 | {'time': Long}] 29 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/clientbound/status/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/clientbound/status/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/keep_alive_packet.py: -------------------------------------------------------------------------------- 1 | from .packet import Packet 2 | 3 | from ..types import ( 4 | VarInt, Long 5 | ) 6 | 7 | 8 | class AbstractKeepAlivePacket(Packet): 9 | packet_name = "keep alive" 10 | get_definition = staticmethod(lambda context: [ 11 | {'keep_alive_id': Long} if context.protocol_version >= 339 12 | else {'keep_alive_id': VarInt} 13 | ]) 14 | 15 | 16 | # This alias is retained for backward compatibility: 17 | KeepAlivePacket = AbstractKeepAlivePacket 18 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/keep_alive_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/keep_alive_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/packet.py: -------------------------------------------------------------------------------- 1 | from .packet_buffer import PacketBuffer 2 | from zlib import compress 3 | from ..types import ( 4 | VarInt, Enum 5 | ) 6 | 7 | 8 | class Packet(object): 9 | packet_name = "base" 10 | id = None 11 | definition = None 12 | 13 | # To define the packet ID, either: 14 | # 1. Define the attribute `id', of type int, in a subclass; or 15 | # 2. Override `get_id' in a subclass and return the correct packet ID 16 | # for the given ConnectionContext. This is necessary if the packet ID 17 | # has changed across protocol versions, for example. 18 | @classmethod 19 | def get_id(cls, context): 20 | return cls.id 21 | 22 | # To define the network data layout of a packet, either: 23 | # 1. Define the attribute `definition', a list of fields, each of which 24 | # is a dict mapping attribute names to data types; or 25 | # 2. Override `get_definition' in a subclass and return the correct 26 | # definition for the given ConnectionContext. This may be necessary 27 | # if the layout has changed across protocol versions, for example; or 28 | # 3. Override the methods `read' and/or `write_fields' in a subclass. 29 | # This may be necessary if the packet layout cannot be described as a 30 | # simple list of fields. 31 | @classmethod 32 | def get_definition(cls, context): 33 | return cls.definition 34 | 35 | def __init__(self, context=None, **kwargs): 36 | self.context = context 37 | self.set_values(**kwargs) 38 | 39 | @property 40 | def context(self): 41 | return self._context 42 | 43 | @context.setter 44 | def context(self, _context): 45 | self._context = _context 46 | self._context_changed() 47 | 48 | def _context_changed(self): 49 | if self._context is not None: 50 | self.id = self.get_id(self._context) 51 | self.definition = self.get_definition(self._context) 52 | else: 53 | self.id = None 54 | self.definition = None 55 | 56 | def set_values(self, **kwargs): 57 | for key, value in kwargs.items(): 58 | setattr(self, key, value) 59 | return self 60 | 61 | def read(self, file_object): 62 | for field in self.definition: 63 | for var_name, data_type in field.items(): 64 | value = data_type.read_with_context(file_object, self.context) 65 | setattr(self, var_name, value) 66 | 67 | # Writes a packet buffer to the socket with the appropriate headers 68 | # and compressing the data if necessary 69 | def _write_buffer(self, socket, packet_buffer, compression_threshold): 70 | # compression_threshold of None means compression is disabled 71 | if compression_threshold is not None: 72 | if len(packet_buffer.get_writable()) > compression_threshold != -1: 73 | # compress the current payload 74 | packet_data = packet_buffer.get_writable() 75 | compressed_data = compress(packet_data) 76 | packet_buffer.reset() 77 | # write out the length of the uncompressed payload 78 | VarInt.send(len(packet_data), packet_buffer) 79 | # write the compressed payload itself 80 | packet_buffer.send(compressed_data) 81 | else: 82 | # write out a 0 to indicate uncompressed data 83 | packet_data = packet_buffer.get_writable() 84 | packet_buffer.reset() 85 | VarInt.send(0, packet_buffer) 86 | packet_buffer.send(packet_data) 87 | 88 | VarInt.send(len(packet_buffer.get_writable()), socket) # Packet Size 89 | socket.send(packet_buffer.get_writable()) # Packet Payload 90 | 91 | def write(self, socket, compression_threshold=None): 92 | # buffer the data since we need to know the length of each packet's 93 | # payload 94 | packet_buffer = PacketBuffer() 95 | # write packet's id right off the bat in the header 96 | VarInt.send(self.id, packet_buffer) 97 | # write every individual field 98 | self.write_fields(packet_buffer) 99 | self._write_buffer(socket, packet_buffer, compression_threshold) 100 | 101 | def write_fields(self, packet_buffer): 102 | # Write the fields comprising the body of the packet (excluding the 103 | # length, packet ID, compression and encryption) into a PacketBuffer. 104 | for field in self.definition: 105 | for var_name, data_type in field.items(): 106 | data = getattr(self, var_name) 107 | data_type.send_with_context(data, packet_buffer, self.context) 108 | 109 | def __repr__(self): 110 | str = type(self).__name__ 111 | if self.id is not None: 112 | str = '0x%02X %s' % (self.id, str) 113 | fields = self.fields 114 | if fields is not None: 115 | inner_str = ', '.join('%s=%s' % (a, self.field_string(a)) 116 | for a in fields if hasattr(self, a)) 117 | str = '%s(%s)' % (str, inner_str) 118 | return str 119 | 120 | @property 121 | def fields(self): 122 | """ An iterable of the names of the packet's fields, or None. """ 123 | if self.definition is None: 124 | return None 125 | return (field for defn in self.definition for field in defn) 126 | 127 | def field_string(self, field): 128 | """ The string representation of the value of a the given named field 129 | of this packet. Override to customise field value representation. 130 | """ 131 | value = getattr(self, field, None) 132 | 133 | enum_class = self.field_enum(field, self.context) 134 | if enum_class is not None: 135 | name = enum_class.name_from_value(value) 136 | if name is not None: 137 | return name 138 | 139 | return repr(value) 140 | 141 | @classmethod 142 | def field_enum(cls, field, context=None): 143 | """ The subclass of 'minecraft.networking.types.Enum' associated with 144 | this field, or None if there is no such class. 145 | """ 146 | enum_name = ''.join(s.capitalize() for s in field.split('_')) 147 | if hasattr(cls, enum_name): 148 | enum_class = getattr(cls, enum_name) 149 | if isinstance(enum_class, type) and issubclass(enum_class, Enum): 150 | return enum_class 151 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/packet_buffer.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | 3 | 4 | class PacketBuffer(object): 5 | def __init__(self): 6 | self.bytes = BytesIO() 7 | 8 | def send(self, value): 9 | """ 10 | Writes the given bytes to the buffer, designed to emulate socket.send 11 | :param value: The bytes to write 12 | """ 13 | self.bytes.write(value) 14 | 15 | def read(self, length=None): 16 | return self.bytes.read(length) 17 | 18 | def recv(self, length=None): 19 | return self.read(length) 20 | 21 | def reset(self): 22 | self.bytes = BytesIO() 23 | 24 | def reset_cursor(self): 25 | self.bytes.seek(0) 26 | 27 | def get_writable(self): 28 | return self.bytes.getvalue() 29 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/packet_buffer.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/packet_buffer.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/packet_listener.py: -------------------------------------------------------------------------------- 1 | from .packet import Packet 2 | 3 | 4 | class PacketListener(object): 5 | def __init__(self, callback, *args): 6 | self.callback = callback 7 | self.packets_to_listen = [] 8 | for arg in args: 9 | if issubclass(arg, Packet): 10 | self.packets_to_listen.append(arg) 11 | 12 | def call_packet(self, packet): 13 | for packet_type in self.packets_to_listen: 14 | if isinstance(packet, packet_type): 15 | self.callback(packet) 16 | return True 17 | return False 18 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/packet_listener.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/packet_listener.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/plugin_message_packet.py: -------------------------------------------------------------------------------- 1 | from .packet import Packet 2 | from ..types import String, TrailingByteArray 3 | 4 | 5 | class AbstractPluginMessagePacket(Packet): 6 | """NOTE: Plugin channels were significantly changed, including changing the 7 | names of channels, between Minecraft 1.12 and 1.13 - see . 9 | """ 10 | definition = [ 11 | {'channel': String}, 12 | {'data': TrailingByteArray}] 13 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/plugin_message_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/plugin_message_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains the serverbound packets for `pyminecraft`. 3 | """ 4 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/serverbound/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/handshake/__init__.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | 3 | from ....types import ( 4 | VarInt, String, UnsignedShort 5 | ) 6 | 7 | 8 | # Formerly known as state_handshake_serverbound. 9 | def get_packets(context): 10 | packets = { 11 | HandShakePacket 12 | } 13 | return packets 14 | 15 | 16 | class HandShakePacket(Packet): 17 | id = 0x00 18 | packet_name = "handshake" 19 | definition = [ 20 | {'protocol_version': VarInt}, 21 | {'server_address': String}, 22 | {'server_port': UnsignedShort}, 23 | {'next_state': VarInt}] 24 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/handshake/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/serverbound/handshake/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/login/__init__.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | 3 | from ....types import ( 4 | VarInt, Boolean, String, VarIntPrefixedByteArray, TrailingByteArray 5 | ) 6 | 7 | 8 | # Formerly known as state_login_serverbound. 9 | def get_packets(context): 10 | packets = { 11 | LoginStartPacket, 12 | EncryptionResponsePacket 13 | } 14 | if context.protocol_version >= 385: 15 | packets |= { 16 | PluginResponsePacket 17 | } 18 | return packets 19 | 20 | 21 | class LoginStartPacket(Packet): 22 | @staticmethod 23 | def get_id(context): 24 | return 0x00 if context.protocol_version >= 391 else \ 25 | 0x01 if context.protocol_version >= 385 else \ 26 | 0x00 27 | 28 | packet_name = "login start" 29 | definition = [ 30 | {'name': String}] 31 | 32 | 33 | class EncryptionResponsePacket(Packet): 34 | @staticmethod 35 | def get_id(context): 36 | return 0x01 if context.protocol_version >= 391 else \ 37 | 0x02 if context.protocol_version >= 385 else \ 38 | 0x01 39 | 40 | packet_name = "encryption response" 41 | definition = [ 42 | {'shared_secret': VarIntPrefixedByteArray}, 43 | {'verify_token': VarIntPrefixedByteArray}] 44 | 45 | 46 | class PluginResponsePacket(Packet): 47 | """ NOTE: see comments on 'clientbound.login.PluginRequestPacket' for 48 | important information on the usage of this packet. 49 | """ 50 | 51 | @staticmethod 52 | def get_id(context): 53 | return 0x02 if context.protocol_version >= 391 else \ 54 | 0x00 55 | 56 | packet_name = 'login plugin response' 57 | fields = ( 58 | 'message_id', # str 59 | 'successful', # bool 60 | 'data', # bytes, or None if 'successful' is False 61 | ) 62 | 63 | def read(self, file_object): 64 | self.message_id = VarInt.read(file_object) 65 | self.successful = Boolean.read(file_object) 66 | if self.successful: 67 | self.data = TrailingByteArray.read(file_object) 68 | else: 69 | self.data = None 70 | 71 | def write_fields(self, packet_buffer): 72 | VarInt.send(self.message_id, packet_buffer) 73 | successful = getattr(self, 'data', None) is not None 74 | successful = getattr(self, 'successful', successful) 75 | Boolean.send(successful, packet_buffer) 76 | if successful: 77 | TrailingByteArray.send(self.data, packet_buffer) 78 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/login/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/serverbound/login/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/play/__init__.py: -------------------------------------------------------------------------------- 1 | from ....packets import ( 2 | Packet, AbstractKeepAlivePacket, AbstractPluginMessagePacket 3 | ) 4 | 5 | from ....types import ( 6 | Double, Float, Boolean, VarInt, String, Byte, Position, Enum, 7 | RelativeHand, BlockFace, Vector, Direction, PositionAndLook, 8 | multi_attribute_alias 9 | ) 10 | 11 | from .client_settings_packet import ClientSettingsPacket 12 | 13 | 14 | # Formerly known as state_playing_serverbound. 15 | def get_packets(context): 16 | packets = { 17 | KeepAlivePacket, 18 | ChatPacket, 19 | PositionAndLookPacket, 20 | AnimationPacket, 21 | ClientStatusPacket, 22 | ClientSettingsPacket, 23 | PluginMessagePacket, 24 | PlayerBlockPlacementPacket, 25 | } 26 | if context.protocol_version >= 69: 27 | packets |= { 28 | UseItemPacket, 29 | } 30 | if context.protocol_version >= 107: 31 | packets |= { 32 | TeleportConfirmPacket, 33 | } 34 | return packets 35 | 36 | 37 | class KeepAlivePacket(AbstractKeepAlivePacket): 38 | @staticmethod 39 | def get_id(context): 40 | return 0x0F if context.protocol_version >= 471 else \ 41 | 0x10 if context.protocol_version >= 464 else \ 42 | 0x0E if context.protocol_version >= 389 else \ 43 | 0x0C if context.protocol_version >= 386 else \ 44 | 0x0B if context.protocol_version >= 345 else \ 45 | 0x0A if context.protocol_version >= 343 else \ 46 | 0x0B if context.protocol_version >= 336 else \ 47 | 0x0C if context.protocol_version >= 318 else \ 48 | 0x0B if context.protocol_version >= 107 else \ 49 | 0x00 50 | 51 | 52 | class ChatPacket(Packet): 53 | @staticmethod 54 | def get_id(context): 55 | return 0x03 if context.protocol_version >= 464 else \ 56 | 0x02 if context.protocol_version >= 389 else \ 57 | 0x01 if context.protocol_version >= 343 else \ 58 | 0x02 if context.protocol_version >= 336 else \ 59 | 0x03 if context.protocol_version >= 318 else \ 60 | 0x02 if context.protocol_version >= 107 else \ 61 | 0x01 62 | 63 | @staticmethod 64 | def get_max_length(context): 65 | return 256 if context.protocol_version >= 306 else \ 66 | 100 67 | 68 | @property 69 | def max_length(self): 70 | if self.context is not None: 71 | return self.get_max_length(self.context) 72 | 73 | packet_name = "chat" 74 | definition = [ 75 | {'message': String}] 76 | 77 | 78 | class PositionAndLookPacket(Packet): 79 | @staticmethod 80 | def get_id(context): 81 | return 0x12 if context.protocol_version >= 471 else \ 82 | 0x13 if context.protocol_version >= 464 else \ 83 | 0x11 if context.protocol_version >= 389 else \ 84 | 0x0F if context.protocol_version >= 386 else \ 85 | 0x0E if context.protocol_version >= 345 else \ 86 | 0x0D if context.protocol_version >= 343 else \ 87 | 0x0E if context.protocol_version >= 336 else \ 88 | 0x0F if context.protocol_version >= 332 else \ 89 | 0x0E if context.protocol_version >= 318 else \ 90 | 0x0D if context.protocol_version >= 107 else \ 91 | 0x06 92 | 93 | packet_name = "position and look" 94 | definition = [ 95 | {'x': Double}, 96 | {'feet_y': Double}, 97 | {'z': Double}, 98 | {'yaw': Float}, 99 | {'pitch': Float}, 100 | {'on_ground': Boolean}] 101 | 102 | # Access the 'x', 'feet_y', 'z' fields as a Vector tuple. 103 | position = multi_attribute_alias(Vector, 'x', 'feet_y', 'z') 104 | 105 | # Access the 'yaw', 'pitch' fields as a Direction tuple. 106 | look = multi_attribute_alias(Direction, 'yaw', 'pitch') 107 | 108 | # Access the 'x', 'feet_y', 'z', 'yaw', 'pitch' fields as a 109 | # PositionAndLook. 110 | # NOTE: modifying the object retrieved from this property will not change 111 | # the packet; it can only be changed by attribute or property assignment. 112 | position_and_look = multi_attribute_alias( 113 | PositionAndLook, 'x', 'feet_y', 'z', 'yaw', 'pitch') 114 | 115 | 116 | class TeleportConfirmPacket(Packet): 117 | # Note: added between protocol versions 47 and 107. 118 | id = 0x00 119 | packet_name = "teleport confirm" 120 | definition = [ 121 | {'teleport_id': VarInt}] 122 | 123 | 124 | class AnimationPacket(Packet): 125 | @staticmethod 126 | def get_id(context): 127 | return 0x2A if context.protocol_version >= 468 else \ 128 | 0x29 if context.protocol_version >= 464 else \ 129 | 0x27 if context.protocol_version >= 389 else \ 130 | 0x25 if context.protocol_version >= 386 else \ 131 | 0x1D if context.protocol_version >= 345 else \ 132 | 0x1C if context.protocol_version >= 343 else \ 133 | 0x1D if context.protocol_version >= 332 else \ 134 | 0x1C if context.protocol_version >= 318 else \ 135 | 0x1A if context.protocol_version >= 107 else \ 136 | 0x0A 137 | 138 | packet_name = "animation" 139 | get_definition = staticmethod(lambda context: [ 140 | {'hand': VarInt} if context.protocol_version >= 107 else {}]) 141 | 142 | Hand = RelativeHand 143 | HAND_MAIN, HAND_OFF = Hand.MAIN, Hand.OFF # For backward compatibility. 144 | 145 | 146 | class ClientStatusPacket(Packet, Enum): 147 | @staticmethod 148 | def get_id(context): 149 | return 0x04 if context.protocol_version >= 464 else \ 150 | 0x03 if context.protocol_version >= 389 else \ 151 | 0x02 if context.protocol_version >= 343 else \ 152 | 0x03 if context.protocol_version >= 336 else \ 153 | 0x04 if context.protocol_version >= 318 else \ 154 | 0x03 if context.protocol_version >= 80 else \ 155 | 0x02 if context.protocol_version >= 67 else \ 156 | 0x17 if context.protocol_version >= 49 else \ 157 | 0x16 158 | 159 | packet_name = "client status" 160 | get_definition = staticmethod(lambda context: [ 161 | {'action_id': VarInt}]) 162 | field_enum = classmethod( 163 | lambda cls, field, context: cls if field == 'action_id' else None) 164 | 165 | RESPAWN = 0 166 | REQUEST_STATS = 1 167 | # Note: Open Inventory (id 2) was removed in protocol version 319 168 | OPEN_INVENTORY = 2 169 | 170 | 171 | class PluginMessagePacket(AbstractPluginMessagePacket): 172 | @staticmethod 173 | def get_id(context): 174 | return 0x0B if context.protocol_version >= 464 else \ 175 | 0x0A if context.protocol_version >= 389 else \ 176 | 0x09 if context.protocol_version >= 345 else \ 177 | 0x08 if context.protocol_version >= 343 else \ 178 | 0x09 if context.protocol_version >= 336 else \ 179 | 0x0A if context.protocol_version >= 317 else \ 180 | 0x09 if context.protocol_version >= 94 else \ 181 | 0x17 182 | 183 | 184 | class PlayerBlockPlacementPacket(Packet): 185 | """Realizaton of http://wiki.vg/Protocol#Player_Block_Placement packet 186 | Usage: 187 | packet = PlayerBlockPlacementPacket() 188 | packet.location = Position(x=1200, y=65, z=-420) 189 | packet.face = packet.Face.TOP # See networking.types.BlockFace. 190 | packet.hand = packet.Hand.MAIN # See networking.types.RelativeHand. 191 | Next values are called in-block coordinates. 192 | They are calculated using raytracing. From 0 to 1 (from Minecraft 1.11) 193 | or integers from 0 to 15 or, in a special case, -1 (1.10.2 and earlier). 194 | packet.x = 0.725 195 | packet.y = 0.125 196 | packet.z = 0.555""" 197 | 198 | @staticmethod 199 | def get_id(context): 200 | return 0x2C if context.protocol_version >= 468 else \ 201 | 0x2B if context.protocol_version >= 464 else \ 202 | 0x29 if context.protocol_version >= 389 else \ 203 | 0x27 if context.protocol_version >= 386 else \ 204 | 0x1F if context.protocol_version >= 345 else \ 205 | 0x1E if context.protocol_version >= 343 else \ 206 | 0x1F if context.protocol_version >= 332 else \ 207 | 0x1E if context.protocol_version >= 318 else \ 208 | 0x1C if context.protocol_version >= 94 else \ 209 | 0x08 210 | 211 | packet_name = 'player block placement' 212 | 213 | @staticmethod 214 | def get_definition(context): 215 | return [ 216 | {'hand': VarInt} if context.protocol_version >= 453 else {}, 217 | {'location': Position}, 218 | {'face': VarInt if context.protocol_version >= 69 else Byte}, 219 | {'hand': VarInt} if context.protocol_version < 453 else {}, 220 | {'x': Float if context.protocol_version >= 309 else Byte}, 221 | {'y': Float if context.protocol_version >= 309 else Byte}, 222 | {'z': Float if context.protocol_version >= 309 else Byte}, 223 | ({'inside_block': Boolean} 224 | if context.protocol_version >= 453 else {}), 225 | ] 226 | 227 | # PlayerBlockPlacementPacket.Hand is an alias for RelativeHand. 228 | Hand = RelativeHand 229 | 230 | # PlayerBlockPlacementPacket.Face is an alias for BlockFace. 231 | Face = BlockFace 232 | 233 | 234 | class UseItemPacket(Packet): 235 | @staticmethod 236 | def get_id(context): 237 | return 0x2D if context.protocol_version >= 468 else \ 238 | 0x2C if context.protocol_version >= 464 else \ 239 | 0x2A if context.protocol_version >= 389 else \ 240 | 0x28 if context.protocol_version >= 386 else \ 241 | 0x20 if context.protocol_version >= 345 else \ 242 | 0x1F if context.protocol_version >= 343 else \ 243 | 0x20 if context.protocol_version >= 332 else \ 244 | 0x1F if context.protocol_version >= 318 else \ 245 | 0x1D if context.protocol_version >= 94 else \ 246 | 0x1A if context.protocol_version >= 70 else \ 247 | 0x08 248 | 249 | packet_name = "use item" 250 | get_definition = staticmethod(lambda context: [ 251 | {'hand': VarInt}]) 252 | 253 | Hand = RelativeHand 254 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/play/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/serverbound/play/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/play/client_settings_packet.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | from ....types import ( 3 | String, Byte, VarInt, Boolean, UnsignedByte, Enum, BitFieldEnum, 4 | AbsoluteHand 5 | ) 6 | 7 | 8 | class ClientSettingsPacket(Packet): 9 | @staticmethod 10 | def get_id(context): 11 | return 0x05 if context.protocol_version >= 464 else \ 12 | 0x04 if context.protocol_version >= 389 else \ 13 | 0x03 if context.protocol_version >= 343 else \ 14 | 0x04 if context.protocol_version >= 336 else \ 15 | 0x05 if context.protocol_version >= 318 else \ 16 | 0x04 if context.protocol_version >= 94 else \ 17 | 0x15 18 | 19 | packet_name = 'client settings' 20 | 21 | get_definition = staticmethod(lambda context: [ 22 | {'locale': String}, 23 | {'view_distance': Byte}, 24 | {'chat_mode': VarInt if context.protocol_version > 47 else Byte}, 25 | {'chat_colors': Boolean}, 26 | {'displayed_skin_parts': UnsignedByte}, 27 | {'main_hand': VarInt} if context.protocol_version > 49 else {}]) 28 | 29 | field_enum = classmethod( 30 | lambda cls, field, context: { 31 | 'chat_mode': cls.ChatMode, 32 | 'displayed_skin_parts': cls.SkinParts, 33 | 'main_hand': AbsoluteHand, 34 | }.get(field)) 35 | 36 | class ChatMode(Enum): 37 | FULL = 0 # Receive all types of chat messages. 38 | SYSTEM = 1 # Receive only command results and game information. 39 | NONE = 2 # Receive only game information. 40 | 41 | class SkinParts(BitFieldEnum): 42 | CAPE = 0x01 43 | JACKET = 0x02 44 | LEFT_SLEEVE = 0x04 45 | RIGHT_SLEEVE = 0x08 46 | LEFT_PANTS_LEG = 0x10 47 | RIGHT_PANTS_LEG = 0x20 48 | HAT = 0x40 49 | 50 | ALL = 0x7F 51 | NONE = 0x00 52 | 53 | # This class alias is retained for backward compatibility. 54 | Hand = AbsoluteHand 55 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/play/client_settings_packet.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/serverbound/play/client_settings_packet.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/status/__init__.py: -------------------------------------------------------------------------------- 1 | from ....packets import Packet 2 | 3 | from ....types import ( 4 | Long 5 | ) 6 | 7 | 8 | # Formerly known as state_status_serverbound. 9 | def get_packets(context): 10 | packets = { 11 | RequestPacket, 12 | PingPacket 13 | } 14 | return packets 15 | 16 | 17 | class RequestPacket(Packet): 18 | id = 0x00 19 | packet_name = "request" 20 | definition = [] 21 | 22 | 23 | class PingPacket(Packet): 24 | id = 0x01 25 | packet_name = "ping" 26 | definition = [ 27 | {'time': Long}] 28 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/packets/serverbound/status/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/packets/serverbound/status/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/types/__init__.py: -------------------------------------------------------------------------------- 1 | from .basic import * # noqa: F401, F403 2 | from .enum import * # noqa: F401, F403 3 | from .utility import * # noqa: F401, F403 4 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/types/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/types/__init__.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/types/basic.py: -------------------------------------------------------------------------------- 1 | """Contains definitions for minecraft's different data types 2 | Each type has a method which is used to read and write it. 3 | These definitions and methods are used by the packet definitions 4 | """ 5 | from __future__ import division 6 | import struct 7 | import uuid 8 | 9 | from .utility import Vector 10 | 11 | 12 | __all__ = ( 13 | 'Type', 'Boolean', 'UnsignedByte', 'Byte', 'Short', 'UnsignedShort', 14 | 'Integer', 'FixedPointInteger', 'Angle', 'VarInt', 'Long', 15 | 'UnsignedLong', 'Float', 'Double', 'ShortPrefixedByteArray', 16 | 'VarIntPrefixedByteArray', 'TrailingByteArray', 'String', 'UUID', 17 | 'Position', 18 | ) 19 | 20 | 21 | class Type(object): 22 | __slots__ = () 23 | 24 | @classmethod 25 | def read_with_context(cls, file_object, _context): 26 | return cls.read(file_object) 27 | 28 | @classmethod 29 | def send_with_context(cls, value, socket, _context): 30 | return cls.send(value, socket) 31 | 32 | @classmethod 33 | def read(cls, file_object): 34 | if cls.read_with_context == Type.read_with_context: 35 | raise NotImplementedError('One of "read" or "read_with_context" ' 36 | 'must be overridden in a subclass.') 37 | else: 38 | raise TypeError('This type requires a ConnectionContext: ' 39 | 'call "read_with_context" instead of "read".') 40 | 41 | @classmethod 42 | def send(cls, value, socket): 43 | if cls.send_with_context == Type.send_with_context: 44 | raise NotImplementedError('One of "send" or "send_with_context" ' 45 | 'must be overridden in a subclass.') 46 | else: 47 | raise TypeError('This type requires a ConnectionContext: ' 48 | 'call "send_with_context" instead of "send".') 49 | 50 | 51 | class Boolean(Type): 52 | @staticmethod 53 | def read(file_object): 54 | return struct.unpack('?', file_object.read(1))[0] 55 | 56 | @staticmethod 57 | def send(value, socket): 58 | socket.send(struct.pack('?', value)) 59 | 60 | 61 | class UnsignedByte(Type): 62 | @staticmethod 63 | def read(file_object): 64 | return struct.unpack('>B', file_object.read(1))[0] 65 | 66 | @staticmethod 67 | def send(value, socket): 68 | socket.send(struct.pack('>B', value)) 69 | 70 | 71 | class Byte(Type): 72 | @staticmethod 73 | def read(file_object): 74 | return struct.unpack('>b', file_object.read(1))[0] 75 | 76 | @staticmethod 77 | def send(value, socket): 78 | socket.send(struct.pack('>b', value)) 79 | 80 | 81 | class Short(Type): 82 | @staticmethod 83 | def read(file_object): 84 | return struct.unpack('>h', file_object.read(2))[0] 85 | 86 | @staticmethod 87 | def send(value, socket): 88 | socket.send(struct.pack('>h', value)) 89 | 90 | 91 | class UnsignedShort(Type): 92 | @staticmethod 93 | def read(file_object): 94 | return struct.unpack('>H', file_object.read(2))[0] 95 | 96 | @staticmethod 97 | def send(value, socket): 98 | socket.send(struct.pack('>H', value)) 99 | 100 | 101 | class Integer(Type): 102 | @staticmethod 103 | def read(file_object): 104 | return struct.unpack('>i', file_object.read(4))[0] 105 | 106 | @staticmethod 107 | def send(value, socket): 108 | socket.send(struct.pack('>i', value)) 109 | 110 | 111 | class FixedPointInteger(Type): 112 | @staticmethod 113 | def read(file_object): 114 | return Integer.read(file_object) / 32 115 | 116 | @staticmethod 117 | def send(value, socket): 118 | Integer.send(int(value * 32), socket) 119 | 120 | 121 | class Angle(Type): 122 | @staticmethod 123 | def read(file_object): 124 | # Linearly transform angle in steps of 1/256 into steps of 1/360 125 | return 360 * UnsignedByte.read(file_object) / 256 126 | 127 | @staticmethod 128 | def send(value, socket): 129 | # Normalize angle between 0 and 255 and convert to int. 130 | UnsignedByte.send(round(256 * ((value % 360) / 360)), socket) 131 | 132 | 133 | class VarInt(Type): 134 | @staticmethod 135 | def read(file_object): 136 | number = 0 137 | # Limit of 5 bytes, otherwise its possible to cause 138 | # a DOS attack by sending VarInts that just keep 139 | # going 140 | bytes_encountered = 0 141 | while True: 142 | byte = file_object.read(1) 143 | if len(byte) < 1: 144 | raise EOFError("Unexpected end of message.") 145 | 146 | byte = ord(byte) 147 | number |= (byte & 0x7F) << 7 * bytes_encountered 148 | if not byte & 0x80: 149 | break 150 | 151 | bytes_encountered += 1 152 | if bytes_encountered > 5: 153 | raise ValueError("Tried to read too long of a VarInt") 154 | return number 155 | 156 | @staticmethod 157 | def send(value, socket): 158 | out = bytes() 159 | while True: 160 | byte = value & 0x7F 161 | value >>= 7 162 | out += struct.pack("B", byte | (0x80 if value > 0 else 0)) 163 | if value == 0: 164 | break 165 | socket.send(out) 166 | 167 | @staticmethod 168 | def size(value): 169 | for max_value, size in VARINT_SIZE_TABLE.items(): 170 | if value < max_value: 171 | return size 172 | raise ValueError("Integer too large") 173 | 174 | 175 | # Maps (maximum integer value -> size of VarInt in bytes) 176 | VARINT_SIZE_TABLE = { 177 | 2 ** 7: 1, 178 | 2 ** 14: 2, 179 | 2 ** 21: 3, 180 | 2 ** 28: 4, 181 | 2 ** 35: 5, 182 | 2 ** 42: 6, 183 | 2 ** 49: 7, 184 | 2 ** 56: 8, 185 | 2 ** 63: 9, 186 | 2 ** 70: 10, 187 | 2 ** 77: 11, 188 | 2 ** 84: 12 189 | } 190 | 191 | 192 | class Long(Type): 193 | @staticmethod 194 | def read(file_object): 195 | return struct.unpack('>q', file_object.read(8))[0] 196 | 197 | @staticmethod 198 | def send(value, socket): 199 | socket.send(struct.pack('>q', value)) 200 | 201 | 202 | class UnsignedLong(Type): 203 | @staticmethod 204 | def read(file_object): 205 | return struct.unpack('>Q', file_object.read(8))[0] 206 | 207 | @staticmethod 208 | def send(value, socket): 209 | socket.send(struct.pack('>Q', value)) 210 | 211 | 212 | class Float(Type): 213 | @staticmethod 214 | def read(file_object): 215 | return struct.unpack('>f', file_object.read(4))[0] 216 | 217 | @staticmethod 218 | def send(value, socket): 219 | socket.send(struct.pack('>f', value)) 220 | 221 | 222 | class Double(Type): 223 | @staticmethod 224 | def read(file_object): 225 | return struct.unpack('>d', file_object.read(8))[0] 226 | 227 | @staticmethod 228 | def send(value, socket): 229 | socket.send(struct.pack('>d', value)) 230 | 231 | 232 | class ShortPrefixedByteArray(Type): 233 | @staticmethod 234 | def read(file_object): 235 | length = Short.read(file_object) 236 | return struct.unpack(str(length) + "s", file_object.read(length))[0] 237 | 238 | @staticmethod 239 | def send(value, socket): 240 | Short.send(len(value), socket) 241 | socket.send(value) 242 | 243 | 244 | class VarIntPrefixedByteArray(Type): 245 | @staticmethod 246 | def read(file_object): 247 | length = VarInt.read(file_object) 248 | return struct.unpack(str(length) + "s", file_object.read(length))[0] 249 | 250 | @staticmethod 251 | def send(value, socket): 252 | VarInt.send(len(value), socket) 253 | socket.send(struct.pack(str(len(value)) + "s", value)) 254 | 255 | 256 | class TrailingByteArray(Type): 257 | """ A byte array consisting of all remaining data. If present in a packet 258 | definition, this should only be the type of the last field. """ 259 | 260 | @staticmethod 261 | def read(file_object): 262 | return file_object.read() 263 | 264 | @staticmethod 265 | def send(value, socket): 266 | socket.send(value) 267 | 268 | 269 | class String(Type): 270 | @staticmethod 271 | def read(file_object): 272 | length = VarInt.read(file_object) 273 | return file_object.read(length).decode("utf-8") 274 | 275 | @staticmethod 276 | def send(value, socket): 277 | value = value.encode('utf-8') 278 | VarInt.send(len(value), socket) 279 | socket.send(value) 280 | 281 | 282 | class UUID(Type): 283 | @staticmethod 284 | def read(file_object): 285 | return str(uuid.UUID(bytes=file_object.read(16))) 286 | 287 | @staticmethod 288 | def send(value, socket): 289 | socket.send(uuid.UUID(value).bytes) 290 | 291 | 292 | class Position(Type, Vector): 293 | """3D position vectors with a specific, compact network representation.""" 294 | __slots__ = () 295 | 296 | @staticmethod 297 | def read_with_context(file_object, context): 298 | location = UnsignedLong.read(file_object) 299 | x = int(location >> 38) # 26 most significant bits 300 | 301 | if context.protocol_version >= 443: 302 | z = int((location >> 12) & 0x3FFFFFF) # 26 intermediate bits 303 | y = int(location & 0xFFF) # 12 least signficant bits 304 | else: 305 | y = int((location >> 26) & 0xFFF) # 12 intermediate bits 306 | z = int(location & 0x3FFFFFF) # 26 least significant bits 307 | 308 | if x >= pow(2, 25): 309 | x -= pow(2, 26) 310 | 311 | if y >= pow(2, 11): 312 | y -= pow(2, 12) 313 | 314 | if z >= pow(2, 25): 315 | z -= pow(2, 26) 316 | 317 | return Position(x=x, y=y, z=z) 318 | 319 | @staticmethod 320 | def send_with_context(position, socket, context): 321 | # 'position' can be either a tuple or Position object. 322 | x, y, z = position 323 | value = ((x & 0x3FFFFFF) << 38 | (z & 0x3FFFFFF) << 12 | (y & 0xFFF) 324 | if context.protocol_version >= 443 else 325 | (x & 0x3FFFFFF) << 38 | (y & 0xFFF) << 26 | (z & 0x3FFFFFF)) 326 | UnsignedLong.send(value, socket) 327 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/types/basic.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/types/basic.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/types/enum.py: -------------------------------------------------------------------------------- 1 | """Types for enumerations of values occurring in packets, including operations 2 | for working with these values. 3 | 4 | The values in an enum are given as class attributes with UPPERCASE names. 5 | 6 | These classes are usually not supposed to be instantiated, but sometimes an 7 | instantiatable class may subclass Enum to provide class enum attributes in 8 | addition to other functionality. 9 | """ 10 | from .utility import Vector 11 | 12 | 13 | __all__ = ( 14 | 'Enum', 'BitFieldEnum', 'AbsoluteHand', 'RelativeHand', 'BlockFace', 15 | 'Difficulty', 'Dimension', 'GameMode', 'OriginPoint' 16 | ) 17 | 18 | 19 | class Enum(object): 20 | # Return a human-readable string representation of an enum value. 21 | @classmethod 22 | def name_from_value(cls, value): 23 | for name, name_value in cls.__dict__.items(): 24 | if name.isupper() and name_value == value: 25 | return name 26 | 27 | 28 | class BitFieldEnum(Enum): 29 | @classmethod 30 | def name_from_value(cls, value): 31 | if not isinstance(value, int): 32 | return 33 | ret_names = [] 34 | ret_value = 0 35 | for cls_name, cls_value in sorted( 36 | [(n, v) for (n, v) in cls.__dict__.items() 37 | if isinstance(v, int) and n.isupper() and v | value == value], 38 | reverse=True, key=lambda p: p[1] 39 | ): 40 | if ret_value | cls_value != ret_value or cls_value == value: 41 | ret_names.append(cls_name) 42 | ret_value |= cls_value 43 | if ret_value == value: 44 | return '|'.join(reversed(ret_names)) if ret_names else '0' 45 | 46 | 47 | # Designation of one of a player's hands, in absolute terms. 48 | class AbsoluteHand(Enum): 49 | LEFT = 0 50 | RIGHT = 1 51 | 52 | 53 | # Designation of one a player's hands, relative to a choice of main/off hand. 54 | class RelativeHand(Enum): 55 | MAIN = 0 56 | OFF = 1 57 | 58 | 59 | # Designation of one of a block's 6 faces. 60 | class BlockFace(Enum): 61 | BOTTOM = 0 # -Y 62 | TOP = 1 # +Y 63 | NORTH = 2 # -Z 64 | SOUTH = 3 # +Z 65 | WEST = 4 # -X 66 | EAST = 5 # +X 67 | 68 | # A dict mapping Vector tuples to the corresponding BlockFace values. 69 | # When accessing this dict, plain tuples also match. For example: 70 | # >>> BlockFace.from_vector[0, 0, -1] == BlockFace.NORTH 71 | # True 72 | from_vector = { 73 | Vector(0, -1, 0): BOTTOM, 74 | Vector(0, +1, 0): TOP, 75 | Vector(0, 0, -1): NORTH, 76 | Vector(0, 0, +1): SOUTH, 77 | Vector(-1, 0, 0): WEST, 78 | Vector(+1, 0, 0): EAST, 79 | } 80 | 81 | # A dict mapping BlockFace values to unit Position tuples. 82 | # This is the inverse mapping of face_by_position. For example: 83 | # >>> BlockFace.to_vector[BlockFace.NORTH] 84 | # Position(x=0, y=0, z=-1) 85 | to_vector = {fce: pos for (pos, fce) in from_vector.items()} 86 | 87 | 88 | # Designation of a world's difficulty. 89 | class Difficulty(Enum): 90 | PEACEFUL = 0 91 | EASY = 1 92 | NORMAL = 2 93 | HARD = 3 94 | 95 | 96 | # Designation of a world's dimension. 97 | class Dimension(Enum): 98 | NETHER = -1 99 | OVERWORLD = 0 100 | END = 1 101 | 102 | 103 | # Designation of a player's gamemode. 104 | class GameMode(Enum): 105 | SURVIVAL = 0 106 | CREATIVE = 1 107 | ADVENTURE = 2 108 | SPECTATOR = 3 109 | 110 | 111 | # Currently designates an entity's feet or eyes. 112 | # Used in the Face Player Packet 113 | class OriginPoint(Enum): 114 | FEET = 0 115 | EYES = 1 116 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/types/enum.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/types/enum.pyc -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/types/utility.py: -------------------------------------------------------------------------------- 1 | """Minecraft data types that are used by packets, but don't have a specific 2 | network representation. 3 | """ 4 | from __future__ import division 5 | 6 | from collections import namedtuple 7 | from itertools import chain 8 | 9 | 10 | __all__ = ( 11 | 'Vector', 'MutableRecord', 'Direction', 'PositionAndLook', 'descriptor', 12 | 'attribute_alias', 'multi_attribute_alias', 13 | ) 14 | 15 | 16 | class Vector(namedtuple('BaseVector', ('x', 'y', 'z'))): 17 | """An immutable type usually used to represent 3D spatial coordinates, 18 | supporting elementwise vector addition, subtraction, and negation; and 19 | scalar multiplication and (right) division. 20 | 21 | NOTE: subclasses of 'Vector' should have '__slots__ = ()' to avoid the 22 | creation of a '__dict__' attribute, which would waste space. 23 | """ 24 | __slots__ = () 25 | 26 | def __add__(self, other): 27 | return NotImplemented if not isinstance(other, Vector) else \ 28 | type(self)(self.x + other.x, self.y + other.y, self.z + other.z) 29 | 30 | def __sub__(self, other): 31 | return NotImplemented if not isinstance(other, Vector) else \ 32 | type(self)(self.x - other.x, self.y - other.y, self.z - other.z) 33 | 34 | def __neg__(self): 35 | return type(self)(-self.x, -self.y, -self.z) 36 | 37 | def __mul__(self, other): 38 | return type(self)(self.x*other, self.y*other, self.z*other) 39 | 40 | def __rmul__(self, other): 41 | return type(self)(other*self.x, other*self.y, other*self.z) 42 | 43 | def __truediv__(self, other): 44 | return type(self)(self.x/other, self.y/other, self.z/other) 45 | 46 | def __floordiv__(self, other): 47 | return type(self)(self.x//other, self.y//other, self.z//other) 48 | 49 | __div__ = __floordiv__ 50 | 51 | def __repr__(self): 52 | return '%s(%r, %r, %r)' % (type(self).__name__, self.x, self.y, self.z) 53 | 54 | 55 | class MutableRecord(object): 56 | """An abstract base class providing namedtuple-like repr(), ==, hash(), and 57 | iter(), implementations for types containing mutable fields given by 58 | __slots__. 59 | """ 60 | __slots__ = () 61 | 62 | def __init__(self, **kwds): 63 | for attr, value in kwds.items(): 64 | setattr(self, attr, value) 65 | 66 | def __repr__(self): 67 | return '%s(%s)' % (type(self).__name__, ', '.join( 68 | '%s=%r' % (a, getattr(self, a)) for a in self._all_slots() 69 | if hasattr(self, a))) 70 | 71 | def __eq__(self, other): 72 | return type(self) is type(other) and all( 73 | getattr(self, a) == getattr(other, a) for a in self._all_slots()) 74 | 75 | def __ne__(self, other): 76 | return not (self == other) 77 | 78 | def __hash__(self): 79 | values = tuple(getattr(self, a, None) for a in self._all_slots()) 80 | return hash((type(self), values)) 81 | 82 | def __iter__(self): 83 | return iter(getattr(self, a) for a in self._all_slots()) 84 | 85 | @classmethod 86 | def _all_slots(cls): 87 | for supcls in reversed(cls.__mro__): 88 | slots = supcls.__dict__.get('__slots__', ()) 89 | slots = (slots,) if isinstance(slots, str) else slots 90 | for slot in slots: 91 | yield slot 92 | 93 | 94 | def attribute_alias(name): 95 | """An attribute descriptor that redirects access to a different attribute 96 | with a given name. 97 | """ 98 | return property(fget=(lambda self: getattr(self, name)), 99 | fset=(lambda self, value: setattr(self, name, value)), 100 | fdel=(lambda self: delattr(self, name))) 101 | 102 | 103 | def multi_attribute_alias(container, *arg_names, **kwd_names): 104 | """A descriptor for an attribute whose value is a container of a given type 105 | with several fields, each of which is aliased to a different attribute 106 | of the parent object. 107 | 108 | The 'n'th name in 'arg_names' is the parent attribute that will be 109 | aliased to the field of 'container' settable by the 'n'th positional 110 | argument to its constructor, and accessible as its 'n'th iterable 111 | element. 112 | 113 | As a special case, 'tuple' may be given as the 'container' when there 114 | are positional arguments, and (even though the tuple constructor does 115 | not take positional arguments), the arguments will be aliased to the 116 | corresponding positions in a tuple. 117 | 118 | The name in 'kwd_names' mapped to by the key 'k' is the parent attribute 119 | that will be aliased to the field of 'container' settable by the keyword 120 | argument 'k' to its constructor, and accessible as its 'k' attribute. 121 | """ 122 | if container is tuple: 123 | container = lambda *args: args # noqa: E731 124 | 125 | @property 126 | def alias(self): 127 | return container( 128 | *(getattr(self, name) for name in arg_names), 129 | **{kwd: getattr(self, name) for (kwd, name) in kwd_names.items()}) 130 | 131 | @alias.setter 132 | def alias(self, values): 133 | if arg_names: 134 | for name, value in zip(arg_names, values): 135 | setattr(self, name, value) 136 | for kwd, name in kwd_names.items(): 137 | setattr(self, name, getattr(values, kwd)) 138 | 139 | @alias.deleter 140 | def alias(self): 141 | for name in chain(arg_names, kwd_names.values()): 142 | delattr(self, name) 143 | 144 | return alias 145 | 146 | 147 | class descriptor(object): 148 | """Behaves identically to the builtin 'property' function of Python, 149 | except that the getter, setter and deleter functions given by the 150 | user are used as the raw __get__, __set__ and __delete__ functions 151 | as defined in Python's descriptor protocol. 152 | """ 153 | __slots__ = '_fget', '_fset', '_fdel' 154 | 155 | def __init__(self, fget=None, fset=None, fdel=None): 156 | self._fget = fget if fget is not None else self._default_get 157 | self._fset = fset if fset is not None else self._default_set 158 | self._fdel = fdel if fdel is not None else self._default_del 159 | 160 | def getter(self, fget): 161 | self._fget = fget 162 | return self 163 | 164 | def setter(self, fset): 165 | self._fset = fset 166 | return self 167 | 168 | def deleter(self, fdel): 169 | self._fdel = fdel 170 | return self 171 | 172 | @staticmethod 173 | def _default_get(instance, owner): 174 | raise AttributeError('unreadable attribute') 175 | 176 | @staticmethod 177 | def _default_set(instance, value): 178 | raise AttributeError("can't set attribute") 179 | 180 | @staticmethod 181 | def _default_del(instance): 182 | raise AttributeError("can't delete attribute") 183 | 184 | def __get__(self, instance, owner): 185 | return self._fget(self, instance, owner) 186 | 187 | def __set__(self, instance, value): 188 | return self._fset(self, instance, value) 189 | 190 | def __delete__(self, instance): 191 | return self._fdel(self, instance) 192 | 193 | 194 | Direction = namedtuple('Direction', ('yaw', 'pitch')) 195 | 196 | 197 | class PositionAndLook(MutableRecord): 198 | """A mutable record containing 3 spatial position coordinates 199 | and 2 rotational coordinates for a look direction. 200 | """ 201 | __slots__ = 'x', 'y', 'z', 'yaw', 'pitch' 202 | 203 | position = multi_attribute_alias(Vector, 'x', 'y', 'z') 204 | 205 | look = multi_attribute_alias(Direction, 'yaw', 'pitch') 206 | -------------------------------------------------------------------------------- /mcdbotUtils/minecraft/networking/types/utility.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TISUnion/mcd-bot/89af75bf465c883cc7f78d7edc7f2ed9e7f6e33d/mcdbotUtils/minecraft/networking/types/utility.pyc -------------------------------------------------------------------------------- /mcdbotUtils/port.ini: -------------------------------------------------------------------------------- 1 | 25565 2 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | mcd-bot plugin 2 | -------- 3 | a plugin for adding bots into ur server 4 | 5 | now supports up to 1.14.4 6 | 7 | Compatible with [MCDaemon](https://github.com/kafuuchino-desu/MCDaemon) and [MCDReforged](https://github.com/Fallen-Breath/MCDReforged) 8 | 9 | HUGE thanks to Ammar Askar and Jeppe Klitgaard for developing https://github.com/ammaraskar/pyCraft -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cryptography>=1.5 2 | requests 3 | future 4 | --------------------------------------------------------------------------------