def encode(self) -> bytes: out = b"" for tags, REG in ( ( self.block, BLOCK_REGISTRY, ), ( self.item, ITEM_REGISTRY, ), ( self.fluid, FLUID_REGISTRY, ), ( self.entity, ENTITY_REGISTRY, ), ): out += Buffer.pack_varint(len(tags)) # pack length for identifier in tags: # pack identifier name and length of upcoming array out += Buffer.pack_string(identifier) + Buffer.pack_varint( len(tags[identifier])) for value in tags[identifier]: # values should be encoded as varints, so we need their id out += Buffer.pack_varint(REG.encode(value)) return out
def encode(self) -> bytes: out = Buffer.pack_varint(len(self.stats)) for entry in self.stats: out += b"".join([Buffer.pack_varint(e) for e in entry]) return out
def encode(self) -> bytes: return ( Buffer.pack_varint(self.entity_id) + Buffer.pack("f", self.yaw) + Buffer.pack("f", self.pitch) + Buffer.pack("?", self.on_ground) )
def encode(self) -> bytes: out = Buffer.pack_varint(len(self.nodes)) for node in self.nodes: pass # idek yet return out + Buffer.pack_varint(0)
def encode(self) -> bytes: return ( Buffer.pack_varint(self.eid) + Buffer.pack("h", self.vel_x) + Buffer.pack("h", self.vel_y) + Buffer.pack("h", self.vel_z) )
def encode(self) -> bytes: return ( Buffer.pack_position(self.x, self.y, self.z) + Buffer.pack_varint(self.block) + Buffer.pack_varint(self.status) + Buffer.pack("?", self.successful) )
async def login_start(stream: Stream, packet: Packet) -> tuple: if share["conf"][ "online_mode"]: # Online mode is enabled, so we request encryption lc = login_cache[stream.remote] = { "username": packet.username, "verify": None } packet = login_packets.LoginEncryptionRequest( share["rsa"]["public"].public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.SubjectPublicKeyInfo)) lc["verify"] = packet.verify_token stream.write(Buffer.pack_packet(packet)) await stream.drain() else: # No need for encryption since online mode is off, just send login success uuid_ = ( uuid.uuid4() ) # This should be only generated if the player name isn't found in the world data, but no way to do that rn stream.write( Buffer.pack_packet( login_packets.LoginSuccess(uuid_, packet.username), share["comp_thresh"])) await stream.drain() states[stream.remote] = 3 # Update state to play return True, stream
async def encrypted_login(stream: Stream, packet: Packet) -> tuple: shared_key, auth = await server_auth(packet, stream.remote, login_cache[stream.remote]) del login_cache[stream.remote] # No longer needed if not auth: # If authentication failed, disconnect client stream.write( Buffer.pack_packet( login_packets.LoginDisconnect( "Failed to authenticate your connection."))) await stream.drain() return False, stream # Generate a cipher for that client using the shared key from the client cipher = encryption.gen_aes_cipher(shared_key) # Replace stream with one which auto decrypts + encrypts data when reading/writing stream = EncryptedStream(stream, cipher) if share["comp_thresh"] > 0: # Send set compression packet if needed stream.write( Buffer.pack_packet(LoginSetCompression(share["comp_thresh"]))) await stream.drain() # Send LoginSuccess packet, tells client they've logged in succesfully stream.write( Buffer.pack_packet(login_packets.LoginSuccess(*auth), share["comp_thresh"])) await stream.drain() return True, stream
def encode(self) -> bytes: return ( Buffer.pack_varint(self.sound_id) + Buffer.pack_varint(self.category) + Buffer.pack_varint(self.eid) + Buffer.pack("f", self.volume) + Buffer.pack("f", self.pitch) )
def encode(self) -> bytes: return ( Buffer.pack_varint(self.entity_id) + Buffer.pack("h", self.dx) + Buffer.pack("h", self.dy) + Buffer.pack("h", self.dz) + Buffer.pack("?", self.on_ground) )
def encode(self) -> bytes: return ( Buffer.pack_string(" " * 20) + Buffer.pack_varint(len(self.public_key)) + self.public_key + Buffer.pack_varint(len(self.verify_token)) + self.verify_token )
def decode(cls, buf: Buffer) -> PlayUpdateSign: return cls( *buf.unpack_position(), buf.unpack_string(), buf.unpack_string(), buf.unpack_string(), buf.unpack_string(), )
def encode(self) -> bytes: return ( Buffer.pack("d", self.x) + Buffer.pack("d", self.y) + Buffer.pack("d", self.z) + Buffer.pack("f", self.yaw) + Buffer.pack("f", self.pitch) )
async def handle_packet(stream: Stream): packet_length = 0 # Basically an implementation of Buffer.unpack_varint() # except designed to read directly from a a StreamReader # and also to handle legacy server list ping packets for i in range(5): try: read = await asyncio.wait_for(stream.read(1), 5) except asyncio.TimeoutError: logger.debug("Closing due to timeout on read...") return False, stream if read == b"": logger.debug("Closing due to invalid read....") return False, stream if i == 0 and read == b"\xFE": logger.warn("Legacy ping attempted, legacy ping is not supported.") return False, stream b = struct.unpack("B", read)[0] packet_length |= (b & 0x7F) << 7 * i if not b & 0x80: break if packet_length & (1 << 31): packet_length -= 1 << 32 buf = Buffer(await stream.read(packet_length)) state = STATES.encode(states.get(stream.remote, 0)) packet = buf.unpack_packet(state, PACKET_MAP) logger.debug( f"IN : state:{state:<11} | id:0x{packet.id:02X} | packet:{type(packet).__name__}" ) for handler in pymine_api.packet.PACKET_HANDLERS[state][packet.id]: resp_value = await handler(stream, packet) try: continue_, stream = resp_value except ( ValueError, TypeError, ): logger.warn( f"Invalid return from packet handler: {handler.__module__}.{handler.__qualname__}" ) continue if not continue_: return False, stream return continue_, stream
def decode(cls, buf: Buffer) -> PlayPlayerPositionAndRotationServerBound: return cls( buf.unpack("d"), buf.unpack("d"), buf.unpack("d"), buf.unpack("d"), buf.unpack("d"), buf.unpack("?"), )
def encode(self) -> bytes: out = Buffer.pack_varint(self.action) if 2 >= self.action >= 0: out += Buffer.pack_chat(Chat(self.data)) elif self.action == 3: out += b"".join([Buffer.pack("i", i) for i in self.data]) return out
def decode(cls, buf: Buffer) -> PlayUpdateJigsawBlock: return cls( *buf.unpack_pos(), buf.unpack_string(), buf.unpack_string(), buf.unpack_string(), buf.unpack_string(), buf.unpack_string(), )
def decode(cls, buf: Buffer) -> PlayClientSettings: return cls( buf.unpack_string(), buf.unpack("b"), buf.unpack_varint(), buf.unpack("?"), buf.unpack("B"), buf.unpack_varint(), )
def decode(cls, buf: Buffer) -> PlayClickWindow: return cls( buf.unpack("B"), buf.unpack("h"), buf.unpack("b"), buf.unpack("h"), buf.unpack_varint(), buf.unpack_slot(), )
def encode(self) -> bytes: out = Buffer.pack_varint(self.entity_id) for prop in self.properties: out += ( Buffer.pack_string(prop["key"]) + Buffer.pack("d", prop["value"]) + Buffer.pack_varint(len(prop["modifiers"])) + b"".join([Buffer.pack_modifier(m) for m in prop["modifiers"]]) )
def decode(cls, buf: Buffer) -> PlayBlockPlacement: return cls( buf.unpack_varint(), *buf.unpack_pos(), buf.unpack_varint(), buf.unpack("f"), buf.unpack("f"), buf.unpack("f"), buf.unpack("?"), )
def decode(cls, buf: Buffer) -> PlayInteractEntity: return cls( buf.unpack_varint(), buf.unpack_varint(), buf.unpack_optional(buf.unpack_varint), buf.unpack_optional(buf.unpack_varint), buf.unpack_optional(buf.unpack_varint), buf.unpack_optional(buf.unpack_varint), buf.unpack("?"), )
def encode(self) -> bytes: return (Buffer.pack("f", self.x) + Buffer.pack("f", self.y) + Buffer.pack("f", self.z) + Buffer.pack("f", self.strength) + Buffer.pack("i", self.record_count) + Buffer.pack_array("b", self.records) + Buffer.pack("f", self.pmx) + Buffer.pack("f", self.pmy) + Buffer.pack("f", self.pmz))
def encode(self) -> bytes: return (Buffer.pack("f", self.x) + Buffer.pack("f", self.y) + Buffer.pack("f", self.z) + Buffer.pack("f", self.strength) + Buffer.pack("i", self.record_count) + b"".join([Buffer.pack("b", r) for r in self.records]) + Buffer.pack("f", self.pmx) + Buffer.pack("f", self.pmy) + Buffer.pack("f", self.pmz))
def test_basic(): buf = Buffer() buf.write( Buffer.pack("i", 123) + Buffer.pack("b", 1) + Buffer.pack("?", True) + Buffer.pack("q", 1234567890456)) assert buf.buf == b"\x00\x00\x00{\x01\x01\x00\x00\x01\x1fq\xfb\x06\x18" assert buf.unpack("i") == 123 assert buf.unpack("b") == 1 assert buf.unpack("?") is True assert buf.unpack("q") == 1234567890456
def encode(self) -> bytes: # if self.event == 0: # start combat # return Buffer.pack_varint(self.event) # # if self.event == 1: # end combat # return Buffer.pack_varint(self.event) + Buffer.pack_varint(self.data['duration']) + \ # Buffer.pack('i', self.data['opponent']) if self.event == 2: # entity dead, only one actually used return (Buffer.pack_varint(self.event) + Buffer.pack_varint(self.data["player_id"]) + Buffer.pack("i", self.data["entity_id"]) + Buffer.pack_chat(self.data["message"]))
def encode(self) -> bytes: out = (Buffer.pack_varint(((self.chunk_sect_x & 0x3FFFFF) << 42) | (self.chunk_sect_y & 0xFFFFF) | ( (self.chunk_sect_z & 0x3FFFFF) << 20)) + Buffer.pack("?", self.trust_edges) + Buffer.pack_varint(len(self.blocks))) for block_id, local_x, local_y, local_z in self.blocks: out += Buffer.pack_varint(block_id << 12 | (local_x << 8 | local_z << 4 | local_y)) return out
def test_varint(var_int, error_msg): buf = Buffer() if error_msg: with pytest.raises(ValueError) as err: buf.write(Buffer.pack_varint(var_int)) assert error_msg in str(err) else: buf.write(Buffer.pack_varint(var_int)) assert buf.unpack_varint() == var_int
def encode(self) -> bytes: return (Buffer.pack_string(self.name) + Buffer.pack_varint(self.category) + Buffer.pack("i", self.category) + Buffer.pack("i", self.effect_pos_x) + Buffer.pack("i", self.effect_pos_y) + Buffer.pack("i", self.effect_pos_z) + Buffer.pack("f", self.volume) + Buffer.pack("f", self.pitch))
async def fetch_player(self, uuid_: uuid.UUID) -> Player: try: return self.cache[int(uuid_)] except KeyError: file = os.path.join(self.data_dir, f"{uuid_}.dat") # filename of the player if not os.path.isfile(file): # create new player if needed level_data = self.server.worlds["minecraft:overworld"].data player = Player.new( self.server.api.eid(), uuid_, (level_data["SpawnX"], level_data["SpawnY"], level_data["SpawnX"]), "minecraft:overworld", ) self.cache[int(player.uuid)] = player return player async with aiofile.async_open(file, "rb") as player_file: # load preexisting player = Player( self.server.eid(), nbt.TAG_Compound.unpack(Buffer(await player_file.read())) ) self.cache[player.uuid] = player return player