import re import time import importlib import random from itertools import count from math import floor from minecraft.networking.types import BlockFace from mosfet.protocol.managers import ChunkNotLoadedException from mosfet import utils from mosfet import path from mosfet.info import blocks from mosfet.info import items from mosfet.info import mobs class GrabSuppliesStates: DEFAULT_SUPPLIES = { tuple(items.BED_IDS): (1, 1), (items.CHEST_ID,): (1, 1), tuple(items.FOOD_IDS): (1, 1), } def default_supplies(self): return dict(self.DEFAULT_SUPPLIES) def idle(self): return None def init(self): print('Started grab supplies states') self.checked_barrels = [] used_slots = self.g.game.count_inventory_slots() print('used:', used_slots, 'total:', self.g.maximum_supply_slots) if used_slots >= self.g.maximum_supply_slots: print('Inventory is too full, aborting') self.state = self.cleanup return self.checked_ground_items = set() self.checked_ground_groups = set() self.ground_stack_counts = {} self.ground_entity_id = None self.ground_position = None if self.supplies: self.state = self.check_supplies else: print('Aborting getting supplies, none specified') self.state = self.cleanup def _has_nearby_ground_items(self, item_ids, maximum_stacks): if (maximum_stacks and self.ground_stack_counts.get(item_ids, 0) >= maximum_stacks): return False p = utils.pint(self.g.pos) return any( obj.entity_id not in self.checked_ground_items and utils.phyp(p, (obj.x, obj.y, obj.z)) <= 16 for obj in self.g.world.find_objects(item_ids)) def has_nearby_ground_supply(self): return any( self._has_nearby_ground_items(item_ids, limits[1]) for item_ids, limits in self.supplies.items()) def _record_ground_pickup(self): self.checked_ground_items.add(self.ground_entity_id) self.ground_stack_counts[self.target_items] = ( self.ground_stack_counts.get(self.target_items, 0) + 1) if (self.target_maximum_stacks and self.ground_stack_counts[self.target_items] >= self.target_maximum_stacks): self.checked_ground_groups.add(self.target_items) self.g.path = [] self.ground_entity_id = None self.ground_position = None def check_supplies(self): # Search every configured item group on the ground before going to a # barrel; a missing bed must not hide a nearby chest or food drop. missing = [] for item_ids, limits in self.supplies.items(): minimum, maximum = limits print('Checking items:', item_ids) num_items = self.g.game.count_items(item_ids) print('Have:', num_items) if (item_ids not in self.checked_ground_groups and self._has_nearby_ground_items(item_ids, maximum)): print('Found matching supplies on the ground') self.target_items = item_ids self.target_minimum = minimum self.target_maximum_stacks = maximum self.state = self.find_ground_items return if num_items >= minimum: print('Have enough, skipping') continue missing.append((item_ids, minimum, maximum)) if item_ids not in self.checked_ground_groups: self.target_items = item_ids self.target_minimum = minimum self.target_maximum_stacks = maximum self.state = self.find_ground_items return if missing: self.target_items, self.target_minimum, self.target_maximum_stacks = missing[0] print('No more ground drops; checking barrels for', self.target_items) self.state = self.find_barrels return print('Aborting, dont need any supplies') self.state = self.cleanup def find_ground_items(self): stack_count = self.ground_stack_counts.get(self.target_items, 0) if self.target_maximum_stacks and stack_count >= self.target_maximum_stacks: self.checked_ground_groups.add(self.target_items) self.state = self.check_supplies return print('Looking for supplies on the ground...') p = utils.pint(self.g.pos) ground_items = self.g.world.find_objects(self.target_items) ground_items.sort(key=lambda obj: utils.phyp( p, (obj.x, obj.y, obj.z))) for obj in ground_items: if obj.entity_id in self.checked_ground_items: continue current_count = self.g.game.count_items(self.target_items) if utils.phyp(p, (obj.x, obj.y, obj.z)) > 16: continue target = utils.pint((obj.x, obj.y, obj.z)) self.ground_entity_id = obj.entity_id self.ground_position = target self.ground_required_count = max( self.target_minimum, current_count + 1) if utils.phyp(p, (obj.x, obj.y, obj.z)) <= 1.25: self.g.path = [] self.wait_time = 1.0 self.state = self.wait_for_ground_item return navpath = self.g.world.path_to_place(p, target) if not navpath: self.checked_ground_items.add(obj.entity_id) continue print('Going to ground item', obj.item_id, target) self.g.path = navpath self.state = self.going_to_ground_item return self.checked_ground_groups.add(self.target_items) self.state = self.check_supplies def going_to_ground_item(self): obj = self.g.objects.get(self.ground_entity_id) have_enough = (self.g.game.count_items(self.target_items) >= self.ground_required_count) if have_enough: print('Picked up required ground supplies') self._record_ground_pickup() self.state = self.check_supplies elif (obj is None or utils.pint(self.g.pos) == self.ground_position or utils.phyp(self.g.pos, (obj.x, obj.y, obj.z)) <= 1.25): self.wait_time = 1.0 self.state = self.wait_for_ground_item def wait_for_ground_item(self): if (self.g.game.count_items(self.target_items) >= self.ground_required_count): print('Picked up required ground supplies') self._record_ground_pickup() self.state = self.check_supplies return if self.wait_time > 0: self.wait_time -= utils.TICK return if self.ground_entity_id not in self.g.objects: # Entity removal and inventory updates can arrive in either order. self.state = self.check_supplies return print('Could not pick up ground item; trying another') self.checked_ground_items.add(self.ground_entity_id) self.g.path = [] self.ground_entity_id = None self.ground_position = None self.state = self.find_ground_items def find_barrels(self): print('Finding barrels...') w = self.g.world p = utils.pint(self.g.pos) self.barrels = w.find_blocks_indexed(p, blocks.BARREL_IDS, 80) print('Found:', self.barrels) self.state = self.choose_barrel def choose_barrel(self): print('Choosing a barrel...') for barrel in self.barrels: if barrel in self.checked_barrels: continue if barrel in self.bad_barrels: continue print('Chose:', barrel) self.barrel = barrel self.state = self.path_to_barrel return print('No barrels') self.state = self.cleanup def path_to_barrel(self): print('Finding path to barrel') w = self.g.world p = utils.pint(self.g.pos) c = self.g.chunks navpath = w.path_to_place_faked(p, self.barrel) print('navpath:', navpath) if navpath: self.g.path = navpath[:-1] self.opening = self.g.path[-1] self.checked_barrels.append(self.barrel) self.area = self.barrel self.state = self.going_to_barrel self.checked_supplies = [] return else: print('No path, blacklisting barrel') time.sleep(0.1) self.bad_barrels.append(self.barrel) if len(self.bad_barrels) > 3: self.state = self.cleanup else: self.state = self.choose_barrel def going_to_barrel(self): if utils.pint(self.g.pos) == self.opening: self.g.look_at = self.area self.state = self.open_barrel def open_barrel(self): print('Opening barrel') self.g.game.open_container(self.area) self.wait_time = 1 self.state = self.wait def wait(self): # wait for server to send us inventory contents if self.wait_time > 0: self.wait_time -= utils.TICK else: self.state = self.choose_items def choose_items(self): print('Selecting next item') for items, limits in self.supplies.items(): minimum_items, maximum_stacks = limits print('Checking items:', items) num_items = self.g.game.count_items(items) print('Have:', num_items) if num_items >= minimum_items: print('Have enough, skipping') continue if items in self.checked_supplies: print('Already checked, skipping') continue print('Need some') self.checked_supplies.append(items) self.target_items = items self.maximum_stacks = maximum_stacks self.count = 0 self.state = self.grab_items return print('Aborting, dont need any more supplies') self.state = self.close_barrel def grab_items(self): if self.g.item_lock: return w = self.g.window if not w: print('Didnt get a window, aborting') self.state = self.cleanup return if w.get('container_slots', 0) != 27: print('Got wrong container size, aborting') self.state = self.cleanup return used_slots = self.g.game.count_inventory_slots() print('used:', used_slots, 'total:', self.g.maximum_supply_slots) if used_slots >= self.g.maximum_supply_slots: print('Inventory is too full, aborting') self.g.game.close_window() self.g.look_at = None self.state = self.cleanup return for slot_num in range(w.container_slots): if slot_num not in w.contents: continue slot = w.contents[slot_num] if not slot.present: continue if slot.item_id not in self.target_items: continue if self.maximum_stacks and self.count >= self.maximum_stacks: break self.count += 1 print('Moving', slot) self.g.item_lock = True self.g.game.click_window(slot_num, 0, 1, slot) return print('None left to move') self.wait_time = 0.25 self.state = self.wait def close_barrel(self): print('Closing barrel') self.g.game.close_window() self.g.look_at = None self.state = self.choose_barrel def cleanup(self): self.state = self.done def done(self): # never gets ran, placeholder return None def __init__(self, global_state): self.g = global_state self.state = self.idle self.supplies = self.default_supplies() self.barrels = [] self.checked_barrels = [] self.bad_barrels = [] self.barrel = None self.checked_supplies = [] self.target_items = None self.maximum_stacks = 0 self.count = 0 self.area = None self.opening = None self.wait_time = 0 def run(self): self.state()