Move command processing to separate file
This commit is contained in:
parent
ae2b0f4875
commit
a5642409d2
|
@ -24,6 +24,7 @@ from mosfet.protocol.managers import DataManager, ChunksManager, ChatManager, Ch
|
||||||
|
|
||||||
from munch import Munch
|
from munch import Munch
|
||||||
|
|
||||||
|
from mosfet import commands
|
||||||
from mosfet import game
|
from mosfet import game
|
||||||
from mosfet import job
|
from mosfet import job
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
|
@ -38,6 +39,7 @@ from mosfet.info import mobs
|
||||||
|
|
||||||
for module in [
|
for module in [
|
||||||
blocks,
|
blocks,
|
||||||
|
commands,
|
||||||
game,
|
game,
|
||||||
items,
|
items,
|
||||||
job,
|
job,
|
||||||
|
@ -267,6 +269,7 @@ def bot(global_state):
|
||||||
|
|
||||||
g.game = game.Game(g)
|
g.game = game.Game(g)
|
||||||
g.world = world.World(g)
|
g.world = world.World(g)
|
||||||
|
g.commands = commands.Commands(g)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while not g.pos:
|
while not g.pos:
|
||||||
|
|
524
mosfet/commands.py
Normal file
524
mosfet/commands.py
Normal file
|
@ -0,0 +1,524 @@
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
from math import hypot
|
||||||
|
from itertools import count
|
||||||
|
from munch import Munch
|
||||||
|
|
||||||
|
from mosfet.protocol.types import Slot
|
||||||
|
|
||||||
|
from mosfet import print_help
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet import bot
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class Commands:
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
|
||||||
|
self.g.chat.set_handler(self.handle_chat)
|
||||||
|
|
||||||
|
def handle_chat(self, message):
|
||||||
|
source, text = message
|
||||||
|
reply = None
|
||||||
|
private = False
|
||||||
|
for_me = False
|
||||||
|
authed = False
|
||||||
|
|
||||||
|
if source == 'SYSTEM':
|
||||||
|
self.g.command_lock = False
|
||||||
|
|
||||||
|
if text == 'You are now AFK.':
|
||||||
|
self.g.afk = True
|
||||||
|
elif text == 'You are no longer AFK.':
|
||||||
|
self.g.afk = False
|
||||||
|
|
||||||
|
match1 = re.match(r'<?(\w+)> (.*)', text)
|
||||||
|
match2 = re.match(r'\[(\w+) -> me] (.*)', text)
|
||||||
|
if match1:
|
||||||
|
sender, text = match1.groups()
|
||||||
|
elif match2:
|
||||||
|
sender, text = match2.groups()
|
||||||
|
private = True
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if sender == 'tanner6':
|
||||||
|
authed = True
|
||||||
|
|
||||||
|
if text.startswith('zzz'):
|
||||||
|
text = '!zzz'
|
||||||
|
|
||||||
|
bot_num = self.g.name[-1]
|
||||||
|
|
||||||
|
if text.startswith(bot_num):
|
||||||
|
text = text[1:]
|
||||||
|
for_me = True
|
||||||
|
elif text.startswith('! '):
|
||||||
|
text = text[2:]
|
||||||
|
elif text.startswith('!'):
|
||||||
|
text = text[1:]
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
if ' ' in text:
|
||||||
|
command = text.split(' ', 1)[0]
|
||||||
|
data = text.split(' ', 1)[1]
|
||||||
|
else:
|
||||||
|
command = text
|
||||||
|
data = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
## ### Public Commands
|
||||||
|
## These can be ran by anyone, all bots will reply.
|
||||||
|
|
||||||
|
## !help - prints this whole help message to console
|
||||||
|
## !help [command] - replies in-game explaining command
|
||||||
|
if command == 'help':
|
||||||
|
if data:
|
||||||
|
for line in print_help.HELP_LINES:
|
||||||
|
if line[1:].startswith(data) or line[1:].startswith(data[1:]):
|
||||||
|
reply = 'command ' + line
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
reply = 'command not found'
|
||||||
|
else:
|
||||||
|
print()
|
||||||
|
print()
|
||||||
|
for line in print_help.HELP_LINES:
|
||||||
|
print(line)
|
||||||
|
reply = 'check console'
|
||||||
|
|
||||||
|
## !ping - replies with "pong"
|
||||||
|
if command == 'ping':
|
||||||
|
reply = 'pong'
|
||||||
|
|
||||||
|
## !echo [data] - replies with "data"
|
||||||
|
if command == 'echo' and data:
|
||||||
|
reply = data
|
||||||
|
|
||||||
|
## !pos - replies with position and dimension
|
||||||
|
if command == 'pos':
|
||||||
|
reply = str(utils.pint(self.g.pos))[1:-1] + ', ' + self.g.dimension
|
||||||
|
|
||||||
|
## !afk - goes AFK with /afk
|
||||||
|
if command == 'afk':
|
||||||
|
if not self.g.afk:
|
||||||
|
reply = '/afk'
|
||||||
|
|
||||||
|
## !unafk - goes not AFK with /afk
|
||||||
|
if command == 'unafk':
|
||||||
|
if self.g.afk:
|
||||||
|
reply = '/afk'
|
||||||
|
|
||||||
|
## !error - raises an error
|
||||||
|
if command == 'error':
|
||||||
|
reply = 'ok'
|
||||||
|
raise
|
||||||
|
|
||||||
|
## !inv - prints current inventory
|
||||||
|
if command == 'inv':
|
||||||
|
inv_list = []
|
||||||
|
for i in self.g.inv.values():
|
||||||
|
if i.present:
|
||||||
|
inv_list.append('{}:{} x {}'.format(items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count))
|
||||||
|
inv_list.sort()
|
||||||
|
result = '\n'.join(inv_list)
|
||||||
|
print(result or 'Empty')
|
||||||
|
|
||||||
|
## !time - replies with Minecraft world time
|
||||||
|
if command == 'time':
|
||||||
|
reply = str(self.g.time)
|
||||||
|
|
||||||
|
## !count [id] - counts the number of items with that id
|
||||||
|
if command == 'count' and data:
|
||||||
|
item = int(data)
|
||||||
|
reply = str(self.g.game.count_items([item]))
|
||||||
|
|
||||||
|
## !loaded - replies with the current loaded area
|
||||||
|
if command == 'loaded':
|
||||||
|
reply = str(self.g.chunks.get_loaded_area())
|
||||||
|
|
||||||
|
## !players - prints the current players
|
||||||
|
## !players clear - clears the current player list
|
||||||
|
if command == 'players':
|
||||||
|
if data == 'clear':
|
||||||
|
self.g.players = {}
|
||||||
|
reply = 'ok'
|
||||||
|
else:
|
||||||
|
for k, v in self.g.players.items():
|
||||||
|
print(str(k) + ':', v, self.g.player_names[v.player_uuid])
|
||||||
|
|
||||||
|
## !objects - prints the current items on ground
|
||||||
|
## !objects clear - clears the current object list
|
||||||
|
if command == 'objects':
|
||||||
|
if data == 'clear':
|
||||||
|
self.g.objects = {}
|
||||||
|
reply = 'ok'
|
||||||
|
else:
|
||||||
|
for k, v in self.g.objects.items():
|
||||||
|
if data and v.item_id != int(data): continue
|
||||||
|
print(str(k) + ':', v, items.ITEM_NAMES[v.item_id])
|
||||||
|
|
||||||
|
## !mobs - prints the current mobs
|
||||||
|
## !mobs clear - clears the current mob list
|
||||||
|
if command == 'mobs':
|
||||||
|
if data == 'clear':
|
||||||
|
self.g.mobs = {}
|
||||||
|
reply = 'ok'
|
||||||
|
else:
|
||||||
|
all_mobs = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
|
||||||
|
for k, v in all_mobs:
|
||||||
|
if data and v.type != int(data): continue
|
||||||
|
print(str(k) + ':', v, mobs.MOB_NAMES[v.type])
|
||||||
|
reply = str(len(all_mobs)) + ' mobs'
|
||||||
|
|
||||||
|
## !monsters - prints the current monsters
|
||||||
|
if command == 'monsters':
|
||||||
|
monsters = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
|
||||||
|
count = 0
|
||||||
|
for k, v in monsters:
|
||||||
|
if v.type not in mobs.EVIL_IDS: continue
|
||||||
|
if data and v.type != int(data): continue
|
||||||
|
count += 1
|
||||||
|
print(str(k) + ':', v, mobs.MOB_NAMES[v.type])
|
||||||
|
reply = str(count) + ' monsters'
|
||||||
|
|
||||||
|
## !villagers - prints the current villagers
|
||||||
|
if command == 'villagers':
|
||||||
|
all_mobs = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
|
||||||
|
count = 0
|
||||||
|
for k, v in all_mobs:
|
||||||
|
type_name = mobs.MOB_NAMES[v.type]
|
||||||
|
if type_name != 'villager' : continue
|
||||||
|
count += 1
|
||||||
|
print(str(k) + ':', v, type_name)
|
||||||
|
reply = str(count) + ' villagers'
|
||||||
|
|
||||||
|
## !threats - prints the dangerous monsters within 20 blocks
|
||||||
|
## !threats [num] - prints the dangerous monsters within num blocks
|
||||||
|
if command == 'threats':
|
||||||
|
distance = int(data) if data else 20
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
threats = self.g.world.find_threats(p, distance)
|
||||||
|
|
||||||
|
for t in threats:
|
||||||
|
print(str(t.entity_id) + ':', t, mobs.MOB_NAMES[t.type])
|
||||||
|
reply = str(len(threats)) + ' threats'
|
||||||
|
|
||||||
|
if command == 'spiral' and data:
|
||||||
|
for i in range(int(data)):
|
||||||
|
print(utils.spiral(i))
|
||||||
|
|
||||||
|
if command == 'sand_slice':
|
||||||
|
result = self.g.world.find_sand_slice(utils.pint(self.g.pos), 50)
|
||||||
|
reply = str(result)
|
||||||
|
|
||||||
|
## "zzz" or !zzz - bot does /afk to let others sleep
|
||||||
|
if command == 'zzz':
|
||||||
|
if not self.g.afk and self.g.dimension == 'overworld':
|
||||||
|
reply = '/afk'
|
||||||
|
self.g.afk_timeout = 5.0
|
||||||
|
|
||||||
|
## !tree - replies with the closest tree
|
||||||
|
if command == 'tree':
|
||||||
|
pos = utils.pint(self.g.pos)
|
||||||
|
tree = next(self.g.world.find_trees(pos, 50))
|
||||||
|
reply = str(tree)[1:-1]
|
||||||
|
|
||||||
|
## !block x y z - replies what block is at (x, y, z)
|
||||||
|
if command == 'block':
|
||||||
|
try:
|
||||||
|
data = data.replace('(', ' ').replace(')', ' ').replace(',', ' ')
|
||||||
|
x1, y1, z1 = [int(x) for x in data.split()]
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
reply = 'usage: !block x1 y1 z1'
|
||||||
|
|
||||||
|
if not reply:
|
||||||
|
coord = (x1, y1, z1)
|
||||||
|
block = self.g.world.block_at(*coord)
|
||||||
|
|
||||||
|
if not reply and block is None:
|
||||||
|
reply = 'first coord out of range'
|
||||||
|
|
||||||
|
if not reply:
|
||||||
|
reply = blocks.BLOCKS[block] + ':' + str(block)
|
||||||
|
|
||||||
|
|
||||||
|
################# Specific commands ##########################
|
||||||
|
|
||||||
|
## ### Bot-specific Commands
|
||||||
|
## These will only run for the bot they are addressed to.
|
||||||
|
|
||||||
|
if for_me:
|
||||||
|
|
||||||
|
## 1respawn - respawns the bot if it's dead
|
||||||
|
if command == 'respawn':
|
||||||
|
self.g.game.respawn()
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
## 1gather wood - gathers wood from the world
|
||||||
|
## 1gather sand - gathers sand from the world
|
||||||
|
if command == 'gather' and data:
|
||||||
|
if data == 'wood':
|
||||||
|
self.g.job.state = self.g.job.gather_wood
|
||||||
|
reply = 'ok'
|
||||||
|
elif data == 'sand':
|
||||||
|
self.g.job.state = self.g.job.gather_sand
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
if reply:
|
||||||
|
for i in self.g.inv.values():
|
||||||
|
print(i.item_id)
|
||||||
|
if i.item_id in items.BED_IDS:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
reply += ', I need a bed'
|
||||||
|
|
||||||
|
## 1farm wood - farms wood from a certain area
|
||||||
|
## 1farm sand - farms sand from a certain area
|
||||||
|
## 1farm wart - farms netherwart from a certain area
|
||||||
|
## 1farm crop - farms mature crops from a certain area
|
||||||
|
if command == 'farm' and data:
|
||||||
|
if data == 'wood':
|
||||||
|
self.g.job.state = self.g.job.farm_wood
|
||||||
|
reply = 'ok'
|
||||||
|
elif data == 'sand':
|
||||||
|
self.g.job.state = self.g.job.farm_sand
|
||||||
|
reply = 'ok'
|
||||||
|
elif data == 'wart':
|
||||||
|
self.g.job.state = self.g.job.farm_wart
|
||||||
|
reply = 'ok'
|
||||||
|
elif data.startswith('crop'):
|
||||||
|
self.g.job.state = self.g.job.farm_crop
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
if reply and self.g.dimension == 'overworld':
|
||||||
|
for i in self.g.inv.values():
|
||||||
|
if i.item_id in items.BED_IDS:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
reply += ', I need a bed'
|
||||||
|
|
||||||
|
## 1loiter - stands still but eats, sleeps, and flees
|
||||||
|
if command == 'loiter':
|
||||||
|
self.g.job.state = self.g.job.loiter
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
## 1trade - sells items to villagers to get emeralds
|
||||||
|
if command == 'trade':
|
||||||
|
self.g.job.state = self.g.job.trade
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
## 1stop - stops the current job and resets bot
|
||||||
|
if command == 'stop':
|
||||||
|
self.g.game.close_window()
|
||||||
|
bot.init(self.g)
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
## 1drop - drops the current stack its holding
|
||||||
|
if command == 'drop':
|
||||||
|
self.g.game.drop_stack()
|
||||||
|
|
||||||
|
## 1select [id] - moves item with id into main hand
|
||||||
|
if command == 'select' and data:
|
||||||
|
item = int(data)
|
||||||
|
if self.g.game.select_item([item]):
|
||||||
|
reply = 'ok'
|
||||||
|
else:
|
||||||
|
reply = 'not found'
|
||||||
|
|
||||||
|
## 1dump [id] - drops all items matching id
|
||||||
|
if command == 'dump' and data:
|
||||||
|
item = int(data)
|
||||||
|
if self.g.game.count_items([item]):
|
||||||
|
self.g.dumping = item
|
||||||
|
reply = 'ok'
|
||||||
|
else:
|
||||||
|
reply = 'not found'
|
||||||
|
|
||||||
|
## 1drain - drops all items in inventory
|
||||||
|
if command == 'drain':
|
||||||
|
self.g.draining = True
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
if command == 'gapple':
|
||||||
|
self.g.job.state = self.g.job.find_gapple
|
||||||
|
if data:
|
||||||
|
self.g.job.find_gapple_states.count = int(data)
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
if command == 'cache':
|
||||||
|
self.g.job.state = self.g.job.cache_items
|
||||||
|
self.g.job.cache_items_states.minimum = 0
|
||||||
|
self.g.job.cache_items_states.silent = True
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
|
||||||
|
## 1fill [x] [y] [z] [x] [y] [z] - fills the cuboid with the block at the first coordinate
|
||||||
|
if command == 'fill':
|
||||||
|
try:
|
||||||
|
data = data.replace('(', ' ').replace(')', ' ').replace(',', ' ')
|
||||||
|
x1, y1, z1, x2, y2, z2 = [int(x) for x in data.split()]
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
reply = 'usage: !fill x1 y1 z1 x2 y2 z2'
|
||||||
|
|
||||||
|
if not reply:
|
||||||
|
coord1 = (x1, y1, z1)
|
||||||
|
coord2 = (x2, y2, z2)
|
||||||
|
block = self.g.world.block_at(*coord1)
|
||||||
|
|
||||||
|
if not reply and y1 > y2:
|
||||||
|
reply = 'can only fill upwards'
|
||||||
|
|
||||||
|
if not reply and block is None:
|
||||||
|
reply = 'first coord out of range'
|
||||||
|
|
||||||
|
if not reply and block == 0:
|
||||||
|
reply = 'can\'t fill with air'
|
||||||
|
|
||||||
|
if not reply:
|
||||||
|
self.g.filling = Munch(coord1=coord1, coord2=coord2, block=block)
|
||||||
|
self.g.job.state = self.g.job.fill_blocks
|
||||||
|
reply = 'filling ' + str(utils.pvolume(coord1, coord2)) + ' with ' + blocks.BLOCKS[block]
|
||||||
|
|
||||||
|
## 1here - bot comes to your location
|
||||||
|
if command == 'here':
|
||||||
|
try:
|
||||||
|
sender_uuid = self.g.player_names[sender]
|
||||||
|
except KeyError:
|
||||||
|
reply = 'can\'t find your uuid'
|
||||||
|
|
||||||
|
if not reply:
|
||||||
|
for p in self.g.players.values():
|
||||||
|
if p.player_uuid == sender_uuid:
|
||||||
|
player = p
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
reply = 'can\'t find you'
|
||||||
|
|
||||||
|
if not reply:
|
||||||
|
pos = utils.pint(self.g.pos)
|
||||||
|
goal = utils.pint((p.x, p.y, p.z))
|
||||||
|
start = time.time()
|
||||||
|
navpath = self.g.world.path_to_place(pos, goal)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath
|
||||||
|
if self.g.job:
|
||||||
|
self.g.job.stop()
|
||||||
|
print(len(navpath))
|
||||||
|
print(navpath)
|
||||||
|
print(round(time.time() - start, 3), 'seconds')
|
||||||
|
if self.g.job:
|
||||||
|
self.g.job.stop()
|
||||||
|
self.g.look_at = None
|
||||||
|
reply = 'ok'
|
||||||
|
else:
|
||||||
|
reply = 'no path'
|
||||||
|
|
||||||
|
## 1goto [x] [y] [z] - sends the bot to coordinate (x, y, z)
|
||||||
|
if command == 'goto':
|
||||||
|
try:
|
||||||
|
data = data.replace('(', ' ').replace(')', ' ').replace(',', ' ')
|
||||||
|
x2, y2, z2 = [int(x) for x in data.split()]
|
||||||
|
except (AttributeError, ValueError):
|
||||||
|
reply = 'usage: !goto x y z'
|
||||||
|
|
||||||
|
if not reply:
|
||||||
|
pos = utils.pint(self.g.pos)
|
||||||
|
goal = utils.pint((x2, y2, z2))
|
||||||
|
start = time.time()
|
||||||
|
navpath = self.g.world.path_to_place(pos, goal)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath
|
||||||
|
if self.g.job:
|
||||||
|
self.g.job.stop()
|
||||||
|
print(len(navpath))
|
||||||
|
print(navpath)
|
||||||
|
print(round(time.time() - start, 3), 'seconds')
|
||||||
|
if self.g.job:
|
||||||
|
self.g.job.stop()
|
||||||
|
self.g.look_at = None
|
||||||
|
reply = 'ok'
|
||||||
|
else:
|
||||||
|
reply = 'no path'
|
||||||
|
|
||||||
|
if command == 'break':
|
||||||
|
self.g.game.break_block(blocks.TEST_BLOCK)
|
||||||
|
reply = 'ok'
|
||||||
|
|
||||||
|
if command == 'open':
|
||||||
|
self.g.game.open_container(blocks.TEST_BLOCK)
|
||||||
|
|
||||||
|
## 1close - closes the current Minecraft window
|
||||||
|
if command == 'close':
|
||||||
|
if self.g.window:
|
||||||
|
self.g.game.close_window()
|
||||||
|
reply = 'ok'
|
||||||
|
else:
|
||||||
|
reply = 'nothing open'
|
||||||
|
|
||||||
|
## 1click [slot] [button] [mode] - clicks the current window
|
||||||
|
if command == 'click' and data:
|
||||||
|
if self.g.window:
|
||||||
|
slot, button, mode = [int(x) for x in data.split(' ')]
|
||||||
|
try:
|
||||||
|
item = self.g.window.contents[slot]
|
||||||
|
except KeyError:
|
||||||
|
item = Slot(present=False)
|
||||||
|
print(item)
|
||||||
|
self.g.game.click_window(slot, button, mode, item)
|
||||||
|
else:
|
||||||
|
reply = 'nothing open'
|
||||||
|
|
||||||
|
## 1use - use the item it's currently holding
|
||||||
|
if command == 'use':
|
||||||
|
self.g.game.use_item(0)
|
||||||
|
|
||||||
|
## 1interact [entity id] - interacts with that entity
|
||||||
|
if command == 'interact' and data:
|
||||||
|
self.g.game.interact(int(data))
|
||||||
|
|
||||||
|
if command == 'test':
|
||||||
|
reply = 'ok'
|
||||||
|
r = self.g.world.find_villager_openings((615, 78, 493))
|
||||||
|
print(r)
|
||||||
|
|
||||||
|
################# Authorized commands ##########################
|
||||||
|
|
||||||
|
## ### Authorized Commands
|
||||||
|
## These dangerous commands can only be ran by the bot owner.
|
||||||
|
|
||||||
|
if authed:
|
||||||
|
|
||||||
|
## 1print [expression] - replies with Python eval(expression)
|
||||||
|
if command == 'print':
|
||||||
|
data = data.replace('`', '.')
|
||||||
|
reply = str(eval(data))
|
||||||
|
|
||||||
|
## 1exit - exits the program
|
||||||
|
if command == 'exit':
|
||||||
|
import os
|
||||||
|
os._exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
except BaseException as e:
|
||||||
|
import traceback
|
||||||
|
print(traceback.format_exc())
|
||||||
|
reply = 'Error: {} - {}\n'.format(e.__class__.__name__, e)
|
||||||
|
pass
|
||||||
|
|
||||||
|
if reply:
|
||||||
|
print(reply)
|
||||||
|
if private and not reply.startswith('/'):
|
||||||
|
self.g.chat.send('/m ' + sender + ' ' + reply)
|
||||||
|
else:
|
||||||
|
self.g.chat.send(reply)
|
||||||
|
|
513
mosfet/game.py
513
mosfet/game.py
|
@ -1,6 +1,5 @@
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import importlib
|
|
||||||
import random
|
import random
|
||||||
from math import hypot
|
from math import hypot
|
||||||
from itertools import count
|
from itertools import count
|
||||||
|
@ -22,7 +21,6 @@ from mosfet.protocol.packets import (
|
||||||
|
|
||||||
from mosfet.protocol.types import Slot
|
from mosfet.protocol.types import Slot
|
||||||
|
|
||||||
from mosfet import print_help
|
|
||||||
from mosfet import utils
|
from mosfet import utils
|
||||||
from mosfet import path
|
from mosfet import path
|
||||||
from mosfet import bot
|
from mosfet import bot
|
||||||
|
@ -64,8 +62,6 @@ class Game:
|
||||||
|
|
||||||
#register(self.handle_packet, Packet, early=True)
|
#register(self.handle_packet, Packet, early=True)
|
||||||
|
|
||||||
self.g.chat.set_handler(self.handle_chat)
|
|
||||||
|
|
||||||
def handle_login_success(self, packet):
|
def handle_login_success(self, packet):
|
||||||
print(packet)
|
print(packet)
|
||||||
self.g.name = packet.Username
|
self.g.name = packet.Username
|
||||||
|
@ -126,510 +122,6 @@ class Game:
|
||||||
if new_path:
|
if new_path:
|
||||||
self.g.path = new_path
|
self.g.path = new_path
|
||||||
|
|
||||||
def handle_chat(self, message):
|
|
||||||
source, text = message
|
|
||||||
reply = None
|
|
||||||
private = False
|
|
||||||
for_me = False
|
|
||||||
authed = False
|
|
||||||
|
|
||||||
if source == 'SYSTEM':
|
|
||||||
self.g.command_lock = False
|
|
||||||
|
|
||||||
if text == 'You are now AFK.':
|
|
||||||
self.g.afk = True
|
|
||||||
elif text == 'You are no longer AFK.':
|
|
||||||
self.g.afk = False
|
|
||||||
|
|
||||||
match1 = re.match(r'<?(\w+)> (.*)', text)
|
|
||||||
match2 = re.match(r'\[(\w+) -> me] (.*)', text)
|
|
||||||
if match1:
|
|
||||||
sender, text = match1.groups()
|
|
||||||
elif match2:
|
|
||||||
sender, text = match2.groups()
|
|
||||||
private = True
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
if sender == 'tanner6':
|
|
||||||
authed = True
|
|
||||||
|
|
||||||
if text.startswith('zzz'):
|
|
||||||
text = '!zzz'
|
|
||||||
|
|
||||||
bot_num = self.g.name[-1]
|
|
||||||
|
|
||||||
if text.startswith(bot_num):
|
|
||||||
text = text[1:]
|
|
||||||
for_me = True
|
|
||||||
elif text.startswith('! '):
|
|
||||||
text = text[2:]
|
|
||||||
elif text.startswith('!'):
|
|
||||||
text = text[1:]
|
|
||||||
else:
|
|
||||||
return
|
|
||||||
|
|
||||||
if ' ' in text:
|
|
||||||
command = text.split(' ', 1)[0]
|
|
||||||
data = text.split(' ', 1)[1]
|
|
||||||
else:
|
|
||||||
command = text
|
|
||||||
data = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
|
|
||||||
## ### Public Commands
|
|
||||||
## These can be ran by anyone, all bots will reply.
|
|
||||||
|
|
||||||
## !help - prints this whole help message to console
|
|
||||||
## !help [command] - replies in-game explaining command
|
|
||||||
if command == 'help':
|
|
||||||
if data:
|
|
||||||
for line in print_help.HELP_LINES:
|
|
||||||
if line[1:].startswith(data) or line[1:].startswith(data[1:]):
|
|
||||||
reply = 'command ' + line
|
|
||||||
break
|
|
||||||
else: # for
|
|
||||||
reply = 'command not found'
|
|
||||||
else:
|
|
||||||
print()
|
|
||||||
print()
|
|
||||||
for line in print_help.HELP_LINES:
|
|
||||||
print(line)
|
|
||||||
reply = 'check console'
|
|
||||||
|
|
||||||
## !ping - replies with "pong"
|
|
||||||
if command == 'ping':
|
|
||||||
reply = 'pong'
|
|
||||||
|
|
||||||
## !echo [data] - replies with "data"
|
|
||||||
if command == 'echo' and data:
|
|
||||||
reply = data
|
|
||||||
|
|
||||||
## !pos - replies with position and dimension
|
|
||||||
if command == 'pos':
|
|
||||||
reply = str(utils.pint(self.g.pos))[1:-1] + ', ' + self.g.dimension
|
|
||||||
|
|
||||||
## !afk - goes AFK with /afk
|
|
||||||
if command == 'afk':
|
|
||||||
if not self.g.afk:
|
|
||||||
reply = '/afk'
|
|
||||||
|
|
||||||
## !unafk - goes not AFK with /afk
|
|
||||||
if command == 'unafk':
|
|
||||||
if self.g.afk:
|
|
||||||
reply = '/afk'
|
|
||||||
|
|
||||||
## !error - raises an error
|
|
||||||
if command == 'error':
|
|
||||||
reply = 'ok'
|
|
||||||
raise
|
|
||||||
|
|
||||||
## !inv - prints current inventory
|
|
||||||
if command == 'inv':
|
|
||||||
inv_list = []
|
|
||||||
for i in self.g.inv.values():
|
|
||||||
if i.present:
|
|
||||||
inv_list.append('{}:{} x {}'.format(items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count))
|
|
||||||
inv_list.sort()
|
|
||||||
result = '\n'.join(inv_list)
|
|
||||||
print(result or 'Empty')
|
|
||||||
|
|
||||||
## !time - replies with Minecraft world time
|
|
||||||
if command == 'time':
|
|
||||||
reply = str(self.g.time)
|
|
||||||
|
|
||||||
## !count [id] - counts the number of items with that id
|
|
||||||
if command == 'count' and data:
|
|
||||||
item = int(data)
|
|
||||||
reply = str(self.count_items([item]))
|
|
||||||
|
|
||||||
## !loaded - replies with the current loaded area
|
|
||||||
if command == 'loaded':
|
|
||||||
reply = str(self.g.chunks.get_loaded_area())
|
|
||||||
|
|
||||||
## !players - prints the current players
|
|
||||||
## !players clear - clears the current player list
|
|
||||||
if command == 'players':
|
|
||||||
if data == 'clear':
|
|
||||||
self.g.players = {}
|
|
||||||
reply = 'ok'
|
|
||||||
else:
|
|
||||||
for k, v in self.g.players.items():
|
|
||||||
print(str(k) + ':', v, self.g.player_names[v.player_uuid])
|
|
||||||
|
|
||||||
## !objects - prints the current items on ground
|
|
||||||
## !objects clear - clears the current object list
|
|
||||||
if command == 'objects':
|
|
||||||
if data == 'clear':
|
|
||||||
self.g.objects = {}
|
|
||||||
reply = 'ok'
|
|
||||||
else:
|
|
||||||
for k, v in self.g.objects.items():
|
|
||||||
if data and v.item_id != int(data): continue
|
|
||||||
print(str(k) + ':', v, items.ITEM_NAMES[v.item_id])
|
|
||||||
|
|
||||||
## !mobs - prints the current mobs
|
|
||||||
## !mobs clear - clears the current mob list
|
|
||||||
if command == 'mobs':
|
|
||||||
if data == 'clear':
|
|
||||||
self.g.mobs = {}
|
|
||||||
reply = 'ok'
|
|
||||||
else:
|
|
||||||
all_mobs = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
|
|
||||||
for k, v in all_mobs:
|
|
||||||
if data and v.type != int(data): continue
|
|
||||||
print(str(k) + ':', v, mobs.MOB_NAMES[v.type])
|
|
||||||
reply = str(len(all_mobs)) + ' mobs'
|
|
||||||
|
|
||||||
## !monsters - prints the current monsters
|
|
||||||
if command == 'monsters':
|
|
||||||
monsters = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
|
|
||||||
count = 0
|
|
||||||
for k, v in monsters:
|
|
||||||
if v.type not in mobs.EVIL_IDS: continue
|
|
||||||
if data and v.type != int(data): continue
|
|
||||||
count += 1
|
|
||||||
print(str(k) + ':', v, mobs.MOB_NAMES[v.type])
|
|
||||||
reply = str(count) + ' monsters'
|
|
||||||
|
|
||||||
## !villagers - prints the current villagers
|
|
||||||
if command == 'villagers':
|
|
||||||
all_mobs = sorted(list(self.g.mobs.items()), key=lambda x: x[1].type)
|
|
||||||
count = 0
|
|
||||||
for k, v in all_mobs:
|
|
||||||
type_name = mobs.MOB_NAMES[v.type]
|
|
||||||
if type_name != 'villager' : continue
|
|
||||||
count += 1
|
|
||||||
print(str(k) + ':', v, type_name)
|
|
||||||
reply = str(count) + ' villagers'
|
|
||||||
|
|
||||||
## !threats - prints the dangerous monsters within 20 blocks
|
|
||||||
## !threats [num] - prints the dangerous monsters within num blocks
|
|
||||||
if command == 'threats':
|
|
||||||
distance = int(data) if data else 20
|
|
||||||
p = utils.pint(self.g.pos)
|
|
||||||
threats = self.g.world.find_threats(p, distance)
|
|
||||||
|
|
||||||
for t in threats:
|
|
||||||
print(str(t.entity_id) + ':', t, mobs.MOB_NAMES[t.type])
|
|
||||||
reply = str(len(threats)) + ' threats'
|
|
||||||
|
|
||||||
if command == 'spiral' and data:
|
|
||||||
for i in range(int(data)):
|
|
||||||
print(utils.spiral(i))
|
|
||||||
|
|
||||||
if command == 'sand_slice':
|
|
||||||
result = self.g.world.find_sand_slice(utils.pint(self.g.pos), 50)
|
|
||||||
reply = str(result)
|
|
||||||
|
|
||||||
## "zzz" or !zzz - bot does /afk to let others sleep
|
|
||||||
if command == 'zzz':
|
|
||||||
if not self.g.afk and self.g.dimension == 'overworld':
|
|
||||||
reply = '/afk'
|
|
||||||
self.g.afk_timeout = 5.0
|
|
||||||
|
|
||||||
## !tree - replies with the closest tree
|
|
||||||
if command == 'tree':
|
|
||||||
pos = utils.pint(self.g.pos)
|
|
||||||
tree = next(self.g.world.find_trees(pos, 50))
|
|
||||||
reply = str(tree)[1:-1]
|
|
||||||
|
|
||||||
## !block x y z - replies what block is at (x, y, z)
|
|
||||||
if command == 'block':
|
|
||||||
try:
|
|
||||||
data = data.replace('(', ' ').replace(')', ' ').replace(',', ' ')
|
|
||||||
x1, y1, z1 = [int(x) for x in data.split()]
|
|
||||||
except (AttributeError, ValueError):
|
|
||||||
reply = 'usage: !block x1 y1 z1'
|
|
||||||
|
|
||||||
if not reply:
|
|
||||||
coord = (x1, y1, z1)
|
|
||||||
block = self.g.world.block_at(*coord)
|
|
||||||
|
|
||||||
if not reply and block is None:
|
|
||||||
reply = 'first coord out of range'
|
|
||||||
|
|
||||||
if not reply:
|
|
||||||
reply = blocks.BLOCKS[block] + ':' + str(block)
|
|
||||||
|
|
||||||
|
|
||||||
################# Specific commands ##########################
|
|
||||||
|
|
||||||
## ### Bot-specific Commands
|
|
||||||
## These will only run for the bot they are addressed to.
|
|
||||||
|
|
||||||
if for_me:
|
|
||||||
pass
|
|
||||||
|
|
||||||
## 1respawn - respawns the bot if it's dead
|
|
||||||
if command == 'respawn':
|
|
||||||
packet = serverbound.play.ClientStatusPacket()
|
|
||||||
packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
|
|
||||||
self.g.connection.write_packet(packet)
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
## 1gather wood - gathers wood from the world
|
|
||||||
## 1gather sand - gathers sand from the world
|
|
||||||
if command == 'gather' and data:
|
|
||||||
if data == 'wood':
|
|
||||||
self.g.job.state = self.g.job.gather_wood
|
|
||||||
reply = 'ok'
|
|
||||||
elif data == 'sand':
|
|
||||||
self.g.job.state = self.g.job.gather_sand
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
if reply:
|
|
||||||
for i in self.g.inv.values():
|
|
||||||
print(i.item_id)
|
|
||||||
if i.item_id in items.BED_IDS:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
reply += ', I need a bed'
|
|
||||||
|
|
||||||
## 1farm wood - farms wood from a certain area
|
|
||||||
## 1farm sand - farms sand from a certain area
|
|
||||||
## 1farm wart - farms netherwart from a certain area
|
|
||||||
## 1farm crop - farms mature crops from a certain area
|
|
||||||
if command == 'farm' and data:
|
|
||||||
if data == 'wood':
|
|
||||||
self.g.job.state = self.g.job.farm_wood
|
|
||||||
reply = 'ok'
|
|
||||||
elif data == 'sand':
|
|
||||||
self.g.job.state = self.g.job.farm_sand
|
|
||||||
reply = 'ok'
|
|
||||||
elif data == 'wart':
|
|
||||||
self.g.job.state = self.g.job.farm_wart
|
|
||||||
reply = 'ok'
|
|
||||||
elif data.startswith('crop'):
|
|
||||||
self.g.job.state = self.g.job.farm_crop
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
if reply and self.g.dimension == 'overworld':
|
|
||||||
for i in self.g.inv.values():
|
|
||||||
if i.item_id in items.BED_IDS:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
reply += ', I need a bed'
|
|
||||||
|
|
||||||
## 1loiter - stands still but eats, sleeps, and flees
|
|
||||||
if command == 'loiter':
|
|
||||||
self.g.job.state = self.g.job.loiter
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
## 1trade - sells items to villagers to get emeralds
|
|
||||||
if command == 'trade':
|
|
||||||
self.g.job.state = self.g.job.trade
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
## 1stop - stops the current job and resets bot
|
|
||||||
if command == 'stop':
|
|
||||||
self.close_window()
|
|
||||||
bot.init(self.g)
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
## 1drop - drops the current stack its holding
|
|
||||||
if command == 'drop':
|
|
||||||
self.drop_stack()
|
|
||||||
|
|
||||||
## 1select [id] - moves item with id into main hand
|
|
||||||
if command == 'select' and data:
|
|
||||||
item = int(data)
|
|
||||||
if self.select_item([item]):
|
|
||||||
reply = 'ok'
|
|
||||||
else:
|
|
||||||
reply = 'not found'
|
|
||||||
|
|
||||||
## 1dump [id] - drops all items matching id
|
|
||||||
if command == 'dump' and data:
|
|
||||||
item = int(data)
|
|
||||||
if self.count_items([item]):
|
|
||||||
self.g.dumping = item
|
|
||||||
reply = 'ok'
|
|
||||||
else:
|
|
||||||
reply = 'not found'
|
|
||||||
|
|
||||||
## 1drain - drops all items in inventory
|
|
||||||
if command == 'drain':
|
|
||||||
self.g.draining = True
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
if command == 'gapple':
|
|
||||||
self.g.job.state = self.g.job.find_gapple
|
|
||||||
if data:
|
|
||||||
self.g.job.find_gapple_states.count = int(data)
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
if command == 'cache':
|
|
||||||
self.g.job.state = self.g.job.cache_items
|
|
||||||
self.g.job.cache_items_states.minimum = 0
|
|
||||||
self.g.job.cache_items_states.silent = True
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
|
|
||||||
## 1fill [x] [y] [z] [x] [y] [z] - fills the cuboid with the block at the first coordinate
|
|
||||||
if command == 'fill':
|
|
||||||
try:
|
|
||||||
data = data.replace('(', ' ').replace(')', ' ').replace(',', ' ')
|
|
||||||
x1, y1, z1, x2, y2, z2 = [int(x) for x in data.split()]
|
|
||||||
except (AttributeError, ValueError):
|
|
||||||
reply = 'usage: !fill x1 y1 z1 x2 y2 z2'
|
|
||||||
|
|
||||||
if not reply:
|
|
||||||
coord1 = (x1, y1, z1)
|
|
||||||
coord2 = (x2, y2, z2)
|
|
||||||
block = self.g.world.block_at(*coord1)
|
|
||||||
|
|
||||||
if not reply and y1 > y2:
|
|
||||||
reply = 'can only fill upwards'
|
|
||||||
|
|
||||||
if not reply and block is None:
|
|
||||||
reply = 'first coord out of range'
|
|
||||||
|
|
||||||
if not reply and block == 0:
|
|
||||||
reply = 'can\'t fill with air'
|
|
||||||
|
|
||||||
if not reply:
|
|
||||||
self.g.filling = Munch(coord1=coord1, coord2=coord2, block=block)
|
|
||||||
self.g.job.state = self.g.job.fill_blocks
|
|
||||||
reply = 'filling ' + str(utils.pvolume(coord1, coord2)) + ' with ' + blocks.BLOCKS[block]
|
|
||||||
|
|
||||||
## 1here - bot comes to your location
|
|
||||||
if command == 'here':
|
|
||||||
try:
|
|
||||||
sender_uuid = self.g.player_names[sender]
|
|
||||||
except KeyError:
|
|
||||||
reply = 'can\'t find your uuid'
|
|
||||||
|
|
||||||
if not reply:
|
|
||||||
for p in self.g.players.values():
|
|
||||||
if p.player_uuid == sender_uuid:
|
|
||||||
player = p
|
|
||||||
break
|
|
||||||
else: # for
|
|
||||||
reply = 'can\'t find you'
|
|
||||||
|
|
||||||
if not reply:
|
|
||||||
pos = utils.pint(self.g.pos)
|
|
||||||
goal = utils.pint((p.x, p.y, p.z))
|
|
||||||
start = time.time()
|
|
||||||
navpath = self.g.world.path_to_place(pos, goal)
|
|
||||||
|
|
||||||
if navpath:
|
|
||||||
self.g.path = navpath
|
|
||||||
if self.g.job:
|
|
||||||
self.g.job.stop()
|
|
||||||
print(len(navpath))
|
|
||||||
print(navpath)
|
|
||||||
print(round(time.time() - start, 3), 'seconds')
|
|
||||||
if self.g.job:
|
|
||||||
self.g.job.stop()
|
|
||||||
self.g.look_at = None
|
|
||||||
reply = 'ok'
|
|
||||||
else:
|
|
||||||
reply = 'no path'
|
|
||||||
|
|
||||||
## 1goto [x] [y] [z] - sends the bot to coordinate (x, y, z)
|
|
||||||
if command == 'goto':
|
|
||||||
try:
|
|
||||||
data = data.replace('(', ' ').replace(')', ' ').replace(',', ' ')
|
|
||||||
x2, y2, z2 = [int(x) for x in data.split()]
|
|
||||||
except (AttributeError, ValueError):
|
|
||||||
reply = 'usage: !goto x y z'
|
|
||||||
|
|
||||||
if not reply:
|
|
||||||
pos = utils.pint(self.g.pos)
|
|
||||||
goal = utils.pint((x2, y2, z2))
|
|
||||||
start = time.time()
|
|
||||||
navpath = self.g.world.path_to_place(pos, goal)
|
|
||||||
|
|
||||||
if navpath:
|
|
||||||
self.g.path = navpath
|
|
||||||
if self.g.job:
|
|
||||||
self.g.job.stop()
|
|
||||||
print(len(navpath))
|
|
||||||
print(navpath)
|
|
||||||
print(round(time.time() - start, 3), 'seconds')
|
|
||||||
if self.g.job:
|
|
||||||
self.g.job.stop()
|
|
||||||
self.g.look_at = None
|
|
||||||
reply = 'ok'
|
|
||||||
else:
|
|
||||||
reply = 'no path'
|
|
||||||
|
|
||||||
if command == 'break':
|
|
||||||
self.break_block(blocks.TEST_BLOCK)
|
|
||||||
reply = 'ok'
|
|
||||||
|
|
||||||
if command == 'open':
|
|
||||||
self.open_container(blocks.TEST_BLOCK)
|
|
||||||
|
|
||||||
## 1close - closes the current Minecraft window
|
|
||||||
if command == 'close':
|
|
||||||
if self.g.window:
|
|
||||||
self.close_window()
|
|
||||||
reply = 'ok'
|
|
||||||
else:
|
|
||||||
reply = 'nothing open'
|
|
||||||
|
|
||||||
## 1click [slot] [button] [mode] - clicks the current window
|
|
||||||
if command == 'click' and data:
|
|
||||||
if self.g.window:
|
|
||||||
slot, button, mode = [int(x) for x in data.split(' ')]
|
|
||||||
try:
|
|
||||||
item = self.g.window.contents[slot]
|
|
||||||
except KeyError:
|
|
||||||
item = Slot(present=False)
|
|
||||||
print(item)
|
|
||||||
self.click_window(slot, button, mode, item)
|
|
||||||
else:
|
|
||||||
reply = 'nothing open'
|
|
||||||
|
|
||||||
## 1use - use the item it's currently holding
|
|
||||||
if command == 'use':
|
|
||||||
self.use_item(0)
|
|
||||||
|
|
||||||
## 1interact [entity id] - interacts with that entity
|
|
||||||
if command == 'interact' and data:
|
|
||||||
self.interact(int(data))
|
|
||||||
|
|
||||||
if command == 'test':
|
|
||||||
reply = 'ok'
|
|
||||||
r = self.g.world.find_villager_openings((615, 78, 493))
|
|
||||||
print(r)
|
|
||||||
|
|
||||||
################# Authorized commands ##########################
|
|
||||||
|
|
||||||
## ### Authorized Commands
|
|
||||||
## These dangerous commands can only be ran by the bot owner.
|
|
||||||
|
|
||||||
if authed:
|
|
||||||
|
|
||||||
## 1print [expression] - replies with Python eval(expression)
|
|
||||||
if command == 'print':
|
|
||||||
data = data.replace('`', '.')
|
|
||||||
reply = str(eval(data))
|
|
||||||
|
|
||||||
## 1exit - exits the program
|
|
||||||
if command == 'exit':
|
|
||||||
import os
|
|
||||||
os._exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
except BaseException as e:
|
|
||||||
import traceback
|
|
||||||
print(traceback.format_exc())
|
|
||||||
reply = 'Error: {} - {}\n'.format(e.__class__.__name__, e)
|
|
||||||
pass
|
|
||||||
|
|
||||||
if reply:
|
|
||||||
print(reply)
|
|
||||||
if private and not reply.startswith('/'):
|
|
||||||
self.g.chat.send('/m ' + sender + ' ' + reply)
|
|
||||||
else:
|
|
||||||
self.g.chat.send(reply)
|
|
||||||
|
|
||||||
def handle_time_update(self, packet):
|
def handle_time_update(self, packet):
|
||||||
self.g.time = packet.time_of_day % 24000
|
self.g.time = packet.time_of_day % 24000
|
||||||
|
|
||||||
|
@ -683,6 +175,11 @@ class Game:
|
||||||
#print(packet)
|
#print(packet)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def respawn(self):
|
||||||
|
packet = serverbound.play.ClientStatusPacket()
|
||||||
|
packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
def animate(self):
|
def animate(self):
|
||||||
packet = serverbound.play.AnimationPacket()
|
packet = serverbound.play.AnimationPacket()
|
||||||
packet.hand = packet.HAND_MAIN
|
packet.hand = packet.HAND_MAIN
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
HELP_LINES = []
|
HELP_LINES = []
|
||||||
|
|
||||||
with open('mosfet/game.py', 'r') as f:
|
with open('mosfet/commands.py', 'r') as f:
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
if line.strip().startswith('## '):
|
if line.strip().startswith('## '):
|
||||||
HELP_LINES.append(line.strip()[3:])
|
HELP_LINES.append(line.strip()[3:])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user