async def decode_login_encryption_response(cls, msg: ClientMessage) ->...: # 1.7.x if msg.protocol_version <= 5: unpack_array = lambda b: b.read(b.unpack('h')) else: unpack_array = lambda b: b.read(b.unpack_varint(max_bits=16)) buffer = msg.buffer p_shared_secret = unpack_array(buffer) p_shared_verify = unpack_array(buffer) shared_secret = decrypt_secret(ServerCore.keypair, p_shared_secret) shared_verify = decrypt_secret(ServerCore.keypair, p_shared_verify) if shared_verify != msg.conn.verify_token: msg.close_connection("Invalid verify token!") msg.conn.cipher.enable(shared_secret) digest = make_digest(msg.conn.server_id.encode("ascii"), shared_secret, ServerCore.pubkey) url = ("https://sessionserver.mojang.com/session/minecraft/hasJoined" + f"?username={msg.conn.display_name}&serverId={digest}") if ServerCore.options["prevent-proxy-connections"]: url += f"&ip={msg.conn.client.server_hostname}" async with ClientSession() as session: async with session.get(url, timeout=ServerCore.auth_timeout) as resp: data = await resp.json() msg.conn.uuid = UUID(data["id"]) cls.update_protocol(msg, "play") return PlayerRegistry.add_player(msg.conn)
async def event_player_join(cls, msg: ClientMessage, player: Player): for _player in PlayerRegistry.players: # PacketHandler.encode_player_info(msg, player, _player, 0) await PacketHandler.encode_chat_message( msg, _player, f"\u00a7e{player.display_name} has joined.") await PacketHandler.encode_player_join(msg, player) await PacketHandler.encode_player_spawn_pos(msg, player) await PacketHandler.encode_player_position_look(msg, player) await PacketHandler.encode_player_spawn(msg, player, player) while msg.conn.do_loop: val = random.randint(0, 2**20) msg.send_packet( "keep_alive", msg.buffer_type.pack_varint(val) if msg.protocol_version <= 338 else msg.buffer_type.pack("Q", val)) keep_alive_packet = await msg.conn.wait_for_packet("keep_alive") # 1.7.x if msg.protocol_version <= 338: verify = keep_alive_packet.unpack_varint() else: verify = keep_alive_packet.unpack("Q") if val != verify: msg.close_connection("Invalid KeepAlive packet!") return await anyio.sleep(1)
async def encode_player_join(cls, msg: ClientMessage, player: Player): msg.send_packet( "join_game", msg.buffer_type.pack("iBiBB", player.entity.id, player.gamemode, player.entity.dimension, ServerCore.options["difficulty"], ServerCore.options["max-players"]) + msg.buffer.pack_string("default") + msg.buffer_type.pack("?", False))
async def decode_handshake(cls, msg: ClientMessage): buffer = msg.buffer msg.set_protocol_version(buffer.unpack_varint()) hostname = buffer.unpack_string() port = buffer.unpack("H") next_state = buffer.unpack_varint() mode = protocol_modes.get(next_state, next_state) msg.conn.protocol_state = mode cls.update_protocol(msg, mode)
async def encode_player_position_look(cls, msg: ClientMessage, player: Player): teleport_id = random.randint(0, 2**20) msg.send_packet( "player_position_and_look", msg.buffer_type.pack("ddd", *player.entity.position) + msg.buffer_type.pack("ff", *player.entity.rotation) + msg.buffer_type.pack("b", player.entity.flags) + msg.buffer_type.pack_varint(teleport_id)) teleport_packet = await msg.conn.wait_for_packet("teleport_confirm") verify = teleport_packet.unpack_varint() if teleport_id != verify: msg.close_connection("Invalid Teleport ID packet")
async def encode_player_abilities(cls, msg: ClientMessage, player: Player): player.conn.send_packet( msg.build_packet( "player_abilities", msg.buffer_type.pack("bff", player.entity.abilities, player.entity.fly_speed, player.entity.walk_speed)))
async def encode_player_spawn(cls, msg: ClientMessage, target_player: Player, player: Player): target_player.conn.send_packet( msg.build_packet( "spawn_player", msg.buffer_type.pack_varint(player.entity.id) + player.uuid.bytes + msg.buffer_type.pack("ddd", *player.entity.position) + msg.buffer_type.pack("ff", *player.entity.rotation) + msg.buffer_type.pack_entity_metadata(player.entity.metadata)))
async def decode_login_start(cls, msg: ClientMessage): msg.conn.display_name = msg.buffer.unpack_string() if ServerCore.options["online-mode"]: # 1.7.x if msg.protocol_version <= 5: pack_array = lambda a: msg.buffer_type.pack('h', len(a)) + a else: pack_array = lambda a: msg.buffer_type.pack_varint( len(a), max_bits=16) + a msg.send_packet( "login_encryption_request", msg.buffer_type.pack_string(msg.conn.server_id) + pack_array(ServerCore.pubkey) + pack_array(msg.conn.verify_token)) else: # TODO pass
async def decode_status_request(cls, msg: ClientMessage): d = { "description": { "text": ServerCore.options["motd"] }, "players": { "online": PlayerRegistry.player_count(), "max": ServerCore.options["max-players"] }, "version": { "name": packets.minecraft_versions.get(msg.protocol_version, "???"), "protocol": msg.protocol_version } } favicon = read_favicon() if favicon: d["favicon"] = f"data:image/png;base64,{favicon}" msg.send_packet("status_response", msg.buffer_type.pack_json(d))
async def serve_loop(self): data = b"" run_again = False async with create_task_group() as tg: while self.do_loop: if not run_again: try: line = await self.client.receive_some(1024) except ConnectionError: line = b"" if line == b"": try: warn(f"Closing connection to {self.client.server_hostname}") except TLSRequired: pass self.do_loop = False break data += self.cipher.decrypt(line) try: msg = ClientMessage(self, data, self.protocol_version) except BufferUnderrun: run_again = False continue else: data = data[msg.old_len:] if data != b"": run_again = True for lock in self._locks: if lock["name"] == msg.name: self._locks.remove(lock) lock["result"] = msg.buffer await lock["lock"].set() break if msg.name == "handshake": await self.handle_msg(msg) else: await tg.spawn(self.handle_msg, msg) for lock in self._locks: await lock["lock"].set() if self.protocol_state == "play": # User was logged in debug("Player left, removing from game...") # TODO: Fix EventHandler # Requires: client_message.py:22 # EventHandler.event_player_leave(self.player) PlayerRegistry.players.remove(self.player)
async def encode_player_info(cls, msg: ClientMessage, target_player: Player, player_updated: Player, action: int): player_packet = player_updated.uuid.bytes display_name = None if player_updated.display_name == player_updated.name else player_updated.display_name if action == 0: player_packet += ( msg.buffer_type.pack_string(player_updated.name) + msg.buffer_type.pack_varint(len(player_updated.properties)) + player_updated.pack_properties(msg.buffer_type) + msg.buffer_type.pack_varint(player_updated.gamemode) + msg.buffer_type.pack_varint(player_updated.ping) + msg.buffer_type.pack( "?", player_updated.display_name != player_updated.name) + msg.buffer_type.pack_optional(msg.buffer_type.pack_chat, display_name)) elif action == 1: player_packet += msg.buffer_type.pack_varint( player_updated.gamemode) elif action == 2: player_packet += msg.buffer_type.pack_varint(player_updated.ping) elif action == 3: player_packet += (msg.buffer_type.pack( "?", player_updated.display_name != player_updated.name) + msg.buffer_type.pack_optional( msg.buffer_type.pack_chat, display_name)) elif action == 4: pass else: raise Exception target_player.conn.send_packet( msg.build_packet( "player_list_item", msg.buffer_type.pack_varint(action) + msg.buffer_type.pack_varint(1) + msg.buffer.pack_array("", player_packet)))
def update_protocol(cls, msg: ClientMessage, mode: str): if mode == "play": if msg.compression_threshold: # Send set compression msg.send_packet( "login_set_compression", msg.buffer_type.pack_varint(msg.compression_threshold)) # Send login success msg.send_packet( "login_success", msg.buffer_type.pack_string(str(msg.conn.uuid)) + msg.buffer_type.pack_string(msg.conn.display_name)) elif mode == "login": if PlayerRegistry.player_count( ) >= ServerCore.options["max-players"]: msg.close_connection("Server is full!") msg.conn.protocol_state = mode
async def encode_chat_message(cls, msg: ClientMessage, target_player: Player, message: str): buf = msg.buffer_type.pack_chat(message) + msg.buffer_type.pack("b", 0) target_player.conn.send_packet(msg.build_packet("chat_message", buf))
async def encode_player_spawn_pos(cls, msg: ClientMessage, player: Player): msg.send_packet("spawn_position", msg.buffer_type.pack_position(*player.entity.position))
async def packet_chat_message(cls, msg: ClientMessage): message = msg.buffer.unpack_string() packet = msg.build_packet("chat_message", msg.buffer_type.pack_chat(message)) for player in PlayerRegistry.players: player.conn.send_packet(packet)
async def decode_status_ping(cls, msg: ClientMessage): time = msg.buffer.unpack("Q") msg.send_packet("status_pong", msg.buffer_type.pack("Q", time))