Move bot files into mosfet/
This commit is contained in:
0
mosfet/__init__.py
Normal file
0
mosfet/__init__.py
Normal file
302
mosfet/blocks.py
Normal file
302
mosfet/blocks.py
Normal 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
294
mosfet/bot.py
Normal 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
1355
mosfet/game.py
Normal file
File diff suppressed because it is too large
Load Diff
122
mosfet/items.py
Normal file
122
mosfet/items.py
Normal 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
261
mosfet/job.py
Normal 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
0
mosfet/jobs/__init__.py
Normal file
231
mosfet/jobs/cache_items.py
Normal file
231
mosfet/jobs/cache_items.py
Normal 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()
|
108
mosfet/jobs/check_threats.py
Normal file
108
mosfet/jobs/check_threats.py
Normal 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()
|
||||
|
91
mosfet/jobs/clear_leaves.py
Normal file
91
mosfet/jobs/clear_leaves.py
Normal 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
72
mosfet/jobs/eat_food.py
Normal 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
216
mosfet/jobs/fill_blocks.py
Normal 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
117
mosfet/jobs/find_gapple.py
Normal 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
146
mosfet/jobs/gather_crop.py
Normal 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
130
mosfet/jobs/gather_sand.py
Normal 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
123
mosfet/jobs/gather_wart.py
Normal 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
189
mosfet/jobs/gather_wood.py
Normal 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
88
mosfet/jobs/grab_sand.py
Normal 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()
|
85
mosfet/jobs/grab_sapling.py
Normal file
85
mosfet/jobs/grab_sapling.py
Normal 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()
|
237
mosfet/jobs/grab_supplies.py
Normal file
237
mosfet/jobs/grab_supplies.py
Normal 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
110
mosfet/jobs/plant_tree.py
Normal 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()
|
212
mosfet/jobs/sell_to_villager.py
Normal file
212
mosfet/jobs/sell_to_villager.py
Normal 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()
|
205
mosfet/jobs/sleep_with_bed.py
Normal file
205
mosfet/jobs/sleep_with_bed.py
Normal 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
85
mosfet/main.py
Normal 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
32
mosfet/mcdata.py
Normal 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
50
mosfet/mobs.py
Normal 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
28
mosfet/monkey_patch.py
Normal 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
314
mosfet/path.py
Normal 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
16
mosfet/print_help.py
Normal 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()
|
0
mosfet/protocol/__init__.py
Normal file
0
mosfet/protocol/__init__.py
Normal file
194
mosfet/protocol/managers.py
Normal file
194
mosfet/protocol/managers.py
Normal 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
460
mosfet/protocol/packets.py
Normal 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
217
mosfet/protocol/types.py
Normal 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
181
mosfet/utils.py
Normal 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
123
mosfet/vector.py
Normal 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')
|
Reference in New Issue
Block a user