Detect monsters and flee to safety
This commit is contained in:
		@@ -16,12 +16,13 @@ for name, data in JSON_BLOCKS.items():
 | 
			
		||||
    for state in data['states']:
 | 
			
		||||
        BLOCKS[state['id']] = name.replace('minecraft:', '')
 | 
			
		||||
 | 
			
		||||
BREAK_DISTANCE = 5
 | 
			
		||||
BREAK_DISTANCE = 6
 | 
			
		||||
 | 
			
		||||
AIR = 0
 | 
			
		||||
SAND = 66
 | 
			
		||||
SINGLE_SNOW = 3921
 | 
			
		||||
SOUL_TORCH = 4008
 | 
			
		||||
EMERALD_BLOCK = 5407
 | 
			
		||||
 | 
			
		||||
TEST_BLOCK = (616, 78, 496)
 | 
			
		||||
 | 
			
		||||
@@ -246,6 +247,7 @@ TRAPPED_CHESTS = [
 | 
			
		||||
INDEXED = [
 | 
			
		||||
    'chest',
 | 
			
		||||
    'trapped_chest',
 | 
			
		||||
    'emerald_block',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								bot.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								bot.py
									
									
									
									
									
								
							@@ -203,6 +203,8 @@ def init(global_state):
 | 
			
		||||
    g.job = jobs.JobStates(g)
 | 
			
		||||
    g.chopped_tree = False
 | 
			
		||||
 | 
			
		||||
    g.queue_afk = False
 | 
			
		||||
 | 
			
		||||
def bot(global_state):
 | 
			
		||||
    g = global_state
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										448
									
								
								game.py
									
									
									
									
									
								
							
							
						
						
									
										448
									
								
								game.py
									
									
									
									
									
								
							@@ -2,6 +2,7 @@ import re
 | 
			
		||||
import time
 | 
			
		||||
import importlib
 | 
			
		||||
import random
 | 
			
		||||
import functools
 | 
			
		||||
from math import hypot
 | 
			
		||||
from itertools import count
 | 
			
		||||
from munch import Munch
 | 
			
		||||
@@ -19,6 +20,7 @@ from protocol.packets import (
 | 
			
		||||
    ClickWindowPacket, CloseWindowPacket, ServerWindowConfirmationPacket,
 | 
			
		||||
    ClientWindowConfirmationPacket, EntityMetadataPacket,
 | 
			
		||||
    SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket,
 | 
			
		||||
    EntityActionPacket,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
from protocol.types import Slot
 | 
			
		||||
@@ -33,6 +35,8 @@ import items
 | 
			
		||||
importlib.reload(items)
 | 
			
		||||
import data
 | 
			
		||||
importlib.reload(data)
 | 
			
		||||
import mobs
 | 
			
		||||
importlib.reload(mobs)
 | 
			
		||||
 | 
			
		||||
class MCWorld:
 | 
			
		||||
    def __init__(self, global_state):
 | 
			
		||||
@@ -41,6 +45,13 @@ class MCWorld:
 | 
			
		||||
    def block_at(self, x, y, z):
 | 
			
		||||
        return self.g.chunks.get_block_at(x, y, z)
 | 
			
		||||
 | 
			
		||||
    def check_air_column(self, pos, distance):
 | 
			
		||||
        for i in range(distance):
 | 
			
		||||
            check = utils.padd(pos, (0, i, 0))
 | 
			
		||||
            if self.block_at(*check) not in blocks.NON_SOLID_IDS:
 | 
			
		||||
                return False
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def find_blocks_3d(self, center, block_ids, distance=0, y_limit=0):
 | 
			
		||||
        for offset in utils.search_3d(distance, y_limit):
 | 
			
		||||
            check = utils.padd(center, offset)
 | 
			
		||||
@@ -189,10 +200,9 @@ class MCWorld:
 | 
			
		||||
        return safe_sand
 | 
			
		||||
 | 
			
		||||
    def check_sand_slice(self, center):
 | 
			
		||||
        # checks if a 5x5x1 slice has diggable sand in it
 | 
			
		||||
        # checks if a 5x5x1 slice has sand in it
 | 
			
		||||
        for i in range(9):
 | 
			
		||||
            s = utils.padd(center, utils.spiral(i))
 | 
			
		||||
 | 
			
		||||
            if self.block_at(*s) != blocks.SAND:
 | 
			
		||||
                continue
 | 
			
		||||
            # make sure it has solid below
 | 
			
		||||
@@ -206,18 +216,17 @@ class MCWorld:
 | 
			
		||||
                continue
 | 
			
		||||
            if not self.sand_adjacent_safe(s):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            return True
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def find_sand_slice(self, center, distance, bad_slices=[]):
 | 
			
		||||
    def find_sand_slice(self, center, distance, y_limit=0, bad_slices=[], prev_layer=0):
 | 
			
		||||
        # returns the centre coord of the next 5x5x1 slice that still has
 | 
			
		||||
        # diggable sand in it. lower slices are only valid if there's an
 | 
			
		||||
        # adjacent slice farther at the same level. this should ensure an
 | 
			
		||||
        # upside down pyramid gets excavated so the edges are still climbable
 | 
			
		||||
        for v in count():
 | 
			
		||||
            peak = utils.padd(center, (0, 20-v, 0))
 | 
			
		||||
        for v in count(prev_layer):
 | 
			
		||||
            peak = utils.padd(center, (0, 10-v, 0))
 | 
			
		||||
 | 
			
		||||
            slices = []
 | 
			
		||||
            layer = 0
 | 
			
		||||
@@ -228,14 +237,16 @@ class MCWorld:
 | 
			
		||||
                check = utils.padd(peak, offset)
 | 
			
		||||
                check = utils.padd(check, (0, layer, 0))
 | 
			
		||||
 | 
			
		||||
                if utils.phyp(center, check) >= distance:
 | 
			
		||||
                if y_limit and check[1] - center[1] > y_limit:
 | 
			
		||||
                    break
 | 
			
		||||
                if utils.phyp_king(center, check) > distance:
 | 
			
		||||
                    break
 | 
			
		||||
 | 
			
		||||
                if self.check_sand_slice(check) and check not in bad_slices:
 | 
			
		||||
                    slices.append(check)
 | 
			
		||||
 | 
			
		||||
            if len(slices):
 | 
			
		||||
                return slices[-1]
 | 
			
		||||
                return v, slices[-1]
 | 
			
		||||
            elif v > 40:
 | 
			
		||||
                return None, None
 | 
			
		||||
 | 
			
		||||
@@ -262,6 +273,31 @@ class MCWorld:
 | 
			
		||||
        for a in self.find_blocks_3d(center, blocks.LEAF_IDS, distance, 10):
 | 
			
		||||
            yield a
 | 
			
		||||
 | 
			
		||||
    def find_monsters(self, center, distance):
 | 
			
		||||
        # finds monsters within distance
 | 
			
		||||
        result = []
 | 
			
		||||
        for eid, mob in self.g.mobs.items():
 | 
			
		||||
            if mob.type not in mobs.EVIL_IDS:
 | 
			
		||||
                continue
 | 
			
		||||
            pos = utils.pint((mob.x, mob.y, mob.z))
 | 
			
		||||
            if utils.phyp(center, pos) > distance:
 | 
			
		||||
                continue
 | 
			
		||||
            result.append(mob)
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def find_threats(self, center, distance):
 | 
			
		||||
        # finds monsters on the surface within distance
 | 
			
		||||
        monsters = self.find_monsters(center, distance)
 | 
			
		||||
        result = []
 | 
			
		||||
        for mob in monsters:
 | 
			
		||||
            pos = utils.pint((mob.x, mob.y, mob.z))
 | 
			
		||||
            # check distance number of blocks above, close enough?
 | 
			
		||||
            if not self.check_air_column(pos, distance):
 | 
			
		||||
                continue
 | 
			
		||||
            result.append(mob)
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Game:
 | 
			
		||||
    def __init__(self, global_state):
 | 
			
		||||
@@ -365,7 +401,7 @@ class Game:
 | 
			
		||||
        else:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if text == 'zzz':
 | 
			
		||||
        if text.startswith('zzz'):
 | 
			
		||||
            text = '!zzz'
 | 
			
		||||
 | 
			
		||||
        if text.startswith('! '):
 | 
			
		||||
@@ -382,166 +418,205 @@ class Game:
 | 
			
		||||
            command = text
 | 
			
		||||
            data = None
 | 
			
		||||
 | 
			
		||||
        if command == 'ping':
 | 
			
		||||
            reply = 'pong'
 | 
			
		||||
        try:
 | 
			
		||||
            if command == 'ping':
 | 
			
		||||
                reply = 'pong'
 | 
			
		||||
 | 
			
		||||
        if command == 'echo' and data:
 | 
			
		||||
            reply = data
 | 
			
		||||
            if command == 'echo' and data:
 | 
			
		||||
                reply = data
 | 
			
		||||
 | 
			
		||||
        if command == 'respawn':
 | 
			
		||||
            packet = serverbound.play.ClientStatusPacket()
 | 
			
		||||
            packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
 | 
			
		||||
            self.g.connection.write_packet(packet)
 | 
			
		||||
            reply = 'ok'
 | 
			
		||||
 | 
			
		||||
        if command == 'pos':
 | 
			
		||||
            reply = str(utils.pint(self.g.pos))[1:-1]
 | 
			
		||||
 | 
			
		||||
        if command == 'afk':
 | 
			
		||||
            reply = '/afk'
 | 
			
		||||
 | 
			
		||||
        if command == 'error':
 | 
			
		||||
            reply = 'ok'
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
        if command == 'break':
 | 
			
		||||
            self.break_block(blocks.TEST_BLOCK)
 | 
			
		||||
            reply = 'ok'
 | 
			
		||||
 | 
			
		||||
        if command == 'gather' and data:
 | 
			
		||||
            if data == 'wood':
 | 
			
		||||
                self.g.job.state = self.g.job.gather_wood
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
            elif data == 'sand':
 | 
			
		||||
                self.g.job.state = self.g.job.gather_sand
 | 
			
		||||
            if command == 'respawn':
 | 
			
		||||
                packet = serverbound.play.ClientStatusPacket()
 | 
			
		||||
                packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
 | 
			
		||||
                self.g.connection.write_packet(packet)
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
 | 
			
		||||
            if reply:
 | 
			
		||||
                for i in self.g.inv.values():
 | 
			
		||||
                    print(i.item_id)
 | 
			
		||||
                    if i.item_id in items.BED_IDS:
 | 
			
		||||
                        break
 | 
			
		||||
                else:
 | 
			
		||||
                    reply += ', I need a bed'
 | 
			
		||||
            if command == 'pos':
 | 
			
		||||
                reply = str(utils.pint(self.g.pos))[1:-1]
 | 
			
		||||
 | 
			
		||||
        if command == 'farm' and data:
 | 
			
		||||
            if data == 'wood':
 | 
			
		||||
                self.g.job.state = self.g.job.farm_wood
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
 | 
			
		||||
            if reply:
 | 
			
		||||
                for i in self.g.inv.values():
 | 
			
		||||
                    print(i.item_id)
 | 
			
		||||
                    if i.item_id in items.BED_IDS:
 | 
			
		||||
                        break
 | 
			
		||||
                else:
 | 
			
		||||
                    reply += ', I need a bed'
 | 
			
		||||
 | 
			
		||||
        if command == 'stop':
 | 
			
		||||
            self.g.job.stop()
 | 
			
		||||
            self.g.path = []
 | 
			
		||||
            self.g.look_at = None
 | 
			
		||||
            reply = 'ok'
 | 
			
		||||
 | 
			
		||||
        if command == 'inv':
 | 
			
		||||
            inv_list = []
 | 
			
		||||
            for i in self.g.inv.values():
 | 
			
		||||
                if i.present:
 | 
			
		||||
                    inv_list.append('{}:{} x {}'.format(items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count))
 | 
			
		||||
            inv_list.sort()
 | 
			
		||||
            result = '\n'.join(inv_list)
 | 
			
		||||
            print(result or 'Empty')
 | 
			
		||||
 | 
			
		||||
        if command == 'drop':
 | 
			
		||||
            self.drop_stack()
 | 
			
		||||
 | 
			
		||||
        if command == 'time':
 | 
			
		||||
            reply = str(self.g.time)
 | 
			
		||||
 | 
			
		||||
        if command == 'select' and data:
 | 
			
		||||
            item = int(data)
 | 
			
		||||
            if self.select_item([item]):
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
            else:
 | 
			
		||||
                reply = 'not found'
 | 
			
		||||
 | 
			
		||||
        if command == 'dump' and data:
 | 
			
		||||
            item = int(data)
 | 
			
		||||
            if self.count_items([item]):
 | 
			
		||||
                self.g.dumping = item
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
            else:
 | 
			
		||||
                reply = 'not found'
 | 
			
		||||
 | 
			
		||||
        if command == 'count' and data:
 | 
			
		||||
            item = int(data)
 | 
			
		||||
            reply = str(self.count_items([item]))
 | 
			
		||||
 | 
			
		||||
        if command == 'open':
 | 
			
		||||
            self.open_container(blocks.TEST_BLOCK)
 | 
			
		||||
 | 
			
		||||
        if command == 'close':
 | 
			
		||||
            if self.g.window:
 | 
			
		||||
                self.close_window()
 | 
			
		||||
            else:
 | 
			
		||||
                reply = 'nothing open'
 | 
			
		||||
 | 
			
		||||
        if command == 'click' and data:
 | 
			
		||||
            if self.g.window:
 | 
			
		||||
                slot, button, mode = [int(x) for x in data.split(' ')]
 | 
			
		||||
                try:
 | 
			
		||||
                    item = self.g.window.contents[slot]
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    item = Slot(present=False, item_id=None, item_count=None, nbt=None)
 | 
			
		||||
                print(item)
 | 
			
		||||
                self.click_window(slot, button, mode, item)
 | 
			
		||||
            else:
 | 
			
		||||
                reply = 'nothing open'
 | 
			
		||||
 | 
			
		||||
        if command == 'loaded':
 | 
			
		||||
            reply = str(self.g.chunks.get_loaded_area())
 | 
			
		||||
 | 
			
		||||
        if command == 'gapple':
 | 
			
		||||
            self.g.job.state = self.g.job.find_gapple
 | 
			
		||||
            if data:
 | 
			
		||||
                self.g.job.find_gapple_states.count = int(data)
 | 
			
		||||
            reply = 'ok'
 | 
			
		||||
 | 
			
		||||
        if command == 'objects':
 | 
			
		||||
            for k, v in self.g.objects.items():
 | 
			
		||||
                if data and v.item_id != int(data): continue
 | 
			
		||||
                print(str(k) + ':', v)
 | 
			
		||||
 | 
			
		||||
        if command == 'cache':
 | 
			
		||||
            self.g.job.state = self.g.job.cache_items
 | 
			
		||||
            self.g.job.cache_items_states.minimum = 0
 | 
			
		||||
            self.g.job.cache_items_states.silent = True
 | 
			
		||||
            reply = 'ok'
 | 
			
		||||
 | 
			
		||||
        if command == 'spiral' and data:
 | 
			
		||||
            for i in range(int(data)):
 | 
			
		||||
                print(utils.spiral(i))
 | 
			
		||||
 | 
			
		||||
        if command == 'sand_slice':
 | 
			
		||||
            try:
 | 
			
		||||
                result = self.g.world.find_sand_slice(utils.pint(self.g.pos), 50)
 | 
			
		||||
                reply = str(result)
 | 
			
		||||
            except:
 | 
			
		||||
                import traceback
 | 
			
		||||
                print(traceback.format_exc())
 | 
			
		||||
                reply = 'error'
 | 
			
		||||
 | 
			
		||||
        if command == 'zzz':
 | 
			
		||||
            if not self.g.afk:
 | 
			
		||||
            if command == 'afk':
 | 
			
		||||
                reply = '/afk'
 | 
			
		||||
 | 
			
		||||
        if command == 'print':
 | 
			
		||||
            try:
 | 
			
		||||
            if command == 'error':
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
                raise
 | 
			
		||||
 | 
			
		||||
            if command == 'break':
 | 
			
		||||
                self.break_block(blocks.TEST_BLOCK)
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
 | 
			
		||||
            if command == 'gather' and data:
 | 
			
		||||
                if data == 'wood':
 | 
			
		||||
                    self.g.job.state = self.g.job.gather_wood
 | 
			
		||||
                    reply = 'ok'
 | 
			
		||||
                elif data == 'sand':
 | 
			
		||||
                    self.g.job.state = self.g.job.gather_sand
 | 
			
		||||
                    reply = 'ok'
 | 
			
		||||
 | 
			
		||||
                if reply:
 | 
			
		||||
                    for i in self.g.inv.values():
 | 
			
		||||
                        print(i.item_id)
 | 
			
		||||
                        if i.item_id in items.BED_IDS:
 | 
			
		||||
                            break
 | 
			
		||||
                    else:
 | 
			
		||||
                        reply += ', I need a bed'
 | 
			
		||||
 | 
			
		||||
            if command == 'farm' and data:
 | 
			
		||||
                if data == 'wood':
 | 
			
		||||
                    self.g.job.state = self.g.job.farm_wood
 | 
			
		||||
                    reply = 'ok'
 | 
			
		||||
                elif data == 'sand':
 | 
			
		||||
                    self.g.job.state = self.g.job.farm_sand
 | 
			
		||||
                    reply = 'ok'
 | 
			
		||||
 | 
			
		||||
                if reply:
 | 
			
		||||
                    for i in self.g.inv.values():
 | 
			
		||||
                        if i.item_id in items.BED_IDS:
 | 
			
		||||
                            break
 | 
			
		||||
                    else:
 | 
			
		||||
                        reply += ', I need a bed'
 | 
			
		||||
 | 
			
		||||
            if command == 'stop':
 | 
			
		||||
                self.g.job.stop()
 | 
			
		||||
                self.g.path = []
 | 
			
		||||
                self.g.look_at = None
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
 | 
			
		||||
            if command == 'inv':
 | 
			
		||||
                inv_list = []
 | 
			
		||||
                for i in self.g.inv.values():
 | 
			
		||||
                    if i.present:
 | 
			
		||||
                        inv_list.append('{}:{} x {}'.format(items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count))
 | 
			
		||||
                inv_list.sort()
 | 
			
		||||
                result = '\n'.join(inv_list)
 | 
			
		||||
                print(result or 'Empty')
 | 
			
		||||
 | 
			
		||||
            if command == 'drop':
 | 
			
		||||
                self.drop_stack()
 | 
			
		||||
 | 
			
		||||
            if command == 'time':
 | 
			
		||||
                reply = str(self.g.time)
 | 
			
		||||
 | 
			
		||||
            if command == 'select' and data:
 | 
			
		||||
                item = int(data)
 | 
			
		||||
                if self.select_item([item]):
 | 
			
		||||
                    reply = 'ok'
 | 
			
		||||
                else:
 | 
			
		||||
                    reply = 'not found'
 | 
			
		||||
 | 
			
		||||
            if command == 'dump' and data:
 | 
			
		||||
                item = int(data)
 | 
			
		||||
                if self.count_items([item]):
 | 
			
		||||
                    self.g.dumping = item
 | 
			
		||||
                    reply = 'ok'
 | 
			
		||||
                else:
 | 
			
		||||
                    reply = 'not found'
 | 
			
		||||
 | 
			
		||||
            if command == 'count' and data:
 | 
			
		||||
                item = int(data)
 | 
			
		||||
                reply = str(self.count_items([item]))
 | 
			
		||||
 | 
			
		||||
            if command == 'open':
 | 
			
		||||
                self.open_container(blocks.TEST_BLOCK)
 | 
			
		||||
 | 
			
		||||
            if command == 'close':
 | 
			
		||||
                if self.g.window:
 | 
			
		||||
                    self.close_window()
 | 
			
		||||
                else:
 | 
			
		||||
                    reply = 'nothing open'
 | 
			
		||||
 | 
			
		||||
            if command == 'click' and data:
 | 
			
		||||
                if self.g.window:
 | 
			
		||||
                    slot, button, mode = [int(x) for x in data.split(' ')]
 | 
			
		||||
                    try:
 | 
			
		||||
                        item = self.g.window.contents[slot]
 | 
			
		||||
                    except KeyError:
 | 
			
		||||
                        item = Slot(present=False, item_id=None, item_count=None, nbt=None)
 | 
			
		||||
                    print(item)
 | 
			
		||||
                    self.click_window(slot, button, mode, item)
 | 
			
		||||
                else:
 | 
			
		||||
                    reply = 'nothing open'
 | 
			
		||||
 | 
			
		||||
            if command == 'loaded':
 | 
			
		||||
                reply = str(self.g.chunks.get_loaded_area())
 | 
			
		||||
 | 
			
		||||
            if command == 'gapple':
 | 
			
		||||
                self.g.job.state = self.g.job.find_gapple
 | 
			
		||||
                if data:
 | 
			
		||||
                    self.g.job.find_gapple_states.count = int(data)
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
 | 
			
		||||
            if command == 'objects':
 | 
			
		||||
                if data == 'clear':
 | 
			
		||||
                    self.g.objects = {}
 | 
			
		||||
                    reply = 'ok'
 | 
			
		||||
                else:
 | 
			
		||||
                    for k, v in self.g.objects.items():
 | 
			
		||||
                        if data and v.item_id != int(data): continue
 | 
			
		||||
                        print(str(k) + ':', v, items.ITEM_NAMES[v.item_id])
 | 
			
		||||
 | 
			
		||||
            if command == 'mobs':
 | 
			
		||||
                if data == 'clear':
 | 
			
		||||
                    self.g.mobs = {}
 | 
			
		||||
                    reply = 'ok'
 | 
			
		||||
                else:
 | 
			
		||||
                    all_mobs = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
 | 
			
		||||
                    for k, v in all_mobs:
 | 
			
		||||
                        if data and v.type != int(data): continue
 | 
			
		||||
                        print(str(k) + ':', v, mobs.MOB_NAMES[v.type])
 | 
			
		||||
                    reply = str(len(all_mobs)) + ' mobs'
 | 
			
		||||
 | 
			
		||||
            if command == 'monsters':
 | 
			
		||||
                monsters = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
 | 
			
		||||
                count = 0
 | 
			
		||||
                for k, v in monsters:
 | 
			
		||||
                    if v.type not in mobs.EVIL_IDS: continue
 | 
			
		||||
                    if data and v.type != int(data): continue
 | 
			
		||||
                    count += 1
 | 
			
		||||
                    print(str(k) + ':', v, mobs.MOB_NAMES[v.type])
 | 
			
		||||
                reply = str(count) + ' monsters'
 | 
			
		||||
 | 
			
		||||
            if command == 'threats':
 | 
			
		||||
                distance = int(data) if data else 20
 | 
			
		||||
                p = utils.pint(self.g.pos)
 | 
			
		||||
                threats = self.g.world.find_threats(p, distance)
 | 
			
		||||
 | 
			
		||||
                for t in threats:
 | 
			
		||||
                    print(str(t.entity_id) + ':', t, mobs.MOB_NAMES[t.type])
 | 
			
		||||
                reply = str(len(threats)) + ' threats'
 | 
			
		||||
 | 
			
		||||
            if command == 'cache':
 | 
			
		||||
                self.g.job.state = self.g.job.cache_items
 | 
			
		||||
                self.g.job.cache_items_states.minimum = 0
 | 
			
		||||
                self.g.job.cache_items_states.silent = True
 | 
			
		||||
                reply = 'ok'
 | 
			
		||||
 | 
			
		||||
            if command == 'spiral' and data:
 | 
			
		||||
                for i in range(int(data)):
 | 
			
		||||
                    print(utils.spiral(i))
 | 
			
		||||
 | 
			
		||||
            if command == 'sand_slice':
 | 
			
		||||
                result = self.g.world.find_sand_slice(utils.pint(self.g.pos), 50)
 | 
			
		||||
                reply = str(result)
 | 
			
		||||
 | 
			
		||||
            if command == 'zzz':
 | 
			
		||||
                if not self.g.afk:
 | 
			
		||||
                    if self.g.path:
 | 
			
		||||
                        travel_time = int(len(self.g.path) * 0.4) + 2
 | 
			
		||||
                        reply = 'give me ' + str(travel_time) + ' secs, moving'
 | 
			
		||||
                        self.g.queue_afk = True
 | 
			
		||||
                    else:
 | 
			
		||||
                        reply = '/afk'
 | 
			
		||||
 | 
			
		||||
            if command == 'print' and sender == 'tanner6':
 | 
			
		||||
                data = data.replace('^', '.')
 | 
			
		||||
                reply = str(eval(data))
 | 
			
		||||
            except BaseException as e:
 | 
			
		||||
                import traceback
 | 
			
		||||
                print(traceback.format_exc())
 | 
			
		||||
                reply = 'Error: {} - {}\n'.format(e.__class__.__name__, e)
 | 
			
		||||
 | 
			
		||||
        except BaseException as e:
 | 
			
		||||
            import traceback
 | 
			
		||||
            print(traceback.format_exc())
 | 
			
		||||
            reply = 'Error: {} - {}\n'.format(e.__class__.__name__, e)
 | 
			
		||||
            pass
 | 
			
		||||
 | 
			
		||||
        if reply:
 | 
			
		||||
            print(reply)
 | 
			
		||||
@@ -567,8 +642,8 @@ class Game:
 | 
			
		||||
 | 
			
		||||
    def break_block(self, location):
 | 
			
		||||
        p = utils.pint(self.g.pos)
 | 
			
		||||
        if utils.phyp(p, location) > blocks.BREAK_DISTANCE:
 | 
			
		||||
            return False
 | 
			
		||||
        #if utils.phyp(p, location) > blocks.BREAK_DISTANCE + 1:
 | 
			
		||||
        #    return False
 | 
			
		||||
 | 
			
		||||
        bid = self.g.chunks.get_block_at(*location)
 | 
			
		||||
        if bid == 0:
 | 
			
		||||
@@ -596,6 +671,7 @@ class Game:
 | 
			
		||||
            self.g.breaking = None
 | 
			
		||||
 | 
			
		||||
    def handle_break_animation(self, packet):
 | 
			
		||||
        return
 | 
			
		||||
        print(packet)
 | 
			
		||||
 | 
			
		||||
    def handle_break_ack(self, packet):
 | 
			
		||||
@@ -761,22 +837,28 @@ class Game:
 | 
			
		||||
                obj.item_count = entry.value.item_count
 | 
			
		||||
 | 
			
		||||
    def handle_spawn_living(self, packet):
 | 
			
		||||
        print(packet)
 | 
			
		||||
        return
 | 
			
		||||
        self.g.mobs[packet.entity_id] = Munch(
 | 
			
		||||
            entity_id=packet.entity_id,
 | 
			
		||||
            entity_uuid=packet.entity_uuid,
 | 
			
		||||
            type=packet.type,
 | 
			
		||||
            x=packet.x,
 | 
			
		||||
            y=packet.y,
 | 
			
		||||
            z=packet.z,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def handle_entity_position(self, packet):
 | 
			
		||||
        obj = self.g.objects.get(packet.entity_id, None)
 | 
			
		||||
        if obj:
 | 
			
		||||
            pass
 | 
			
		||||
            #obj.x += packet.delta_x
 | 
			
		||||
            #obj.y += packet.delta_y
 | 
			
		||||
            #obj.z += packet.delta_z
 | 
			
		||||
        mob = self.g.mobs.get(packet.entity_id, None)
 | 
			
		||||
        if mob:
 | 
			
		||||
            mob.x += packet.delta_x / 4096.0
 | 
			
		||||
            mob.y += packet.delta_y / 4096.0
 | 
			
		||||
            mob.z += packet.delta_z / 4096.0
 | 
			
		||||
 | 
			
		||||
    def handle_entity_position_rotation(self, packet):
 | 
			
		||||
        obj = self.g.objects.get(packet.entity_id, None)
 | 
			
		||||
        if obj:
 | 
			
		||||
            print('object rotation found:', packet)
 | 
			
		||||
            raise
 | 
			
		||||
        mob = self.g.mobs.get(packet.entity_id, None)
 | 
			
		||||
        if mob:
 | 
			
		||||
            mob.x += packet.delta_x / 4096.0
 | 
			
		||||
            mob.y += packet.delta_y / 4096.0
 | 
			
		||||
            mob.z += packet.delta_z / 4096.0
 | 
			
		||||
 | 
			
		||||
    def handle_entity_velocity(self, packet):
 | 
			
		||||
        obj = self.g.objects.get(packet.entity_id, None)
 | 
			
		||||
@@ -790,6 +872,16 @@ class Game:
 | 
			
		||||
        for eid in packet.entity_ids:
 | 
			
		||||
            if eid in self.g.objects:
 | 
			
		||||
                del self.g.objects[eid]
 | 
			
		||||
            if eid in self.g.mobs:
 | 
			
		||||
                del self.g.mobs[eid]
 | 
			
		||||
 | 
			
		||||
    def leave_bed(self):
 | 
			
		||||
        packet = EntityActionPacket()
 | 
			
		||||
        packet.entity_id = self.g.eid
 | 
			
		||||
        packet.action_id = 2
 | 
			
		||||
        packet.jump_boost = 0
 | 
			
		||||
        self.g.connection.write_packet(packet)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def tick(self):
 | 
			
		||||
        if self.g.breaking:
 | 
			
		||||
@@ -805,6 +897,10 @@ class Game:
 | 
			
		||||
            else:
 | 
			
		||||
                self.g.dumping = None
 | 
			
		||||
 | 
			
		||||
        if not len(self.g.path):
 | 
			
		||||
        if not self.g.path:
 | 
			
		||||
            self.g.correction_count = 0
 | 
			
		||||
 | 
			
		||||
            if self.g.queue_afk:
 | 
			
		||||
                self.g.chat.send('/afk')
 | 
			
		||||
                self.g.queue_afk = False
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										153
									
								
								jobs.py
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								jobs.py
									
									
									
									
									
								
							@@ -20,6 +20,8 @@ import items
 | 
			
		||||
importlib.reload(items)
 | 
			
		||||
import data
 | 
			
		||||
importlib.reload(data)
 | 
			
		||||
import mobs
 | 
			
		||||
importlib.reload(mobs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class FindGappleStates:
 | 
			
		||||
@@ -308,12 +310,13 @@ class GatherSandStates:
 | 
			
		||||
        w = self.g.world
 | 
			
		||||
 | 
			
		||||
        print('using origin', self.origin)
 | 
			
		||||
        s = w.find_sand_slice(self.origin, 50, self.bad_slices)
 | 
			
		||||
        print('Found slice:', s)
 | 
			
		||||
        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.bad_slices.append(s)
 | 
			
		||||
            self.state = self.find_new_sand
 | 
			
		||||
        else:
 | 
			
		||||
            print('No slices remaining.')
 | 
			
		||||
@@ -323,24 +326,23 @@ class GatherSandStates:
 | 
			
		||||
        print('Finding new sand...')
 | 
			
		||||
        w = self.g.world
 | 
			
		||||
        p = utils.pint(self.g.pos)
 | 
			
		||||
        head = utils.padd(p, path.BLOCK_ABOVE)
 | 
			
		||||
 | 
			
		||||
        sand = w.find_sand(self.slice, 2, p)
 | 
			
		||||
        print('Found sand:', sand)
 | 
			
		||||
 | 
			
		||||
        if not len(sand):
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        for check in sand:
 | 
			
		||||
            if check in self.bad_sand:
 | 
			
		||||
                continue
 | 
			
		||||
            self.sand = check
 | 
			
		||||
            break
 | 
			
		||||
        self.sand = sand
 | 
			
		||||
 | 
			
		||||
        if utils.phyp(p, self.sand) > blocks.BREAK_DISTANCE:
 | 
			
		||||
            self.state = self.nav_to_sand
 | 
			
		||||
        else:
 | 
			
		||||
        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
 | 
			
		||||
@@ -356,6 +358,7 @@ class GatherSandStates:
 | 
			
		||||
            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
 | 
			
		||||
 | 
			
		||||
@@ -387,8 +390,10 @@ class GatherSandStates:
 | 
			
		||||
        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
 | 
			
		||||
@@ -519,7 +524,7 @@ class SleepWithBedStates:
 | 
			
		||||
 | 
			
		||||
    def going_to_area(self):
 | 
			
		||||
        if utils.pint(self.g.pos) == self.opening:
 | 
			
		||||
            self.g.look_at = self.area
 | 
			
		||||
            self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW)
 | 
			
		||||
            self.state = self.place_bed
 | 
			
		||||
 | 
			
		||||
    def place_bed(self):
 | 
			
		||||
@@ -535,8 +540,16 @@ class SleepWithBedStates:
 | 
			
		||||
            self.state = self.sleep_bed
 | 
			
		||||
 | 
			
		||||
    def sleep_bed(self):
 | 
			
		||||
        if self.g.time < 100:
 | 
			
		||||
            print('Woke up')
 | 
			
		||||
        w = self.g.world
 | 
			
		||||
        p = utils.pint(self.g.pos)
 | 
			
		||||
 | 
			
		||||
        threats = w.find_threats(p, 30)
 | 
			
		||||
        if threats:
 | 
			
		||||
            print('Waking up due to 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):
 | 
			
		||||
@@ -1004,6 +1017,96 @@ class GrabSaplingStates:
 | 
			
		||||
        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')
 | 
			
		||||
            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 JobStates:
 | 
			
		||||
    def idle(self):
 | 
			
		||||
        return []
 | 
			
		||||
@@ -1018,6 +1121,7 @@ class JobStates:
 | 
			
		||||
        self.clear_leaves_states = ClearLeavesStates(self.g)
 | 
			
		||||
        self.grab_sapling_states = GrabSaplingStates(self.g)
 | 
			
		||||
        self.grab_sand_states = GrabSandStates(self.g)
 | 
			
		||||
        self.check_threats_states = CheckThreatsStates(self.g)
 | 
			
		||||
 | 
			
		||||
    def run_machines(self, machines):
 | 
			
		||||
        for m in machines:
 | 
			
		||||
@@ -1040,6 +1144,18 @@ class JobStates:
 | 
			
		||||
        ]
 | 
			
		||||
        return machines
 | 
			
		||||
 | 
			
		||||
    def farm_sand(self):
 | 
			
		||||
        machines = [
 | 
			
		||||
            self.check_threats_states,
 | 
			
		||||
            self.gather_sand_states,
 | 
			
		||||
            self.grab_sand_states,
 | 
			
		||||
            self.cache_items_states,
 | 
			
		||||
            self.sleep_with_bed_states,
 | 
			
		||||
        ]
 | 
			
		||||
        self.sleep_with_bed_states.silent = True
 | 
			
		||||
        self.cache_items_states.silent = True
 | 
			
		||||
        return machines
 | 
			
		||||
 | 
			
		||||
    def cache_items(self):
 | 
			
		||||
        machines = [
 | 
			
		||||
            self.cache_items_states,
 | 
			
		||||
@@ -1070,7 +1186,6 @@ class JobStates:
 | 
			
		||||
            self.sleep_with_bed_states,
 | 
			
		||||
            self.cache_items_states,
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        self.sleep_with_bed_states.silent = True
 | 
			
		||||
        self.cache_items_states.silent = True
 | 
			
		||||
        return machines
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										50
									
								
								mobs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								mobs.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
with open('mcdata/registries.json') as f:
 | 
			
		||||
    MOBS = json.load(f)['minecraft:entity_type']['entries']
 | 
			
		||||
 | 
			
		||||
EVIL = [
 | 
			
		||||
    'blaze',
 | 
			
		||||
    'cave_spider',
 | 
			
		||||
    'creeper',
 | 
			
		||||
    'drowned',
 | 
			
		||||
    'elder_guardian',
 | 
			
		||||
    'ender_dragon',
 | 
			
		||||
    'enderman',
 | 
			
		||||
    'endermite',
 | 
			
		||||
    'evoker',
 | 
			
		||||
    'ghast',
 | 
			
		||||
    'giant',
 | 
			
		||||
    'guardian',
 | 
			
		||||
    'hoglin',
 | 
			
		||||
    'husk',
 | 
			
		||||
    'illusioner',
 | 
			
		||||
    'magma_cube',
 | 
			
		||||
    'phantom',
 | 
			
		||||
    'piglin',
 | 
			
		||||
    'piglin_brute',
 | 
			
		||||
    'pillager',
 | 
			
		||||
    'ravager',
 | 
			
		||||
    'shulker',
 | 
			
		||||
    'silverfish',
 | 
			
		||||
    'skeleton',
 | 
			
		||||
    'skeleton_horse',
 | 
			
		||||
    'slime',
 | 
			
		||||
    'spider',
 | 
			
		||||
    'stray',
 | 
			
		||||
    'vex',
 | 
			
		||||
    'vindicator',
 | 
			
		||||
    'witch',
 | 
			
		||||
    'wither',
 | 
			
		||||
    'zoglin',
 | 
			
		||||
    'zombie',
 | 
			
		||||
    'zombie_villager',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
EVIL_IDS = set()
 | 
			
		||||
for mob_name in EVIL:
 | 
			
		||||
    EVIL_IDS.add(MOBS['minecraft:'+mob_name]['protocol_id'])
 | 
			
		||||
 | 
			
		||||
MOB_NAMES = {}
 | 
			
		||||
for mob_name, mob in MOBS.items():
 | 
			
		||||
    MOB_NAMES[MOBS[mob_name]['protocol_id']] = mob_name.replace('minecraft:', '')
 | 
			
		||||
@@ -23,6 +23,7 @@ def get_packets(old_get_packets):
 | 
			
		||||
        mc_packets.add(packets.SpawnLivingEntityPacket)
 | 
			
		||||
        mc_packets.add(packets.EntityPositionRotationPacket)
 | 
			
		||||
        mc_packets.add(packets.DestroyEntitiesPacket)
 | 
			
		||||
        mc_packets.add(packets.EntityActionPacket)
 | 
			
		||||
 | 
			
		||||
        return mc_packets
 | 
			
		||||
    return lambda x: wrapper(old_get_packets, x)
 | 
			
		||||
 
 | 
			
		||||
@@ -361,3 +361,16 @@ class DestroyEntitiesPacket(Packet):
 | 
			
		||||
        for _ in range(self.count):
 | 
			
		||||
            eid = VarInt.read(file_object)
 | 
			
		||||
            self.entity_ids.append(eid)
 | 
			
		||||
 | 
			
		||||
class EntityActionPacket(Packet):
 | 
			
		||||
    # Sent by the client when it performs an action
 | 
			
		||||
    # https://wiki.vg/Protocol#Entity_Action
 | 
			
		||||
 | 
			
		||||
    id = 0x1C
 | 
			
		||||
    packet_name = 'entity action'
 | 
			
		||||
 | 
			
		||||
    definition = [
 | 
			
		||||
        {'entity_id': VarInt},
 | 
			
		||||
        {'action_id': VarInt},
 | 
			
		||||
        {'jump_boost': VarInt},
 | 
			
		||||
    ]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user