diff --git a/bot.py b/bot.py index 37a7116..1cb84db 100644 --- a/bot.py +++ b/bot.py @@ -34,8 +34,8 @@ import utils importlib.reload(utils) import path importlib.reload(path) -import jobs -importlib.reload(jobs) +import job +importlib.reload(job) import mcdata importlib.reload(mcdata) @@ -219,7 +219,7 @@ def init(global_state): g.trades = [] - g.job = jobs.JobStates(g) + g.job = job.JobStates(g) g.chopped_tree = False g.afk_timeout = 0 diff --git a/job.py b/job.py new file mode 100644 index 0000000..817a901 --- /dev/null +++ b/job.py @@ -0,0 +1,266 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +from jobs import ( + cache_items, + check_threats, + clear_leaves, + eat_food, + fill_blocks, + find_gapple, + gather_crop, + gather_sand, + gather_wart, + gather_wood, + grab_sand, + grab_sapling, + grab_supplies, + plant_tree, + sell_to_villager, + sleep_with_bed, +) +for module in [ + cache_items, + check_threats, + clear_leaves, + eat_food, + fill_blocks, + find_gapple, + gather_crop, + gather_sand, + gather_wart, + gather_wood, + grab_sand, + grab_sapling, + grab_supplies, + plant_tree, + sell_to_villager, + sleep_with_bed, +]: + importlib.reload(module) + + +class JobStates: + def idle(self): + return [] + + def init_machines(self): + self.gather_wood_states = gather_wood.GatherWoodStates(self.g) + self.gather_sand_states = gather_sand.GatherSandStates(self.g) + self.sleep_with_bed_states = sleep_with_bed.SleepWithBedStates(self.g) + self.cache_items_states = cache_items.CacheItemsStates(self.g) + self.grab_supplies_states = grab_supplies.GrabSuppliesStates(self.g) + self.find_gapple_states = find_gapple.FindGappleStates(self.g) + self.plant_tree_states = plant_tree.PlantTreeStates(self.g) + self.clear_leaves_states = clear_leaves.ClearLeavesStates(self.g) + self.grab_sapling_states = grab_sapling.GrabSaplingStates(self.g) + self.grab_sand_states = grab_sand.GrabSandStates(self.g) + self.fill_blocks_states = fill_blocks.FillBlocksStates(self.g) + self.check_threats_states = check_threats.CheckThreatsStates(self.g) + self.gather_wart_states = gather_wart.GatherWartStates(self.g) + self.gather_crop_states = gather_crop.GatherCropStates(self.g) + self.eat_food_states = eat_food.EatFoodStates(self.g) + self.sell_to_villager_states = sell_to_villager.SellToVillagerStates(self.g) + + def run_machines(self, machines): + for m in machines: + if m.state == m.idle: + continue + if m.state != m.done: + m.run() + return + # if we went through them all + for m in machines: + m.state = m.init + + def gather_sand(self): + machines = [ + self.gather_sand_states, + self.grab_sand_states, + self.cache_items_states, + self.sleep_with_bed_states, + self.eat_food_states, + ] + return machines + + def farm_sand(self): + machines = [ + self.grab_supplies_states, + self.check_threats_states, + self.gather_sand_states, + self.grab_sand_states, + self.cache_items_states, + self.sleep_with_bed_states, + self.eat_food_states, + ] + self.sleep_with_bed_states.silent = True + self.cache_items_states.silent = True + self.grab_supplies_states.supplies = { + tuple(items.SHOVEL_IDS): (1, 9), + } + + items.set_needed(items.SHOVEL_IDS) + return machines + + def cache_items(self): + machines = [ + self.cache_items_states, + ] + return machines + + + def find_gapple(self): + machines = [ + self.find_gapple_states, + ] + return machines + + def gather_wood(self): + machines = [ + self.gather_wood_states, + self.sleep_with_bed_states, + self.eat_food_states, + self.cache_items_states, + ] + return machines + + def farm_wood(self): + machines = [ + self.grab_supplies_states, + self.gather_wood_states, + self.clear_leaves_states, + self.plant_tree_states, + self.grab_sapling_states, + self.sleep_with_bed_states, + self.eat_food_states, + self.cache_items_states, + ] + self.sleep_with_bed_states.silent = True + self.cache_items_states.silent = True + self.grab_supplies_states.supplies = { + tuple(items.AXE_IDS): (1, 9), + } + + items.set_needed(items.AXE_IDS) + items.set_wanted(items.SAPLING_IDS) + return machines + + def farm_wart(self): + machines = [ + self.gather_wart_states, + self.sleep_with_bed_states, + self.eat_food_states, + self.cache_items_states, + ] + self.sleep_with_bed_states.silent = True + self.cache_items_states.silent = True + + items.set_wanted(set([items.NETHERWART_ID])) + return machines + + def farm_crop(self): + machines = [ + self.gather_crop_states, + self.sleep_with_bed_states, + self.eat_food_states, + self.cache_items_states, + ] + self.sleep_with_bed_states.silent = True + self.cache_items_states.silent = True + + items.set_wanted(set([ + items.CARROT_ID, + items.POTATO_ID, + items.WHEAT_SEEDS_ID, + items.BEETROOT_SEEDS_ID, + ])) + return machines + + def fill_blocks(self): + machines = [ + self.grab_supplies_states, + self.fill_blocks_states, + self.sleep_with_bed_states, + self.eat_food_states, + ] + self.sleep_with_bed_states.silent = True + + f = self.g.filling + if f: + name = blocks.BLOCKS[f.block] + item = items.ITEMS['minecraft:'+name]['protocol_id'] + + self.grab_supplies_states.supplies = { + tuple([item]): (1, 0), + } + return machines + + def loiter(self): + machines = [ + self.check_threats_states, + self.sleep_with_bed_states, + self.eat_food_states, + ] + self.sleep_with_bed_states.silent = True + return machines + + def trade(self): + machines = [ + self.grab_supplies_states, + self.sell_to_villager_states, + self.sleep_with_bed_states, + self.eat_food_states, + self.cache_items_states, + ] + self.sleep_with_bed_states.silent = True + self.cache_items_states.silent = True + self.grab_supplies_states.supplies = { + tuple([items.PUMPKIN_ID]): (64, 3), + tuple([items.BERRIES_ID]): (64, 3), + tuple([items.IRON_INGOT_ID]): (64, 3), + tuple([items.WHEAT_ID]): (64, 3), + tuple([items.POTATO_ID]): (64, 3), + } + + items.set_needed(set([ + items.PUMPKIN_ID, + items.BERRIES_ID, + items.IRON_INGOT_ID, + items.WHEAT_ID, + items.POTATO_ID, + ])) + return machines + + def stop(self): + self.init_machines() + self.state = self.idle + + def __init__(self, global_state): + self.g = global_state + + self.init_machines() + self.state = self.idle + + def tick(self): + self.run_machines(self.state()) diff --git a/jobs.py b/jobs.py deleted file mode 100644 index 9d00e86..0000000 --- a/jobs.py +++ /dev/null @@ -1,2328 +0,0 @@ -import re -import time -import importlib -import random -from itertools import count -from math import hypot, floor - -from minecraft.networking.types import BlockFace - -from protocol.managers import ChunkNotLoadedException - -import utils -importlib.reload(utils) -import path -importlib.reload(path) -import blocks -importlib.reload(blocks) -import items -importlib.reload(items) -import mcdata -importlib.reload(mcdata) -import mobs -importlib.reload(mobs) - - -class FindGappleStates: - def idle(self): - return None - - def init(self): - self.state = self.go_spectator - - def go_spectator(self): - print('Going spectator...') - self.g.chat.send('/gamemode spectator') - - self.state = self.tp_to_coord - - def tp_to_coord(self): - step = utils.spiral(self.count) - step_scaled = utils.pmul(step, 192) - self.coord = utils.padd(self.origin, step_scaled) - self.coord = (self.coord[0], 50, self.coord[2]) - - print('count:', self.count, 'teleporting to:', self.coord) - self.g.chat.send('/tp {} {} {}'.format(*self.coord)) - - self.g.command_lock = True - self.state = self.wait_for_load - - def wait_for_load(self): - if self.g.command_lock: - return - - if self.g.chunks.check_loaded(self.g.pos, 169): - print('chunks have been loaded') - self.state = self.pick_chest - - def pick_chest(self): - chest_list = [] - for chest_id in blocks.CHEST_IDS: - chest_list.extend(self.g.chunks.index.get(chest_id, [])) - - for chest in chest_list: - if chest in self.checked_chests: - # slow but simple - continue - - if utils.phyp_king(self.coord, chest) > 96: - # skip because we can't detect item drops - continue - - self.current_chest = chest - self.checked_chests.append(self.current_chest) - self.state = self.break_chest - break - else: # for - print('exhausted chest list') - self.state = self.cleanup - - def break_chest(self): - print('Breaking chest', self.current_chest) - self.g.command_lock = True - self.g.item_lock = True - self.g.chat.send('/setblock {} {} {} air destroy'.format(*self.current_chest)) - - self.wait_time = 0.5 - self.state = self.wait_for_items - - def wait_for_items(self): - # wait for command to execute - if self.g.command_lock: - return - - # wait for items to drop - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - print('done waiting for items') - self.state = self.pick_chest - - def cleanup(self): - self.count += 1 - 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.origin = utils.pint(self.g.pos) - self.count = 0 - self.coord = None - self.current_chest = None - self.checked_chests = [] - self.wait_time = 0 - - def run(self): - self.state() - - -class GatherCropStates: - def idle(self): - return None - - def init(self): - self.state = self.find_new_crop - - def find_new_crop(self): - print('Finding new crop...') - w = self.g.world - p = utils.pint(self.g.pos) - - mature_crops = [ - blocks.MATURE_WHEAT_ID, - blocks.MATURE_POTATO_ID, - blocks.MATURE_CARROT_ID, - blocks.MATURE_BEETROOT_ID, - ] - - for crop in w.find_blocks_3d(p, mature_crops, 50, 20): - print('Found crop:', crop) - if crop not in self.bad_crops: - break - else: # for - print('No good crops left, aborting.') - self.state = self.cleanup - return - - self.crop = crop - self.type_id = w.block_at(*crop) - self.state = self.nav_to_crop - - def nav_to_crop(self): - w = self.g.world - p = utils.pint(self.g.pos) - - navpath = w.path_to_place(p, self.crop) - - if navpath: - self.g.path = navpath - self.g.look_at = utils.padd(self.crop, path.BLOCK_BELOW) - self.state = self.going_to_crop - else: - self.bad_crops.append(self.crop) - self.wait_time = 0.5 - self.state = self.wait_to_restart - - def wait_to_restart(self): - # prevent timeouts - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.find_new_crop - - def going_to_crop(self): - if utils.pint(self.g.pos) == self.crop: - print('At the crop') - self.state = self.break_crop - - def break_crop(self): - self.g.game.break_block(self.crop) - self.wait_time = 0.5 - self.state = self.wait - - def wait(self): - # wait for the item - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.select_seed - - def select_seed(self): - p = utils.pint(self.g.pos) - - crop_seeds = { - blocks.MATURE_WHEAT_ID: items.WHEAT_SEEDS_ID, - blocks.MATURE_POTATO_ID: items.POTATO_ID, - blocks.MATURE_CARROT_ID: items.CARROT_ID, - blocks.MATURE_BEETROOT_ID: items.BEETROOT_SEEDS_ID, - } - - if self.g.game.select_item([crop_seeds[self.type_id]]): - self.state = self.wait_select - self.wait_time = 0.5 - else: - print('Aborting planting, no crop') - self.state = self.cleanup - - def wait_select(self): - # wait a bit to select - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.place_crop - - def place_crop(self): - p = utils.pint(self.g.pos) - self.g.game.place_block(p, BlockFace.TOP) - print('Placed crop') - self.state = self.wait_place - self.wait_time = 0.5 - - def wait_place(self): - # wait a bit for chunk data to update - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.cleanup - - def cleanup(self): - self.g.look_at = None - 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.crop = None - self.type_id = None - self.bad_crops = [] - self.wait_time = 0 - - def run(self): - self.state() - - -class GatherWartStates: - def idle(self): - return None - - def init(self): - self.state = self.find_new_wart - - def find_new_wart(self): - print('Finding new wart...') - w = self.g.world - p = utils.pint(self.g.pos) - - mature_wart = max(blocks.NETHERWART_IDS) - for wart in w.find_blocks_3d(p, [mature_wart], 50, 20): - print('Found wart:', wart) - if wart not in self.bad_warts: - break - else: # for - print('No good warts left, aborting.') - self.state = self.cleanup - return - - self.wart = wart - self.state = self.nav_to_wart - - def nav_to_wart(self): - w = self.g.world - p = utils.pint(self.g.pos) - - navpath = w.path_to_place(p, self.wart) - - if navpath: - self.g.path = navpath - self.g.look_at = utils.padd(self.wart, path.BLOCK_BELOW) - self.state = self.going_to_wart - else: - self.bad_warts.append(wart) - self.state = self.find_new_wart - - def going_to_wart(self): - if utils.pint(self.g.pos) == self.wart: - print('At the wart') - self.state = self.break_wart - - def break_wart(self): - self.g.game.break_block(self.wart) - self.wait_time = 0.5 - self.state = self.wait - - def wait(self): - # wait for the item - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.select_wart - - def select_wart(self): - p = utils.pint(self.g.pos) - - if self.g.game.select_item([items.NETHERWART_ID]): - self.state = self.wait_select - self.wait_time = 0.5 - else: - print('Aborting planting, no wart') - self.state = self.cleanup - - def wait_select(self): - # wait a bit to select - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.place_wart - - def place_wart(self): - p = utils.pint(self.g.pos) - self.g.game.place_block(p, BlockFace.TOP) - print('Placed wart') - self.state = self.wait_place - self.wait_time = 0.5 - - def wait_place(self): - # wait a bit for chunk data to update - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.cleanup - - def cleanup(self): - self.g.look_at = None - 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.wart = None - self.bad_warts = [] - self.wait_time = 0 - - def run(self): - self.state() - -class GatherWoodStates: - def bair(self, p): - return self.g.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS - - def blog(self, p): - return self.g.chunks.get_block_at(*p) in blocks.LOG_IDS - - def idle(self): - return None - - def init(self): - self.g.chopped_tree = False - self.state = self.select_axe - - def select_axe(self): - self.g.game.select_item(items.AXE_IDS) - self.state = self.find_new_tree - - def find_new_tree(self): - print('Finding new tree...') - w = self.g.world - p = utils.pint(self.g.pos) - - for tree in w.find_trees(p, 100): - print('Found tree:', tree) - if tree not in self.bad_trees: - break - else: # for - print('No good trees left, aborting.') - self.state = self.cleanup - return - - self.tree = tree - self.type = blocks.BLOCKS[w.block_at(*tree)].replace('_log', '') - print('Type:', self.type) - - self.state = self.find_openings - - def find_openings(self): - w = self.g.world - self.openings = w.find_tree_openings(self.tree) - self.state = self.choose_opening - - def choose_opening(self): - w = self.g.world - p = utils.pint(self.g.pos) - - print('openings:', self.openings) - - if not len(self.openings): - print('Unable to get to tree', self.tree) - if self.tree not in self.good_trees: - self.bad_trees.append(self.tree) - print('Added to bad trees list') - self.state = self.cleanup - return - - navpath = w.path_to_place(p, self.openings[0]) - - if navpath: - self.g.path = navpath - self.state = self.going_to_tree - else: - self.openings.pop(0) - - def going_to_tree(self): - if utils.pint(self.g.pos) == self.openings[0]: - self.g.look_at = self.tree - self.state = self.clear_leaves - - def clear_leaves(self): - if not self.g.breaking: - p = utils.pint(self.g.pos) - diff = utils.psub(self.tree, p) - - for x in utils.diffrange(diff[0]): - for z in utils.diffrange(diff[2]): - for y in range(2): - check = utils.padd(p, (x, y, z)) - if check == self.tree: - break - if not self.bair(check): - print('Breaking leaf') - self.g.game.break_block(check) - return - - self.state = self.clear_trunk_base - - def clear_trunk_base(self): - if not self.g.breaking: - base = self.tree - above = utils.padd(self.tree, path.BLOCK_ABOVE) - - if self.blog(base): - self.g.game.break_block(base) - print('breaking base') - elif self.blog(above): - self.g.game.break_block(above) - print('breaking above') - else: - w = self.g.world - p = utils.pint(self.g.pos) - navpath = w.path_to_place(p, self.tree) - - if navpath: - self.g.path = navpath - self.state = self.going_to_trunk_base - else: - self.openings.pop(0) - self.state = self.choose_opening - - def going_to_trunk_base(self): - if utils.pint(self.g.pos) == self.tree: - self.g.look_at = utils.padd(self.tree, path.BLOCK_ABOVE2) - self.state = self.clear_trunk - - def clear_trunk(self): - if not self.g.breaking: - check = self.tree - - count = 0 - while self.bair(check) and count < 6: - check = utils.padd(check, path.BLOCK_ABOVE) - count += 1 - - if self.blog(check): - print('breaking log', check) - self.g.game.break_block(check) - else: - print('Finished clearing tree') - self.wait_time = 0.5 - self.state = self.wait - - def wait(self): - # wait for the last log to fall - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.g.chopped_tree = self.type - self.good_trees.append(self.tree) - self.state = self.check_pos - - def check_pos(self): - # make sure we are at base of trunk - # doesn't always happen, for some reason - if utils.pint(self.g.pos) == self.tree: - self.state = self.cleanup - else: - self.state = self.find_openings - - def cleanup(self): - self.g.look_at = None - 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.tree = None - self.type = None - self.openings = [] - self.bad_trees = [] - self.good_trees = [] - self.wait_time = 0 - - def run(self): - self.state() - - -class GatherSandStates: - def bair(self, p): - return self.g.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS - - def bsand(self, p): - return self.g.chunks.get_block_at(*p) == blocks.SAND - - def idle(self): - return None - - def init(self): - self.state = self.select_shovel - - def select_shovel(self): - self.g.game.select_item(items.SHOVEL_IDS) - self.state = self.find_new_slice - - def find_new_slice(self): - print('Finding new slice...') - w = self.g.world - - print('using origin', self.origin) - start = time.time() - self.prev_layer, s = w.find_sand_slice(self.origin, 200, 10, self.bad_slices, self.prev_layer) - print('Found slice:', s, 'in', time.time() - start, 'seconds') - - if s: - self.slice = s - self.bad_slices.append(s) - self.state = self.find_new_sand - else: - print('No slices remaining.') - self.state = self.cleanup - - def find_new_sand(self): - print('Finding new sand...') - w = self.g.world - p = utils.pint(self.g.pos) - head = utils.padd(p, path.BLOCK_ABOVE) - - for sand in w.find_sand(self.slice, 2, p): - if sand not in self.bad_sand: - print('Found sand:', sand) - break - else: # for - print('No good sands left, aborting.') - self.state = self.cleanup - return - - self.sand = sand - - if utils.phyp(head, self.sand) < blocks.BREAK_DISTANCE: - self.state = self.dig_sand - else: - self.state = self.nav_to_sand - - def nav_to_sand(self): - w = self.g.world - p = utils.pint(self.g.pos) - c = self.g.chunks - - tmp = c.get_block_at(*self.sand) - c.set_block_at(*self.sand, blocks.AIR) - navpath = w.path_to_place(p, self.sand) - c.set_block_at(*self.sand, tmp) - - if navpath: - self.g.path = navpath[:-1] - self.state = self.going_to_sand - else: - print('Cant get to that sand') - self.bad_sand.append(self.sand) - self.state = self.find_new_sand - - def going_to_sand(self): - if not len(self.g.path): - self.g.look_at = self.sand - self.state = self.dig_sand - - def dig_sand(self): - if not self.g.breaking: - if self.bsand(self.sand): - self.g.look_at = self.sand - self.g.game.break_block(self.sand) - print('digging sand') - else: - self.state = self.find_new_sand - - def cleanup(self): - self.g.look_at = None - 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.origin = utils.pint(self.g.pos) - self.origin = (2019, 64, 238) - self.slice = None - self.bad_slices = [] - self.prev_layer = 0 - self.sand = None - self.bad_sand = [] - self.wait_time = 0 - - def run(self): - self.state() - - -class GrabSandStates: - def idle(self): - return None - - def init(self): - self.state = self.find_sand - print('Trying to grab sand') - - def find_sand(self): - w = self.g.world - p = utils.pint(self.g.pos) - - sand = w.find_objects([items.SAND_ID]) - - if not sand: - print('No sand objects found, aborting') - self.state = self.cleanup - return - - sand.sort(key=lambda s: utils.phyp(p, (s.x, s.y, s.z))) - - for s in sand: - s_pos = utils.pint((s.x, s.y, s.z)) - check = utils.padd(s_pos, path.BLOCK_BELOW) - - if utils.phyp(p, s_pos) > 6: - continue - # skip if the sand is floating - if self.g.chunks.get_block_at(*check) in {0}: - continue - if s.entity_id in self.eid_blacklist: - continue - - self.eid_blacklist.append(s.entity_id) - - navpath = w.path_to_place(p, s_pos) - - if navpath: - self.g.path = navpath - self.state = self.going_to_sand - self.sand = s_pos - print('Going to sand', self.sand) - return - else: - print('Cant get to sand', self.sand) - - print('Cant get to any more sand, aborting') - self.state = self.cleanup - - def going_to_sand(self): - if utils.pint(self.g.pos) == self.sand: - self.state = self.cleanup - - def cleanup(self): - self.g.look_at = None - 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.sand = None - self.eid_blacklist = [] - - def run(self): - self.state() - - -class SleepWithBedStates: - def idle(self): - return None - - def init(self): - if self.g.time < 12000: - print('Aborting sleep, not night') - self.state = self.cleanup - return - - if self.g.dimension != 'overworld': - print('Aborting sleep, not in overworld') - self.state = self.cleanup - return - - self.state = self.find_beds - - def find_beds(self): - print('Finding beds...') - w = self.g.world - p = utils.pint(self.g.pos) - - result = w.find_blocks_indexed(p, blocks.BED_IDS) - - self.beds = [] - for bed in result: - if bed not in self.bad_beds: - self.beds.append(bed) - - print('Found:', self.beds) - self.state = self.choose_bed - - def choose_bed(self): - print('Choosing a bed...') - w = self.g.world - p = utils.pint(self.g.pos) - c = self.g.chunks - - if not len(self.beds): - print('No beds') - self.state = self.select_bed - return - - bed = self.beds[0] - print('Chose:', bed) - - tmp = c.get_block_at(*bed) - c.set_block_at(*bed, blocks.AIR) - navpath = w.path_to_place(p, bed) - c.set_block_at(*bed, tmp) - - print('navpath:', navpath) - - if navpath: - self.g.path = navpath[:-1] - self.opening = self.g.path[-1] - self.g.look_at = self.opening - self.area = bed - self.state = self.going_to_bed - return - else: - self.beds.pop(0) - self.bad_beds.append(bed) - print('Cant get to bed, blacklisting') - self.state = self.select_bed - - def going_to_bed(self): - if utils.pint(self.g.pos) == self.opening: - self.my_bed = False - self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW) - self.state = self.use_bed - - def select_bed(self): - if self.g.game.select_item(items.BED_IDS): - self.state = self.find_bed_spot - else: - print('No bed, aborting.') - self.state = self.cleanup - - def find_bed_spot(self): - print('Finding a bed spot...') - w = self.g.world - p = utils.pint(self.g.pos) - - for area in w.find_bed_areas(p, 100): - print('Found area:', area) - if area not in self.bad_areas: - break - else: # for - print('Unable to find area') - self.state = self.cleanup - return - - self.area = area - openings = w.find_bed_openings(self.area) - - for o in openings: - navpath = w.path_to_place(p, o) - self.opening = o - if navpath: break - else: # for - print('Unable to get to bed area', self.area) - self.bad_areas.append(self.area) - self.state = self.cleanup - return - - self.g.path = navpath - self.g.look_at = self.opening - self.state = self.going_to_area - - def going_to_area(self): - if utils.pint(self.g.pos) == self.opening: - self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW) - self.state = self.place_bed - - def place_bed(self): - self.g.game.place_block(self.area, BlockFace.TOP) - self.my_bed = True - self.state = self.use_bed - - def use_bed(self): - if self.g.time > 12550: - print('Sleeping') - self.g.game.place_block(self.area, BlockFace.TOP) - if not self.silent: - self.g.chat.send('zzz') - self.state = self.sleep_bed - - def sleep_bed(self): - w = self.g.world - p = utils.pint(self.g.pos) - - threats = w.find_threats(p, 30) - if threats: - print('Waking up due to threats:') - print(threats) - self.g.game.leave_bed() - self.state = self.break_bed - elif self.g.time < 100: - print('Woke up time') - self.state = self.break_bed - - def break_bed(self): - if self.my_bed: - self.g.game.break_block(self.area) - self.state = self.collect_bed - else: - self.state = self.cleanup - - - def collect_bed(self): - if not self.g.breaking: - self.g.path = [utils.padd(self.area, utils.spiral(n)) for n in range(9)] - self.wait_time = 2 - self.state = self.wait - - def wait(self): - # wait to pick up bed - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.cleanup - - def cleanup(self): - self.g.look_at = None - 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.silent = False - - self.my_bed = False - self.beds = [] - self.bad_beds = [] - self.area = None - self.opening = None - self.bad_areas = [] - self.wait_time = 0 - - def run(self): - self.state() - - -class CacheItemsStates: - def idle(self): - return None - - def init(self): - self.skip_slots = [] - self.skip_items = [] - - num_stacks = self.g.game.count_inventory_slots() - print('Inventory amount:', num_stacks) - if num_stacks >= self.g.minimum_cache_slots: - self.state = self.find_trapped_chests - else: - print('Aborting caching, not full') - self.state = self.cleanup - - def find_trapped_chests(self): - print('Finding trapped chests...') - w = self.g.world - p = utils.pint(self.g.pos) - - self.trapped_chests = w.find_blocks_indexed(p, blocks.TRAPPED_CHEST_IDS) - print('Found:', self.trapped_chests) - self.state = self.choose_trapped_chest - - def choose_trapped_chest(self): - print('Choosing a trapped chest...') - w = self.g.world - p = utils.pint(self.g.pos) - c = self.g.chunks - - if not len(self.trapped_chests): - print('No trapped chests') - self.state = self.select_chest - return - - chest = self.trapped_chests[0] - - tmp = c.get_block_at(*chest) - c.set_block_at(*chest, blocks.AIR) - navpath = w.path_to_place(p, chest) - c.set_block_at(*chest, tmp) - - print('navpath:', navpath) - - if navpath: - self.g.path = navpath[:-1] - self.opening = self.g.path[-1] - self.area = chest - self.state = self.going_to_trapped_chest - return - else: - self.trapped_chests.pop(0) - - def going_to_trapped_chest(self): - if utils.pint(self.g.pos) == self.opening: - self.g.look_at = self.area - self.state = self.open_chest - - def select_chest(self): - if self.g.game.select_item([items.CHEST_ID]): - self.state = self.find_cache_spot - else: - print('No chest, aborting') - self.state = self.cleanup - - def find_cache_spot(self): - print('Finding a chest spot...') - w = self.g.world - p = utils.pint(self.g.pos) - - for area in w.find_cache_areas(p, 100): - print('Found area:', area) - if area not in self.bad_areas: - break - else: # for - print('Unable to find area') - self.state = self.cleanup - return - - self.area = area - openings = w.find_cache_openings(self.area) - - for o in openings: - navpath = w.path_to_place(p, o) - self.opening = o - if navpath: break - else: # for - print('Unable to get to cache area', self.area) - self.bad_areas.append(self.area) - self.state = self.cleanup - return - - self.g.path = navpath - self.state = self.going_to_area - - def going_to_area(self): - if utils.pint(self.g.pos) == self.opening: - self.g.look_at = self.area - self.state = self.place_chest - - def place_chest(self): - self.g.game.place_block(self.area, BlockFace.TOP) - self.state = self.open_chest - - def open_chest(self): - print('Opening chest') - self.g.game.open_container(self.area) - self.wait_time = 1 - self.state = self.wait - - def wait(self): - # wait for server to send us chest contents - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.move_items - - def move_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.data.window_type != mcdata.SINGLE_CHEST: - print('Got wrong window, aborting') - self.state = self.cleanup - return - - w_info = mcdata.WINDOWS[w.data.window_type] - w_inventory_slots = w_info.inventory - w_container_slots = w_info.container - - used_slots = self.g.game.count_window_slots() - print('used:', used_slots, 'total:', len(w_container_slots)) - if used_slots >= len(w_container_slots): - print('Container is too full, aborting') - self.g.game.close_window() - self.g.look_at = None - self.state = self.cleanup - return - - slot_list = [] - - for slot_num in w_inventory_slots: - if slot_num not in w.contents: - continue - - slot = w.contents[slot_num] - - if not slot.present: - continue - - if slot.item_id in items.NEEDED_ITEMS: - continue - - if slot_num in self.skip_slots: - continue - - slot_list.append((slot_num, slot)) - - slot_list.sort(key=lambda x: x[1].item_count, reverse=True) - - for slot_num, slot in slot_list: - if slot.item_id in items.WANTED_ITEMS and slot.item_id not in self.skip_items: - print('skipping wanted item', slot) - self.skip_slots.append(slot_num) - self.skip_items.append(slot.item_id) - continue - - print('moving', slot) - - self.g.item_lock = True - self.g.game.click_window(slot_num, 0, 1, slot) - return - - print('nothing left to move') - self.state = self.close_chest - - def close_chest(self): - print('closing chest') - self.g.game.close_window() - if not self.silent: - self.g.chat.send('cache at ' + str(self.area)[1:-1]) - self.state = self.cleanup - - def cleanup(self): - self.g.look_at = None - 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.silent = False - - self.skip_slots = [] - self.skip_items = [] - - self.area = None - self.opening = None - self.trapped_chests = [] - self.bad_areas = [] - self.wait_time = 0 - - def run(self): - self.state() - - - -class GrabSuppliesStates: - 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 - - if self.supplies: - self.state = self.check_supplies - else: - print('Aborting getting supplies, none specified') - self.state = self.cleanup - - def check_supplies(self): - # check if we need to grab anything - for items, limits in self.supplies.items(): - minimum, maximum = limits - print('Checking items:', items) - num_items = self.g.game.count_items(items) - print('Have:', num_items) - - if num_items >= minimum: - print('Have enough, skipping') - continue - - print('Need at least one item') - self.state = self.find_barrels - return - - print('Aborting, dont need any supplies') - self.state = self.cleanup - - 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) - 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 - - 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 - - barrel = self.barrel - - tmp = c.get_block_at(*barrel) - c.set_block_at(*barrel, blocks.AIR) - navpath = w.path_to_place(p, barrel) - c.set_block_at(*barrel, tmp) - - print('navpath:', navpath) - - if navpath: - self.g.path = navpath[:-1] - self.opening = self.g.path[-1] - self.checked_barrels.append(barrel) - self.area = barrel - self.state = self.going_to_barrel - self.checked_supplies = [] - return - else: - print('No path, blacklisting barrel') - self.bad_barrels.append(barrel) - 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.data.window_type != mcdata.SINGLE_CHEST: - print('Got wrong window, 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 - - w_info = mcdata.WINDOWS[w.data.window_type] - w_container_slots = w_info.container - - for slot_num in 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.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() - - -class PlantTreeStates: - def idle(self): - return None - - def init(self): - if self.g.chopped_tree: - self.state = self.check_feet - else: - print('Aborting planting, did not plant') - self.state = self.cleanup - - def check_feet(self): - p = utils.pint(self.g.pos) - - # check for air at feet - if self.g.chunks.get_block_at(*p) in [0]: - self.state = self.select_sapling - else: - print('Aborting planting, feet not air') - self.state = self.cleanup - - def select_sapling(self): - p = utils.pint(self.g.pos) - sapling_type = self.g.chopped_tree + '_sapling' - sapling_item = items.get_id(sapling_type) - - if self.g.game.select_item([sapling_item]): - self.g.look_at = utils.padd(p, path.BLOCK_BELOW) - self.state = self.wait_select - self.wait_time = 1 - else: - print('Aborting planting, no', sapling_type) - self.state = self.cleanup - - def wait_select(self): - # wait a bit to look down - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.place_sapling - - def place_sapling(self): - p = utils.pint(self.g.pos) - self.g.game.place_block(p, BlockFace.TOP) - print('Placed sapling') - self.state = self.wait_place - self.wait_time = 1 - - def wait_place(self): - # wait a bit for chunk data to update - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.find_open_spot - - def find_open_spot(self): - w = self.g.world - p = utils.pint(self.g.pos) - - for opening in w.find_tree_openings(p)[::-1]: - print('trying sapling opening', opening) - navpath = w.path_to_place(p, opening) - if navpath: - self.g.path = navpath - self.area = opening - self.state = self.going_to_area - return - else: # for - print('cant escape sapling') - self.state = self.cleanup - - def going_to_area(self): - if utils.pint(self.g.pos) == self.area: - self.state = self.cleanup - - def cleanup(self): - self.g.look_at = None - 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.wait_time = 0 - self.area = None - - def run(self): - self.state() - - -class ClearLeavesStates: - def idle(self): - return None - - def init(self): - if not self.g.chopped_tree: - print('Didnt chop tree, clearing leaves') - self.state = self.cleanup - return - - sapling_type = self.g.chopped_tree + '_sapling' - sapling_item = items.get_id(sapling_type) - num_saplings = self.g.game.count_items([sapling_item]) - print('Have', num_saplings, sapling_type, 'in inventory') - - if num_saplings > 8: - print('Have enough saplings, aborting clearing leaves') - self.state = self.cleanup - return - - self.state = self.select_log - print('Clearing leaves...') - - def select_log(self): - # select a log to avoid using tools - self.g.game.select_item(items.LOG_IDS) - self.state = self.find_leaves - - def find_leaves(self): - w = self.g.world - p = utils.pint(self.g.pos) - pos = utils.padd(p, path.BLOCK_ABOVE) - - for l in w.find_leaves(pos, blocks.BREAK_DISTANCE): - self.leaves.append(l) - - self.state = self.break_leaves - - def break_leaves(self): - if not self.g.breaking: - if self.leaves: - leaf = self.leaves.pop(0) - self.g.look_at = leaf - self.g.game.break_block(leaf) - print('Breaking leaf', leaf) - else: - self.wait_time = 1 - self.state = self.wait - - def wait(self): - # wait for the items to drop - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.cleanup - - def cleanup(self): - self.g.look_at = None - 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.leaves = [] - self.wait_time = 0 - - def run(self): - self.state() - - -class GrabSaplingStates: - def idle(self): - return None - - def init(self): - self.state = self.find_saplings - print('Trying to grab a sapling') - - def find_saplings(self): - w = self.g.world - p = utils.pint(self.g.pos) - - saplings = w.find_objects(items.SAPLING_IDS) - - if not saplings: - print('No sapling objects found, aborting') - self.state = self.cleanup - return - - saplings.sort(key=lambda s: utils.phyp(p, (s.x, s.y, s.z))) - - for s in saplings: - s_pos = utils.pint((s.x, s.y, s.z)) - - check = utils.padd(s_pos, path.BLOCK_BELOW) - - if s.entity_id in self.eid_blacklist: - continue - - # skip if the sapling is floating - if self.g.chunks.get_block_at(*check) in blocks.LEAF_IDS | {0}: - continue - - navpath = w.path_to_place(p, s_pos) - - if navpath: - self.g.path = navpath - self.state = self.going_to_sapling - self.sapling = s_pos - self.eid_blacklist.append(s.entity_id) - print('Going to sapling', self.sapling) - return - - print('Cant get to any more saplings, aborting') - self.state = self.cleanup - - def going_to_sapling(self): - if utils.pint(self.g.pos) == self.sapling: - self.state = self.find_saplings - - def cleanup(self): - self.g.look_at = None - 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.sapling = None - self.eid_blacklist = [] - - def run(self): - self.state() - - -class CheckThreatsStates: - def idle(self): - return None - - def init(self): - self.state = self.find_threats - print('Checking for threats') - - def find_threats(self): - w = self.g.world - p = utils.pint(self.g.pos) - - threats = w.find_threats(p, 40) - - if threats: - print('Found', len(threats), 'threats, fleeing:') - print(threats) - self.state = self.find_safety - else: - print('Aborting, no threats') - self.state = self.cleanup - - def find_safety(self): - w = self.g.world - p = utils.pint(self.g.pos) - - safety = w.find_blocks_indexed(p, [blocks.EMERALD_BLOCK]) - - if not safety: - print('No emerald blocks found, aborting') - self.state = self.cleanup - return - - safety.sort(key=lambda s: utils.phyp(p, s)) - print('Found emerald blocks:', safety) - - for s in safety: - s = utils.padd(s, path.BLOCK_ABOVE) - navpath = w.path_to_place(p, s) - - if navpath: - self.g.path = navpath - self.state = self.going_to_safety - self.safety = s - print('Going to safety', self.safety) - return - else: - print('Cant get to safety', self.safety) - - print('Cant get to safety, aborting') - self.state = self.cleanup - - def going_to_safety(self): - if utils.pint(self.g.pos) == self.safety: - print('At safety spot, waiting to be moved') - self.state = self.wait_for_move - - def wait_for_move(self): - # wait for the server to move the bot when it's safe - # ie. a piston + daylight sensor - if utils.pint(self.g.pos) != self.safety: - print('Moved, resuming job') - self.state = self.wait - self.wait_time = 3 - - def wait(self): - # wait to land, etc - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.cleanup - - def cleanup(self): - self.g.look_at = None - 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.safety = None - self.wait_time = 0 - - def run(self): - self.state() - - -class FillBlocksStates: - def idle(self): - return None - - def init(self): - f = self.g.filling - - if not f: - self.state = self.cleanup - print('Aborting, nothing to fill') - return - - if self.last_block: - self.state = self.select_item - else: - self.state = self.find_last_block - - def find_last_block(self): - w = self.g.world - f = self.g.filling - print('Finding last block') - - b1, b2 = utils.pboundingbox(f.coord1, f.coord2) - box = utils.psub(b2, b1) - xz_distance = hypot(box[0]+1, box[2]+1) - y_start = f.coord1[1] - y_end = f.coord2[1] - - for y in range(y_start, y_end+1): - for offset in utils.search_2d(xz_distance): - check = utils.padd(f.coord1, offset) - check = (check[0], y, check[2]) - - # ensure block is within fill area - if check[0] < b1[0] or check[0] > b2[0]: - continue - if check[2] < b1[2] or check[2] > b2[2]: - continue - - if w.block_at(*check) == blocks.AIR: - self.state = self.select_item - return - - self.last_block = check - else: # for - self.state = self.cleanup - print('Aborting, no air left') - return - - def select_item(self): - f = self.g.filling - name = blocks.BLOCKS[f.block] - item = items.ITEMS['minecraft:'+name]['protocol_id'] - - if self.g.game.select_item([item]): - #self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW) - self.state = self.find_next_block - else: - print('No blocks, aborting') - self.state = self.cleanup - - def find_next_block(self): - w = self.g.world - f = self.g.filling - print('Finding next block, last:', self.last_block) - - b1, b2 = utils.pboundingbox(f.coord1, f.coord2) - box = utils.psub(b2, b1) - xz_distance = hypot(box[0]+1, box[2]+1) - y_start = f.coord1[1] - y_end = f.coord2[1] - - for y in range(y_start, y_end+1): - if y not in self.iterators: - self.iterators[y] = utils.search_2d(xz_distance) - - for offset in self.iterators[y]: - check = utils.padd(f.coord1, offset) - check = (check[0], y, check[2]) - - # ensure block is within fill area - if check[0] < b1[0] or check[0] > b2[0]: - continue - if check[2] < b1[2] or check[2] > b2[2]: - continue - - if w.block_at(*check) == blocks.AIR: - print('Found next block:', check) - self.next_block = check - self.state = self.check_block_distance - return - - # if there's nothing left to fill - self.g.filling = None - self.state = self.cleanup - - def check_block_distance(self): - w = self.g.world - p = utils.pint(self.g.pos) - head = utils.padd(p, path.BLOCK_ABOVE) - - if utils.phyp(head, self.next_block) < 4: - self.state = self.fill_block - else: - self.state = self.nav_to_block - - def nav_to_block(self): - w = self.g.world - p = utils.pint(self.g.pos) - c = self.g.chunks - - tmp = c.get_block_at(*self.next_block) - c.set_block_at(*self.next_block, blocks.STONE) - pos = utils.padd(self.next_block, path.BLOCK_ABOVE) - navpath = w.path_to_place(p, pos) - c.set_block_at(*self.next_block, tmp) - - if navpath: - self.g.path = navpath[:-1] - self.state = self.going_to_block - else: - print('Cant get to that block') - self.state = self.cleanup - #self.bad_sand.append(self.sand) - #self.state = self.find_new_sand - - def going_to_block(self): - if not len(self.g.path): - self.state = self.fill_block - - def fill_block(self): - print('Filling block', self.next_block) - - self.g.game.place_block(self.next_block, BlockFace.TOP) - self.g.look_at = self.next_block - - self.wait_time = 0.25 - self.state = self.wait_for_block - - def wait_for_block(self): - w = self.g.world - if w.block_at(*self.next_block) != blocks.AIR: - self.last_block = self.next_block - self.state = self.check_obstruction - elif self.wait_time > 0: - self.wait_time -= utils.TICK - else: - print('Block didnt appear') - self.state = self.check_obstruction - - def check_obstruction(self): - p = utils.pint(self.g.pos) - f = self.g.filling - print('last', self.last_block) - print('p', p) - if self.last_block[1] >= p[1] and f.block not in blocks.NON_SOLID_IDS: - print('Obstructed, going to last block') - self.state = self.nav_to_last_block - else: - self.state = self.cleanup - - def nav_to_last_block(self): - w = self.g.world - p = utils.pint(self.g.pos) - c = self.g.chunks - - pos = utils.padd(self.last_block, path.BLOCK_ABOVE) - navpath = w.path_to_place(p, pos) - - if navpath: - self.g.path = navpath - self.state = self.going_to_last_block - else: - print('Cant get to that block') - self.state = self.cleanup - - def going_to_last_block(self): - if not len(self.g.path): - self.state = self.cleanup - - 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.iterators = {} - self.wait_time = 0 - self.last_block = None - self.next_block = None - - def run(self): - self.state() - - -class EatFoodStates: - def idle(self): - return None - - def init(self): - if self.g.food < 12: - print('Hungry, eating') - self.state = self.select_food - return - - if self.g.health < 20 and self.g.food < 18: - print('Low health, eating') - self.state = self.select_food - return - - print('Don\'t need to eat, aborting') - self.state = self.cleanup - - def select_food(self): - if self.g.game.select_item(items.FOOD_IDS): - self.state = self.eat_food - else: - print('No food, aborting') - self.state = self.cleanup - - def eat_food(self): - self.g.game.use_item(0) - - print('Eating food') - self.wait_time = 3 - self.state = self.wait - - def wait(self): - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.cleanup - - 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.wait_time = 0 - - def run(self): - self.state() - - -class SellToVillagerStates: - def idle(self): - return None - - def init(self): - self.trade = None - self.state = self.find_villager - - def find_villager(self): - print('Finding new villager...') - w = self.g.world - p = utils.pint(self.g.pos) - - for v in w.find_villagers(p, 100): - print('Found villager:', v) - if v not in self.bad_villagers and v not in self.spent_villagers: - break - else: # for - print('No good villagers left, aborting.') - self.spent_villagers = [] - self.state = self.cleanup - return - - self.villager = v - self.villager_pos = utils.pint((v.x, v.y, v.z)) - self.state = self.find_openings - - def find_openings(self): - w = self.g.world - self.openings = w.find_villager_openings(self.villager_pos) - self.state = self.choose_opening - - def choose_opening(self): - w = self.g.world - p = utils.pint(self.g.pos) - - print('openings:', self.openings) - - if not len(self.openings): - print('Unable to get to villager:', self.villager) - if self.villager not in self.good_villagers: - self.bad_villagers.append(self.villager) - print('Added to bad villager list') - self.state = self.cleanup - return - - navpath = w.path_to_place(p, self.openings[0]) - - if navpath: - self.g.path = navpath - self.state = self.going_to_villager - else: - self.openings.pop(0) - - def going_to_villager(self): - if utils.pint(self.g.pos) == self.openings[0]: - print('Arrived at villager') - self.g.look_at = self.villager_pos - self.wait_time = 0.5 - self.state = self.wait_to_interact - - def wait_to_interact(self): - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.interact_villager - - def interact_villager(self): - print('Interacting with villager') - self.g.game.interact(self.villager.entity_id) - self.g.game.animate() - self.wait_time = 0.5 - self.state = self.wait_for_window - - def wait_for_window(self): - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.choose_trade - - def choose_trade(self): - g = self.g.game - w = self.g.window - - if not w or not self.g.trades: - print('Didnt get a trade window, aborting') - self.state = self.cleanup - return - if w.data.window_type != mcdata.VILLAGER_TRADE: - print('Got wrong window, aborting') - self.state = self.cleanup - return - - for trade_num, trade in enumerate(self.g.trades): - in_id = trade.input_item_1.item_id - in_num = trade.input_item_1.item_count - out_id = trade.output_item.item_id - - price = in_num \ - + floor(in_num * trade.price_multiplier * trade.demand) \ - + trade.special_price - if price < 1: price = 1 - - if g.count_items([in_id]) < price: - continue - - print('Checking trade #', trade_num) - if trade.trade_disabled: - print('Trade disabled, skipping') - continue - if out_id != items.EMERALD_ID: - print('Not for emeralds, skipping') - continue - if price > in_num: - print('Trade in demand, skipping') - continue - - print('Found trade:', trade) - print('Adjusted price:', price) - self.trade = trade - self.trade_num = trade_num - self.state = self.click_trade - break - else: - print('Villager has been spent, aborting') - self.g.game.close_window() - self.spent_villagers.append(self.villager) - self.state = self.wait - self.wait_time = 10 - return - - def click_trade(self): - print('Clicking trade') - self.g.item_lock = True - self.g.game.select_trade(self.trade_num) - self.state = self.execute_trade - - def execute_trade(self): - if self.g.item_lock: - return - - w = self.g.window - w_info = mcdata.WINDOWS[w.data.window_type] - slot_num = w_info.output - - if slot_num in w.contents: - print('Executing trade') - slot = w.contents[slot_num] - self.g.game.click_window(slot_num, 0, 1, slot) - else: - print('Bad trade, aborting') - - self.state = self.end_trade - - def end_trade(self): - print('Trading complete') - self.g.game.close_window() - self.wait_time = 1 - self.state = self.wait_for_trade - - def wait_for_trade(self): - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.interact_villager - - def wait(self): - if self.wait_time > 0: - self.wait_time -= utils.TICK - else: - self.state = self.cleanup - - 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.villager = None - self.villager_pos = None - self.bad_villagers = [] - self.good_villagers = [] - self.spent_villagers = [] - self.openings = [] - self.trade = None - self.wait_time = 0 - - def run(self): - self.state() - - -class JobStates: - def idle(self): - return [] - - def init_machines(self): - self.gather_wood_states = GatherWoodStates(self.g) - self.gather_sand_states = GatherSandStates(self.g) - self.sleep_with_bed_states = SleepWithBedStates(self.g) - self.cache_items_states = CacheItemsStates(self.g) - self.grab_supplies_states = GrabSuppliesStates(self.g) - self.find_gapple_states = FindGappleStates(self.g) - self.plant_tree_states = PlantTreeStates(self.g) - self.clear_leaves_states = ClearLeavesStates(self.g) - self.grab_sapling_states = GrabSaplingStates(self.g) - self.grab_sand_states = GrabSandStates(self.g) - self.fill_blocks_states = FillBlocksStates(self.g) - self.check_threats_states = CheckThreatsStates(self.g) - self.gather_wart_states = GatherWartStates(self.g) - self.gather_crop_states = GatherCropStates(self.g) - self.eat_food_states = EatFoodStates(self.g) - self.sell_to_villager = SellToVillagerStates(self.g) - - def run_machines(self, machines): - for m in machines: - if m.state == m.idle: - continue - if m.state != m.done: - m.run() - return - # if we went through them all - for m in machines: - m.state = m.init - - def gather_sand(self): - machines = [ - self.gather_sand_states, - self.grab_sand_states, - self.cache_items_states, - self.sleep_with_bed_states, - self.eat_food_states, - ] - return machines - - def farm_sand(self): - machines = [ - self.grab_supplies_states, - self.check_threats_states, - self.gather_sand_states, - self.grab_sand_states, - self.cache_items_states, - self.sleep_with_bed_states, - self.eat_food_states, - ] - self.sleep_with_bed_states.silent = True - self.cache_items_states.silent = True - self.grab_supplies_states.supplies = { - tuple(items.SHOVEL_IDS): (1, 9), - } - - items.set_needed(items.SHOVEL_IDS) - return machines - - def cache_items(self): - machines = [ - self.cache_items_states, - ] - return machines - - - def find_gapple(self): - machines = [ - self.find_gapple_states, - ] - return machines - - def gather_wood(self): - machines = [ - self.gather_wood_states, - self.sleep_with_bed_states, - self.eat_food_states, - self.cache_items_states, - ] - return machines - - def farm_wood(self): - machines = [ - self.grab_supplies_states, - self.gather_wood_states, - self.clear_leaves_states, - self.plant_tree_states, - self.grab_sapling_states, - self.sleep_with_bed_states, - self.eat_food_states, - self.cache_items_states, - ] - self.sleep_with_bed_states.silent = True - self.cache_items_states.silent = True - self.grab_supplies_states.supplies = { - tuple(items.AXE_IDS): (1, 9), - } - - items.set_needed(items.AXE_IDS) - items.set_wanted(items.SAPLING_IDS) - return machines - - def farm_wart(self): - machines = [ - self.gather_wart_states, - self.sleep_with_bed_states, - self.eat_food_states, - self.cache_items_states, - ] - self.sleep_with_bed_states.silent = True - self.cache_items_states.silent = True - - items.set_wanted(set([items.NETHERWART_ID])) - return machines - - def farm_crop(self): - machines = [ - self.gather_crop_states, - self.sleep_with_bed_states, - self.eat_food_states, - self.cache_items_states, - ] - self.sleep_with_bed_states.silent = True - self.cache_items_states.silent = True - - items.set_wanted(set([ - items.CARROT_ID, - items.POTATO_ID, - items.WHEAT_SEEDS_ID, - items.BEETROOT_SEEDS_ID, - ])) - return machines - - def fill_blocks(self): - machines = [ - self.grab_supplies_states, - self.fill_blocks_states, - self.sleep_with_bed_states, - self.eat_food_states, - ] - self.sleep_with_bed_states.silent = True - - f = self.g.filling - if f: - name = blocks.BLOCKS[f.block] - item = items.ITEMS['minecraft:'+name]['protocol_id'] - - self.grab_supplies_states.supplies = { - tuple([item]): (1, 0), - } - return machines - - def loiter(self): - machines = [ - self.check_threats_states, - self.sleep_with_bed_states, - self.eat_food_states, - ] - self.sleep_with_bed_states.silent = True - return machines - - def trade(self): - machines = [ - self.grab_supplies_states, - self.sell_to_villager, - self.sleep_with_bed_states, - self.eat_food_states, - self.cache_items_states, - ] - self.sleep_with_bed_states.silent = True - self.cache_items_states.silent = True - self.grab_supplies_states.supplies = { - tuple([items.PUMPKIN_ID]): (64, 3), - tuple([items.BERRIES_ID]): (64, 3), - tuple([items.IRON_INGOT_ID]): (64, 3), - tuple([items.WHEAT_ID]): (64, 3), - tuple([items.POTATO_ID]): (64, 3), - } - - items.set_needed(set([ - items.PUMPKIN_ID, - items.BERRIES_ID, - items.IRON_INGOT_ID, - items.WHEAT_ID, - items.POTATO_ID, - ])) - return machines - - def stop(self): - self.init_machines() - self.state = self.idle - - def __init__(self, global_state): - self.g = global_state - - self.init_machines() - self.state = self.idle - - def tick(self): - self.run_machines(self.state()) diff --git a/jobs/__init__.py b/jobs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/jobs/cache_items.py b/jobs/cache_items.py new file mode 100644 index 0000000..ee7629e --- /dev/null +++ b/jobs/cache_items.py @@ -0,0 +1,237 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class CacheItemsStates: + def idle(self): + return None + + def init(self): + self.skip_slots = [] + self.skip_items = [] + + num_stacks = self.g.game.count_inventory_slots() + print('Inventory amount:', num_stacks) + if num_stacks >= self.g.minimum_cache_slots: + self.state = self.find_trapped_chests + else: + print('Aborting caching, not full') + self.state = self.cleanup + + def find_trapped_chests(self): + print('Finding trapped chests...') + w = self.g.world + p = utils.pint(self.g.pos) + + self.trapped_chests = w.find_blocks_indexed(p, blocks.TRAPPED_CHEST_IDS) + print('Found:', self.trapped_chests) + self.state = self.choose_trapped_chest + + def choose_trapped_chest(self): + print('Choosing a trapped chest...') + w = self.g.world + p = utils.pint(self.g.pos) + c = self.g.chunks + + if not len(self.trapped_chests): + print('No trapped chests') + self.state = self.select_chest + return + + chest = self.trapped_chests[0] + + tmp = c.get_block_at(*chest) + c.set_block_at(*chest, blocks.AIR) + navpath = w.path_to_place(p, chest) + c.set_block_at(*chest, tmp) + + print('navpath:', navpath) + + if navpath: + self.g.path = navpath[:-1] + self.opening = self.g.path[-1] + self.area = chest + self.state = self.going_to_trapped_chest + return + else: + self.trapped_chests.pop(0) + + def going_to_trapped_chest(self): + if utils.pint(self.g.pos) == self.opening: + self.g.look_at = self.area + self.state = self.open_chest + + def select_chest(self): + if self.g.game.select_item([items.CHEST_ID]): + self.state = self.find_cache_spot + else: + print('No chest, aborting') + self.state = self.cleanup + + def find_cache_spot(self): + print('Finding a chest spot...') + w = self.g.world + p = utils.pint(self.g.pos) + + for area in w.find_cache_areas(p, 100): + print('Found area:', area) + if area not in self.bad_areas: + break + else: # for + print('Unable to find area') + self.state = self.cleanup + return + + self.area = area + openings = w.find_cache_openings(self.area) + + for o in openings: + navpath = w.path_to_place(p, o) + self.opening = o + if navpath: break + else: # for + print('Unable to get to cache area', self.area) + self.bad_areas.append(self.area) + self.state = self.cleanup + return + + self.g.path = navpath + self.state = self.going_to_area + + def going_to_area(self): + if utils.pint(self.g.pos) == self.opening: + self.g.look_at = self.area + self.state = self.place_chest + + def place_chest(self): + self.g.game.place_block(self.area, BlockFace.TOP) + self.state = self.open_chest + + def open_chest(self): + print('Opening chest') + self.g.game.open_container(self.area) + self.wait_time = 1 + self.state = self.wait + + def wait(self): + # wait for server to send us chest contents + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.move_items + + def move_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.data.window_type != mcdata.SINGLE_CHEST: + print('Got wrong window, aborting') + self.state = self.cleanup + return + + w_info = mcdata.WINDOWS[w.data.window_type] + w_inventory_slots = w_info.inventory + w_container_slots = w_info.container + + used_slots = self.g.game.count_window_slots() + print('used:', used_slots, 'total:', len(w_container_slots)) + if used_slots >= len(w_container_slots): + print('Container is too full, aborting') + self.g.game.close_window() + self.g.look_at = None + self.state = self.cleanup + return + + slot_list = [] + + for slot_num in w_inventory_slots: + if slot_num not in w.contents: + continue + + slot = w.contents[slot_num] + + if not slot.present: + continue + + if slot.item_id in items.NEEDED_ITEMS: + continue + + if slot_num in self.skip_slots: + continue + + slot_list.append((slot_num, slot)) + + slot_list.sort(key=lambda x: x[1].item_count, reverse=True) + + for slot_num, slot in slot_list: + if slot.item_id in items.WANTED_ITEMS and slot.item_id not in self.skip_items: + print('skipping wanted item', slot) + self.skip_slots.append(slot_num) + self.skip_items.append(slot.item_id) + continue + + print('moving', slot) + + self.g.item_lock = True + self.g.game.click_window(slot_num, 0, 1, slot) + return + + print('nothing left to move') + self.state = self.close_chest + + def close_chest(self): + print('closing chest') + self.g.game.close_window() + if not self.silent: + self.g.chat.send('cache at ' + str(self.area)[1:-1]) + self.state = self.cleanup + + def cleanup(self): + self.g.look_at = None + 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.silent = False + + self.skip_slots = [] + self.skip_items = [] + + self.area = None + self.opening = None + self.trapped_chests = [] + self.bad_areas = [] + self.wait_time = 0 + + def run(self): + self.state() diff --git a/jobs/check_threats.py b/jobs/check_threats.py new file mode 100644 index 0000000..dfe54c5 --- /dev/null +++ b/jobs/check_threats.py @@ -0,0 +1,114 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class CheckThreatsStates: + def idle(self): + return None + + def init(self): + self.state = self.find_threats + print('Checking for threats') + + def find_threats(self): + w = self.g.world + p = utils.pint(self.g.pos) + + threats = w.find_threats(p, 40) + + if threats: + print('Found', len(threats), 'threats, fleeing:') + print(threats) + self.state = self.find_safety + else: + print('Aborting, no threats') + self.state = self.cleanup + + def find_safety(self): + w = self.g.world + p = utils.pint(self.g.pos) + + safety = w.find_blocks_indexed(p, [blocks.EMERALD_BLOCK]) + + if not safety: + print('No emerald blocks found, aborting') + self.state = self.cleanup + return + + safety.sort(key=lambda s: utils.phyp(p, s)) + print('Found emerald blocks:', safety) + + for s in safety: + s = utils.padd(s, path.BLOCK_ABOVE) + navpath = w.path_to_place(p, s) + + if navpath: + self.g.path = navpath + self.state = self.going_to_safety + self.safety = s + print('Going to safety', self.safety) + return + else: + print('Cant get to safety', self.safety) + + print('Cant get to safety, aborting') + self.state = self.cleanup + + def going_to_safety(self): + if utils.pint(self.g.pos) == self.safety: + print('At safety spot, waiting to be moved') + self.state = self.wait_for_move + + def wait_for_move(self): + # wait for the server to move the bot when it's safe + # ie. a piston + daylight sensor + if utils.pint(self.g.pos) != self.safety: + print('Moved, resuming job') + self.state = self.wait + self.wait_time = 3 + + def wait(self): + # wait to land, etc + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.cleanup + + def cleanup(self): + self.g.look_at = None + 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.safety = None + self.wait_time = 0 + + def run(self): + self.state() + diff --git a/jobs/clear_leaves.py b/jobs/clear_leaves.py new file mode 100644 index 0000000..f7ed832 --- /dev/null +++ b/jobs/clear_leaves.py @@ -0,0 +1,97 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class ClearLeavesStates: + def idle(self): + return None + + def init(self): + if not self.g.chopped_tree: + print('Didnt chop tree, clearing leaves') + self.state = self.cleanup + return + + sapling_type = self.g.chopped_tree + '_sapling' + sapling_item = items.get_id(sapling_type) + num_saplings = self.g.game.count_items([sapling_item]) + print('Have', num_saplings, sapling_type, 'in inventory') + + if num_saplings > 8: + print('Have enough saplings, aborting clearing leaves') + self.state = self.cleanup + return + + self.state = self.select_log + print('Clearing leaves...') + + def select_log(self): + # select a log to avoid using tools + self.g.game.select_item(items.LOG_IDS) + self.state = self.find_leaves + + def find_leaves(self): + w = self.g.world + p = utils.pint(self.g.pos) + pos = utils.padd(p, path.BLOCK_ABOVE) + + for l in w.find_leaves(pos, blocks.BREAK_DISTANCE): + self.leaves.append(l) + + self.state = self.break_leaves + + def break_leaves(self): + if not self.g.breaking: + if self.leaves: + leaf = self.leaves.pop(0) + self.g.look_at = leaf + self.g.game.break_block(leaf) + print('Breaking leaf', leaf) + else: + self.wait_time = 1 + self.state = self.wait + + def wait(self): + # wait for the items to drop + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.cleanup + + def cleanup(self): + self.g.look_at = None + 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.leaves = [] + self.wait_time = 0 + + def run(self): + self.state() diff --git a/jobs/eat_food.py b/jobs/eat_food.py new file mode 100644 index 0000000..e41f7a4 --- /dev/null +++ b/jobs/eat_food.py @@ -0,0 +1,78 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class EatFoodStates: + def idle(self): + return None + + def init(self): + if self.g.food < 12: + print('Hungry, eating') + self.state = self.select_food + return + + if self.g.health < 20 and self.g.food < 18: + print('Low health, eating') + self.state = self.select_food + return + + print('Don\'t need to eat, aborting') + self.state = self.cleanup + + def select_food(self): + if self.g.game.select_item(items.FOOD_IDS): + self.state = self.eat_food + else: + print('No food, aborting') + self.state = self.cleanup + + def eat_food(self): + self.g.game.use_item(0) + + print('Eating food') + self.wait_time = 3 + self.state = self.wait + + def wait(self): + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.cleanup + + 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.wait_time = 0 + + def run(self): + self.state() + diff --git a/jobs/fill_blocks.py b/jobs/fill_blocks.py new file mode 100644 index 0000000..00be475 --- /dev/null +++ b/jobs/fill_blocks.py @@ -0,0 +1,222 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class FillBlocksStates: + def idle(self): + return None + + def init(self): + f = self.g.filling + + if not f: + self.state = self.cleanup + print('Aborting, nothing to fill') + return + + if self.last_block: + self.state = self.select_item + else: + self.state = self.find_last_block + + def find_last_block(self): + w = self.g.world + f = self.g.filling + print('Finding last block') + + b1, b2 = utils.pboundingbox(f.coord1, f.coord2) + box = utils.psub(b2, b1) + xz_distance = hypot(box[0]+1, box[2]+1) + y_start = f.coord1[1] + y_end = f.coord2[1] + + for y in range(y_start, y_end+1): + for offset in utils.search_2d(xz_distance): + check = utils.padd(f.coord1, offset) + check = (check[0], y, check[2]) + + # ensure block is within fill area + if check[0] < b1[0] or check[0] > b2[0]: + continue + if check[2] < b1[2] or check[2] > b2[2]: + continue + + if w.block_at(*check) == blocks.AIR: + self.state = self.select_item + return + + self.last_block = check + else: # for + self.state = self.cleanup + print('Aborting, no air left') + return + + def select_item(self): + f = self.g.filling + name = blocks.BLOCKS[f.block] + item = items.ITEMS['minecraft:'+name]['protocol_id'] + + if self.g.game.select_item([item]): + #self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW) + self.state = self.find_next_block + else: + print('No blocks, aborting') + self.state = self.cleanup + + def find_next_block(self): + w = self.g.world + f = self.g.filling + print('Finding next block, last:', self.last_block) + + b1, b2 = utils.pboundingbox(f.coord1, f.coord2) + box = utils.psub(b2, b1) + xz_distance = hypot(box[0]+1, box[2]+1) + y_start = f.coord1[1] + y_end = f.coord2[1] + + for y in range(y_start, y_end+1): + if y not in self.iterators: + self.iterators[y] = utils.search_2d(xz_distance) + + for offset in self.iterators[y]: + check = utils.padd(f.coord1, offset) + check = (check[0], y, check[2]) + + # ensure block is within fill area + if check[0] < b1[0] or check[0] > b2[0]: + continue + if check[2] < b1[2] or check[2] > b2[2]: + continue + + if w.block_at(*check) == blocks.AIR: + print('Found next block:', check) + self.next_block = check + self.state = self.check_block_distance + return + + # if there's nothing left to fill + self.g.filling = None + self.state = self.cleanup + + def check_block_distance(self): + w = self.g.world + p = utils.pint(self.g.pos) + head = utils.padd(p, path.BLOCK_ABOVE) + + if utils.phyp(head, self.next_block) < 4: + self.state = self.fill_block + else: + self.state = self.nav_to_block + + def nav_to_block(self): + w = self.g.world + p = utils.pint(self.g.pos) + c = self.g.chunks + + tmp = c.get_block_at(*self.next_block) + c.set_block_at(*self.next_block, blocks.STONE) + pos = utils.padd(self.next_block, path.BLOCK_ABOVE) + navpath = w.path_to_place(p, pos) + c.set_block_at(*self.next_block, tmp) + + if navpath: + self.g.path = navpath[:-1] + self.state = self.going_to_block + else: + print('Cant get to that block') + self.state = self.cleanup + #self.bad_sand.append(self.sand) + #self.state = self.find_new_sand + + def going_to_block(self): + if not len(self.g.path): + self.state = self.fill_block + + def fill_block(self): + print('Filling block', self.next_block) + + self.g.game.place_block(self.next_block, BlockFace.TOP) + self.g.look_at = self.next_block + + self.wait_time = 0.25 + self.state = self.wait_for_block + + def wait_for_block(self): + w = self.g.world + if w.block_at(*self.next_block) != blocks.AIR: + self.last_block = self.next_block + self.state = self.check_obstruction + elif self.wait_time > 0: + self.wait_time -= utils.TICK + else: + print('Block didnt appear') + self.state = self.check_obstruction + + def check_obstruction(self): + p = utils.pint(self.g.pos) + f = self.g.filling + print('last', self.last_block) + print('p', p) + if self.last_block[1] >= p[1] and f.block not in blocks.NON_SOLID_IDS: + print('Obstructed, going to last block') + self.state = self.nav_to_last_block + else: + self.state = self.cleanup + + def nav_to_last_block(self): + w = self.g.world + p = utils.pint(self.g.pos) + c = self.g.chunks + + pos = utils.padd(self.last_block, path.BLOCK_ABOVE) + navpath = w.path_to_place(p, pos) + + if navpath: + self.g.path = navpath + self.state = self.going_to_last_block + else: + print('Cant get to that block') + self.state = self.cleanup + + def going_to_last_block(self): + if not len(self.g.path): + self.state = self.cleanup + + 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.iterators = {} + self.wait_time = 0 + self.last_block = None + self.next_block = None + + def run(self): + self.state() diff --git a/jobs/find_gapple.py b/jobs/find_gapple.py new file mode 100644 index 0000000..5f88807 --- /dev/null +++ b/jobs/find_gapple.py @@ -0,0 +1,123 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class FindGappleStates: + def idle(self): + return None + + def init(self): + self.state = self.go_spectator + + def go_spectator(self): + print('Going spectator...') + self.g.chat.send('/gamemode spectator') + + self.state = self.tp_to_coord + + def tp_to_coord(self): + step = utils.spiral(self.count) + step_scaled = utils.pmul(step, 192) + self.coord = utils.padd(self.origin, step_scaled) + self.coord = (self.coord[0], 50, self.coord[2]) + + print('count:', self.count, 'teleporting to:', self.coord) + self.g.chat.send('/tp {} {} {}'.format(*self.coord)) + + self.g.command_lock = True + self.state = self.wait_for_load + + def wait_for_load(self): + if self.g.command_lock: + return + + if self.g.chunks.check_loaded(self.g.pos, 169): + print('chunks have been loaded') + self.state = self.pick_chest + + def pick_chest(self): + chest_list = [] + for chest_id in blocks.CHEST_IDS: + chest_list.extend(self.g.chunks.index.get(chest_id, [])) + + for chest in chest_list: + if chest in self.checked_chests: + # slow but simple + continue + + if utils.phyp_king(self.coord, chest) > 96: + # skip because we can't detect item drops + continue + + self.current_chest = chest + self.checked_chests.append(self.current_chest) + self.state = self.break_chest + break + else: # for + print('exhausted chest list') + self.state = self.cleanup + + def break_chest(self): + print('Breaking chest', self.current_chest) + self.g.command_lock = True + self.g.item_lock = True + self.g.chat.send('/setblock {} {} {} air destroy'.format(*self.current_chest)) + + self.wait_time = 0.5 + self.state = self.wait_for_items + + def wait_for_items(self): + # wait for command to execute + if self.g.command_lock: + return + + # wait for items to drop + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + print('done waiting for items') + self.state = self.pick_chest + + def cleanup(self): + self.count += 1 + 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.origin = utils.pint(self.g.pos) + self.count = 0 + self.coord = None + self.current_chest = None + self.checked_chests = [] + self.wait_time = 0 + + def run(self): + self.state() + + diff --git a/jobs/gather_crop.py b/jobs/gather_crop.py new file mode 100644 index 0000000..decd2eb --- /dev/null +++ b/jobs/gather_crop.py @@ -0,0 +1,152 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class GatherCropStates: + def idle(self): + return None + + def init(self): + self.state = self.find_new_crop + + def find_new_crop(self): + print('Finding new crop...') + w = self.g.world + p = utils.pint(self.g.pos) + + mature_crops = [ + blocks.MATURE_WHEAT_ID, + blocks.MATURE_POTATO_ID, + blocks.MATURE_CARROT_ID, + blocks.MATURE_BEETROOT_ID, + ] + + for crop in w.find_blocks_3d(p, mature_crops, 50, 20): + print('Found crop:', crop) + if crop not in self.bad_crops: + break + else: # for + print('No good crops left, aborting.') + self.state = self.cleanup + return + + self.crop = crop + self.type_id = w.block_at(*crop) + self.state = self.nav_to_crop + + def nav_to_crop(self): + w = self.g.world + p = utils.pint(self.g.pos) + + navpath = w.path_to_place(p, self.crop) + + if navpath: + self.g.path = navpath + self.g.look_at = utils.padd(self.crop, path.BLOCK_BELOW) + self.state = self.going_to_crop + else: + self.bad_crops.append(self.crop) + self.wait_time = 0.5 + self.state = self.wait_to_restart + + def wait_to_restart(self): + # prevent timeouts + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.find_new_crop + + def going_to_crop(self): + if utils.pint(self.g.pos) == self.crop: + print('At the crop') + self.state = self.break_crop + + def break_crop(self): + self.g.game.break_block(self.crop) + self.wait_time = 0.5 + self.state = self.wait + + def wait(self): + # wait for the item + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.select_seed + + def select_seed(self): + p = utils.pint(self.g.pos) + + crop_seeds = { + blocks.MATURE_WHEAT_ID: items.WHEAT_SEEDS_ID, + blocks.MATURE_POTATO_ID: items.POTATO_ID, + blocks.MATURE_CARROT_ID: items.CARROT_ID, + blocks.MATURE_BEETROOT_ID: items.BEETROOT_SEEDS_ID, + } + + if self.g.game.select_item([crop_seeds[self.type_id]]): + self.state = self.wait_select + self.wait_time = 0.5 + else: + print('Aborting planting, no crop') + self.state = self.cleanup + + def wait_select(self): + # wait a bit to select + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.place_crop + + def place_crop(self): + p = utils.pint(self.g.pos) + self.g.game.place_block(p, BlockFace.TOP) + print('Placed crop') + self.state = self.wait_place + self.wait_time = 0.5 + + def wait_place(self): + # wait a bit for chunk data to update + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.cleanup + + def cleanup(self): + self.g.look_at = None + 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.crop = None + self.type_id = None + self.bad_crops = [] + self.wait_time = 0 + + def run(self): + self.state() diff --git a/jobs/gather_sand.py b/jobs/gather_sand.py new file mode 100644 index 0000000..6e3d783 --- /dev/null +++ b/jobs/gather_sand.py @@ -0,0 +1,136 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class GatherSandStates: + def bair(self, p): + return self.g.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS + + def bsand(self, p): + return self.g.chunks.get_block_at(*p) == blocks.SAND + + def idle(self): + return None + + def init(self): + self.state = self.select_shovel + + def select_shovel(self): + self.g.game.select_item(items.SHOVEL_IDS) + self.state = self.find_new_slice + + def find_new_slice(self): + print('Finding new slice...') + w = self.g.world + + print('using origin', self.origin) + start = time.time() + self.prev_layer, s = w.find_sand_slice(self.origin, 200, 10, self.bad_slices, self.prev_layer) + print('Found slice:', s, 'in', time.time() - start, 'seconds') + + if s: + self.slice = s + self.bad_slices.append(s) + self.state = self.find_new_sand + else: + print('No slices remaining.') + self.state = self.cleanup + + def find_new_sand(self): + print('Finding new sand...') + w = self.g.world + p = utils.pint(self.g.pos) + head = utils.padd(p, path.BLOCK_ABOVE) + + for sand in w.find_sand(self.slice, 2, p): + if sand not in self.bad_sand: + print('Found sand:', sand) + break + else: # for + print('No good sands left, aborting.') + self.state = self.cleanup + return + + self.sand = sand + + if utils.phyp(head, self.sand) < blocks.BREAK_DISTANCE: + self.state = self.dig_sand + else: + self.state = self.nav_to_sand + + def nav_to_sand(self): + w = self.g.world + p = utils.pint(self.g.pos) + c = self.g.chunks + + tmp = c.get_block_at(*self.sand) + c.set_block_at(*self.sand, blocks.AIR) + navpath = w.path_to_place(p, self.sand) + c.set_block_at(*self.sand, tmp) + + if navpath: + self.g.path = navpath[:-1] + self.state = self.going_to_sand + else: + print('Cant get to that sand') + self.bad_sand.append(self.sand) + self.state = self.find_new_sand + + def going_to_sand(self): + if not len(self.g.path): + self.g.look_at = self.sand + self.state = self.dig_sand + + def dig_sand(self): + if not self.g.breaking: + if self.bsand(self.sand): + self.g.look_at = self.sand + self.g.game.break_block(self.sand) + print('digging sand') + else: + self.state = self.find_new_sand + + def cleanup(self): + self.g.look_at = None + 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.origin = utils.pint(self.g.pos) + self.origin = (2019, 64, 238) + self.slice = None + self.bad_slices = [] + self.prev_layer = 0 + self.sand = None + self.bad_sand = [] + self.wait_time = 0 + + def run(self): + self.state() diff --git a/jobs/gather_wart.py b/jobs/gather_wart.py new file mode 100644 index 0000000..f09f027 --- /dev/null +++ b/jobs/gather_wart.py @@ -0,0 +1,129 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class GatherWartStates: + def idle(self): + return None + + def init(self): + self.state = self.find_new_wart + + def find_new_wart(self): + print('Finding new wart...') + w = self.g.world + p = utils.pint(self.g.pos) + + mature_wart = max(blocks.NETHERWART_IDS) + for wart in w.find_blocks_3d(p, [mature_wart], 50, 20): + print('Found wart:', wart) + if wart not in self.bad_warts: + break + else: # for + print('No good warts left, aborting.') + self.state = self.cleanup + return + + self.wart = wart + self.state = self.nav_to_wart + + def nav_to_wart(self): + w = self.g.world + p = utils.pint(self.g.pos) + + navpath = w.path_to_place(p, self.wart) + + if navpath: + self.g.path = navpath + self.g.look_at = utils.padd(self.wart, path.BLOCK_BELOW) + self.state = self.going_to_wart + else: + self.bad_warts.append(wart) + self.state = self.find_new_wart + + def going_to_wart(self): + if utils.pint(self.g.pos) == self.wart: + print('At the wart') + self.state = self.break_wart + + def break_wart(self): + self.g.game.break_block(self.wart) + self.wait_time = 0.5 + self.state = self.wait + + def wait(self): + # wait for the item + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.select_wart + + def select_wart(self): + p = utils.pint(self.g.pos) + + if self.g.game.select_item([items.NETHERWART_ID]): + self.state = self.wait_select + self.wait_time = 0.5 + else: + print('Aborting planting, no wart') + self.state = self.cleanup + + def wait_select(self): + # wait a bit to select + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.place_wart + + def place_wart(self): + p = utils.pint(self.g.pos) + self.g.game.place_block(p, BlockFace.TOP) + print('Placed wart') + self.state = self.wait_place + self.wait_time = 0.5 + + def wait_place(self): + # wait a bit for chunk data to update + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.cleanup + + def cleanup(self): + self.g.look_at = None + 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.wart = None + self.bad_warts = [] + self.wait_time = 0 + + def run(self): + self.state() diff --git a/jobs/gather_wood.py b/jobs/gather_wood.py new file mode 100644 index 0000000..8d51c32 --- /dev/null +++ b/jobs/gather_wood.py @@ -0,0 +1,195 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class GatherWoodStates: + def bair(self, p): + return self.g.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS + + def blog(self, p): + return self.g.chunks.get_block_at(*p) in blocks.LOG_IDS + + def idle(self): + return None + + def init(self): + self.g.chopped_tree = False + self.state = self.select_axe + + def select_axe(self): + self.g.game.select_item(items.AXE_IDS) + self.state = self.find_new_tree + + def find_new_tree(self): + print('Finding new tree...') + w = self.g.world + p = utils.pint(self.g.pos) + + for tree in w.find_trees(p, 100): + print('Found tree:', tree) + if tree not in self.bad_trees: + break + else: # for + print('No good trees left, aborting.') + self.state = self.cleanup + return + + self.tree = tree + self.type = blocks.BLOCKS[w.block_at(*tree)].replace('_log', '') + print('Type:', self.type) + + self.state = self.find_openings + + def find_openings(self): + w = self.g.world + self.openings = w.find_tree_openings(self.tree) + self.state = self.choose_opening + + def choose_opening(self): + w = self.g.world + p = utils.pint(self.g.pos) + + print('openings:', self.openings) + + if not len(self.openings): + print('Unable to get to tree', self.tree) + if self.tree not in self.good_trees: + self.bad_trees.append(self.tree) + print('Added to bad trees list') + self.state = self.cleanup + return + + navpath = w.path_to_place(p, self.openings[0]) + + if navpath: + self.g.path = navpath + self.state = self.going_to_tree + else: + self.openings.pop(0) + + def going_to_tree(self): + if utils.pint(self.g.pos) == self.openings[0]: + self.g.look_at = self.tree + self.state = self.clear_leaves + + def clear_leaves(self): + if not self.g.breaking: + p = utils.pint(self.g.pos) + diff = utils.psub(self.tree, p) + + for x in utils.diffrange(diff[0]): + for z in utils.diffrange(diff[2]): + for y in range(2): + check = utils.padd(p, (x, y, z)) + if check == self.tree: + break + if not self.bair(check): + print('Breaking leaf') + self.g.game.break_block(check) + return + + self.state = self.clear_trunk_base + + def clear_trunk_base(self): + if not self.g.breaking: + base = self.tree + above = utils.padd(self.tree, path.BLOCK_ABOVE) + + if self.blog(base): + self.g.game.break_block(base) + print('breaking base') + elif self.blog(above): + self.g.game.break_block(above) + print('breaking above') + else: + w = self.g.world + p = utils.pint(self.g.pos) + navpath = w.path_to_place(p, self.tree) + + if navpath: + self.g.path = navpath + self.state = self.going_to_trunk_base + else: + self.openings.pop(0) + self.state = self.choose_opening + + def going_to_trunk_base(self): + if utils.pint(self.g.pos) == self.tree: + self.g.look_at = utils.padd(self.tree, path.BLOCK_ABOVE2) + self.state = self.clear_trunk + + def clear_trunk(self): + if not self.g.breaking: + check = self.tree + + count = 0 + while self.bair(check) and count < 6: + check = utils.padd(check, path.BLOCK_ABOVE) + count += 1 + + if self.blog(check): + print('breaking log', check) + self.g.game.break_block(check) + else: + print('Finished clearing tree') + self.wait_time = 0.5 + self.state = self.wait + + def wait(self): + # wait for the last log to fall + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.g.chopped_tree = self.type + self.good_trees.append(self.tree) + self.state = self.check_pos + + def check_pos(self): + # make sure we are at base of trunk + # doesn't always happen, for some reason + if utils.pint(self.g.pos) == self.tree: + self.state = self.cleanup + else: + self.state = self.find_openings + + def cleanup(self): + self.g.look_at = None + 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.tree = None + self.type = None + self.openings = [] + self.bad_trees = [] + self.good_trees = [] + self.wait_time = 0 + + def run(self): + self.state() diff --git a/jobs/grab_sand.py b/jobs/grab_sand.py new file mode 100644 index 0000000..323210b --- /dev/null +++ b/jobs/grab_sand.py @@ -0,0 +1,94 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class GrabSandStates: + def idle(self): + return None + + def init(self): + self.state = self.find_sand + print('Trying to grab sand') + + def find_sand(self): + w = self.g.world + p = utils.pint(self.g.pos) + + sand = w.find_objects([items.SAND_ID]) + + if not sand: + print('No sand objects found, aborting') + self.state = self.cleanup + return + + sand.sort(key=lambda s: utils.phyp(p, (s.x, s.y, s.z))) + + for s in sand: + s_pos = utils.pint((s.x, s.y, s.z)) + check = utils.padd(s_pos, path.BLOCK_BELOW) + + if utils.phyp(p, s_pos) > 6: + continue + # skip if the sand is floating + if self.g.chunks.get_block_at(*check) in {0}: + continue + if s.entity_id in self.eid_blacklist: + continue + + self.eid_blacklist.append(s.entity_id) + + navpath = w.path_to_place(p, s_pos) + + if navpath: + self.g.path = navpath + self.state = self.going_to_sand + self.sand = s_pos + print('Going to sand', self.sand) + return + else: + print('Cant get to sand', self.sand) + + print('Cant get to any more sand, aborting') + self.state = self.cleanup + + def going_to_sand(self): + if utils.pint(self.g.pos) == self.sand: + self.state = self.cleanup + + def cleanup(self): + self.g.look_at = None + 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.sand = None + self.eid_blacklist = [] + + def run(self): + self.state() diff --git a/jobs/grab_sapling.py b/jobs/grab_sapling.py new file mode 100644 index 0000000..689cf32 --- /dev/null +++ b/jobs/grab_sapling.py @@ -0,0 +1,91 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class GrabSaplingStates: + def idle(self): + return None + + def init(self): + self.state = self.find_saplings + print('Trying to grab a sapling') + + def find_saplings(self): + w = self.g.world + p = utils.pint(self.g.pos) + + saplings = w.find_objects(items.SAPLING_IDS) + + if not saplings: + print('No sapling objects found, aborting') + self.state = self.cleanup + return + + saplings.sort(key=lambda s: utils.phyp(p, (s.x, s.y, s.z))) + + for s in saplings: + s_pos = utils.pint((s.x, s.y, s.z)) + + check = utils.padd(s_pos, path.BLOCK_BELOW) + + if s.entity_id in self.eid_blacklist: + continue + + # skip if the sapling is floating + if self.g.chunks.get_block_at(*check) in blocks.LEAF_IDS | {0}: + continue + + navpath = w.path_to_place(p, s_pos) + + if navpath: + self.g.path = navpath + self.state = self.going_to_sapling + self.sapling = s_pos + self.eid_blacklist.append(s.entity_id) + print('Going to sapling', self.sapling) + return + + print('Cant get to any more saplings, aborting') + self.state = self.cleanup + + def going_to_sapling(self): + if utils.pint(self.g.pos) == self.sapling: + self.state = self.find_saplings + + def cleanup(self): + self.g.look_at = None + 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.sapling = None + self.eid_blacklist = [] + + def run(self): + self.state() diff --git a/jobs/grab_supplies.py b/jobs/grab_supplies.py new file mode 100644 index 0000000..1b6797a --- /dev/null +++ b/jobs/grab_supplies.py @@ -0,0 +1,243 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class GrabSuppliesStates: + 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 + + if self.supplies: + self.state = self.check_supplies + else: + print('Aborting getting supplies, none specified') + self.state = self.cleanup + + def check_supplies(self): + # check if we need to grab anything + for items, limits in self.supplies.items(): + minimum, maximum = limits + print('Checking items:', items) + num_items = self.g.game.count_items(items) + print('Have:', num_items) + + if num_items >= minimum: + print('Have enough, skipping') + continue + + print('Need at least one item') + self.state = self.find_barrels + return + + print('Aborting, dont need any supplies') + self.state = self.cleanup + + 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) + 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 + + 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 + + barrel = self.barrel + + tmp = c.get_block_at(*barrel) + c.set_block_at(*barrel, blocks.AIR) + navpath = w.path_to_place(p, barrel) + c.set_block_at(*barrel, tmp) + + print('navpath:', navpath) + + if navpath: + self.g.path = navpath[:-1] + self.opening = self.g.path[-1] + self.checked_barrels.append(barrel) + self.area = barrel + self.state = self.going_to_barrel + self.checked_supplies = [] + return + else: + print('No path, blacklisting barrel') + self.bad_barrels.append(barrel) + 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.data.window_type != mcdata.SINGLE_CHEST: + print('Got wrong window, 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 + + w_info = mcdata.WINDOWS[w.data.window_type] + w_container_slots = w_info.container + + for slot_num in 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.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() diff --git a/jobs/plant_tree.py b/jobs/plant_tree.py new file mode 100644 index 0000000..2f4cc28 --- /dev/null +++ b/jobs/plant_tree.py @@ -0,0 +1,116 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class PlantTreeStates: + def idle(self): + return None + + def init(self): + if self.g.chopped_tree: + self.state = self.check_feet + else: + print('Aborting planting, did not plant') + self.state = self.cleanup + + def check_feet(self): + p = utils.pint(self.g.pos) + + # check for air at feet + if self.g.chunks.get_block_at(*p) in [0]: + self.state = self.select_sapling + else: + print('Aborting planting, feet not air') + self.state = self.cleanup + + def select_sapling(self): + p = utils.pint(self.g.pos) + sapling_type = self.g.chopped_tree + '_sapling' + sapling_item = items.get_id(sapling_type) + + if self.g.game.select_item([sapling_item]): + self.g.look_at = utils.padd(p, path.BLOCK_BELOW) + self.state = self.wait_select + self.wait_time = 1 + else: + print('Aborting planting, no', sapling_type) + self.state = self.cleanup + + def wait_select(self): + # wait a bit to look down + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.place_sapling + + def place_sapling(self): + p = utils.pint(self.g.pos) + self.g.game.place_block(p, BlockFace.TOP) + print('Placed sapling') + self.state = self.wait_place + self.wait_time = 1 + + def wait_place(self): + # wait a bit for chunk data to update + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.find_open_spot + + def find_open_spot(self): + w = self.g.world + p = utils.pint(self.g.pos) + + for opening in w.find_tree_openings(p)[::-1]: + print('trying sapling opening', opening) + navpath = w.path_to_place(p, opening) + if navpath: + self.g.path = navpath + self.area = opening + self.state = self.going_to_area + return + else: # for + print('cant escape sapling') + self.state = self.cleanup + + def going_to_area(self): + if utils.pint(self.g.pos) == self.area: + self.state = self.cleanup + + def cleanup(self): + self.g.look_at = None + 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.wait_time = 0 + self.area = None + + def run(self): + self.state() diff --git a/jobs/sell_to_villager.py b/jobs/sell_to_villager.py new file mode 100644 index 0000000..ba627b0 --- /dev/null +++ b/jobs/sell_to_villager.py @@ -0,0 +1,218 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class SellToVillagerStates: + def idle(self): + return None + + def init(self): + self.trade = None + self.state = self.find_villager + + def find_villager(self): + print('Finding new villager...') + w = self.g.world + p = utils.pint(self.g.pos) + + for v in w.find_villagers(p, 100): + print('Found villager:', v) + if v not in self.bad_villagers and v not in self.spent_villagers: + break + else: # for + print('No good villagers left, aborting.') + self.spent_villagers = [] + self.state = self.cleanup + return + + self.villager = v + self.villager_pos = utils.pint((v.x, v.y, v.z)) + self.state = self.find_openings + + def find_openings(self): + w = self.g.world + self.openings = w.find_villager_openings(self.villager_pos) + self.state = self.choose_opening + + def choose_opening(self): + w = self.g.world + p = utils.pint(self.g.pos) + + print('openings:', self.openings) + + if not len(self.openings): + print('Unable to get to villager:', self.villager) + if self.villager not in self.good_villagers: + self.bad_villagers.append(self.villager) + print('Added to bad villager list') + self.state = self.cleanup + return + + navpath = w.path_to_place(p, self.openings[0]) + + if navpath: + self.g.path = navpath + self.state = self.going_to_villager + else: + self.openings.pop(0) + + def going_to_villager(self): + if utils.pint(self.g.pos) == self.openings[0]: + print('Arrived at villager') + self.g.look_at = self.villager_pos + self.wait_time = 0.5 + self.state = self.wait_to_interact + + def wait_to_interact(self): + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.interact_villager + + def interact_villager(self): + print('Interacting with villager') + self.g.game.interact(self.villager.entity_id) + self.g.game.animate() + self.wait_time = 0.5 + self.state = self.wait_for_window + + def wait_for_window(self): + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.choose_trade + + def choose_trade(self): + g = self.g.game + w = self.g.window + + if not w or not self.g.trades: + print('Didnt get a trade window, aborting') + self.state = self.cleanup + return + if w.data.window_type != mcdata.VILLAGER_TRADE: + print('Got wrong window, aborting') + self.state = self.cleanup + return + + for trade_num, trade in enumerate(self.g.trades): + in_id = trade.input_item_1.item_id + in_num = trade.input_item_1.item_count + out_id = trade.output_item.item_id + + price = in_num \ + + floor(in_num * trade.price_multiplier * trade.demand) \ + + trade.special_price + if price < 1: price = 1 + + if g.count_items([in_id]) < price: + continue + + print('Checking trade #', trade_num) + if trade.trade_disabled: + print('Trade disabled, skipping') + continue + if out_id != items.EMERALD_ID: + print('Not for emeralds, skipping') + continue + if price > in_num: + print('Trade in demand, skipping') + continue + + print('Found trade:', trade) + print('Adjusted price:', price) + self.trade = trade + self.trade_num = trade_num + self.state = self.click_trade + break + else: + print('Villager has been spent, aborting') + self.g.game.close_window() + self.spent_villagers.append(self.villager) + self.state = self.wait + self.wait_time = 10 + return + + def click_trade(self): + print('Clicking trade') + self.g.item_lock = True + self.g.game.select_trade(self.trade_num) + self.state = self.execute_trade + + def execute_trade(self): + if self.g.item_lock: + return + + w = self.g.window + w_info = mcdata.WINDOWS[w.data.window_type] + slot_num = w_info.output + + if slot_num in w.contents: + print('Executing trade') + slot = w.contents[slot_num] + self.g.game.click_window(slot_num, 0, 1, slot) + else: + print('Bad trade, aborting') + + self.state = self.end_trade + + def end_trade(self): + print('Trading complete') + self.g.game.close_window() + self.wait_time = 1 + self.state = self.wait_for_trade + + def wait_for_trade(self): + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.interact_villager + + def wait(self): + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.cleanup + + 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.villager = None + self.villager_pos = None + self.bad_villagers = [] + self.good_villagers = [] + self.spent_villagers = [] + self.openings = [] + self.trade = None + self.wait_time = 0 + + def run(self): + self.state() diff --git a/jobs/sleep_with_bed.py b/jobs/sleep_with_bed.py new file mode 100644 index 0000000..a3c50ba --- /dev/null +++ b/jobs/sleep_with_bed.py @@ -0,0 +1,211 @@ +import re +import time +import importlib +import random +from itertools import count +from math import hypot, floor + +from minecraft.networking.types import BlockFace + +from protocol.managers import ChunkNotLoadedException + +import utils +importlib.reload(utils) +import path +importlib.reload(path) +import blocks +importlib.reload(blocks) +import items +importlib.reload(items) +import mcdata +importlib.reload(mcdata) +import mobs +importlib.reload(mobs) + +class SleepWithBedStates: + def idle(self): + return None + + def init(self): + if self.g.time < 12000: + print('Aborting sleep, not night') + self.state = self.cleanup + return + + if self.g.dimension != 'overworld': + print('Aborting sleep, not in overworld') + self.state = self.cleanup + return + + self.state = self.find_beds + + def find_beds(self): + print('Finding beds...') + w = self.g.world + p = utils.pint(self.g.pos) + + result = w.find_blocks_indexed(p, blocks.BED_IDS) + + self.beds = [] + for bed in result: + if bed not in self.bad_beds: + self.beds.append(bed) + + print('Found:', self.beds) + self.state = self.choose_bed + + def choose_bed(self): + print('Choosing a bed...') + w = self.g.world + p = utils.pint(self.g.pos) + c = self.g.chunks + + if not len(self.beds): + print('No beds') + self.state = self.select_bed + return + + bed = self.beds[0] + print('Chose:', bed) + + tmp = c.get_block_at(*bed) + c.set_block_at(*bed, blocks.AIR) + navpath = w.path_to_place(p, bed) + c.set_block_at(*bed, tmp) + + print('navpath:', navpath) + + if navpath: + self.g.path = navpath[:-1] + self.opening = self.g.path[-1] + self.g.look_at = self.opening + self.area = bed + self.state = self.going_to_bed + return + else: + self.beds.pop(0) + self.bad_beds.append(bed) + print('Cant get to bed, blacklisting') + self.state = self.select_bed + + def going_to_bed(self): + if utils.pint(self.g.pos) == self.opening: + self.my_bed = False + self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW) + self.state = self.use_bed + + def select_bed(self): + if self.g.game.select_item(items.BED_IDS): + self.state = self.find_bed_spot + else: + print('No bed, aborting.') + self.state = self.cleanup + + def find_bed_spot(self): + print('Finding a bed spot...') + w = self.g.world + p = utils.pint(self.g.pos) + + for area in w.find_bed_areas(p, 100): + print('Found area:', area) + if area not in self.bad_areas: + break + else: # for + print('Unable to find area') + self.state = self.cleanup + return + + self.area = area + openings = w.find_bed_openings(self.area) + + for o in openings: + navpath = w.path_to_place(p, o) + self.opening = o + if navpath: break + else: # for + print('Unable to get to bed area', self.area) + self.bad_areas.append(self.area) + self.state = self.cleanup + return + + self.g.path = navpath + self.g.look_at = self.opening + self.state = self.going_to_area + + def going_to_area(self): + if utils.pint(self.g.pos) == self.opening: + self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW) + self.state = self.place_bed + + def place_bed(self): + self.g.game.place_block(self.area, BlockFace.TOP) + self.my_bed = True + self.state = self.use_bed + + def use_bed(self): + if self.g.time > 12550: + print('Sleeping') + self.g.game.place_block(self.area, BlockFace.TOP) + if not self.silent: + self.g.chat.send('zzz') + self.state = self.sleep_bed + + def sleep_bed(self): + w = self.g.world + p = utils.pint(self.g.pos) + + threats = w.find_threats(p, 30) + if threats: + print('Waking up due to threats:') + print(threats) + self.g.game.leave_bed() + self.state = self.break_bed + elif self.g.time < 100: + print('Woke up time') + self.state = self.break_bed + + def break_bed(self): + if self.my_bed: + self.g.game.break_block(self.area) + self.state = self.collect_bed + else: + self.state = self.cleanup + + + def collect_bed(self): + if not self.g.breaking: + self.g.path = [utils.padd(self.area, utils.spiral(n)) for n in range(9)] + self.wait_time = 2 + self.state = self.wait + + def wait(self): + # wait to pick up bed + if self.wait_time > 0: + self.wait_time -= utils.TICK + else: + self.state = self.cleanup + + def cleanup(self): + self.g.look_at = None + 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.silent = False + + self.my_bed = False + self.beds = [] + self.bad_beds = [] + self.area = None + self.opening = None + self.bad_areas = [] + self.wait_time = 0 + + def run(self): + self.state()