Move bot files into mosfet/

This commit is contained in:
2021-04-21 22:09:28 +00:00
parent 4d2b09e578
commit 3c373ccfe3
36 changed files with 0 additions and 157 deletions

0
mosfet/__init__.py Normal file
View File

302
mosfet/blocks.py Normal file
View File

@@ -0,0 +1,302 @@
import json
import importlib
import mcdata
MCD_BLOCKS = {}
for d in mcdata.mcd.blocks.values():
MCD_BLOCKS[d['name']] = d
with open('minecraft_data/blocks.json') as f:
JSON_BLOCKS = json.load(f)
BLOCKS = {}
for name, data in JSON_BLOCKS.items():
for state in data['states']:
BLOCKS[state['id']] = name.replace('minecraft:', '')
BREAK_DISTANCE = 6
AIR = 0
STONE = 1
SAND = 66
SINGLE_SNOW = 3921
SOUL_TORCH = 4008
EMERALD_BLOCK = 5407
TEST_BLOCK = (616, 78, 496)
AVOID = [
'lava',
'water',
'fire',
'magma_block',
'oak_fence',
'oak_fence_gate',
'nether_brick_fence',
'spruce_fence_gate',
'birch_fence_gate',
'jungle_fence_gate',
'acacia_fence_gate',
'dark_oak_fence_gate',
'spruce_fence',
'birch_fence',
'jungle_fence',
'acacia_fence',
'dark_oak_fence',
'sweet_berry_bush',
'nether_portal',
'end_portal',
'cobblestone_wall',
'mossy_cobblestone_wall',
'brick_wall',
'prismarine_wall',
'red_sandstone_wall',
'mossy_stone_brick_wall',
'granite_wall',
'stone_brick_wall',
'nether_brick_wall',
'andesite_wall',
'red_nether_brick_wall',
'sandstone_wall',
'end_stone_brick_wall',
'diorite_wall',
'oak_sapling', # saplings can grow up and hurt
'spruce_sapling',
'birch_sapling',
'jungle_sapling',
'acacia_sapling',
'dark_oak_sapling',
]
NON_SOLID = [
'air',
'powered_rail',
'detector_rail',
'grass',
'fern',
'dead_bush',
'seagrass',
'tall_seagrass',
'dandelion',
'poppy',
'blue_orchid',
'allium',
'azure_bluet',
'red_tulip',
'orange_tulip',
'white_tulip',
'pink_tulip',
'oxeye_daisy',
'cornflower',
'wither_rose',
'lily_of_the_valley',
'brown_mushroom',
'red_mushroom',
'torch',
'wall_torch',
'redstone_wire',
'wheat',
'oak_sign',
'spruce_sign',
'birch_sign',
'acacia_sign',
'jungle_sign',
'dark_oak_sign',
'rail',
'oak_wall_sign',
'spruce_wall_sign',
'birch_wall_sign',
'acacia_wall_sign',
'jungle_wall_sign',
'dark_oak_wall_sign',
'lever',
'stone_pressure_plate',
'oak_pressure_plate',
'spruce_pressure_plate',
'birch_pressure_plate',
'jungle_pressure_plate',
'acacia_pressure_plate',
'dark_oak_pressure_plate',
'redstone_torch',
'redstone_wall_torch',
'stone_button',
'sugar_cane',
'repeater',
'attached_pumpkin_stem',
'attached_melon_stem',
'pumpkin_stem',
'melon_stem',
'nether_wart',
'tripwire_hook',
'tripwire',
'carrots',
'potatoes',
'oak_button',
'spruce_button',
'birch_button',
'jungle_button',
'acacia_button',
'dark_oak_button',
'light_weighted_pressure_plate',
'heavy_weighted_pressure_plate',
'comparator',
'activator_rail',
'white_carpet',
'orange_carpet',
'magenta_carpet',
'light_blue_carpet',
'yellow_carpet',
'lime_carpet',
'pink_carpet',
'gray_carpet',
'light_gray_carpet',
'cyan_carpet',
'purple_carpet',
'blue_carpet',
'brown_carpet',
'green_carpet',
'red_carpet',
'black_carpet',
'sunflower',
'lilac',
'rose_bush',
'peony',
'tall_grass',
'large_fern',
'white_banner',
'orange_banner',
'magenta_banner',
'light_blue_banner',
'yellow_banner',
'lime_banner',
'pink_banner',
'gray_banner',
'light_gray_banner',
'cyan_banner',
'purple_banner',
'blue_banner',
'brown_banner',
'green_banner',
'red_banner',
'black_banner',
'white_wall_banner',
'orange_wall_banner',
'magenta_wall_banner',
'light_blue_wall_banner',
'yellow_wall_banner',
'lime_wall_banner',
'pink_wall_banner',
'gray_wall_banner',
'light_gray_wall_banner',
'cyan_wall_banner',
'purple_wall_banner',
'blue_wall_banner',
'brown_wall_banner',
'green_wall_banner',
'red_wall_banner',
'black_wall_banner',
'beetroots',
'bamboo_sapling',
'void_air',
'cave_air',
'lantern',
'soul_torch',
]
LOGS = [
'oak_log',
'spruce_log',
'birch_log',
'jungle_log',
'acacia_log',
'dark_oak_log',
]
LEAVES = [
'oak_leaves',
'spruce_leaves',
'birch_leaves',
'jungle_leaves',
'acacia_leaves',
'dark_oak_leaves',
]
SAPLINGS = [
'oak_sapling',
'spruce_sapling',
'birch_sapling',
'jungle_sapling',
'acacia_sapling',
'dark_oak_sapling',
]
BEDS = [
'white_bed',
'orange_bed',
'magenta_bed',
'light_blue_bed',
'yellow_bed',
'lime_bed',
'pink_bed',
'gray_bed',
'light_gray_bed',
'cyan_bed',
'purple_bed',
'blue_bed',
'brown_bed',
'green_bed',
'red_bed',
'black_bed',
]
INDEXED = [
'chest',
'trapped_chest',
'emerald_block',
'barrel',
] + BEDS
def get_set(ids):
result = set()
for block_name in ids:
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
result.add(state['id'])
return result
NON_SOLID_IDS = get_set(NON_SOLID)
NON_SOLID_IDS.add(SINGLE_SNOW)
AVOID_IDS = get_set(AVOID)
WATER_IDS = get_set(['water'])
LOG_IDS = get_set(LOGS)
LEAF_IDS = get_set(LEAVES)
CHEST_IDS = get_set(['chest'])
BARREL_IDS = get_set(['barrel'])
TRAPPED_CHEST_IDS = get_set(['trapped_chest'])
NETHERWART_IDS = get_set(['nether_wart'])
WHEAT_IDS = get_set(['wheat'])
POTATO_IDS = get_set(['potatoes'])
CARROT_IDS = get_set(['carrots'])
BEETROOT_IDS = get_set(['beetroots'])
SAPLING_IDS = get_set(SAPLINGS)
BED_IDS = get_set(BEDS)
INDEXED_IDS = set()
for block_name in INDEXED:
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
INDEXED_IDS.add(state['id'])
MATURE_WHEAT_ID = max(WHEAT_IDS)
MATURE_POTATO_ID = max(POTATO_IDS)
MATURE_CARROT_ID = max(CARROT_IDS)
MATURE_BEETROOT_ID = max(BEETROOT_IDS)
def get(bid):
name = BLOCKS[bid]
return MCD_BLOCKS[name]
def find(name):
return MCD_BLOCKS[name]

294
mosfet/bot.py Normal file
View File

@@ -0,0 +1,294 @@
if __name__ == '__main__':
print('Run main.py instead.')
exit(1)
import os
import time
import importlib
from math import floor, ceil
from copy import copy
USERNAME = os.environ['USERNAME']
PASSWORD = os.environ['PASSWORD']
SERVER = os.environ['SERVER']
PORT = int(os.environ.get('PORT', 25565))
import monkey_patch # must be before any possible pyCraft imports
from minecraft import authentication
from minecraft.exceptions import YggdrasilError
from minecraft.networking.connection import Connection
from minecraft.networking.packets import Packet, clientbound, serverbound
from protocol.managers import DataManager, ChunksManager, ChatManager, ChunkNotLoadedException
from munch import Munch
from vector import Point3D, Vector3D
import blocks
import game
import items
import job
import mcdata
import mobs
import path
import print_help
import utils
import vector
for module in [
blocks,
game,
items,
job,
mcdata,
mobs,
path,
print_help,
utils,
vector,
]:
importlib.reload(module)
last_tick = time.time()
PITCH_ANGLE_DIR = Vector3D((0, 1, 0))
YAW_ANGLE_DIR = Vector3D((0, 0, -1))
YAW_ANGLE_REF = Vector3D((0, 1, 0))
YAW_LOOK_AHEAD = 4
def tick(global_state):
g = global_state
p = g.pos
target = None
# make sure current chunks are loaded for physics
if not g.chunks.check_loaded(p, 288):
if not g.chunks.loading:
print('Loading chunks', end='', flush=True)
g.chunks.loading = True
packet = serverbound.play.PositionAndLookPacket(x=p.x, feet_y=p.y, z=p.z, pitch=0, yaw=0, on_ground=True)
g.connection.write_packet(packet, force=True)
return
else:
if g.chunks.loading:
print()
print('Chunks loaded.')
g.chunks.loading = False
g.chunks.unload_chunks(p)
########## object physics ##########
# note: it's possible the chunk data is out of date when this runs
for eid, obj in copy(g.objects).items():
if obj.velocity_x:
obj.x += obj.velocity_x / 8000
if obj.velocity_y:
obj.y += obj.velocity_y / 8000
if obj.velocity_z:
obj.z += obj.velocity_z / 8000
block_below = g.chunks.get_block_at(floor(obj.x), floor(obj.y-0.20), floor(obj.z))
in_air = block_below in blocks.NON_SOLID_IDS
if in_air:
obj.velocity_x *= 0.988
obj.velocity_y -= 390
obj.velocity_z *= 0.988
else:
obj.y = int(obj.y-0.20)+1
obj.velocity_x *= 0.5
obj.velocity_y = 0
obj.velocity_z *= 0.5
# float object back up in case it clipped through multiple blocks
if g.chunks.get_block_at(floor(obj.x), floor(obj.y), floor(obj.z)) not in blocks.NON_SOLID_IDS:
obj.y += 0.05
if abs(obj.velocity_x) < 1: obj.velocity_x = 0
if abs(obj.velocity_z) < 1: obj.velocity_z = 0
########## player physics ##########
if g.path and len(g.path):
target = Point3D(g.path[0])
target.x += 0.5
target.z += 0.5
if g.afk_timeout > 0:
target = None
g.afk_timeout -= utils.TICK
if target:
d = p - target
# jump up block
if d.y < -0.9 and not g.y_v:
g.y_v = 8.5
g.y_a = -36.0
# jump gap
if d.xz.length() > 1.6 and not g.y_v:
g.y_v = 8.5
g.y_a = -36.0
if d.length() > 0:
if g.y_v < 5:
p.x -= utils.cap(d.x, 0.2)
p.z -= utils.cap(d.z, 0.2)
if len(g.path) > 1 and d.length() < 0.2:
# removes some jitter in walking
g.path.pop(0)
elif d.length() == 0:
g.path.pop(0)
if g.y_v or g.y_a:
p.y += g.y_v * utils.TICK
g.y_v += g.y_a * utils.TICK
block_below = g.chunks.get_block_at(floor(p.x), ceil(p.y-1), floor(p.z))
block_above = g.chunks.get_block_at(floor(p.x), ceil(p.y+1), floor(p.z))
in_void = p.y < 0
in_air = block_below in blocks.NON_SOLID_IDS or in_void
in_water = block_below in blocks.WATER_IDS
g.crawling = block_above not in blocks.NON_SOLID_IDS
if in_air:
g.y_a = -36.0
elif in_water:
g.y_a = -16.0
else:
p.y = ceil(p.y)
g.y_v = 0
g.y_a = 0
if g.look_at:
look_at = Point3D(g.look_at)
elif g.path and len(g.path) > YAW_LOOK_AHEAD:
look_at = Point3D(g.path[YAW_LOOK_AHEAD])
elif g.path and len(g.path):
look_at = Point3D(g.path[-1])
else:
look_at = None
if look_at:
look_at.x += 0.5
look_at.z += 0.5
look_at_d = p - look_at
if look_at_d.length() > 0.6:
target_pitch = look_at_d.normalized().angleDeg(PITCH_ANGLE_DIR)
target_pitch = (target_pitch - 90) * -1
target_pitch_d = target_pitch - g.pitch
g.pitch += utils.cap(target_pitch_d, 10)
# remove vertical component for yaw calculation
look_at_d.y = 0
if look_at_d.length() > 0.6:
target_yaw = look_at_d.normalized().signedAngleDeg(other=YAW_ANGLE_DIR, ref=YAW_ANGLE_REF)
target_yaw_d = target_yaw - g.yaw
target_yaw_d = (target_yaw_d + 180) % 360 - 180
g.yaw += utils.cap(target_yaw_d, 30)
else:
target_pitch_d = 0 - g.pitch
g.pitch += utils.cap(target_pitch_d, 10)
packet = serverbound.play.PositionAndLookPacket(x=p.x, feet_y=p.y, z=p.z, pitch=g.pitch, yaw=g.yaw, on_ground=(not in_air))
g.connection.write_packet(packet)
g.game.tick()
g.job.tick()
def init(global_state):
g = global_state
g.time = 0
g.path = []
g.look_at = None
g.y_v = 0
g.y_a = 0
g.yaw = 360
g.pitch = 0
g.crawling = False
g.breaking = None
g.break_time = 0
g.dumping = None
g.draining = False
g.item_lock = False
g.command_lock = False
g.trades = []
g.job = job.JobStates(g)
g.chopped_tree = False
g.afk_timeout = 0
g.filling = False
g.minimum_cache_slots = 27
g.maximum_supply_slots = 33
def bot(global_state):
g = global_state
if not g.mcdata:
g.mcdata = DataManager('./minecraft_data')
if not g.connection:
auth_token = authentication.AuthenticationToken()
try:
auth_token.authenticate(USERNAME, PASSWORD)
except YggdrasilError as e:
print(e)
sys.exit()
print("Logged in as %s..." % auth_token.username)
g.connection = Connection(SERVER, PORT, auth_token=auth_token)
g.chunks = ChunksManager(g.mcdata)
g.connection.connect()
g.chunks.register(g.connection)
g.chat = ChatManager(g)
g.game = game.Game(g)
g.world = game.MCWorld(g)
try:
while not g.pos:
time.sleep(utils.TICK)
print('Player loaded.')
init(g)
g.game.close_window()
print('Initialized.')
while g.running:
tick(g)
global last_tick
sleep_time = utils.TICK + last_tick - time.time()
if sleep_time < 0: sleep_time = 0
time.sleep(sleep_time)
last_tick = time.time()
finally:
print('Removing listeners...')
g.connection.packet_listeners = []
g.connection.early_packet_listeners = []
g.connection.outgoing_packet_listeners = []
g.connection.early_outgoing_packet_listeners = []
print('Bot module loaded.')

1355
mosfet/game.py Normal file

File diff suppressed because it is too large Load Diff

122
mosfet/items.py Normal file
View File

@@ -0,0 +1,122 @@
import json
with open('minecraft_data/registries.json') as f:
ITEMS = json.load(f)['minecraft:item']['entries']
BEDS = [
'white_bed',
'orange_bed',
'magenta_bed',
'light_blue_bed',
'yellow_bed',
'lime_bed',
'pink_bed',
'gray_bed',
'light_gray_bed',
'cyan_bed',
'purple_bed',
'blue_bed',
'brown_bed',
'green_bed',
'red_bed',
'black_bed',
]
SHOVELS = [
'wooden_shovel',
'stone_shovel',
'golden_shovel',
'iron_shovel',
'diamond_shovel',
'netherite_shovel',
]
AXES = [
'wooden_axe',
'stone_axe',
'golden_axe',
'iron_axe',
'diamond_axe',
'netherite_axe',
]
SAPLINGS = [
'oak_sapling',
'spruce_sapling',
'birch_sapling',
'jungle_sapling',
'acacia_sapling',
'dark_oak_sapling',
]
FOOD = [
'cooked_porkchop',
'cooked_beef',
'bread',
'cooked_chicken',
'cooked_cod',
'cooked_salmon',
]
LOGS = [
'oak_log',
'spruce_log',
'birch_log',
'jungle_log',
'acacia_log',
'dark_oak_log',
]
def get_set(ids):
result = set()
for item_name in ids:
result.add(ITEMS['minecraft:'+item_name]['protocol_id'])
return result
BED_IDS = get_set(BEDS)
SHOVEL_IDS = get_set(SHOVELS)
AXE_IDS = get_set(AXES)
FOOD_IDS = get_set(FOOD)
SAPLING_IDS = get_set(SAPLINGS)
LOG_IDS = get_set(LOGS)
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 = get_id('chest')
GAPPLE_ID = get_id('enchanted_golden_apple')
SAND_ID = get_id('sand')
NETHERWART_ID = get_id('nether_wart')
CARROT_ID = get_id('carrot')
POTATO_ID = get_id('potato')
WHEAT_ID = get_id('wheat')
WHEAT_SEEDS_ID = get_id('wheat_seeds')
BEETROOT_SEEDS_ID = get_id('beetroot_seeds')
PUMPKIN_ID = get_id('pumpkin')
EMERALD_ID = get_id('emerald')
BERRIES_ID = get_id('sweet_berries')
IRON_INGOT_ID = get_id('iron_ingot')
INIT_NEEDED_ITEMS = BED_IDS | FOOD_IDS
INIT_NEEDED_ITEMS.add(CHEST_ID)
NEEDED_ITEMS = INIT_NEEDED_ITEMS
INIT_WANTED_ITEMS = set()
WANTED_ITEMS = INIT_WANTED_ITEMS
def set_needed(items):
NEEDED_ITEMS = INIT_NEEDED_ITEMS | items
def set_wanted(items):
WANTED_ITEMS = INIT_WANTED_ITEMS | items

261
mosfet/job.py Normal file
View File

@@ -0,0 +1,261 @@
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
import path
import blocks
import items
import mcdata
import 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())

0
mosfet/jobs/__init__.py Normal file
View File

231
mosfet/jobs/cache_items.py Normal file
View File

@@ -0,0 +1,231 @@
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
import path
import blocks
import items
import mcdata
import 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()

View File

@@ -0,0 +1,108 @@
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
import path
import blocks
import items
import mcdata
import 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()

View File

@@ -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
import path
import blocks
import items
import mcdata
import 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()

72
mosfet/jobs/eat_food.py Normal file
View File

@@ -0,0 +1,72 @@
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
import path
import blocks
import items
import mcdata
import 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()

216
mosfet/jobs/fill_blocks.py Normal file
View File

@@ -0,0 +1,216 @@
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
import path
import blocks
import items
import mcdata
import 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()

117
mosfet/jobs/find_gapple.py Normal file
View File

@@ -0,0 +1,117 @@
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
import path
import blocks
import items
import mcdata
import 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()

146
mosfet/jobs/gather_crop.py Normal file
View File

@@ -0,0 +1,146 @@
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
import path
import blocks
import items
import mcdata
import 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()

130
mosfet/jobs/gather_sand.py Normal file
View File

@@ -0,0 +1,130 @@
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
import path
import blocks
import items
import mcdata
import 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()

123
mosfet/jobs/gather_wart.py Normal file
View File

@@ -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
import path
import blocks
import items
import mcdata
import 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()

189
mosfet/jobs/gather_wood.py Normal file
View File

@@ -0,0 +1,189 @@
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
import path
import blocks
import items
import mcdata
import 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()

88
mosfet/jobs/grab_sand.py Normal file
View File

@@ -0,0 +1,88 @@
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
import path
import blocks
import items
import mcdata
import 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()

View File

@@ -0,0 +1,85 @@
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
import path
import blocks
import items
import mcdata
import 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()

View File

@@ -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
import path
import blocks
import items
import mcdata
import 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()

110
mosfet/jobs/plant_tree.py Normal file
View File

@@ -0,0 +1,110 @@
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
import path
import blocks
import items
import mcdata
import 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()

View File

@@ -0,0 +1,212 @@
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
import path
import blocks
import items
import mcdata
import 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()

View File

@@ -0,0 +1,205 @@
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
import path
import blocks
import items
import mcdata
import 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()

85
mosfet/main.py Normal file
View File

@@ -0,0 +1,85 @@
import importlib
import threading
import time
import traceback
import json
from flask import Flask
app = Flask(__name__)
from munch import Munch
from watchdog.observers import Observer
from watchdog.events import PatternMatchingEventHandler
import bot
global_state = Munch()
g = global_state
g.connection = False
g.name = None
g.mcdata = False
g.pos = False
g.dimension = None
g.inv = {}
g.objects = {}
g.mobs = {}
g.players = {}
g.player_names = {}
g.window = None
g.job = None
g.correction_count = 0
g.holding = 0
g.afk = False
g.health = 20
g.food = 20
@app.route('/')
def hello_world():
data = json.dumps(g, default=lambda o: str(o), indent=4)
response = app.response_class(
response=data,
status=200,
mimetype='application/json'
)
return response
reload_timeout = time.time()
def main():
def reload_bot(event):
global reload_timeout
if time.time() - reload_timeout > 2.0:
reload_timeout = time.time()
print('Reloading...')
g.running = False
event_handler = PatternMatchingEventHandler(patterns=['*.py'], ignore_patterns=['./main.py'])
event_handler.on_any_event = reload_bot
observer = Observer()
observer.schedule(event_handler, '.', recursive=True)
observer.start()
try:
while True:
try:
g.running = True
bot.bot(global_state)
importlib.reload(bot)
except BaseException as e:
g.running = True
traceback.print_exc()
print('Locking...')
while g.running:
time.sleep(1)
importlib.reload(bot)
except KeyboardInterrupt:
observer.stop()
observer.join()
if __name__ == '__main__':
threading.Thread(target=app.run).start()
time.sleep(1)
main()

32
mosfet/mcdata.py Normal file
View File

@@ -0,0 +1,32 @@
import json
from munch import Munch
import minecraft_data
mcd = minecraft_data('1.16.4')
with open('minecraft_data/registries.json') as f:
DATA = json.load(f)
SINGLE_CHEST = 2
DOUBLE_CHEST = 5
VILLAGER_TRADE = 18
WINDOWS = {
SINGLE_CHEST: Munch(
container=list(range(0, 27)),
inventory=list(range(27, 63)),
slot_diff=18,
),
DOUBLE_CHEST: Munch(
container=list(range(0, 54)),
inventory=list(range(54, 90)),
slot_diff=45,
),
VILLAGER_TRADE: Munch(
input1=0,
input2=1,
output=2,
inventory=list(range(3, 38)),
),
}

50
mosfet/mobs.py Normal file
View File

@@ -0,0 +1,50 @@
import json
with open('minecraft_data/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:', '')

28
mosfet/monkey_patch.py Normal file
View File

@@ -0,0 +1,28 @@
import minecraft.networking.packets
from protocol import packets
def get_packets(old_get_packets):
def wrapper(func, context):
print('Monkey-patch worked.')
mc_packets = func(context)
# add any custom CLIENTBOUND packets here
mc_packets.add(packets.ChunkDataPacket)
mc_packets.add(packets.AcknowledgePlayerDiggingPacket)
mc_packets.add(packets.BlockBreakAnimationPacket)
mc_packets.add(packets.SetSlotPacket)
mc_packets.add(packets.OpenWindowPacket)
mc_packets.add(packets.ClientWindowConfirmationPacket)
mc_packets.add(packets.EntityMetadataPacket)
mc_packets.add(packets.SpawnLivingEntityPacket)
mc_packets.add(packets.EntityPositionRotationPacket)
mc_packets.add(packets.DestroyEntitiesPacket)
mc_packets.add(packets.EntityTeleport)
mc_packets.add(packets.TradeListPacket)
mc_packets.add(packets.DisconnectPacket)
return mc_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)

314
mosfet/path.py Normal file
View File

@@ -0,0 +1,314 @@
import importlib
import functools
import time
from math import hypot, sqrt
from astar import AStar
import blocks
import utils
class AStarTimeout(Exception):
pass
BLOCK_ABOVE = (0, +1, 0)
BLOCK_ABOVE2 = (0, +2, 0)
BLOCK_ABOVE3 = (0, +3, 0)
BLOCK_ABOVE4 = (0, +4, 0)
BLOCK_BELOW = (0, -1, 0)
BLOCK_BELOW2 = (0, -2, 0)
TRAVERSE_NORTH = (0, 0, -1)
TRAVERSE_SOUTH = (0, 0, +1)
TRAVERSE_EAST = (+1, 0, 0)
TRAVERSE_WEST = (-1, 0, 0)
ASCEND_NORTH = (0, +1, -1)
ASCEND_SOUTH = (0, +1, +1)
ASCEND_EAST = (+1, +1, 0)
ASCEND_WEST = (-1, +1, 0)
DESCEND_EAST = (+1, -1, 0)
DESCEND_WEST = (-1, -1, 0)
DESCEND_NORTH = (0, -1, -1)
DESCEND_SOUTH = (0, -1, +1)
DESCEND2_EAST = (+1, -2, 0)
DESCEND2_WEST = (-1, -2, 0)
DESCEND2_NORTH = (0, -2, -1)
DESCEND2_SOUTH = (0, -2, +1)
DESCEND3_EAST = (+1, -3, 0)
DESCEND3_WEST = (-1, -3, 0)
DESCEND3_NORTH = (0, -3, -1)
DESCEND3_SOUTH = (0, -3, +1)
DIAGONAL_NORTHEAST = (+1, 0, -1)
DIAGONAL_NORTHWEST = (-1, 0, -1)
DIAGONAL_SOUTHEAST = (+1, 0, +1)
DIAGONAL_SOUTHWEST = (-1, 0, +1)
PARKOUR_NORTH = (0, 0, -2)
PARKOUR_SOUTH = (0, 0, +2)
PARKOUR_EAST = (+2, 0, 0)
PARKOUR_WEST = (-2, 0, 0)
CHECK_NORTH = (0, 0, -1)
CHECK_SOUTH = (0, 0, +1)
CHECK_EAST = (+1, 0, 0)
CHECK_WEST = (-1, 0, 0)
CHECK_DIRECTIONS = [
CHECK_NORTH,
CHECK_SOUTH,
CHECK_EAST,
CHECK_WEST,
]
TRAVERSE = [
TRAVERSE_NORTH,
TRAVERSE_SOUTH,
TRAVERSE_EAST,
TRAVERSE_WEST,
]
ASCEND = [
ASCEND_NORTH,
ASCEND_SOUTH,
ASCEND_EAST,
ASCEND_WEST,
]
DESCEND = [
DESCEND_EAST,
DESCEND_WEST,
DESCEND_NORTH,
DESCEND_SOUTH,
]
DESCEND2 = [
DESCEND2_EAST,
DESCEND2_WEST,
DESCEND2_NORTH,
DESCEND2_SOUTH,
]
DESCEND3 = [
DESCEND3_EAST,
DESCEND3_WEST,
DESCEND3_NORTH,
DESCEND3_SOUTH,
]
DIAGONAL = [
DIAGONAL_NORTHEAST,
DIAGONAL_NORTHWEST,
DIAGONAL_SOUTHEAST,
DIAGONAL_SOUTHWEST,
]
PARKOUR = [
PARKOUR_NORTH,
PARKOUR_SOUTH,
PARKOUR_EAST,
PARKOUR_WEST,
]
HALF_PARKOUR = {
(0, 0, -2): (0, 0, -1),
(0, 0, 2): (0, 0, 1),
(2, 0, 0): (1, 0, 0),
(-2, 0, 0): (-1, 0, 0),
}
# larger started being slower
BLOCK_CACHE_SIZE = 2**14
class Pathfinder(AStar):
def __init__(self, g):
self.g = g
self.chunks = g.chunks
self.start_time = time.time()
@functools.lru_cache(maxsize=BLOCK_CACHE_SIZE)
def bair(self, p):
return self.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS
@functools.lru_cache(maxsize=BLOCK_CACHE_SIZE)
def bavoid(self, p):
return self.chunks.get_block_at(*p) in blocks.AVOID_IDS or p[1] < 0
def check_traverse_crawling(self, node, offset):
dest = utils.padd(node, offset)
if not self.bair(dest):
return False
if self.bair(utils.padd(dest, BLOCK_BELOW)):
return False
if self.bavoid(dest):
return False
if self.bavoid(utils.padd(dest, BLOCK_BELOW)):
return False
return True
def check_traverse(self, node, offset):
dest = utils.padd(node, offset)
if not self.bair(dest):
return False
if self.bair(utils.padd(dest, BLOCK_BELOW)):
return False
if not self.bair(utils.padd(dest, BLOCK_ABOVE)):
return False
if self.bavoid(dest):
return False
if self.bavoid(utils.padd(dest, BLOCK_BELOW)):
return False
if self.bavoid(utils.padd(dest, BLOCK_ABOVE)):
return False
return True
def check_diagonal(self, node, offset):
if not self.check_traverse(node, offset):
return False
dest = utils.padd(node, offset)
thru1 = (node[0], node[1], dest[2])
thru2 = (dest[0], node[1], node[2])
if not self.bair(thru1):
return False
if not self.bair(utils.padd(thru1, BLOCK_ABOVE)):
return False
if self.bavoid(utils.padd(thru1, BLOCK_BELOW)):
return False
if not self.bair(thru2):
return False
if not self.bair(utils.padd(thru2, BLOCK_ABOVE)):
return False
if self.bavoid(utils.padd(thru2, BLOCK_BELOW)):
return False
return True
def check_ascend(self, node, offset):
dest = utils.padd(node, offset)
if not self.bair(utils.padd(node, BLOCK_ABOVE2)):
return False
if not self.check_traverse(node, offset):
return False
return True
def check_descend(self, node, offset):
dest = utils.padd(node, offset)
if not self.bair(utils.padd(dest, BLOCK_ABOVE2)):
return False
if not self.check_traverse(node, offset):
return False
return True
def check_descend2(self, node, offset):
dest = utils.padd(node, offset)
if not self.bair(utils.padd(dest, BLOCK_ABOVE3)):
return False
if not self.check_descend(node, offset):
return False
return True
def check_descend3(self, node, offset):
dest = utils.padd(node, offset)
if not self.bair(utils.padd(dest, BLOCK_ABOVE4)):
return False
if not self.check_descend2(node, offset):
return False
return True
def check_parkour(self, node, offset):
dest = utils.padd(node, offset)
half_offset = HALF_PARKOUR[offset]
middle = utils.padd(node, half_offset)
# dont jump if we can walk instead
if not self.bair(utils.padd(middle, BLOCK_BELOW)):
return False
if not self.check_ascend(node, offset):
return False
if not self.bair(utils.padd(dest, BLOCK_ABOVE2)):
return False
if not self.bair(utils.padd(middle, BLOCK_ABOVE)):
return False
if not self.bair(utils.padd(middle, BLOCK_ABOVE2)):
return False
return True
def neighbors(self, node):
results = []
if self.g.crawling:
for offset in TRAVERSE:
if self.check_traverse_crawling(node, offset):
results.append(utils.padd(node, offset))
else:
for offset in TRAVERSE:
if self.check_traverse(node, offset):
results.append(utils.padd(node, offset))
for offset in DIAGONAL:
if self.check_diagonal(node, offset):
results.append(utils.padd(node, offset))
for offset in ASCEND:
if self.check_ascend(node, offset):
results.append(utils.padd(node, offset))
for offset in DESCEND:
if self.check_descend(node, offset):
results.append(utils.padd(node, offset))
for offset in DESCEND2:
if self.check_descend2(node, offset):
results.append(utils.padd(node, offset))
for offset in DESCEND3:
if self.check_descend3(node, offset):
results.append(utils.padd(node, offset))
for offset in PARKOUR:
if self.check_parkour(node, offset):
results.append(utils.padd(node, offset))
if not results:
if time.time() - self.start_time > 2.0:
raise(AStarTimeout)
return results
def distance_between(self, n1, n2):
(x1, y1, z1) = n1
(x2, y2, z2) = n2
return hypot(x2-x1, y2-y1, z2-z1)
def heuristic_cost_estimate(self, n1, n2):
(x1, y1, z1) = n1
(x2, y2, z2) = n2
return abs(x2-x1) + abs(y2-y1) + abs(z2-z1)

16
mosfet/print_help.py Normal file
View File

@@ -0,0 +1,16 @@
HELP_LINES = []
with open('game.py', 'r') as f:
for line in f.readlines():
if line.strip().startswith('## '):
HELP_LINES.append(line.strip()[3:])
if __name__ == '__main__':
for line in HELP_LINES:
if ' - ' in line:
command, doc = line.split(' - ')
print('`{}` - {}\n'.format(command, doc))
else:
print(line)
print()

View File

194
mosfet/protocol/managers.py Normal file
View File

@@ -0,0 +1,194 @@
import os
from math import floor
import json
import time
from minecraft.networking.packets import clientbound, serverbound
from protocol import packets
import utils
class DataManager:
def __init__(self, directory):
self.blocks = {}
self.blocks_states = {}
self.blocks_properties = {}
self.registries = {}
self.biomes = {}
self.entity_type = {}
if not os.path.isdir(directory):
raise FileNotFoundError("%s is not a valid directory")
if not os.path.isfile("%s/registries.json"%(directory)):
raise FileNotFoundError("%s is not a valid minecraft data directory")
with open("%s/blocks.json"%(directory)) as f:
blocks = json.loads(f.read())
for x in blocks:
for s in blocks[x]['states']:
self.blocks_states[s['id']] = x
self.blocks_properties[s['id']] = s.get('properties', {})
with open("%s/registries.json"%(directory)) as f:
registries = json.loads(f.read())
#for x in registries["minecraft:biome"]["entries"]:
# self.biomes[registries["minecraft:biome"]["entries"][x]["protocol_id"]] = x
for x in registries["minecraft:entity_type"]["entries"]:
self.entity_type[registries["minecraft:entity_type"]["entries"][x]["protocol_id"]] = x
class ChunksManager:
def __init__(self, data_manager):
self.data = data_manager
self.chunks = {}
self.biomes = {}
self.index = {}
self.loading = False
def handle_block(self, block_packet):
self.set_block_at(block_packet.location.x, block_packet.location.y, block_packet.location.z, block_packet.block_state_id)
def handle_multiblock(self, multiblock_packet):
dx = 16 * multiblock_packet.chunk_section_pos.x
dy = 16 * multiblock_packet.chunk_section_pos.y
dz = 16 * multiblock_packet.chunk_section_pos.z
for b in multiblock_packet.records:
self.set_block_at(dx+b.x, dy+b.y, dz+b.z, b.block_state_id)
def handle_chunk(self, chunk_packet):
for i in chunk_packet.chunks:
chunk = chunk_packet.chunks[i]
self.chunks[(chunk_packet.x, i, chunk_packet.z)] = chunk
if chunk.sub_index:
dx = chunk.x * 16
dy = chunk.y * 16
dz = chunk.z * 16
for item_id, locations in chunk.sub_index.items():
if item_id not in self.index:
self.index[item_id] = []
for l in locations:
coords = (dx + l%16, dy + l//256, dz + l%256//16)
self.index[item_id].append(coords)
self.biomes[(chunk_packet.x, None, chunk_packet.z)] = chunk_packet.biomes # FIXME
if self.loading:
print('.', end='', flush=True)
def register(self, connection):
connection.register_packet_listener(self.handle_block, clientbound.play.BlockChangePacket)
connection.register_packet_listener(self.handle_multiblock, clientbound.play.MultiBlockChangePacket)
connection.register_packet_listener(self.handle_chunk, packets.ChunkDataPacket)
def get_chunk(self, x, y, z):
index = (x, y, z)
if not index in self.chunks:
return None
#raise ChunkNotLoadedException(index)
return self.chunks[index]
def get_loaded_area(self, ignore_empty=False):
first = next(iter(self.chunks.keys()))
x0 = x1 = first[0]
y0 = y1 = first[1]
z0 = z1 = first[2]
for k in self.chunks.keys():
if ignore_empty and self.chunks[k].empty:
continue
x0 = min(x0, k[0])
x1 = max(x1, k[0])
y0 = min(y0, k[1])
y1 = max(y1, k[1])
z0 = min(z0, k[2])
z1 = max(z1, k[2])
return ((x0,y0,z0),(x1,y1,z1))
def get_block_at(self, x, y, z):
c = self.get_chunk(floor(x/16), floor(y/16), floor(z/16))
if not c: return None
return c.get_block_at(x%16, y%16, z%16)
def set_block_at(self, x, y, z, block):
c = self.get_chunk(floor(x/16), floor(y/16), floor(z/16))
if not c: return None
c.set_block_at(x%16, y%16, z%16, block)
def check_loaded(self, position, steps):
x, y, z = utils.pint(position)
player_chunk = (x//16, 1, z//16)
for i in range(steps): # TODO: base off render_distance?
offset = utils.spiral(i)
check = utils.padd(player_chunk, offset)
if check not in self.chunks:
return False
return True
def unload_chunks(self, position):
x, y, z = utils.pint(position)
player_chunk = (x//16, 0, z//16)
loaded_chunks = list(self.chunks.keys())
for chunk in loaded_chunks:
check = (chunk[0], 0, chunk[2])
if utils.phyp_king(player_chunk, check) > 20:
del self.chunks[chunk]
class ChunkNotLoadedException(Exception):
def __str__(self):
pos = self.args[0]
return "Chunk at %d %d %d not loaded (yet?)"%(pos[0], pos[1], pos[2])
class ChatManager:
def __init__(self, global_state):
self.g = global_state
self.handler = None
self.g.connection.register_packet_listener(self.print_chat, clientbound.play.ChatMessagePacket)
def translate_chat(self, data):
if isinstance(data, str):
return data
elif 'extra' in data:
return ''.join([self.translate_chat(x) for x in data['extra']])
elif 'text' in data:
return data['text']
elif 'with' in data:
if len(data['with']) >= 2:
return '<{}> {}'.format(*[self.translate_chat(x) for x in data['with']])
else:
return self.translate_chat(data['with'][0])
elif 'translate' in data:
return data['translate']
else:
print(data)
return '?'
def print_chat(self, chat_packet):
try:
source = chat_packet.field_string('position')
text = self.translate_chat(json.loads(chat_packet.json_data))
print('[%s] %s'%(source, text))
except Exception as ex:
print('Exception %r on message (%s): %s' % (ex, chat_packet.field_string('position'), chat_packet.json_data))
return
if self.handler:
self.handler((source, text))
def set_handler(self, func):
self.handler = func
def send(self, text):
if not text:
# Prevents connection bug when sending empty chat message
return
packet = serverbound.play.ChatPacket()
packet.message = text
self.g.connection.write_packet(packet)

460
mosfet/protocol/packets.py Normal file
View File

@@ -0,0 +1,460 @@
from math import floor
from minecraft.networking.packets import Packet, PacketBuffer
from minecraft.networking.types import (
VarInt, Integer, UnsignedByte, Position, Vector, MutableRecord,
attribute_alias, multi_attribute_alias, Long, Boolean, VarLong,
Short, UnsignedLong, Byte, BlockFace, String, UUID, Angle, Double,
Float, Direction, PositionAndLook
)
from protocol.types import Nbt, Slot, Entry, Trade
import blocks
class ChunkDataPacket(Packet):
id = 0x20
packet_name = 'chunk data'
fields = 'x', 'bit_mask_y', 'z', 'full_chunk'
def read(self, file_object):
self.x = Integer.read(file_object)
self.z = Integer.read(file_object)
self.full_chunk = Boolean.read(file_object)
self.bit_mask_y = VarInt.read(file_object)
self.heightmaps = Nbt.read(file_object)
self.biomes = []
if self.full_chunk:
biomes_length = VarInt.read(file_object)
for i in range(biomes_length):
self.biomes.append(VarInt.read(file_object))
size = VarInt.read(file_object)
self.data = file_object.read(size)
size_entities = VarInt.read(file_object)
self.entities = []
for i in range(size_entities):
self.entities.append(Nbt.read(file_object))
self.decode_chunk_data()
def write_fields(self, packet_buffer):
Integer.send(self.x, packet_buffer)
Integer.send(self.z, packet_buffer)
Boolean.send(self.full_chunk, packet_buffer)
VarInt.send(self.bit_mask_y, packet_buffer)
Nbt.send(self.heightmaps, packet_buffer)
if self.full_chunk:
for i in range(1024):
Integer.send(self.biomes[i], packet_buffer)
VarInt.send(len(self.data), packet_buffer)
packet_buffer.send(self.data)
VarInt.send(len(self.entities), packet_buffer)
for e in self.entities:
Nbt.send(e, packet_buffer)
def decode_chunk_data(self):
packet_data = PacketBuffer()
packet_data.send(self.data)
packet_data.reset_cursor()
self.chunks = {}
for i in range(16): #0-15
self.chunks[i] = Chunk(self.x, i, self.z)
if self.bit_mask_y & (1 << i):
self.chunks[i].read(packet_data)
for e in self.entities:
y = e['y']
self.chunks[floor(y/16)].entities.append(e)
class Chunk:
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
def __init__(self, x, y, z, empty=True):
self.x = x
self.y = y
self.z = z
self.empty = empty
self.entities = []
self.sub_index = {}
def __repr__(self):
return 'Chunk(%r, %r, %r)' % (self.x, self.y, self.z)
def read(self, file_object):
self.empty = False
self.block_count = Short.read(file_object)
self.bpb = UnsignedByte.read(file_object)
if self.bpb <= 4:
self.bpb = 4
if self.bpb <= 8: # Indirect palette
self.palette = []
size = VarInt.read(file_object)
for i in range(size):
self.palette.append(VarInt.read(file_object))
else: # Direct palette
self.palette = None
size = VarInt.read(file_object)
longs = []
for i in range(size):
longs.append(UnsignedLong.read(file_object))
self.blocks = []
mask = (1 << self.bpb)-1
bits_used = int(64 / self.bpb) * self.bpb
for i in range(4096):
l1 = int((i*self.bpb)/bits_used)
offset = (i*self.bpb)%bits_used
n = longs[l1] >> offset
n &= mask
if self.palette:
n = self.palette[n]
self.blocks.append(n)
if n in blocks.INDEXED_IDS:
if n not in self.sub_index:
self.sub_index[n] = []
self.sub_index[n].append(i)
def write_fields(self, packet_buffer):
pass # TODO
def get_block_at(self, x, y, z):
if self.empty:
return 0
return self.blocks[x+y*256+z*16]
def set_block_at(self, x, y, z, block):
if self.empty:
self.init_empty()
self.blocks[x+y*256+z*16] = block
def init_empty(self):
self.blocks = []
for i in range(4096):
self.blocks.append(0)
self.empty = False
@property
def origin(self):
return self.position*16
class AcknowledgePlayerDiggingPacket(Packet):
id = 0x07
packet_name = 'acknowledge player digging'
definition = [
{'location': Position},
{'block': VarInt},
{'status': VarInt},
{'successful': Boolean},
]
class BlockBreakAnimationPacket(Packet):
id = 0x08
packet_name = 'block break animation'
definition = [
{'entity_id': VarInt},
{'location': Position},
{'destroy_stage': Byte},
]
class SetSlotPacket(Packet):
id = 0x15
packet_name = 'set slot'
definition = [
{'window_id': Byte},
{'slot': Short},
{'slot_data': Slot},
]
class PlayerDiggingPacket(Packet):
# used when player mines / breaks blocks
# https://wiki.vg/Protocol#Player_Digging
id = 0x1B
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 = 0x18
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 = 0x25
packet_name = 'held item change'
definition = [
{'slot': Short},
]
class OpenWindowPacket(Packet):
# Sent to the client when it should open an inventory, such as a chest, workbench, or furnace
# https://wiki.vg/Protocol#Open_Window
id = 0x2D
packet_name = 'open window'
definition = [
{'window_id': VarInt},
{'window_type': VarInt},
{'window_title': String},
]
class CloseWindowPacket(Packet):
# Sent by the client when closing a window
# https://wiki.vg/Protocol#Close_Window_.28serverbound.29
id = 0x0A
packet_name = 'close window'
definition = [
{'window_id': UnsignedByte},
]
class ClickWindowPacket(Packet):
# Sent by the player when it clicks on a slot in a window
# https://wiki.vg/Protocol#Click_Window
id = 0x09
packet_name = 'click window'
definition = [
{'window_id': UnsignedByte},
{'slot': Short},
{'button': Byte},
{'action_number': Short},
{'mode': VarInt},
{'clicked_item': Slot},
]
class ClientWindowConfirmationPacket(Packet):
# Sent by the server indicating whether a request from the client was accepted
# https://wiki.vg/Protocol#Window_Confirmation_.28clientbound.29
id = 0x11
packet_name = 'client window confirmation'
definition = [
{'window_id': Byte},
{'action_number': Short},
{'accepted': Boolean},
]
class ServerWindowConfirmationPacket(Packet):
# Sent by the client to confirm an unaccepted click
# https://wiki.vg/Protocol#Window_Confirmation_.28serverbound.29
id = 0x07
packet_name = 'client window confirmation'
definition = [
{'window_id': Byte},
{'action_number': Short},
{'accepted': Boolean},
]
class EntityMetadataPacket(Packet):
# Updates one or more metadata properties for an existing entity
# https://wiki.vg/Protocol#Entity_Metadata
id = 0x44
packet_name = 'entity metadata'
fields = 'entity_id', 'metadata'
class Entry:
__slots__ = 'index', 'type', 'value'
def read(self, file_object):
self.entity_id = VarInt.read(file_object)
self.metadata = []
for _ in range(99):
entry = Entry.read(file_object, self.context)
if not entry: break
self.metadata.append(entry)
class SpawnLivingEntityPacket(Packet):
# Sent by the server when a living entity is spawned
# https://wiki.vg/Protocol#Spawn_Entity
id = 0x02
packet_name = 'spawn living entity'
definition = [
{'entity_id': VarInt},
{'entity_uuid': UUID},
{'type': VarInt},
{'x': Double},
{'y': Double},
{'z': Double},
{'yaw': Angle},
{'pitch': Angle},
{'head_pitch': Angle},
{'velocity_x': Short},
{'velocity_y': Short},
{'velocity_z': Short},
]
class EntityTeleport(Packet):
# Sent by the server when an entity moves more than 8 blocks
# https://wiki.vg/Protocol#Entity_Teleport
id = 0x56
packet_name = 'entity teleport'
definition = [
{'entity_id': VarInt},
{'x': Double},
{'y': Double},
{'z': Double},
{'yaw': Angle},
{'pitch': Angle},
{'on_ground': Boolean},
]
class EntityPositionRotationPacket(Packet):
# Sent by the server when an entity rotates and moves
# https://wiki.vg/Protocol#Entity_Position_and_Rotation
id = 0x28
packet_name = 'entity position and rotation'
definition = [
{'entity_id': VarInt},
{'delta_x': Short},
{'delta_y': Short},
{'delta_z': Short},
{'yaw': Angle},
{'pitch': Angle},
{'on_ground': Boolean},
]
class DestroyEntitiesPacket(Packet):
# Sent by the server when a list of entities is to be destroyed on the client
# https://wiki.vg/Protocol#Destroy_Entities
id = 0x36
packet_name = 'destroy entities'
fields = 'count', 'entity_ids'
def read(self, file_object):
self.count = VarInt.read(file_object)
self.entity_ids = []
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},
]
class InteractEntityPacket(Packet):
# Sent when the client attacks or right-clicks another entity
# https://wiki.vg/Protocol#Interact_Entity
id = 0x0E
packet_name = 'interact entity'
definition = [
{'entity_id': VarInt},
{'type': VarInt},
#{'target_x': Float},
#{'target_y': Float},
#{'target_z': Float},
{'hand': VarInt},
{'sneaking': Boolean},
]
class TradeListPacket(Packet):
# The list of trades a villager NPC is offering.
# https://wiki.vg/Protocol#Trade_List
id = 0x26
packet_name = 'trade list'
fields = (
'window_id',
'size',
'trades',
'villager_level',
'experience',
'is_regular_villager',
'can_restock',
)
def read(self, file_object):
self.window_id = VarInt.read(file_object)
self.size = Byte.read(file_object)
self.trades = []
for _ in range(self.size):
trade = Trade.read(file_object)
self.trades.append(trade)
self.villager_level = VarInt.read(file_object)
self.experience = VarInt.read(file_object)
self.is_regular_villager = Boolean.read(file_object)
self.can_restock = Boolean.read(file_object)
class SelectTradePacket(Packet):
# Sent when a player selects a specific trade offered by a villager
# https://wiki.vg/Protocol#Select_Trade
id = 0x23
packet_name = 'select trade entity'
definition = [
{'selected_slot': VarInt},
]
class DisconnectPacket(Packet):
# Sent by the server before it disconnects a client
# https://wiki.vg/Protocol#Disconnect_.28play.29
id = 0x19
packet_name = 'disconnect'
definition = [
{'reason': String},
]

217
mosfet/protocol/types.py Normal file
View File

@@ -0,0 +1,217 @@
from __future__ import division
import struct
from minecraft.networking.types.basic import (
Type, Byte, Short, Integer, Long, Float, Double,
ShortPrefixedByteArray, Boolean, VarInt, TrailingByteArray,
Position, String, UnsignedByte
)
from minecraft.networking.types.utility import Vector
class IntegerPrefixedByteArray(Type):
@staticmethod
def read(file_object):
length = Integer.read(file_object)
return struct.unpack(str(length) + "s", file_object.read(length))[0]
@staticmethod
def send(value, socket):
Integer.send(len(value), socket)
socket.send(value)
TAG_End = 0
TAG_Byte = 1
TAG_Short = 2
TAG_Int = 3
TAG_Long = 4
TAG_Float = 5
TAG_Double = 6
TAG_Byte_Array = 7
TAG_String = 8
TAG_List = 9
TAG_Compound = 10
TAG_Int_Array = 11
TAG_Long_Array = 12
class Nbt(Type):
@staticmethod
def read(file_object):
type_id = Byte.read(file_object)
if type_id != TAG_Compound:
#raise Exception("Invalid NBT header")
return None
name = ShortPrefixedByteArray.read(file_object).decode('utf-8')
a = Nbt.decode_tag(file_object, TAG_Compound)
a['_name'] = name
return a
@staticmethod
def decode_tag(file_object, type_id):
if type_id == TAG_Byte:
return Byte.read(file_object)
elif type_id == TAG_Short:
return Short.read(file_object)
elif type_id == TAG_Int:
return Integer.read(file_object)
elif type_id == TAG_Long:
return Long.read(file_object)
elif type_id == TAG_Float:
return Float.read(file_object)
elif type_id == TAG_Double:
return Double.read(file_object)
elif type_id == TAG_Byte_Array:
return IntegerPrefixedByteArray.read(file_object).decode('utf-8')
elif type_id == TAG_String:
return ShortPrefixedByteArray.read(file_object)
elif type_id == TAG_List:
list_type_id = Byte.read(file_object)
size = Integer.read(file_object)
a = []
for i in range(size):
a.append(Nbt.decode_tag(file_object, list_type_id))
return a
elif type_id == TAG_Compound:
c = { }
child_type_id = Byte.read(file_object)
while child_type_id != TAG_End:
child_name = ShortPrefixedByteArray.read(file_object).decode('utf-8')
c[child_name] = Nbt.decode_tag(file_object, child_type_id)
child_type_id = Byte.read(file_object)
return c
elif type_id == TAG_Int_Array:
size = Integer.read(file_object)
a = []
for i in range(size):
a.append(Integer.read(file_object))
return a
elif type_id == TAG_Long_Array:
size = Integer.read(file_object)
a = []
for i in range(size):
a.append(Long.read(file_object))
return a
else:
raise Exception("Invalid NBT tag type")
@staticmethod
def send(value, socket):
# TODO
pass
class Slot(Type):
def __init__(self, present, item_id=None, item_count=None, nbt=None):
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):
if self.present:
return 'Slot(present={}, item_id={}, item_count={}, nbt={}'.format(
self.present, self.item_id, self.item_count, self.nbt)
else:
return 'Slot(present={})'.format(self.present)
@staticmethod
def read(file_object):
present = Boolean.read(file_object)
if present:
item_id = VarInt.read(file_object)
item_count = Byte.read(file_object)
nbt = Nbt.read(file_object)
else:
item_id = None
item_count = None
nbt = None
return Slot(present, item_id, item_count, nbt)
@staticmethod
def send(value, socket):
Boolean.send(value.present, socket)
if value.present:
VarInt.send(value.item_id, socket)
Byte.send(value.item_count, socket)
Byte.send(0x00, socket)
class Entry(Type):
types = {
0: Byte,
1: VarInt,
2: Float,
3: String,
5: Boolean,
6: Slot,
7: Boolean,
9: Position,
18: VarInt,
}
def __init__(self, index, type, value):
self.index = index
self.type = type
self.value = value
def __str__(self):
return str(self.__dict__)
def __repr__(self):
return 'Entry(index={}, type={}, value={})'.format(
self.index, self.type, self.value)
@staticmethod
def read(file_object, context):
index = UnsignedByte.read(file_object)
if index == 0xff: return None
type = VarInt.read(file_object)
try:
value = Entry.types[type].read(file_object)
except TypeError:
value = Entry.types[type].read_with_context(file_object, context)
except KeyError:
return None
return Entry(index, type, value)
class Trade(Type):
fields = (
'input_item_1',
'output_item',
'has_second_item',
'input_item_2',
'trade_disabled',
'num_uses',
'max_num_uses',
'xp',
'special_price',
'price_multiplier',
'demand',
)
def __str__(self):
return str(self.__dict__)
def __repr__(self):
inner_str = ', '.join('%s=%s' % (a, getattr(self, a, None)) for a in self.fields if hasattr(self, a))
return 'Trade(%s)' % inner_str
@staticmethod
def read(file_object):
trade = Trade()
trade.input_item_1 = Slot.read(file_object)
trade.output_item = Slot.read(file_object)
trade.has_second_item = Boolean.read(file_object)
if trade.has_second_item:
trade.input_item_2 = Slot.read(file_object)
trade.trade_disabled = Boolean.read(file_object)
trade.num_uses = Integer.read(file_object)
trade.max_num_uses = Integer.read(file_object)
trade.xp = Integer.read(file_object)
trade.special_price = Integer.read(file_object)
trade.price_multiplier = Float.read(file_object)
trade.demand = Integer.read(file_object)
return trade

181
mosfet/utils.py Normal file
View File

@@ -0,0 +1,181 @@
import importlib
import collections
from math import floor, ceil, sqrt, hypot
import blocks
import mcdata
TICK = 0.05
def padd(p1, p2):
return (p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2])
def psub(p1, p2):
return (p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2])
def pmul(p, s):
return (s*p[0], s*p[1], s*p[2])
def phyp(p1, p2):
return hypot(p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2])
def phyp_bias(p1, p2, origin):
origin_distance = phyp(origin, p2)
height_diff = p2[1] - p1[1]
height_diff = height_diff*8 if height_diff < 0 else height_diff*0.5
return hypot(p1[0] - p2[0], height_diff, p1[2] - p2[2]) + origin_distance*0.5
def phyp_king(p1, p2):
# calculates the Chebyshev distance
return max(abs(p1[0] - p2[0]), abs(p1[1] - p2[1]), abs(p1[2] - p2[2]))
def pint(p):
return (floor(p[0]), floor(p[1]), floor(p[2]))
def pboundingbox(p1, p2):
b1 = (min(p1[0], p2[0]), min(p1[1], p2[1]), min(p1[2], p2[2]))
b2 = (max(p1[0], p2[0]), max(p1[1], p2[1]), max(p1[2], p2[2]))
return b1, b2
def pvolume(p1, p2):
b1, b2 = pboundingbox(p1, p2)
box = psub(b2, b1)
return (box[0]+1) * (box[1]+1) * (box[2]+1)
def cap(x, amount):
sign = 1 if x >= 0 else -1
return sign * min(abs(x), amount)
def spiral(n):
# return x, 0, z coords along a spiral at step n
# I forget where I found this
n += 1
k = ceil((sqrt(n)-1)/2)
t = 2 * k + 1
m = t**2
t = t - 1
if n >= m-t:
return k-(m-n), 0, -k
else:
m = m-t
if n >= m-t:
return -k, 0, -k+(m-n)
else:
m = m-t
if n >= m-t:
return -k+(m-n), 0, k
else:
return k, 0, k-(m-n-t)
def alternate(n, amount):
# return 0, y, 0 where y alternates +/- by amount
# example: 0, 2, -2, 4, -4, 6, -6 for amount = 2
sign = 1 if n % 2 else -1
return (0, ceil(n/2) * sign * amount, 0)
def diffrange(n):
# same as range(n+1) but can go negative
sign = 1 if n >= 0 else -1
return range(0, n+sign, sign)
def break_time(block_id, held_item=0, in_water=False, on_ground=True, enchantments=[], effects={}):
# from PrismarineJS/prismarine-block
held_item = str(held_item)
block_data = blocks.get(block_id)
can_harvest = 'harvestTools' not in block_data or str(held_item) in block_data['harvestTools']
material = block_data.get('material', 'n/a')
tool_multipliers = mcdata.mcd.materials.get(material, [])
is_best_tool = held_item in tool_multipliers
time = block_data['hardness']
if can_harvest:
time *= 1.5
else:
time *= 5.0
if is_best_tool:
speed_multiplier = tool_multipliers[held_item]
# TODO: calc efficiency, haste, mining fatigue
else:
speed_multiplier = 1.0
time /= speed_multiplier
if in_water: time *= 5.0
if not on_ground: time *= 5.0
return time
def search_2d(distance=0):
def get_neighbors(x,y,z):
return [
(x+1, y+0, z+0),
#(x+1, y+0, z+1),
(x+0, y+0, z-1),
#(x-1, y+0, z+1),
(x-1, y+0, z+0),
#(x-1, y+0, z-1),
(x+0, y+0, z+1),
#(x+1, y+0, z-1),
]
to_visit = collections.deque([(0, 0, 0)])
visited = set()
while to_visit:
cur = to_visit.pop()
if cur in visited:
continue
if distance and hypot(*cur) > distance:
continue
for neighbor in get_neighbors(*cur):
to_visit.appendleft(neighbor)
visited.add(cur)
yield cur
def search_3d(distance=0, y_limit=0):
def get_neighbors(x,y,z):
return [
(x+1, y+1, z+0),
(x+1, y-1, z+0),
(x+1, y+1, z+1),
(x+1, y+0, z+1),
(x+1, y-1, z+1),
(x+1, y+1, z-1),
(x+1, y+0, z-1),
(x+1, y-1, z-1),
(x+1, y+0, z+0),
(x+0, y+1, z+0),
(x+0, y-1, z+0),
(x+0, y+1, z+1),
(x+0, y+0, z+1),
(x+0, y-1, z+1),
(x+0, y+1, z-1),
(x+0, y+0, z-1),
(x+0, y-1, z-1),
(x-1, y+1, z+0),
(x-1, y-1, z+0),
(x-1, y+1, z+1),
(x-1, y+0, z+1),
(x-1, y-1, z+1),
(x-1, y+1, z-1),
(x-1, y+0, z-1),
(x-1, y-1, z-1),
(x-1, y+0, z+0),
]
to_visit = collections.deque([(0, 0, 0)])
visited = set()
while to_visit:
cur = to_visit.pop()
if cur in visited:
continue
if y_limit and abs(cur[1]) > y_limit:
continue
if distance and hypot(*cur) > distance:
continue
for neighbor in get_neighbors(*cur):
to_visit.appendleft(neighbor)
visited.add(cur)
yield cur

123
mosfet/vector.py Normal file
View File

@@ -0,0 +1,123 @@
import math
class Vector3D:
def __init__(self, vector):
self.x = vector[0]
self.y = vector[1]
self.z = vector[2]
@property
def xz(self):
return Vector3D((self.x, 0, self.z))
def tuple(self):
return (self.x, self.y, self.z)
def __getitem__(self, key):
return self.tuple()[key]
def length(self):
return math.hypot(self.x, self.y, self.z)
def normalized(self):
x = self.x / self.length()
y = self.y / self.length()
z = self.z / self.length()
return Vector3D((x, y, z))
def dot(self, other):
return self.x * other.x + self.y * other.y + self.z * other.z
def cross(self, other):
a1, a2, a3 = self.tuple()
b1, b2, b3 = other.tuple()
return Vector3D((a2*b3-a3*b2, a3*b1-a1*b3, a1*b2-a2*b1))
def angleDeg(self, other):
ratio = self.dot(other) / (self.length() * other.length())
rads = math.acos(ratio)
return math.degrees(rads)
def signedAngleDeg(self, other, ref):
angle1 = self.angleDeg(other)
cross = self.cross(ref)
angle2 = other.angleDeg(cross)
if angle2 < 90:
return -angle1
else:
return angle1
def __repr__(self):
return 'Vector3D(x={}, y={}, z={})'.format(self.x, self.y, self.z)
def __str__(self):
return '[{}, {}, {}]'.format(self.x, self.y, self.z)
class Point3D:
def __init__(self, point):
self.x = point[0]
self.y = point[1]
self.z = point[2]
def __sub__(self, other):
#x = other.x - self.x
#y = other.y - self.y
#z = other.z - self.z
x = self.x - other.x
y = self.y - other.y
z = self.z - other.z
return Vector3D((x, y, z))
def tuple(self):
return (self.x, self.y, self.z)
def __getitem__(self, key):
return self.tuple()[key]
def __repr__(self):
return 'Point3D(x={}, y={}, z={})'.format(self.x, self.y, self.z)
def __str__(self):
return '({}, {}, {})'.format(self.x, self.y, self.z)
if __name__ == '__main__':
# test to make sure our Vector module is the same as Panda3D
from panda3d.core import LPoint3f, LVector3f
import random
pPITCH_ANGLE_DIR = LVector3f(x=0, y=1, z=0)
pYAW_ANGLE_DIR = LVector3f(x=0, y=0, z=-1)
pYAW_ANGLE_REF = LVector3f(x=0, y=1, z=0)
PITCH_ANGLE_DIR = Vector3D((0, 1, 0))
YAW_ANGLE_DIR = Vector3D((0, 0, -1))
YAW_ANGLE_REF = Vector3D((0, 1, 0))
for _ in range(1000):
r = lambda: random.uniform(-10, 10)
a, b, c = r(), r(), r()
plook_at_d = LVector3f(x=a, y=b, z=c)
look_at_d = Vector3D((a, b, c))
ptarget_pitch = plook_at_d.normalized().angleDeg(pPITCH_ANGLE_DIR)
target_pitch = look_at_d.normalized().angleDeg(PITCH_ANGLE_DIR)
if round(ptarget_pitch) != round(target_pitch):
print('mismatch:', ptarget_pitch, target_pitch)
break
else: # for
print('no mismatches')
for _ in range(1000):
r = lambda: random.uniform(-10, 10)
a, b, c = r(), r(), r()
plook_at_d = LVector3f(x=a, y=b, z=c)
look_at_d = Vector3D((a, b, c))
ptarget_yaw = plook_at_d.normalized().signedAngleDeg(other=pYAW_ANGLE_DIR, ref=pYAW_ANGLE_REF)
target_yaw = look_at_d.normalized().signedAngleDeg(other=YAW_ANGLE_DIR, ref=YAW_ANGLE_REF)
if round(ptarget_yaw) != round(target_yaw):
print('mismatch:', ptarget_yaw, target_yaw)
break
else: # for
print('no mismatches')