Compare commits

..

No commits in common. "787474b28d171dba27d7c8cf5e1fc1a6151132b9" and "6d62c074283be4bcfaae3515f217e23c96a82f6f" have entirely different histories.

12 changed files with 121973 additions and 41 deletions

View File

@ -7,8 +7,6 @@ Assuming Debian / Ubuntu based distro:
```
$ sudo apt update
$ sudo apt install build-essential python3 python3-dev python3-pip python-virtualenv python3-virtualenv
$ bash download_mcdata.sh
$ virtualenv -p python3 env
$ source env/bin/activate
(env) $ pip install -r requirements.txt

View File

@ -37,8 +37,6 @@ import mcdata
importlib.reload(mcdata)
import mobs
importlib.reload(mobs)
import bot
importlib.reload(bot)
class MCWorld:
def __init__(self, global_state):
@ -653,7 +651,9 @@ class Game:
reply += ', I need a bed'
if command == 'stop':
bot.init(self.g)
self.g.job.stop()
self.g.path = []
self.g.look_at = None
reply = 'ok'
if command == 'drop':
@ -739,9 +739,6 @@ class Game:
print(len(navpath))
print(navpath)
print(round(time.time() - start, 3), 'seconds')
if self.g.job:
self.g.job.stop()
self.g.look_at = None
reply = 'ok'
else:
reply = 'no path'

View File

@ -56,13 +56,13 @@ ITEM_NAMES = {}
for item_name, item in ITEMS.items():
ITEM_NAMES[ITEMS[item_name]['protocol_id']] = item_name.replace('minecraft:', '')
def get_id(name):
return ITEMS['minecraft:' + name]['protocol_id']
CHEST_ID = set([ITEMS['minecraft:chest']['protocol_id']])
CHEST_ID = get_id('chest')
GAPPLE_ID = get_id('enchanted_golden_apple')
SAND_ID = get_id('sand')
NETHERWART_ID = get_id('nether_wart')
GAPPLE_ID = set([ITEMS['minecraft:enchanted_golden_apple']['protocol_id']])
NEEDED_ITEMS = BED_IDS | set([CHEST_ID])
WANTED_ITEMS = SAPLING_IDS | set([NETHERWART_ID])
SAND_ID = set([ITEMS['minecraft:sand']['protocol_id']])
NETHERWART_ID = set([ITEMS['minecraft:nether_wart']['protocol_id']])
NEEDED_ITEMS = BED_IDS | CHEST_ID
WANTED_ITEMS = SAPLING_IDS | NETHERWART_ID

39
jobs.py
View File

@ -259,9 +259,6 @@ class GatherWoodStates:
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):
@ -363,7 +360,7 @@ class GatherWoodStates:
if self.wait_time > 0:
self.wait_time -= utils.TICK
else:
self.g.chopped_tree = self.type
self.g.chopped_tree = True
self.good_trees.append(self.tree)
self.state = self.check_pos
@ -389,7 +386,6 @@ class GatherWoodStates:
self.state = self.idle
self.tree = None
self.type = None
self.openings = []
self.bad_trees = []
self.good_trees = []
@ -770,7 +766,7 @@ class CacheItemsStates:
def select_chest(self):
if self.g.game.select_item([items.CHEST_ID]):
if self.g.game.select_item(items.CHEST_ID):
self.state = self.find_cache_spot
else:
print('No chest, aborting')
@ -935,15 +931,13 @@ class PlantTreeStates:
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]):
if self.g.game.select_random_item(items.SAPLING_IDS):
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)
print('Aborting planting, no saplings')
self.state = self.cleanup
def wait_select(self):
@ -971,7 +965,7 @@ class PlantTreeStates:
w = self.g.world
p = utils.pint(self.g.pos)
for opening in w.find_tree_openings(p)[::-1]:
for opening in w.find_tree_openings(p):
print('trying sapling opening', opening)
navpath = w.path_to_place(p, opening)
if navpath:
@ -1011,19 +1005,14 @@ class ClearLeavesStates:
return None
def init(self):
if self.g.chopped_tree:
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('Aborting clearing leaves')
self.state = self.cleanup
return
self.state = self.find_leaves
print('Clearing leaves...')
num_saplings = self.g.game.count_items(items.SAPLING_IDS)
print('Have', num_saplings, 'saplings in inventory')
if num_saplings < 8:
self.state = self.find_leaves
print('Clearing leaves...')
else:
print('Aborting clearing leaves')
self.state = self.cleanup
def find_leaves(self):
w = self.g.world
@ -1502,8 +1491,8 @@ class JobStates:
def farm_wood(self):
machines = [
self.gather_wood_states,
self.clear_leaves_states,
self.plant_tree_states,
self.clear_leaves_states,
self.grab_sapling_states,
self.sleep_with_bed_states,
self.cache_items_states,

110013
old/blocks.json Normal file

File diff suppressed because it is too large Load Diff

216
old/blocks.py Normal file
View File

@ -0,0 +1,216 @@
import json
with open('blocks.json') as f:
BLOCKS = json.load(f)
AVOID = [
'minecraft:lava',
'minecraft:water',
'minecraft:fire',
'minecraft:magma_block',
'minecraft:oak_fence',
'minecraft:oak_fence_gate',
'minecraft:nether_brick_fence',
'minecraft:spruce_fence_gate',
'minecraft:birch_fence_gate',
'minecraft:jungle_fence_gate',
'minecraft:acacia_fence_gate',
'minecraft:dark_oak_fence_gate',
'minecraft:spruce_fence',
'minecraft:birch_fence',
'minecraft:jungle_fence',
'minecraft:acacia_fence',
'minecraft:dark_oak_fence',
'minecraft:sweet_berry_bush',
'minecraft:nether_portal',
'minecraft:end_portal',
'minecraft:cobblestone_wall',
'minecraft:mossy_cobblestone_wall',
'minecraft:brick_wall',
'minecraft:prismarine_wall',
'minecraft:red_sandstone_wall',
'minecraft:mossy_stone_brick_wall',
'minecraft:granite_wall',
'minecraft:stone_brick_wall',
'minecraft:nether_brick_wall',
'minecraft:andesite_wall',
'minecraft:red_nether_brick_wall',
'minecraft:sandstone_wall',
'minecraft:end_stone_brick_wall',
'minecraft:diorite_wall',
]
NON_SOLID = [
'minecraft:air',
'minecraft:powered_rail',
'minecraft:detector_rail',
'minecraft:grass',
'minecraft:fern',
'minecraft:dead_bush',
'minecraft:seagrass',
'minecraft:tall_seagrass',
'minecraft:dandelion',
'minecraft:poppy',
'minecraft:blue_orchid',
'minecraft:allium',
'minecraft:azure_bluet',
'minecraft:red_tulip',
'minecraft:orange_tulip',
'minecraft:white_tulip',
'minecraft:pink_tulip',
'minecraft:oxeye_daisy',
'minecraft:cornflower',
'minecraft:wither_rose',
'minecraft:lily_of_the_valley',
'minecraft:brown_mushroom',
'minecraft:red_mushroom',
'minecraft:torch',
'minecraft:wall_torch',
'minecraft:redstone_wire',
'minecraft:wheat',
'minecraft:oak_sign',
'minecraft:spruce_sign',
'minecraft:birch_sign',
'minecraft:acacia_sign',
'minecraft:jungle_sign',
'minecraft:dark_oak_sign',
'minecraft:rail',
'minecraft:oak_wall_sign',
'minecraft:spruce_wall_sign',
'minecraft:birch_wall_sign',
'minecraft:acacia_wall_sign',
'minecraft:jungle_wall_sign',
'minecraft:dark_oak_wall_sign',
'minecraft:lever',
'minecraft:stone_pressure_plate',
'minecraft:oak_pressure_plate',
'minecraft:spruce_pressure_plate',
'minecraft:birch_pressure_plate',
'minecraft:jungle_pressure_plate',
'minecraft:acacia_pressure_plate',
'minecraft:dark_oak_pressure_plate',
'minecraft:redstone_torch',
'minecraft:redstone_wall_torch',
'minecraft:stone_button',
'minecraft:sugar_cane',
'minecraft:repeater',
'minecraft:attached_pumpkin_stem',
'minecraft:attached_melon_stem',
'minecraft:pumpkin_stem',
'minecraft:melon_stem',
'minecraft:nether_wart',
'minecraft:tripwire_hook',
'minecraft:tripwire',
'minecraft:carrots',
'minecraft:potatoes',
'minecraft:oak_button',
'minecraft:spruce_button',
'minecraft:birch_button',
'minecraft:jungle_button',
'minecraft:acacia_button',
'minecraft:dark_oak_button',
'minecraft:light_weighted_pressure_plate',
'minecraft:heavy_weighted_pressure_plate',
'minecraft:comparator',
'minecraft:activator_rail',
'minecraft:white_carpet',
'minecraft:orange_carpet',
'minecraft:magenta_carpet',
'minecraft:light_blue_carpet',
'minecraft:yellow_carpet',
'minecraft:lime_carpet',
'minecraft:pink_carpet',
'minecraft:gray_carpet',
'minecraft:light_gray_carpet',
'minecraft:cyan_carpet',
'minecraft:purple_carpet',
'minecraft:blue_carpet',
'minecraft:brown_carpet',
'minecraft:green_carpet',
'minecraft:red_carpet',
'minecraft:black_carpet',
'minecraft:sunflower',
'minecraft:lilac',
'minecraft:rose_bush',
'minecraft:peony',
'minecraft:tall_grass',
'minecraft:large_fern',
'minecraft:white_banner',
'minecraft:orange_banner',
'minecraft:magenta_banner',
'minecraft:light_blue_banner',
'minecraft:yellow_banner',
'minecraft:lime_banner',
'minecraft:pink_banner',
'minecraft:gray_banner',
'minecraft:light_gray_banner',
'minecraft:cyan_banner',
'minecraft:purple_banner',
'minecraft:blue_banner',
'minecraft:brown_banner',
'minecraft:green_banner',
'minecraft:red_banner',
'minecraft:black_banner',
'minecraft:white_wall_banner',
'minecraft:orange_wall_banner',
'minecraft:magenta_wall_banner',
'minecraft:light_blue_wall_banner',
'minecraft:yellow_wall_banner',
'minecraft:lime_wall_banner',
'minecraft:pink_wall_banner',
'minecraft:gray_wall_banner',
'minecraft:light_gray_wall_banner',
'minecraft:cyan_wall_banner',
'minecraft:purple_wall_banner',
'minecraft:blue_wall_banner',
'minecraft:brown_wall_banner',
'minecraft:green_wall_banner',
'minecraft:red_wall_banner',
'minecraft:black_wall_banner',
'minecraft:beetroots',
'minecraft:bamboo_sapling',
'minecraft:void_air',
'minecraft:cave_air',
'minecraft:lantern',
]
SINGLE_SNOW = 3919
LOGS = [
'minecraft:oak_log',
'minecraft:spruce_log',
'minecraft:birch_log',
'minecraft:jungle_log',
'minecraft:acacia_log',
'minecraft:dark_oak_log',
]
LEAVES = [
'minecraft:oak_leaves',
'minecraft:spruce_leaves',
'minecraft:birch_leaves',
'minecraft:jungle_leaves',
'minecraft:acacia_leaves',
'minecraft:dark_oak_leaves',
]
NON_SOLID_IDS = set([SINGLE_SNOW])
for block_name in NON_SOLID:
for state in BLOCKS[block_name]['states']:
NON_SOLID_IDS.add(state['id'])
AVOID_IDS = set()
for block_name in AVOID:
for state in BLOCKS[block_name]['states']:
AVOID_IDS.add(state['id'])
LOG_IDS = set()
for block_name in LOGS:
for state in BLOCKS[block_name]['states']:
LOG_IDS.add(state['id'])
LEAF_IDS = set()
for block_name in LEAVES:
for state in BLOCKS[block_name]['states']:
LEAF_IDS.add(state['id'])

1397
old/bot.py Normal file

File diff suppressed because it is too large Load Diff

149
old/custom_packets.py Normal file
View File

@ -0,0 +1,149 @@
import minecraft.networking.packets
from minecraft.networking.packets import Packet
from minecraft.networking.types import BlockFace, VarInt, Position, Boolean, Byte, UnsignedByte, Short, TrailingByteArray, Long
from minecraft.networking.types.basic import Type
#def qot(x):
# print('qot.')
# return set()
#
#minecraft.networking.packets.clientbound.play.get_packets = qot
class AcknowledgePlayerDiggingPacket(Packet):
id = 0x08
packet_name = 'acknowledge player digging'
definition = [
{'location': Position},
{'block': VarInt},
{'status': VarInt},
{'successful': Boolean},
]
class BlockBreakAnimationPacket(Packet):
id = 0x09
packet_name = 'block break animation'
definition = [
{'entity_id': VarInt},
{'location': Position},
{'destroy_stage': Byte},
]
#class WindowItemsPacket(Packet):
# id = 0x15
# packet_name = 'window items'
# definition = [
# {'window_id': UnsignedByte},
# {'count': Short},
# {'destroy_stage': Byte},
# ]
class Slot(Type):
def __init__(self, present, item_id, item_count, nbt):
self.present = present
self.item_id = item_id
self.item_count = item_count
self.nbt = nbt
def __str__(self):
return str(self.__dict__)
def __repr__(self):
return 'Slot(present={}, item_id={}, item_count={}, nbt={}'.format(
self.present, self.item_id, self.item_count, self.nbt)
@staticmethod
def read(file_object):
present = Boolean.read(file_object)
item_id = VarInt.read(file_object) if present else None
item_count = Byte.read(file_object) if present else None
nbt = TrailingByteArray.read(file_object) if present else None
return Slot(present, item_id, item_count, nbt)
#a = {}
#a['present'] = Boolean.read(file_object)
#a['item_id'] = VarInt.read(file_object) if a['present'] else None
#a['item_count'] = Byte.read(file_object) if a['present'] else None
#a['nbt'] = TrailingByteArray.read(file_object) if a['present'] else None
#return a
@staticmethod
def send(value, socket):
# TODO
pass
class SetSlotPacket(Packet):
id = 0x17
packet_name = 'set slot'
definition = [
{'window_id': Byte},
{'slot': Short},
{'slot_data': Slot},
]
class TimeUpdatePacket(Packet):
id = 0x4F
packet_name = 'time update'
definition = [
{'world_age': Long},
{'time_of_day': Long},
]
def get_packets(old_get_packets):
def wrapper(func, context):
print('Monkey-patched.')
packets = func(context)
packets.add(AcknowledgePlayerDiggingPacket)
packets.add(BlockBreakAnimationPacket)
packets.add(SetSlotPacket)
packets.add(TimeUpdatePacket)
return packets
return lambda x: wrapper(old_get_packets, x)
minecraft.networking.packets.clientbound.play.get_packets = get_packets(minecraft.networking.packets.clientbound.play.get_packets)
class PlayerDiggingPacket(Packet):
# used when player mines / breaks blocks
# https://wiki.vg/Protocol#Player_Digging
id = 0x1A
packet_name = 'player digging'
definition = [
{'status': VarInt},
{'location': Position},
{'face': VarInt},
]
STARTED = 0
CANCELLED = 1
FINISHED = 2
# PlayerBlockPlacementPacket.Face is an alias for BlockFace.
Face = BlockFace
class PickItemPacket(Packet):
# used when player picks item (middle click)
# https://wiki.vg/Protocol#Pick_Item
id = 0x17
packet_name = 'pick item'
definition = [
{'slot_to_use': VarInt},
]
class HeldItemChangePacket(Packet):
# Sent when the player changes the slot selection
# https://wiki.vg/Protocol#Held_Item_Change_.28serverbound.29
id = 0x23
packet_name = 'held item change'
definition = [
{'slot': Short},
]

31
old/items.py Normal file
View File

@ -0,0 +1,31 @@
import json
with open('registries.json') as f:
ITEMS = json.load(f)['minecraft:item']['entries']
BEDS = [
'minecraft:white_bed',
'minecraft:orange_bed',
'minecraft:magenta_bed',
'minecraft:light_blue_bed',
'minecraft:yellow_bed',
'minecraft:lime_bed',
'minecraft:pink_bed',
'minecraft:gray_bed',
'minecraft:light_gray_bed',
'minecraft:cyan_bed',
'minecraft:purple_bed',
'minecraft:blue_bed',
'minecraft:brown_bed',
'minecraft:green_bed',
'minecraft:red_bed',
'minecraft:black_bed',
]
BED_IDS = set()
for item_name in BEDS:
BED_IDS.add(ITEMS[item_name]['protocol_id'])
ITEM_NAMES = {}
for item_name, item in ITEMS.items():
ITEM_NAMES[ITEMS[item_name]['protocol_id']] = item_name

9999
old/registries.json Normal file

File diff suppressed because it is too large Load Diff

3
old/requirements.txt Normal file
View File

@ -0,0 +1,3 @@
cryptography>=1.5
requests
future

140
old/start.py Normal file
View File

@ -0,0 +1,140 @@
#!/usr/bin/env python
from __future__ import print_function
import threading
import importlib
import getpass
import sys
import os
import re
import time
from optparse import OptionParser
import bot
from minecraft import authentication
from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection
from minecraft.networking.packets import Packet, clientbound, serverbound
from minecraft.compat import input
get_mod_time = lambda: os.path.getmtime('bot.py')
class PlayerInfo:
eid = None
pos = None
inv = {}
mcdata = None
chunks = None
player_info = PlayerInfo()
def get_options():
parser = OptionParser()
parser.add_option("-u", "--username", dest="username", default=None,
help="username to log in with")
parser.add_option("-p", "--password", dest="password", default=None,
help="password to log in with")
parser.add_option("-s", "--server", dest="server", default=None,
help="server host or host:port "
"(enclose IPv6 addresses in square brackets)")
parser.add_option("-o", "--offline", dest="offline", action="store_true",
help="connect to a server in offline mode "
"(no password required)")
parser.add_option("-d", "--dump-packets", dest="dump_packets",
action="store_true",
help="print sent and received packets to standard error")
(options, args) = parser.parse_args()
if not options.username:
options.username = input("Enter your username: ")
if not options.password and not options.offline:
options.password = getpass.getpass("Enter your password (leave "
"blank for offline mode): ")
options.offline = options.offline or (options.password == "")
if not options.server:
options.server = input("Enter server host or host:port "
"(enclose IPv6 addresses in square brackets): ")
# Try to split out port and address
match = re.match(r"((?P<host>[^\[\]:]+)|\[(?P<addr>[^\[\]]+)\])"
r"(:(?P<port>\d+))?$", options.server)
if match is None:
raise ValueError("Invalid server address: '%s'." % options.server)
options.address = match.group("host") or match.group("addr")
options.port = int(match.group("port") or 25565)
return options
def main():
global last_mod_time
options = get_options()
if options.offline:
print("Connecting in offline mode...")
connection = Connection(
options.address, options.port, username=options.username)
else:
auth_token = authentication.AuthenticationToken()
try:
auth_token.authenticate(options.username, options.password)
except YggdrasilError as e:
print(e)
sys.exit()
print("Logged in as %s..." % auth_token.username)
connection = Connection(
options.address, options.port, auth_token=auth_token)
if options.dump_packets:
def print_incoming(packet):
if type(packet) is Packet:
# This is a direct instance of the base Packet type, meaning
# that it is a packet of unknown type, so we do not print it.
return
print('--> %s' % packet, file=sys.stderr)
def print_outgoing(packet):
print('<-- %s' % packet, file=sys.stderr)
connection.register_packet_listener(
print_incoming, Packet, early=True)
connection.register_packet_listener(
print_outgoing, Packet, outgoing=True)
connection.connect()
while True:
try:
importlib.reload(bot)
bot.main(connection, player_info)
except KeyboardInterrupt:
print("Bye!")
sys.exit()
except BaseException as e:
import traceback
print(traceback.format_exc())
last_mod_time = get_mod_time()
print('locking')
while get_mod_time() == last_mod_time:
time.sleep(0.1)
if __name__ == "__main__":
main()