def interact_entity(self, pos=None): if pos is None: pos = self.clientinfo.eye_pos target_dist = Vector3(*pos).dist_sq nearest_dist = float('inf') nearest_ent = None for current_entity in self.entities.entities.values(): # TODO check if entity can be interacted with try: current_pos = Vector3(current_entity) except AttributeError: # has no position continue current_dist = target_dist(current_pos) if current_dist > reach_dist_sq: continue if nearest_dist > current_dist: nearest_dist = current_dist nearest_ent = current_entity if nearest_ent: self.interact.use_entity(nearest_ent) logger.debug('Clicked entity %s at %s', nearest_ent.__class__.__name__, Vector3(nearest_ent)) else: logger.warn('No entity near %s', pos)
def start_digging(self, pos, face=constants.FACE_TOP): pos = Vector3(pos) if self.auto_look: self.look_at(pos.floor() + Vector3(0.5, 0.5, 0.5)) self._send_dig_block(status=constants.DIG_START, pos=pos, face=face) if self.auto_swing: self.swing_arm()
def on_entity_move(self, evt, packet): eid = packet.data['eid'] entity = self.entities.entities[eid] dist_sq = self.clientinfo.position.dist_sq # look at nearby players if eid in self.entities.players: entity_head = Vector3(entity).iadd((0, PLAYER_EYE_HEIGHT, 0)) if id(entity) == id(self.nearest_player): self.interact.look_at(entity_head) elif self.nearest_player is None: self.nearest_player = entity self.interact.look_at(entity_head) else: if dist_sq(entity_head) < dist_sq(Vector3( self.nearest_player)): self.nearest_player = entity self.interact.look_at(entity_head) # force field # runs max. one per tick, but only when there are moving entities if self.aggro and not self.checked_entities_this_tick: for entity in self.entities.mobs.values(): if reach_dist_sq > dist_sq(Vector3(entity)): self.interact.attack_entity(entity) self.checked_entities_this_tick = True
def activate_item(self): """ Use (hold right-click) the item in the active slot. Examples: pull the bow, start eating once, throw an egg. """ self._send_click_block(pos=Vector3(-1, 255, -1), face=-1, cursor_pos=Vector3(-1, -1, -1))
def physics_tick(self, _, __): if self.skip_tick: self.skip_tick = False return self.apply_accel() mtv = self.get_mtv() self.apply_vector(mtv) self.pos.on_ground = mtv.y > 0 self.vec -= Vector3(0, const.PHY_GAV_ACC, 0) self.apply_drag() self.pc.direction = Vector3()
def handle_client_join(self, name, data): pos = self.clientinfo.position x, y, z = mvu.get_nearest_position(pos.x, pos.y, pos.z) self.state.pos = Vector3(x, y, z) facing = mvu.get_nearest_direction(pos.yaw) self.state.dir = facing logger.info("Initializing agent's internal state of motion")
def __init__(self, pos, vec, abilities): self.pos = pos self.vec = vec self.sprinting = False self.move_accel = abilities.walking_speed self.abilities = abilities self.direction = Vector3()
def jump(self): if self.pos.on_ground: if self.sprinting: ground_speed = Vector3(self.vec.x, 0, self.vec.z) if ground_speed: self.vec += ground_speed.norm() * const.PHY_JMP_MUL self.vec.y = const.PHY_JMP_ABS
def test_look(self): self.plug.look(123.4, -42.2) self.assertEqual(ClientInfoMock.position.yaw, 123.4) self.assertEqual(ClientInfoMock.position.pitch, -42.2) self.plug.look_rel(1.4, 2.1) self.assertAlmostEqual(ClientInfoMock.position.yaw, 124.8) self.assertAlmostEqual(ClientInfoMock.position.pitch, -40.1) self.plug.look_at_rel(Vector3(1, 0, 0)) self.assertAlmostEqual(ClientInfoMock.position.yaw, -90) self.assertAlmostEqual(ClientInfoMock.position.pitch, 0) self.plug.look_at(Vector3(0, 2 + constants.PLAYER_EYE_HEIGHT, 3)) self.assertAlmostEqual(ClientInfoMock.position.yaw, 90) self.assertAlmostEqual(ClientInfoMock.position.pitch, 0)
def raycast_bbox(self, start, end): pos = PathNode(uncenter_position(start, self.col.bbox)) path = end - start if not path: return True depth = Vector3( *map(lambda a: a[0] * a[1], zip(path.norm(), self.col.bbox)) ) i, r = divmod(path.dist(), depth.dist()) for j in range(int(i)): pos += depth if not self.get_block(pos.floor() - Vector3(0, 1, 0)).bounding_box: return False if any(self.col.block_collision(pos)): return False return True
def print_plan(self, dy=0): center = self.clientinfo.position.floor() visible_blocks = set() msg = '' for dz in range(-5, 6, 1): msg += '\n' for dx in range(-5, 6, 1): block_pos = Vector3(dx, dy, dz).iadd(center) block_id, meta = self.world.get_block(*block_pos) visible_blocks.add((block_id, meta)) if block_id == 0: msg += ' ' elif meta == 0: msg += '%3i ' % block_id else: msg += '%3i:%-2i ' % (block_id, meta) if dx == dz == 0: # mark bot position with brackets: [123:45] msg = msg[:-8] + ('[%s]' % msg[-7:-1]) for block_id, meta in sorted(visible_blocks): if block_id != 0: display_name = blocks.get_block(block_id, meta).display_name logger.info('%3i:%-2i %s', block_id, meta, display_name) logger.info(msg)
def click_block(self, pos, look_at_block=True, swing=True, **kwargs): """ Click on a block. Examples: push button, open window, make redstone ore glow Args: face (int): side of the block on which the block is placed on cursor_pos (Vector3): where to click inside the block, each dimension 0-15 """ pos = Vector3(pos) if look_at_block and self.auto_look: # TODO look at cursor_pos self.look_at(pos.floor() + Vector3(0.5, 0.5, 0.5)) self._send_click_block(pos, **kwargs) if swing and self.auto_swing: self.swing_arm()
def check_for_bbox(self, pos): pos = pos.floor() block = self.get_block(pos) if block.bounding_box: return True block = self.get_block(pos + Vector3(0, 1, 0)) if block.bounding_box: return True return False
def _send_click_block(self, pos, face=1, cursor_pos=Vector3(8, 8, 8)): self.net.push_packet('PLAY>Player Block Placement', { 'location': pos.get_dict(), 'direction': face, 'held_item': self.inventory.active_slot.get_dict(), 'cur_pos_x': int(cursor_pos.x), 'cur_pos_y': int(cursor_pos.y), 'cur_pos_z': int(cursor_pos.z), })
def block_collision(self, cb, x = 0, y = 0, z = 0): block_id, meta = self.world.get_block(cb.x+x, cb.y+y, cb.z+z) block = mapdata.get_block(block_id, meta) if block == None: return False #possibly we want to use the centers of blocks as the starting points for bounding boxes instead of 0,0,0 #this might make thinks easier when we get to more complex shapes that are in the center of a block aka fences but more complicated for the player #uncenter the player position and bump it up a little down to prevent colliding in the floor pos1 = Vector3(self.pos.x-self.playerbb.w/2, self.pos.y-0.2, self.pos.z-self.playerbb.d/2) bb1 = self.playerbb bb2 = block.bounding_box if bb2 != None: pos2 = Vector3(cb.x+x+bb2.x, cb.y+y+bb2.y, cb.z+z+bb2.z) if ((pos1.x + bb1.w) >= (pos2.x) and (pos1.x) <= (pos2.x + bb2.w)) and \ ((pos1.y + bb1.h) >= (pos2.y) and (pos1.y) <= (pos2.y + bb2.h)) and \ ((pos1.z + bb1.d) >= (pos2.z) and (pos1.z) <= (pos2.z + bb2.d)): return True return False
def __init__(self, ploader, settings): super(PhysicsPlugin, self).__init__(ploader, settings) self.vec = Vector3(0.0, 0.0, 0.0) self.col = collision.MTVTest( self.world, BoundingBox(const.PLAYER_WIDTH, const.PLAYER_HEIGHT)) self.pos = self.clientinfo.position self.skip_tick = False self.pc = PhysicsCore(self.pos, self.vec, self.clientinfo.abilities) ploader.provides('Physics', self.pc)
def handle_update_sign(self, event, packet): location = Vector3(packet.data['location']) sign_data = smpmap.SignData(packet.data) old_data = self.world.set_block_entity_data(location, data=sign_data) self.event.emit('world_block_entity_data', { 'location': location, 'data': sign_data, 'old_data': old_data, })
def handle_update_block_entity(self, event, packet): location = Vector3(packet.data['location']) block_entity_class = smpmap.block_entities[packet.data['action']] data = block_entity_class(packet.data['nbt']) old_data = self.world.set_block_entity_data(location, data=data) self.event.emit('world_block_entity_data', { 'location': location, 'data': data, 'old_data': old_data, })
def __init__(self, ploader, settings): self.vec = Vector3(0.0, 0.0, 0.0) self.playerbb = BoundingBox(0.8, 1.8) self.world = ploader.requires('World') self.event = ploader.requires('Event') clinfo = ploader.requires('ClientInfo') self.pos = clinfo.position ploader.reg_event_handler('physics_tick', self.tick) self.pycore = NewPhysicsCore(self.vec, self.pos) ploader.provides('NewPhysics', self.pycore)
def check_node(self, node, offset, node_list, walk_fall=True, jump=True): w_node = PathNode(node + offset).set(node) if walk_fall and not self.check_for_bbox(w_node): f_node = PathNode(w_node - Vector3(0, 1, 0)).set(node, True) if not self.check_for_bbox(f_node): node_list.append(f_node) walk_bool, fall_bool = False, True else: node_list.append(w_node) walk_bool, fall_bool = True, False else: walk_bool, fall_bool = False, False j_node = PathNode(w_node + Vector3(0, 1, 0)).set(node, is_jump=True) if jump and not walk_bool and not (node.is_fall or node.is_jump) \ and not self.check_for_bbox(j_node): node_list.append(j_node) jump_bool = True else: jump_bool = False return walk_bool or fall_bool, jump_bool
def place_block(self, pos, face=1, cursor_pos=Vector3(8, 8, 8), sneak=True, look_at_block=True, swing=True): """ Place a block next to ``pos``. If the block at ``pos`` is air, place at ``pos``. """ sneaking_before = self.sneaking if sneak: self.sneak() self.click_block(pos, face, cursor_pos, look_at_block, swing) if sneak: self.sneak(sneaking_before)
def check_collision(self): cb = Vector3(math.floor(self.pos.x), math.floor(self.pos.y), math.floor(self.pos.z)) if self.block_collision(cb, y=2): #we check +2 because above my head self.vec.y = 0 if self.block_collision(cb, y=-1): #we check below feet self.pos.on_ground = True self.vec.y = 0 self.pos.y = cb.y else: self.pos.on_ground = False #self.vec.add_vector(y = -PLAYER_ENTITY_GAV) self.vec += Vector3(0, -PLAYER_ENTITY_GAV, 0) self.apply_vertical_drag() #feet or head collide with x if self.block_collision(cb, x=1) or self.block_collision(cb, x=-1) or self.block_collision(cb, y=1, x=1) or self.block_collision(cb, y=1, x=-1): self.vec.x = 0 #replace with real info in event self.event.emit("phy_collision", "x") #feet or head collide with z if self.block_collision(cb, z=1) or self.block_collision(cb, z=-1) or self.block_collision(cb, y=1, z=1) or self.block_collision(cb, y=1, z=-1): self.vec.z = 0 #replace with real info in event self.event.emit("phy_collision", "z")
def get_mtv(self): pos = self.pos + self.vec pos = collision.uncenter_position(pos, self.col.bbox) q = collections.deque((Vector3(),)) while q: current_vector = q.popleft() transform_vectors = self.col.check_collision(pos, current_vector) if not all(transform_vectors): break for vector in transform_vectors: test_vec = self.vec + current_vector + vector if test_vec.dist_sq() <= self.vec.dist_sq() + FP_MAGIC: q.append(current_vector + vector) else: logger.warn('Physics failed to generate an MTV, bailing out') self.vec.zero() return Vector3() possible_mtv = [current_vector] while q: current_vector = q.popleft() transform_vectors = self.col.check_collision(pos, current_vector) if not all(transform_vectors): possible_mtv.append(current_vector) return min(possible_mtv)
def __init__(self, ploader, settings): super(TestRoomPlugin, self).__init__(ploader, settings) self.dims = { 'min_x': MIN_X, 'min_y': MIN_Y, 'min_z': MIN_Z, 'max_x': MAX_X, 'max_y': MAX_Y, 'max_z': MAX_Z, } self.start_location = Vector3(*START_COORDS) # initialize the 'provides' to make this available to other plugins self.testroom_core = TestRoomCore(self.world, self.dims, self) ploader.provides('TestRoom', self.testroom_core) logger.info("test room plugin loaded")
def find_valid_nodes(self, node): node_list = [] pos_x, pos_jump_x = self.check_node(node, Vector3(1, 0, 0), node_list) pos_z, pos_jump_z = self.check_node(node, Vector3(0, 0, 1), node_list) neg_x, neg_jump_x = self.check_node(node, Vector3(-1, 0, 0), node_list) neg_z, neg_jump_z = self.check_node(node, Vector3(0, 0, -1), node_list) self.check_node(node, Vector3(1, 0, 1), node_list, pos_x and pos_z, pos_jump_x and pos_jump_z) self.check_node(node, Vector3(-1, 0, -1), node_list, neg_x and neg_z, neg_jump_x and neg_jump_z) self.check_node(node, Vector3(1, 0, -1), node_list, pos_x and neg_z, pos_jump_x and neg_jump_z) self.check_node(node, Vector3(-1, 0, 1), node_list, neg_x and pos_z, neg_jump_x and pos_jump_z) return node_list
def update_sensors(self): pos = self.clientinfo.position # discretize the absolute position and direction x, y, z = mvu.get_nearest_position(pos.x, pos.y, pos.z) cur_pos = Vector3(x, y, z) cur_dir = mvu.get_nearest_direction(pos.yaw) # update absolute state and current movement primitive delta_pos = mvu.get_nearest_delta_pos(self.state.pos, cur_pos) self.motion.delta_pos = motion_labels[delta_pos] delta_dir = mvu.get_nearest_delta_dir(self.state.dir, cur_dir) self.motion.delta_dir = motion_labels[delta_dir] self.state.pos = cur_pos self.state.dir = cur_dir #print("delta pos: {}, delta dir: {}".format(delta_pos, delta_dir)) mvu.log_agent_motion(self.motion) mvu.log_agent_state(self.state)
def move(self, direction, motion, jump): acc = motions[motion] # as before, we assume angles are in degrees angle = math.radians(direction) z = math.sin(angle)*acc x = math.cos(angle)*acc y = 0.0 if jump: if self.pos.on_ground: self.pos.on_ground = False y = PLAYER_JMP_ACC #self.vec.add_vector(x=x, y=y, z=z) self.vec += Vector3(x,y,z)
def periodic_event_handler(self): """Triggered every 5 seconds by a timer""" logger.info('My position: {0} pitch: {1} yaw: {2}'.format( self.clientinfo.position, self.clientinfo.position.pitch, self.clientinfo.position.yaw)) # Place a block in front of the player self.interact.place_block(self.clientinfo.position + Vector3(-1, 0, -1)) # Read a block under the player block_pos = self.clientinfo.position.floor() block_id, meta = self.world.get_block(*block_pos) block_at = blocks.get_block(block_id, meta) self.chat.chat('Found block %s at %s' % (block_at.display_name, block_pos))
def use_entity(self, entity, cursor_pos=None, action=constants.INTERACT_ENTITY): """ Uses (right-click) an entity to open its window. Setting ``cursor_pos`` sets ``action`` to "interact at". """ if self.auto_look: self.look_at(Vector3(entity)) # TODO look at cursor_pos if cursor_pos is not None: action = constants.INTERACT_ENTITY_AT packet = {'target': entity.eid, 'action': action} if action == constants.INTERACT_ENTITY_AT: packet['target_x'] = cursor_pos.x packet['target_y'] = cursor_pos.y packet['target_z'] = cursor_pos.z self.net.push_packet('PLAY>Use Entity', packet) if self.auto_swing: self.swing_arm()
def block_collision(self, pos): # This line is confusing. Basically it figures out how many block # coordinates the bounding box currently occupies. It's not clear what # "b" stands for so take your pick: Blocks, Bounds, how-Big-is-the-Box b = (pos + self.bbox).ceil() - pos.floor() for block_pos in gen_block_set(pos, *zip((0, -1, 0), b)): block = blocks.get_block(*self.world.get_block(*block_pos)) if not block.bounding_box: continue transform_vectors = [] for i, axis in enumerate(UNIT_VECTORS): axis_pen = check_axis( axis, pos[i], pos[i] + self.bbox[i], block_pos[i], block_pos[i] + block.bounding_box[i] ) if not axis_pen: break transform_vectors.append(axis_pen) else: return transform_vectors return [Vector3()]*3