Compare commits
46 Commits
ae2b0f4875
...
master
Author | SHA1 | Date | |
---|---|---|---|
bbe2d9f99c | |||
2f77c7b990 | |||
3540e5580e | |||
261684ea90 | |||
09b09b3f96 | |||
4d2d358175 | |||
324ad41bf7 | |||
343268af24 | |||
e5fd062a4a | |||
5184d4a173 | |||
396c1e2e33 | |||
217bb0dae0 | |||
f7400a1225 | |||
3c31c41acd | |||
15d4dd7922 | |||
8f0d2eb417 | |||
6315f8c309 | |||
2fc08be581 | |||
c39a8c6d93 | |||
43e8bf97a0 | |||
b59c260e90 | |||
a41d9f2793 | |||
0d2be1378f | |||
0d23dbaf9f | |||
a87cc85eab | |||
080895421d | |||
e4ea9aeaa0 | |||
ad6c412802 | |||
aec057c89c | |||
|
af9cc4b546 | ||
|
666435ddae | ||
1c0824a167 | |||
47c2fca00a | |||
76cdf90b44 | |||
33145cb3f2 | |||
963b3d9736 | |||
a49caaedf5 | |||
7caa51f011 | |||
e588c8fa1a | |||
650398255b | |||
d69f9cf09e | |||
8b85de2b2a | |||
ce9613c00c | |||
09b9002c58 | |||
4026b410f4 | |||
a5642409d2 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,7 +8,6 @@ __pycache__/
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
|
51
README.md
51
README.md
@@ -1,10 +1,16 @@
|
||||
# Mosfet Minecraft Bot
|
||||
|
||||
A general-purpose Minecraft 1.16 bot written in Python.
|
||||
A general-purpose Minecraft 1.16 bot written in Python that uses an actual
|
||||
Minecraft account to play.
|
||||
|
||||
Mosfet is able to farm wood by cutting trees, gather sand, gather netherwart,
|
||||
and trade with villagers to get emeralds. He can eat, sleep, and flee from
|
||||
threats.
|
||||
Mosfet is able to farm wood by cutting trees, farm crops, gather sand, farm
|
||||
netherwart, and trade with villagers to get emeralds. He can eat, sleep, and
|
||||
flee from threats.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python >= 3.6
|
||||
- pip, virtualenv, git, wget, unzip
|
||||
|
||||
## Linux Setup
|
||||
|
||||
@@ -12,7 +18,7 @@ Assuming Debian / Ubuntu based distro:
|
||||
|
||||
```
|
||||
$ sudo apt update
|
||||
$ sudo apt install build-essential python3 python3-dev python3-pip python3-virtualenv git wget unzip
|
||||
$ sudo apt install build-essential python3 python3-dev python3-pip virtualenv git wget unzip
|
||||
|
||||
$ git clone https://git.tannercollin.com/tanner/minecraft-bot.git
|
||||
$ cd minecraft-bot/
|
||||
@@ -20,15 +26,38 @@ $ cd minecraft-bot/
|
||||
|
||||
## Running
|
||||
|
||||
If you want to use the built-in burner account (Minecraft name `mattstack`):
|
||||
|
||||
```
|
||||
$ USERNAME=you@domain.com PASSWORD=supersecret SERVER=example.com ./run-linux.sh
|
||||
$ SERVER=minecraft.example.com ./run_linux.sh
|
||||
```
|
||||
|
||||
Or, use `PORT` to specify a custom port to connect to:
|
||||
|
||||
```
|
||||
$ SERVER=localhost PORT=12345 ./run_linux.sh
|
||||
```
|
||||
|
||||
Or, if you have your own alt account for the bot:
|
||||
|
||||
```
|
||||
$ EMAIL=you@domain.com PASSWORD=supersecret SERVER=minecraft.example.com ./run_linux.sh
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
[How to Cache Items](docs/cache_items.md)
|
||||
|
||||
[How to Grab Supplies](docs/grab_supplies.md)
|
||||
|
||||
## Commands
|
||||
|
||||
The command prefix character is the last character of the bot's name. For
|
||||
example, if the bot's name is `mosfet1`, then you would issue commands like
|
||||
`1farm wood` or `1pos`. This lets you run multiple bots on the same server.
|
||||
example, if the bot's name is `mattstack`, then you would issue commands like
|
||||
`kfarm wood` or `kpos`. This lets you run multiple bots on the same server.
|
||||
|
||||
In the following examples, we'll assume the bot's name is `mosfet1`, so commands
|
||||
would be ran like `1farm wood` or `1pos`.
|
||||
|
||||
The exception are the below public commands, they can optionally be prefixed with `!`
|
||||
and all bots will run the command.
|
||||
@@ -59,7 +88,7 @@ These can be ran by anyone, all bots will reply.
|
||||
|
||||
`!error` - raises an error
|
||||
|
||||
`!inv` - prints current inventory
|
||||
`!inv` - replies and prints current inventory
|
||||
|
||||
`!time` - replies with Minecraft world time
|
||||
|
||||
@@ -95,7 +124,7 @@ These can be ran by anyone, all bots will reply.
|
||||
|
||||
### Bot-specific Commands
|
||||
|
||||
These will only run for the bot they are addressed to.
|
||||
These will only run for the bot they are addressed to. Replace `1` with the last character of the bot's name.
|
||||
|
||||
`1respawn` - respawns the bot if it's dead
|
||||
|
||||
@@ -160,4 +189,4 @@ put to use.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Thanks to Isaia and the devs behind pyCraft.
|
||||
Thanks to Isaia, sose, and the devs behind pyCraft.
|
||||
|
28
docs/cache_items.md
Normal file
28
docs/cache_items.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Mosfet Minecraft Bot
|
||||
|
||||
## How to Cache Items
|
||||
|
||||

|
||||
|
||||
Mosfet will automatically dump items into the closest [trapped
|
||||
chest](https://minecraft.fandom.com/wiki/Trapped_Chest#Crafting) once his
|
||||
inventory gets full enough. Normally this is when the number of used inventory
|
||||
slots is >= 27.
|
||||
|
||||
If Mosfet can't find a trapped chest nearby, he will place down a normal chest
|
||||
if he has one in his inventory. Then he will store the items in it.
|
||||
|
||||
Mosfet won't cache items related to his current job. For example, if he's
|
||||
farming wood, he will keep axes, beds, chests, and food.
|
||||
|
||||
### Advanced Details
|
||||
|
||||
The global `g.minimum_cache_slots` in `bot.py` controls the minimum amount of
|
||||
used slots needed to trigger item caching.
|
||||
|
||||
In `jobs.py`, each job calls `items.set_wanted()` and `items.set_needed()` to
|
||||
control which items Mosfet shouldn't cache. No items in the "needed" list will
|
||||
be cached and all items in the "wanted" list—except for one stack of each—will
|
||||
be cached. The "needed" list is meant for essentials like tools, food, and beds,
|
||||
while the "wanted" list is for stuff that's good to have a stack of like seeds
|
||||
and saplings.
|
30
docs/grab_supplies.md
Normal file
30
docs/grab_supplies.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Mosfet Minecraft Bot
|
||||
|
||||
## How to Grab Supplies
|
||||
|
||||

|
||||
|
||||
Mosfet will automatically grab supplies he needs to do his job from nearby
|
||||
[barrels](https://minecraft.fandom.com/wiki/Barrel#Crafting). Before starting
|
||||
the job, he will check each barrel for related supplies and grab some depending
|
||||
on the job. He will recheck the barrels for supplies periodically to try and get
|
||||
more.
|
||||
|
||||
If he can't find any barrels, he will try to continue doing the job anyway. If
|
||||
his inventory is too full, he will skip grabbing supplies.
|
||||
|
||||
### Advanced Details
|
||||
|
||||
The global `g.maximum_supply_slots` in `bot.py` controls the maximum amount of
|
||||
used slots allowed while grabbing supplies.
|
||||
|
||||
In `jobs.py`, each job sets the `grab_supplies_states.supplies` dictionary to
|
||||
specify what items to grab and how many. Here's an example:
|
||||
|
||||
```
|
||||
self.grab_supplies_states.supplies = {
|
||||
tuple([items.PUMPKIN_ID]): (64, 3),
|
||||
}
|
||||
```
|
||||
|
||||
This tells Mosfet to grab three stacks of pumpkins if he ever has less than 64.
|
BIN
docs/media/cache_items.png
Normal file
BIN
docs/media/cache_items.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 525 KiB |
BIN
docs/media/grab_supplies.png
Normal file
BIN
docs/media/grab_supplies.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 490 KiB |
37
main.py
37
main.py
@@ -4,8 +4,15 @@ import time
|
||||
import traceback
|
||||
import json
|
||||
|
||||
import logging
|
||||
log = logging.getLogger('werkzeug')
|
||||
log.setLevel(logging.ERROR)
|
||||
|
||||
from flask import Flask
|
||||
app = Flask(__name__)
|
||||
from flask_cors import CORS
|
||||
build_folder = 'web_interface/build'
|
||||
app = Flask(__name__, template_folder=build_folder, static_folder=build_folder, static_url_path='')
|
||||
CORS(app)
|
||||
|
||||
from munch import Munch
|
||||
from watchdog.observers import Observer
|
||||
@@ -20,6 +27,7 @@ g.name = None
|
||||
g.mcdata = False
|
||||
g.pos = False
|
||||
g.dimension = None
|
||||
g.item_lock = False
|
||||
g.inv = {}
|
||||
g.objects = {}
|
||||
g.mobs = {}
|
||||
@@ -32,9 +40,10 @@ g.holding = 0
|
||||
g.afk = False
|
||||
g.health = 20
|
||||
g.food = 20
|
||||
g.sand_origin = None
|
||||
|
||||
@app.route('/')
|
||||
def hello_world():
|
||||
@app.route('/api/global')
|
||||
def api_global():
|
||||
data = json.dumps(g, default=lambda o: str(o), indent=4)
|
||||
|
||||
response = app.response_class(
|
||||
@@ -44,6 +53,10 @@ def hello_world():
|
||||
)
|
||||
return response
|
||||
|
||||
@app.route('/')
|
||||
def root():
|
||||
return app.send_static_file('index.html')
|
||||
|
||||
reload_timeout = time.time()
|
||||
|
||||
def main():
|
||||
@@ -58,7 +71,7 @@ def main():
|
||||
event_handler.on_any_event = reload_bot
|
||||
|
||||
observer = Observer()
|
||||
observer.schedule(event_handler, '.', recursive=True)
|
||||
observer.schedule(event_handler, 'mosfet', recursive=True)
|
||||
observer.start()
|
||||
|
||||
try:
|
||||
@@ -78,8 +91,22 @@ def main():
|
||||
observer.stop()
|
||||
observer.join()
|
||||
|
||||
def run_api():
|
||||
host = '0.0.0.0'
|
||||
port = 3300
|
||||
|
||||
while True:
|
||||
print('Trying to run web interface on port:', port)
|
||||
print('If it works, go to http://localhost:' + str(port))
|
||||
try:
|
||||
app.run(host=host, port=port)
|
||||
except OSError:
|
||||
print()
|
||||
print('Error: Port already taken.')
|
||||
port += 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
threading.Thread(target=app.run).start()
|
||||
threading.Thread(target=run_api).start()
|
||||
time.sleep(1)
|
||||
|
||||
main()
|
||||
|
@@ -3,16 +3,12 @@ if __name__ == '__main__':
|
||||
exit(1)
|
||||
|
||||
import os
|
||||
import sys
|
||||
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))
|
||||
|
||||
from . import monkey_patch # must be before any possible pyCraft imports
|
||||
|
||||
from minecraft import authentication
|
||||
@@ -24,6 +20,7 @@ from mosfet.protocol.managers import DataManager, ChunksManager, ChatManager, Ch
|
||||
|
||||
from munch import Munch
|
||||
|
||||
from mosfet import commands
|
||||
from mosfet import game
|
||||
from mosfet import job
|
||||
from mosfet import path
|
||||
@@ -38,6 +35,7 @@ from mosfet.info import mobs
|
||||
|
||||
for module in [
|
||||
blocks,
|
||||
commands,
|
||||
game,
|
||||
items,
|
||||
job,
|
||||
@@ -67,21 +65,19 @@ def tick(global_state):
|
||||
target = None
|
||||
|
||||
# make sure current chunks are loaded for physics
|
||||
if not g.chunks.check_loaded(p, 288):
|
||||
if not g.chunks.check_loaded(g.info.render_distance):
|
||||
if not g.chunks.loading:
|
||||
print('Loading chunks', end='', flush=True)
|
||||
g.chunks.loading = True
|
||||
g.chunks.loading = time.time()
|
||||
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.')
|
||||
print('Chunks loaded in', round(time.time() - g.chunks.loading, 2), 's')
|
||||
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
|
||||
|
||||
@@ -204,8 +200,8 @@ def tick(global_state):
|
||||
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()
|
||||
g.game.tick() # order important for correction_count
|
||||
|
||||
|
||||
def init(global_state):
|
||||
@@ -242,20 +238,57 @@ def init(global_state):
|
||||
g.maximum_supply_slots = 33
|
||||
|
||||
def bot(global_state):
|
||||
EMAIL = os.getenv('EMAIL')
|
||||
PASSWORD = os.getenv('PASSWORD')
|
||||
SERVER = os.getenv('SERVER')
|
||||
PORT = int(os.environ.get('PORT', 25565))
|
||||
|
||||
g = global_state
|
||||
|
||||
if not g.mcdata:
|
||||
g.mcdata = DataManager('./minecraft_data')
|
||||
|
||||
if not g.connection:
|
||||
if not SERVER:
|
||||
print()
|
||||
print('You must specify a server to connect to. For example:')
|
||||
print('SERVER=minecraft.example.com ./run_linux.sh')
|
||||
print('SERVER=localhost PORT=12345 ./run_linux.sh')
|
||||
print()
|
||||
print('If you want to use your own account:')
|
||||
print('EMAIL=you@domain.com PASSWORD=supersecret SERVER=minecraft.example.com ./run_linux.sh')
|
||||
os._exit(0)
|
||||
elif not g.connection:
|
||||
if EMAIL and PASSWORD:
|
||||
auth_token = authentication.AuthenticationToken()
|
||||
try:
|
||||
auth_token.authenticate(USERNAME, PASSWORD)
|
||||
auth_token.authenticate(EMAIL, PASSWORD)
|
||||
except YggdrasilError as e:
|
||||
print(e)
|
||||
sys.exit()
|
||||
os._exit(0)
|
||||
print("Logged in as %s..." % auth_token.username)
|
||||
g.connection = Connection(SERVER, PORT, auth_token=auth_token)
|
||||
elif EMAIL:
|
||||
print('No password provided, attempting to connect in offline mode...')
|
||||
g.connection = Connection(SERVER, PORT, username=EMAIL)
|
||||
elif PASSWORD:
|
||||
print('')
|
||||
print('Did you forget to specify an email?')
|
||||
print('If you want to use your own account:')
|
||||
print('EMAIL=you@domain.com PASSWORD=supersecret SERVER=minecraft.example.com ./run_linux.sh')
|
||||
os._exit(0)
|
||||
else:
|
||||
print('No username or password provided, using burner minecraft account...')
|
||||
EMAIL = 'moc.liamg@monortem'[::-1]
|
||||
PASSWORD = '!8891anteR'[::-1]
|
||||
auth_token = authentication.AuthenticationToken()
|
||||
try:
|
||||
auth_token.authenticate(EMAIL, PASSWORD)
|
||||
except YggdrasilError as e:
|
||||
print(e)
|
||||
os._exit(0)
|
||||
print("Logged in as %s..." % auth_token.username)
|
||||
g.connection = Connection(SERVER, PORT, auth_token=auth_token)
|
||||
|
||||
|
||||
g.chunks = ChunksManager(g.mcdata)
|
||||
|
||||
@@ -267,6 +300,7 @@ def bot(global_state):
|
||||
|
||||
g.game = game.Game(g)
|
||||
g.world = world.World(g)
|
||||
g.commands = commands.Commands(g)
|
||||
|
||||
try:
|
||||
while not g.pos:
|
||||
|
585
mosfet/commands.py
Normal file
585
mosfet/commands.py
Normal file
@@ -0,0 +1,585 @@
|
||||
import re
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
import random
|
||||
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, sender, text = message
|
||||
reply = ''
|
||||
private = False
|
||||
for_me = False
|
||||
authed = sender == '0c123cfa-1697-4427-9413-4b645dee7ec0'
|
||||
bot_num = self.g.name[-1]
|
||||
|
||||
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
|
||||
|
||||
text = text.replace('zzz', '!zzz')
|
||||
|
||||
match = re.match(r'(.*\W+)\s+(['+bot_num+'|!])(\w+) ?(.*)', text)
|
||||
if match:
|
||||
meta, prefix, command, data = match.groups()
|
||||
else:
|
||||
return
|
||||
|
||||
if '-> me' in meta:
|
||||
private = True
|
||||
|
||||
if prefix == bot_num:
|
||||
for_me = True
|
||||
|
||||
if data.startswith('[') and data.endswith(']'):
|
||||
command = 'nosquarebrackets'
|
||||
|
||||
|
||||
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
|
||||
|
||||
if command == 'nosquarebrackets':
|
||||
reply = 'don\'t literally put the [ ]'
|
||||
|
||||
## !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 - replies and prints current inventory
|
||||
if command == 'inv' or command == 'inventory':
|
||||
inv_list = []
|
||||
uniq_item_counts = {}
|
||||
for i in self.g.inv.values():
|
||||
if i.present:
|
||||
inv_list.append((items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count))
|
||||
|
||||
if i.item_id not in uniq_item_counts:
|
||||
uniq_item_counts[i.item_id] = 0
|
||||
uniq_item_counts[i.item_id] += i.item_count
|
||||
|
||||
inv_list.sort()
|
||||
console_result = '\n'.join(['{}:{} x {}'.format(*x) for x in inv_list])
|
||||
|
||||
if not console_result:
|
||||
print('Empty')
|
||||
reply = 'empty'
|
||||
else:
|
||||
print(console_result)
|
||||
|
||||
reply_result_1 = ', '.join(['{}:{} x {}'.format(*x) for x in inv_list])
|
||||
reply_result_2 = ', '.join(['{}:{} x {}'.format(items.ITEM_NAMES[k], str(k), v) for k,v in uniq_item_counts.items()])
|
||||
reply_result_3 = ', '.join(['{}:{}x{}'.format(items.ITEM_NAMES[k], str(k), v) for k,v in uniq_item_counts.items()])
|
||||
reply_result_4 = ', '.join(['{}:{}x{}'.format(re.sub(r'[aeiou]', '', items.ITEM_NAMES[k]), str(k), v) for k,v in uniq_item_counts.items()])
|
||||
reply_result_5 = ' '.join(['{}{}x{}'.format(re.sub(r'[aeiou]', '', items.ITEM_NAMES[k]), str(k), v) for k,v in uniq_item_counts.items()])
|
||||
|
||||
for r in [reply_result_1, reply_result_2, reply_result_3, reply_result_4, reply_result_5]:
|
||||
reply = r
|
||||
if len(reply) < 256:
|
||||
break
|
||||
|
||||
## !time - replies with Minecraft world time
|
||||
if command == 'time':
|
||||
seconds = self.g.time * 3.6
|
||||
start = datetime(2000, 1, 1, hour=6)
|
||||
mctime = start + timedelta(seconds=seconds)
|
||||
reply = str(self.g.time) + ' - ' + mctime.strftime('%I:%M %p')
|
||||
|
||||
## !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':
|
||||
l = self.g.chunks.get_loaded_area()
|
||||
reply = '{}, {}, {} to {}, {}, {}'.format(*l[0], *l[1])
|
||||
reply += ' - ' + str(len(self.g.chunks.chunks)//16) + ' chunks'
|
||||
|
||||
## !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]
|
||||
|
||||
## !info [query] - replies with info on a coordinate, block, item, or player
|
||||
if command == 'info':
|
||||
if not reply:
|
||||
try:
|
||||
check = data.replace('(', ' ').replace(')', ' ').replace(',', ' ')
|
||||
x1, y1, z1 = [int(x) for x in check.split()]
|
||||
coord = (x1, y1, z1)
|
||||
block = self.g.world.block_at(*coord)
|
||||
|
||||
if not reply and block is None:
|
||||
reply = 'coord out of range'
|
||||
|
||||
if not reply:
|
||||
reply = 'Block: ' + blocks.BLOCKS[block] + ':' + str(block)
|
||||
if blocks.PROPS[block]:
|
||||
reply += ' - ' + ', '.join(['{}:{}'.format(k, v) for k, v in blocks.PROPS[block].items()])
|
||||
except (AttributeError, ValueError):
|
||||
pass
|
||||
|
||||
if not reply:
|
||||
try:
|
||||
check = int(data)
|
||||
|
||||
if check in blocks.BLOCKS:
|
||||
block = check
|
||||
reply += 'Block: ' + blocks.BLOCKS[block] + ':' + str(block)
|
||||
if blocks.PROPS[block]:
|
||||
reply += ' - ' + ', '.join(['{}:{}'.format(k, v) for k, v in blocks.PROPS[block].items()])
|
||||
|
||||
if check in blocks.BLOCKS and check in items.ITEM_NAMES:
|
||||
reply += ' / '
|
||||
|
||||
if check in items.ITEM_NAMES:
|
||||
item = check
|
||||
reply += 'Item: ' + items.ITEM_NAMES[item] + ':' + str(item)
|
||||
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
check = data.lower()
|
||||
if not reply and check in self.g.player_names:
|
||||
uuid = self.g.player_names[check]
|
||||
|
||||
for p in self.g.players.values():
|
||||
if p.player_uuid == uuid:
|
||||
player = p
|
||||
break
|
||||
else: # for
|
||||
reply = 'player out of range'
|
||||
|
||||
if not reply:
|
||||
reply += 'Player: '
|
||||
|
||||
results = []
|
||||
for k, v in player.items():
|
||||
try:
|
||||
results.append('{}:{}'.format(k, int(v)))
|
||||
except ValueError:
|
||||
results.append('{}:{}'.format(k, str(v)))
|
||||
|
||||
reply += ', '.join(results)
|
||||
|
||||
|
||||
################# 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':
|
||||
if not self.g.sand_origin or not self.g.chunks.check_loaded(self.g.sand_origin):
|
||||
self.g.sand_origin = utils.pint(self.g.pos)
|
||||
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':
|
||||
if not self.g.sand_origin or not self.g.chunks.check_loaded(self.g.sand_origin):
|
||||
self.g.sand_origin = utils.pint(self.g.pos)
|
||||
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':
|
||||
if not reply:
|
||||
for p in self.g.players.values():
|
||||
if p.player_uuid == sender:
|
||||
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:', reply)
|
||||
|
||||
if len(reply) >= 256:
|
||||
reply = 'reply too long, check console'
|
||||
|
||||
if private and not reply.startswith('/'):
|
||||
self.g.chat.send('/r ' + reply)
|
||||
else:
|
||||
self.g.chat.send(reply)
|
||||
|
540
mosfet/game.py
540
mosfet/game.py
@@ -1,8 +1,6 @@
|
||||
import re
|
||||
import time
|
||||
import importlib
|
||||
import random
|
||||
from math import hypot
|
||||
from itertools import count
|
||||
from munch import Munch
|
||||
|
||||
@@ -17,12 +15,11 @@ from mosfet.protocol.packets import (
|
||||
ClientWindowConfirmationPacket, EntityMetadataPacket,
|
||||
SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket,
|
||||
EntityActionPacket, EntityTeleport, InteractEntityPacket, TradeListPacket,
|
||||
SelectTradePacket, DisconnectPacket,
|
||||
SelectTradePacket, DisconnectPacket, UnloadChunkPacket,
|
||||
)
|
||||
|
||||
from mosfet.protocol.types import Slot
|
||||
|
||||
from mosfet import print_help
|
||||
from mosfet import utils
|
||||
from mosfet import path
|
||||
from mosfet import bot
|
||||
@@ -52,6 +49,7 @@ class Game:
|
||||
register(self.handle_spawn_living, SpawnLivingEntityPacket)
|
||||
register(self.handle_entity_position, clientbound.play.EntityPositionDeltaPacket)
|
||||
register(self.handle_entity_position_rotation, EntityPositionRotationPacket)
|
||||
register(self.handle_entity_look, clientbound.play.EntityLookPacket)
|
||||
register(self.handle_destroy_entities, DestroyEntitiesPacket)
|
||||
register(self.handle_spawn_player, clientbound.play.SpawnPlayerPacket)
|
||||
register(self.handle_respawn, clientbound.play.RespawnPacket)
|
||||
@@ -61,11 +59,10 @@ class Game:
|
||||
#register(self.handle_entity_velocity, clientbound.play.EntityVelocityPacket)
|
||||
register(self.handle_trade_list, TradeListPacket)
|
||||
register(self.handle_disconnect, DisconnectPacket)
|
||||
register(self.handle_unload_chunk, UnloadChunkPacket)
|
||||
|
||||
#register(self.handle_packet, Packet, early=True)
|
||||
|
||||
self.g.chat.set_handler(self.handle_chat)
|
||||
|
||||
def handle_login_success(self, packet):
|
||||
print(packet)
|
||||
self.g.name = packet.Username
|
||||
@@ -126,510 +123,6 @@ class Game:
|
||||
if 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):
|
||||
self.g.time = packet.time_of_day % 24000
|
||||
|
||||
@@ -683,6 +176,11 @@ class Game:
|
||||
#print(packet)
|
||||
return
|
||||
|
||||
def respawn(self):
|
||||
packet = serverbound.play.ClientStatusPacket()
|
||||
packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
|
||||
self.g.connection.write_packet(packet)
|
||||
|
||||
def animate(self):
|
||||
packet = serverbound.play.AnimationPacket()
|
||||
packet.hand = packet.HAND_MAIN
|
||||
@@ -897,6 +395,13 @@ class Game:
|
||||
obj.item_id = entry.value.item_id
|
||||
obj.item_count = entry.value.item_count
|
||||
|
||||
mob = self.g.mobs.get(packet.entity_id, None)
|
||||
if mob:
|
||||
for entry in packet.metadata:
|
||||
if mob.type == mobs.VILLAGER:
|
||||
if entry.index == 17:
|
||||
mob.profession = entry.value[1]
|
||||
|
||||
player = self.g.players.get(packet.entity_id, None)
|
||||
if player:
|
||||
return
|
||||
@@ -938,8 +443,14 @@ class Game:
|
||||
player.x += packet.delta_x / 4096.0
|
||||
player.y += packet.delta_y / 4096.0
|
||||
player.z += packet.delta_z / 4096.0
|
||||
player.yaw = packet.yaw
|
||||
player.pitch = packet.pitch
|
||||
|
||||
#if player.player_uuid == '0c123cfa-1697-4427-9413-4b645dee7ec0': print(packet)
|
||||
def handle_entity_look(self, packet):
|
||||
player = self.g.players.get(packet.entity_id, None)
|
||||
if player:
|
||||
player.yaw = packet.yaw
|
||||
player.pitch = packet.pitch
|
||||
|
||||
def handle_entity_teleport(self, packet):
|
||||
mob = self.g.mobs.get(packet.entity_id, None)
|
||||
@@ -983,12 +494,14 @@ class Game:
|
||||
def handle_respawn(self, packet):
|
||||
print(packet)
|
||||
self.g.dimension = packet.world_name.replace('minecraft:', '')
|
||||
self.g.chunks.unload_all_chunks()
|
||||
|
||||
def handle_player_list(self, packet):
|
||||
for action in packet.actions:
|
||||
if isinstance(action, packet.AddPlayerAction):
|
||||
self.g.player_names[action.uuid] = action.name
|
||||
self.g.player_names[action.name] = action.uuid # porque no los dos?
|
||||
self.g.player_names[action.name] = action.uuid
|
||||
self.g.player_names[action.name.lower()] = action.uuid # porque no los dos?
|
||||
|
||||
def handle_update_health(self, packet):
|
||||
print(packet)
|
||||
@@ -1029,6 +542,9 @@ class Game:
|
||||
import os
|
||||
os._exit(1)
|
||||
|
||||
def handle_unload_chunk(self, packet):
|
||||
self.g.chunks.unload_chunk(packet.chunk_x, packet.chunk_z)
|
||||
|
||||
def tick(self):
|
||||
if self.g.breaking:
|
||||
self.animate()
|
||||
|
@@ -15,6 +15,11 @@ for name, data in JSON_BLOCKS.items():
|
||||
for state in data['states']:
|
||||
BLOCKS[state['id']] = name.replace('minecraft:', '')
|
||||
|
||||
PROPS = {}
|
||||
for name, data in JSON_BLOCKS.items():
|
||||
for state in data['states']:
|
||||
PROPS[state['id']] = state.get('properties', {})
|
||||
|
||||
BREAK_DISTANCE = 6
|
||||
|
||||
AIR = 0
|
||||
|
@@ -89,6 +89,9 @@ for item_name, item in ITEMS.items():
|
||||
def get_id(name):
|
||||
return ITEMS['minecraft:' + name]['protocol_id']
|
||||
|
||||
def get_name(idx):
|
||||
return ITEM_NAMES[idx]
|
||||
|
||||
CHEST_ID = get_id('chest')
|
||||
GAPPLE_ID = get_id('enchanted_golden_apple')
|
||||
SAND_ID = get_id('sand')
|
||||
@@ -100,6 +103,7 @@ WHEAT_ID = get_id('wheat')
|
||||
WHEAT_SEEDS_ID = get_id('wheat_seeds')
|
||||
BEETROOT_SEEDS_ID = get_id('beetroot_seeds')
|
||||
PUMPKIN_ID = get_id('pumpkin')
|
||||
BEETROOT_ID = get_id('beetroot')
|
||||
|
||||
EMERALD_ID = get_id('emerald')
|
||||
BERRIES_ID = get_id('sweet_berries')
|
||||
@@ -115,8 +119,10 @@ INIT_WANTED_ITEMS = set()
|
||||
WANTED_ITEMS = INIT_WANTED_ITEMS
|
||||
|
||||
def set_needed(items):
|
||||
global NEEDED_ITEMS
|
||||
NEEDED_ITEMS = INIT_NEEDED_ITEMS | items
|
||||
|
||||
def set_wanted(items):
|
||||
global WANTED_ITEMS
|
||||
WANTED_ITEMS = INIT_WANTED_ITEMS | items
|
||||
|
||||
|
@@ -1,8 +1,33 @@
|
||||
from mosfet.info import items
|
||||
import json
|
||||
|
||||
with open('minecraft_data/registries.json') as f:
|
||||
MOBS = json.load(f)['minecraft:entity_type']['entries']
|
||||
|
||||
VILLAGER = 93
|
||||
ARMORER = 1
|
||||
BUTCHER = 2
|
||||
CARTOGRAPHER = 3
|
||||
CLERIC = 4
|
||||
FARMER = 5
|
||||
FISHERMAN = 6
|
||||
FLETCHER = 7
|
||||
LEATHERWORKER = 8
|
||||
LIBRARIAN = 9
|
||||
MASON = 10
|
||||
NITWIT = 11
|
||||
SHEPHERD = 12
|
||||
TOOLSMITH = 13
|
||||
WEAPONSMITH = 14
|
||||
|
||||
TRADES = {
|
||||
ARMORER: [items.IRON_INGOT_ID],
|
||||
BUTCHER: [items.BERRIES_ID],
|
||||
FARMER: [items.PUMPKIN_ID, items.WHEAT_ID, items.POTATO_ID, items.CARROT_ID, items.BEETROOT_ID],
|
||||
TOOLSMITH: [items.IRON_INGOT_ID],
|
||||
WEAPONSMITH: [items.IRON_INGOT_ID],
|
||||
}
|
||||
|
||||
EVIL = [
|
||||
'blaze',
|
||||
'cave_spider',
|
||||
|
@@ -3,7 +3,6 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -236,6 +235,8 @@ class JobStates:
|
||||
tuple([items.IRON_INGOT_ID]): (64, 3),
|
||||
tuple([items.WHEAT_ID]): (64, 3),
|
||||
tuple([items.POTATO_ID]): (64, 3),
|
||||
tuple([items.CARROT_ID]): (64, 3),
|
||||
tuple([items.BEETROOT_ID]): (64, 3),
|
||||
}
|
||||
|
||||
items.set_needed(set([
|
||||
@@ -244,6 +245,8 @@ class JobStates:
|
||||
items.IRON_INGOT_ID,
|
||||
items.WHEAT_ID,
|
||||
items.POTATO_ID,
|
||||
items.CARROT_ID,
|
||||
items.BEETROOT_ID,
|
||||
]))
|
||||
return machines
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -54,11 +54,7 @@ class CacheItemsStates:
|
||||
|
||||
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)
|
||||
|
||||
navpath = w.path_to_place_faked(p, chest)
|
||||
print('navpath:', navpath)
|
||||
|
||||
if navpath:
|
||||
@@ -69,6 +65,7 @@ class CacheItemsStates:
|
||||
return
|
||||
else:
|
||||
self.trapped_chests.pop(0)
|
||||
time.sleep(0.1)
|
||||
|
||||
def going_to_trapped_chest(self):
|
||||
if utils.pint(self.g.pos) == self.opening:
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -64,6 +64,7 @@ class CheckThreatsStates:
|
||||
return
|
||||
else:
|
||||
print('Cant get to safety', self.safety)
|
||||
time.sleep(0.1)
|
||||
|
||||
print('Cant get to safety, aborting')
|
||||
self.state = self.cleanup
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -40,7 +40,7 @@ class FillBlocksStates:
|
||||
|
||||
b1, b2 = utils.pboundingbox(f.coord1, f.coord2)
|
||||
box = utils.psub(b2, b1)
|
||||
xz_distance = hypot(box[0]+1, box[2]+1)
|
||||
xz_distance = utils.hypot(box[0]+1, box[2]+1)
|
||||
y_start = f.coord1[1]
|
||||
y_end = f.coord2[1]
|
||||
|
||||
@@ -84,7 +84,7 @@ class FillBlocksStates:
|
||||
|
||||
b1, b2 = utils.pboundingbox(f.coord1, f.coord2)
|
||||
box = utils.psub(b2, b1)
|
||||
xz_distance = hypot(box[0]+1, box[2]+1)
|
||||
xz_distance = utils.hypot(box[0]+1, box[2]+1)
|
||||
y_start = f.coord1[1]
|
||||
y_end = f.coord2[1]
|
||||
|
||||
@@ -139,8 +139,7 @@ class FillBlocksStates:
|
||||
else:
|
||||
print('Cant get to that block')
|
||||
self.state = self.cleanup
|
||||
#self.bad_sand.append(self.sand)
|
||||
#self.state = self.find_new_sand
|
||||
time.sleep(0.1)
|
||||
|
||||
def going_to_block(self):
|
||||
if not len(self.g.path):
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -61,15 +61,8 @@ class GatherCropStates:
|
||||
self.state = self.going_to_crop
|
||||
else:
|
||||
print('Cant get to it, blacklisting')
|
||||
time.sleep(0.1)
|
||||
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):
|
||||
@@ -79,14 +72,6 @@ class GatherCropStates:
|
||||
|
||||
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):
|
||||
@@ -98,18 +83,17 @@ class GatherCropStates:
|
||||
blocks.MATURE_CARROT_ID: items.CARROT_ID,
|
||||
blocks.MATURE_BEETROOT_ID: items.BEETROOT_SEEDS_ID,
|
||||
}
|
||||
self.target_seed = crop_seeds[self.type_id]
|
||||
|
||||
if self.g.game.select_item([crop_seeds[self.type_id]]):
|
||||
if self.g.game.select_item([self.target_seed]):
|
||||
self.state = self.wait_select
|
||||
self.wait_time = 0.5
|
||||
else:
|
||||
print('Aborting planting, no crop')
|
||||
self.state = self.cleanup
|
||||
print('Havent picked up seed yet')
|
||||
return
|
||||
|
||||
def wait_select(self):
|
||||
# wait a bit to select
|
||||
if self.wait_time > 0:
|
||||
self.wait_time -= utils.TICK
|
||||
if self.target_seed != self.g.holding:
|
||||
return
|
||||
else:
|
||||
self.state = self.place_crop
|
||||
|
||||
@@ -118,12 +102,11 @@ class GatherCropStates:
|
||||
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
|
||||
w = self.g.world
|
||||
if w.block_at(*self.crop) == blocks.AIR:
|
||||
return
|
||||
else:
|
||||
self.state = self.cleanup
|
||||
|
||||
@@ -141,8 +124,8 @@ class GatherCropStates:
|
||||
|
||||
self.crop = None
|
||||
self.type_id = None
|
||||
self.target_seed = None
|
||||
self.bad_crops = []
|
||||
self.wait_time = 0
|
||||
|
||||
def run(self):
|
||||
self.state()
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -27,6 +27,9 @@ class GatherSandStates:
|
||||
return None
|
||||
|
||||
def init(self):
|
||||
if not self.g.sand_origin or not self.g.chunks.check_loaded(self.g.sand_origin):
|
||||
self.g.sand_origin = utils.pint(self.g.pos)
|
||||
|
||||
self.state = self.select_shovel
|
||||
|
||||
def select_shovel(self):
|
||||
@@ -36,10 +39,11 @@ class GatherSandStates:
|
||||
def find_new_slice(self):
|
||||
print('Finding new slice...')
|
||||
w = self.g.world
|
||||
origin = self.g.sand_origin
|
||||
|
||||
print('using origin', self.origin)
|
||||
print('using origin', self.g.sand_origin)
|
||||
start = time.time()
|
||||
self.prev_layer, s = w.find_sand_slice(self.origin, 200, 10, self.bad_slices, self.prev_layer)
|
||||
self.prev_layer, s = w.find_sand_slice(self.g.sand_origin, 200, 10, self.bad_slices, self.prev_layer)
|
||||
print('Found slice:', s, 'in', time.time() - start, 'seconds')
|
||||
|
||||
if s:
|
||||
@@ -77,16 +81,15 @@ class GatherSandStates:
|
||||
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)
|
||||
navpath = w.path_to_place_faked(p, self.sand)
|
||||
print('navpath:', navpath)
|
||||
|
||||
if navpath:
|
||||
self.g.path = navpath[:-1]
|
||||
self.state = self.going_to_sand
|
||||
else:
|
||||
print('Cant get to that sand')
|
||||
time.sleep(0.1)
|
||||
self.bad_sand.append(self.sand)
|
||||
self.state = self.find_new_sand
|
||||
|
||||
@@ -117,8 +120,6 @@ class GatherSandStates:
|
||||
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
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -54,15 +54,8 @@ class GatherWartStates:
|
||||
self.state = self.going_to_wart
|
||||
else:
|
||||
print('Cant get to it, blacklisting')
|
||||
time.sleep(0.1)
|
||||
self.bad_warts.append(wart)
|
||||
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_wart
|
||||
|
||||
def going_to_wart(self):
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -80,6 +80,7 @@ class GatherWoodStates:
|
||||
self.state = self.going_to_tree
|
||||
else:
|
||||
self.openings.pop(0)
|
||||
time.sleep(0.1)
|
||||
|
||||
def going_to_tree(self):
|
||||
if utils.pint(self.g.pos) == self.openings[0]:
|
||||
@@ -126,6 +127,7 @@ class GatherWoodStates:
|
||||
else:
|
||||
self.openings.pop(0)
|
||||
self.state = self.choose_opening
|
||||
time.sleep(0.1)
|
||||
|
||||
def going_to_trunk_base(self):
|
||||
if utils.pint(self.g.pos) == self.tree:
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -61,6 +61,7 @@ class GrabSandStates:
|
||||
return
|
||||
else:
|
||||
print('Cant get to sand', self.sand)
|
||||
time.sleep(0.1)
|
||||
|
||||
print('Cant get to any more sand, aborting')
|
||||
self.state = self.cleanup
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -61,7 +61,7 @@ class GrabSuppliesStates:
|
||||
w = self.g.world
|
||||
p = utils.pint(self.g.pos)
|
||||
|
||||
self.barrels = w.find_blocks_indexed(p, blocks.BARREL_IDS)
|
||||
self.barrels = w.find_blocks_indexed(p, blocks.BARREL_IDS, 80)
|
||||
print('Found:', self.barrels)
|
||||
self.state = self.choose_barrel
|
||||
|
||||
@@ -73,6 +73,7 @@ class GrabSuppliesStates:
|
||||
if barrel in self.bad_barrels:
|
||||
continue
|
||||
|
||||
print('Chose:', barrel)
|
||||
self.barrel = barrel
|
||||
self.state = self.path_to_barrel
|
||||
return
|
||||
@@ -86,26 +87,24 @@ class GrabSuppliesStates:
|
||||
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)
|
||||
|
||||
navpath = w.path_to_place_faked(p, self.barrel)
|
||||
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.checked_barrels.append(self.barrel)
|
||||
self.area = self.barrel
|
||||
self.state = self.going_to_barrel
|
||||
self.checked_supplies = []
|
||||
return
|
||||
else:
|
||||
print('No path, blacklisting barrel')
|
||||
self.bad_barrels.append(barrel)
|
||||
time.sleep(0.1)
|
||||
self.bad_barrels.append(self.barrel)
|
||||
if len(self.bad_barrels) > 3:
|
||||
self.state = self.cleanup
|
||||
else:
|
||||
self.state = self.choose_barrel
|
||||
|
||||
def going_to_barrel(self):
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -31,7 +31,28 @@ class SellToVillagerStates:
|
||||
|
||||
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:
|
||||
|
||||
if v in self.bad_villagers:
|
||||
print('In bad villager list')
|
||||
continue
|
||||
|
||||
if v in self.spent_villagers:
|
||||
print('In spent villager list')
|
||||
continue
|
||||
|
||||
if 'profession' not in v:
|
||||
print('Villager has unknown profession')
|
||||
continue
|
||||
|
||||
if v.profession not in mobs.TRADES:
|
||||
print('Villager doesnt sell stuff we collect')
|
||||
continue
|
||||
|
||||
trades = mobs.TRADES[v.profession]
|
||||
if not self.g.game.count_items(trades):
|
||||
print('We dont have anything to sell him')
|
||||
continue
|
||||
|
||||
break
|
||||
else: # for
|
||||
print('No good villagers left, aborting.')
|
||||
@@ -59,7 +80,7 @@ class SellToVillagerStates:
|
||||
if self.villager not in self.good_villagers:
|
||||
self.bad_villagers.append(self.villager)
|
||||
print('Added to bad villager list')
|
||||
self.state = self.cleanup
|
||||
self.state = self.find_villager
|
||||
return
|
||||
|
||||
navpath = w.path_to_place(p, self.openings[0])
|
||||
@@ -67,8 +88,10 @@ class SellToVillagerStates:
|
||||
if navpath:
|
||||
self.g.path = navpath
|
||||
self.state = self.going_to_villager
|
||||
self.g.look_at = None
|
||||
else:
|
||||
self.openings.pop(0)
|
||||
time.sleep(0.1)
|
||||
|
||||
def going_to_villager(self):
|
||||
if utils.pint(self.g.pos) == self.openings[0]:
|
||||
@@ -143,8 +166,7 @@ class SellToVillagerStates:
|
||||
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
|
||||
self.state = self.find_villager
|
||||
return
|
||||
|
||||
def click_trade(self):
|
||||
|
@@ -3,7 +3,7 @@ import time
|
||||
import importlib
|
||||
import random
|
||||
from itertools import count
|
||||
from math import hypot, floor
|
||||
from math import floor
|
||||
|
||||
from minecraft.networking.types import BlockFace
|
||||
|
||||
@@ -38,11 +38,16 @@ class SleepWithBedStates:
|
||||
w = self.g.world
|
||||
p = utils.pint(self.g.pos)
|
||||
|
||||
result = w.find_blocks_indexed(p, blocks.BED_IDS)
|
||||
result = w.find_blocks_indexed(p, blocks.BED_IDS, 80)
|
||||
|
||||
self.beds = []
|
||||
for bed in result:
|
||||
if bed not in self.bad_beds:
|
||||
if bed in self.bad_beds:
|
||||
continue
|
||||
|
||||
if w.check_bed_occupied(bed):
|
||||
continue
|
||||
|
||||
self.beds.append(bed)
|
||||
|
||||
print('Found:', self.beds)
|
||||
@@ -62,11 +67,7 @@ class SleepWithBedStates:
|
||||
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)
|
||||
|
||||
navpath = w.path_to_place_faked(p, bed)
|
||||
print('navpath:', navpath)
|
||||
|
||||
if navpath:
|
||||
@@ -80,10 +81,12 @@ class SleepWithBedStates:
|
||||
self.beds.pop(0)
|
||||
self.bad_beds.append(bed)
|
||||
print('Cant get to bed, blacklisting')
|
||||
time.sleep(0.1)
|
||||
self.state = self.select_bed
|
||||
|
||||
def going_to_bed(self):
|
||||
if utils.pint(self.g.pos) == self.opening:
|
||||
print('At existing bed')
|
||||
self.my_bed = False
|
||||
self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW)
|
||||
self.state = self.use_bed
|
||||
@@ -128,17 +131,36 @@ class SleepWithBedStates:
|
||||
|
||||
def going_to_area(self):
|
||||
if utils.pint(self.g.pos) == self.opening:
|
||||
print('At bed area')
|
||||
self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW)
|
||||
self.state = self.place_bed
|
||||
|
||||
def place_bed(self):
|
||||
print('Placing bed')
|
||||
self.g.game.place_block(self.area, BlockFace.TOP)
|
||||
self.my_bed = True
|
||||
self.wait_time = 0.5
|
||||
self.state = self.wait_use
|
||||
|
||||
def wait_use(self):
|
||||
# wait to use the bed
|
||||
if self.wait_time > 0:
|
||||
self.wait_time -= utils.TICK
|
||||
else:
|
||||
self.state = self.use_bed
|
||||
|
||||
def use_bed(self):
|
||||
if self.g.time > 12550:
|
||||
print('Sleeping')
|
||||
w = self.g.world
|
||||
|
||||
if self.g.time < 12550:
|
||||
return
|
||||
|
||||
if w.check_bed_occupied(self.area):
|
||||
print('Bed occupied, aborting.')
|
||||
self.state = self.cleanup
|
||||
return
|
||||
|
||||
print('Using bed')
|
||||
self.g.game.place_block(self.area, BlockFace.TOP)
|
||||
if not self.silent:
|
||||
self.g.chat.send('zzz')
|
||||
@@ -147,16 +169,19 @@ class SleepWithBedStates:
|
||||
def sleep_bed(self):
|
||||
w = self.g.world
|
||||
p = utils.pint(self.g.pos)
|
||||
threats = w.find_threats(p, 10)
|
||||
|
||||
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
|
||||
self.state = self.cleanup
|
||||
elif self.g.time < 100:
|
||||
print('Woke up time')
|
||||
self.state = self.break_bed
|
||||
elif self.g.correction_count:
|
||||
print('Woke up by movement')
|
||||
self.state = self.break_bed
|
||||
|
||||
def break_bed(self):
|
||||
if self.my_bed:
|
||||
@@ -165,7 +190,6 @@ class SleepWithBedStates:
|
||||
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)]
|
||||
|
@@ -20,6 +20,7 @@ def get_packets(old_get_packets):
|
||||
mc_packets.add(packets.EntityTeleport)
|
||||
mc_packets.add(packets.TradeListPacket)
|
||||
mc_packets.add(packets.DisconnectPacket)
|
||||
mc_packets.add(packets.UnloadChunkPacket)
|
||||
|
||||
|
||||
return mc_packets
|
||||
|
@@ -1,7 +1,6 @@
|
||||
import importlib
|
||||
import functools
|
||||
import time
|
||||
from math import hypot, sqrt
|
||||
|
||||
from astar import AStar
|
||||
|
||||
@@ -306,7 +305,7 @@ class Pathfinder(AStar):
|
||||
def distance_between(self, n1, n2):
|
||||
(x1, y1, z1) = n1
|
||||
(x2, y2, z2) = n2
|
||||
return hypot(x2-x1, y2-y1, z2-z1)
|
||||
return utils.hypot(x2-x1, y2-y1, z2-z1)
|
||||
|
||||
def heuristic_cost_estimate(self, n1, n2):
|
||||
(x1, y1, z1) = n1
|
||||
|
@@ -1,6 +1,6 @@
|
||||
HELP_LINES = []
|
||||
|
||||
with open('mosfet/game.py', 'r') as f:
|
||||
with open('mosfet/commands.py', 'r') as f:
|
||||
for line in f.readlines():
|
||||
if line.strip().startswith('## '):
|
||||
HELP_LINES.append(line.strip()[3:])
|
||||
|
@@ -7,6 +7,7 @@ from minecraft.networking.packets import clientbound, serverbound
|
||||
from mosfet.protocol import packets
|
||||
|
||||
from mosfet import utils
|
||||
from mosfet.info import blocks
|
||||
|
||||
class DataManager:
|
||||
def __init__(self, directory):
|
||||
@@ -42,7 +43,7 @@ class ChunksManager:
|
||||
def __init__(self, data_manager):
|
||||
self.data = data_manager
|
||||
self.chunks = {}
|
||||
self.biomes = {}
|
||||
#self.biomes = {}
|
||||
self.index = {}
|
||||
self.loading = False
|
||||
|
||||
@@ -68,13 +69,12 @@ class ChunksManager:
|
||||
|
||||
for item_id, locations in chunk.sub_index.items():
|
||||
if item_id not in self.index:
|
||||
self.index[item_id] = []
|
||||
|
||||
self.index[item_id] = set()
|
||||
for l in locations:
|
||||
coords = (dx + l%16, dy + l//256, dz + l%256//16)
|
||||
self.index[item_id].append(coords)
|
||||
self.index[item_id].add(coords)
|
||||
|
||||
self.biomes[(chunk_packet.x, None, chunk_packet.z)] = chunk_packet.biomes # FIXME
|
||||
#self.biomes[(chunk_packet.x, None, chunk_packet.z)] = chunk_packet.biomes # FIXME
|
||||
if self.loading:
|
||||
print('.', end='', flush=True)
|
||||
|
||||
@@ -116,28 +116,26 @@ class ChunksManager:
|
||||
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 block in blocks.INDEXED_IDS:
|
||||
if block not in self.index:
|
||||
self.index[block] = set()
|
||||
self.index[block].add((x, y, z))
|
||||
|
||||
if check not in self.chunks:
|
||||
return False
|
||||
def check_loaded(self, chunk_distance):
|
||||
num = (chunk_distance * 2 + 1) ** 2
|
||||
num_subchunks = num * 16
|
||||
return len(self.chunks) >= num_subchunks
|
||||
|
||||
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]
|
||||
def unload_chunk(self, x, z):
|
||||
for y in range(16):
|
||||
try:
|
||||
del self.chunks[(x, y, z)]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def unload_all_chunks(self):
|
||||
self.chunks = {}
|
||||
self.index = {}
|
||||
|
||||
class ChunkNotLoadedException(Exception):
|
||||
def __str__(self):
|
||||
@@ -155,24 +153,25 @@ class ChatManager:
|
||||
def translate_chat(self, data):
|
||||
if isinstance(data, str):
|
||||
return data
|
||||
|
||||
result = data.get('text', '')
|
||||
result += data.get('translate', '')
|
||||
|
||||
if 'with' in data:
|
||||
result += ' ' + ' '.join([self.translate_chat(x) for x in data['with']])
|
||||
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 '?'
|
||||
result += ''.join([self.translate_chat(x) for x in data['extra']])
|
||||
|
||||
return result
|
||||
|
||||
def print_chat(self, chat_packet):
|
||||
#print(chat_packet)
|
||||
#print(chat_packet.json_data)
|
||||
#import json
|
||||
#print(json.dumps(json.loads(chat_packet.json_data), indent=4))
|
||||
try:
|
||||
source = chat_packet.field_string('position')
|
||||
sender = chat_packet.sender
|
||||
text = self.translate_chat(json.loads(chat_packet.json_data))
|
||||
print('[%s] %s'%(source, text))
|
||||
except Exception as ex:
|
||||
@@ -180,7 +179,7 @@ class ChatManager:
|
||||
return
|
||||
|
||||
if self.handler:
|
||||
self.handler((source, text))
|
||||
self.handler((source, sender, text))
|
||||
|
||||
def set_handler(self, func):
|
||||
self.handler = func
|
||||
|
@@ -31,10 +31,10 @@ class ChunkDataPacket(Packet):
|
||||
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))
|
||||
#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()
|
||||
|
||||
@@ -49,9 +49,9 @@ class ChunkDataPacket(Packet):
|
||||
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)
|
||||
#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()
|
||||
@@ -64,9 +64,9 @@ class ChunkDataPacket(Packet):
|
||||
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)
|
||||
#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')
|
||||
@@ -304,7 +304,7 @@ class EntityMetadataPacket(Packet):
|
||||
self.metadata = []
|
||||
for _ in range(99):
|
||||
entry = Entry.read(file_object, self.context)
|
||||
if not entry: break
|
||||
if not entry: break # hit an unimplemented type, stops parsing
|
||||
self.metadata.append(entry)
|
||||
|
||||
|
||||
@@ -458,3 +458,15 @@ class DisconnectPacket(Packet):
|
||||
definition = [
|
||||
{'reason': String},
|
||||
]
|
||||
|
||||
class UnloadChunkPacket(Packet):
|
||||
# Tells the client to unload a chunk column
|
||||
# https://wiki.vg/Protocol#Unload_Chunk
|
||||
|
||||
id = 0x1C
|
||||
packet_name = 'unload chunk'
|
||||
|
||||
definition = [
|
||||
{'chunk_x': Integer},
|
||||
{'chunk_z': Integer},
|
||||
]
|
||||
|
@@ -141,7 +141,7 @@ class Slot(Type):
|
||||
|
||||
|
||||
class Entry(Type):
|
||||
types = {
|
||||
simple_types = {
|
||||
0: Byte,
|
||||
1: VarInt,
|
||||
2: Float,
|
||||
@@ -169,12 +169,19 @@ class Entry(Type):
|
||||
index = UnsignedByte.read(file_object)
|
||||
if index == 0xff: return None
|
||||
type = VarInt.read(file_object)
|
||||
|
||||
if type == 10: # optional position
|
||||
present = Boolean.read(file_object)
|
||||
value = Position.read_with_context(file_object, context) if present else None
|
||||
elif type == 16: # villager data
|
||||
value = (VarInt.read(file_object), VarInt.read(file_object), VarInt.read(file_object))
|
||||
else:
|
||||
try:
|
||||
value = Entry.types[type].read(file_object)
|
||||
value = Entry.simple_types[type].read(file_object)
|
||||
except TypeError:
|
||||
value = Entry.types[type].read_with_context(file_object, context)
|
||||
value = Entry.simple_types[type].read_with_context(file_object, context)
|
||||
except KeyError:
|
||||
return None
|
||||
return None # unimplemented data type, stops parsing entries
|
||||
return Entry(index, type, value)
|
||||
|
||||
|
||||
|
@@ -1,12 +1,16 @@
|
||||
import importlib
|
||||
import collections
|
||||
from math import floor, ceil, sqrt, hypot
|
||||
from math import floor, ceil, sqrt
|
||||
|
||||
from mosfet.info import blocks
|
||||
from mosfet.info import mcdata
|
||||
|
||||
TICK = 0.05
|
||||
|
||||
def hypot(*coordinates):
|
||||
# python's 3D hypot is too new, so we'll use our own
|
||||
return sqrt(sum(x**2 for x in coordinates))
|
||||
|
||||
def padd(p1, p2):
|
||||
return (p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2])
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import math
|
||||
from mosfet import utils
|
||||
|
||||
class Vector3D:
|
||||
def __init__(self, vector):
|
||||
@@ -17,7 +18,7 @@ class Vector3D:
|
||||
return self.tuple()[key]
|
||||
|
||||
def length(self):
|
||||
return math.hypot(self.x, self.y, self.z)
|
||||
return utils.hypot(self.x, self.y, self.z)
|
||||
|
||||
def normalized(self):
|
||||
x = self.x / self.length()
|
||||
|
@@ -2,7 +2,6 @@ import collections
|
||||
import re
|
||||
import time
|
||||
import random
|
||||
from math import hypot
|
||||
from itertools import count
|
||||
from copy import copy
|
||||
|
||||
@@ -35,7 +34,7 @@ class World:
|
||||
continue
|
||||
if y_limit and abs(cur[1]) > y_limit:
|
||||
continue
|
||||
if distance and hypot(*cur) > distance:
|
||||
if distance and utils.hypot(*cur) > distance:
|
||||
continue
|
||||
|
||||
check = utils.padd(center, cur)
|
||||
@@ -76,7 +75,7 @@ class World:
|
||||
offset = utils.spiral(n)
|
||||
check = utils.padd(center, offset)
|
||||
if self.block_at(*check) in block_ids:
|
||||
if hypot(*offset) < distance:
|
||||
if utils.hypot(*offset) < distance:
|
||||
result.append(check)
|
||||
if limit and len(result) == limit:
|
||||
return result
|
||||
@@ -141,6 +140,26 @@ class World:
|
||||
except path.AStarTimeout:
|
||||
return None
|
||||
|
||||
def path_to_place_faked(self, start, place):
|
||||
# same as above, but adds a fake block below and air before pathfinding
|
||||
# so that the pathfinder can actually make it to the block
|
||||
c = self.g.chunks
|
||||
above = utils.padd(place, path.BLOCK_ABOVE)
|
||||
below = utils.padd(place, path.BLOCK_BELOW)
|
||||
|
||||
tmp = c.get_block_at(*place)
|
||||
tmp2 = c.get_block_at(*above)
|
||||
tmp3 = c.get_block_at(*below)
|
||||
c.set_block_at(*place, blocks.AIR)
|
||||
c.set_block_at(*above, blocks.AIR)
|
||||
c.set_block_at(*below, blocks.STONE)
|
||||
navpath = self.path_to_place(start, place)
|
||||
c.set_block_at(*place, tmp)
|
||||
c.set_block_at(*above, tmp2)
|
||||
c.set_block_at(*below, tmp3)
|
||||
|
||||
return navpath
|
||||
|
||||
def find_bed_areas(self, center, distance):
|
||||
bed_clearance = 9 # 5x5 area
|
||||
clear_distance = 2
|
||||
@@ -259,6 +278,16 @@ class World:
|
||||
result.append(utils.padd(area, direction))
|
||||
return result
|
||||
|
||||
def check_bed_occupied(self, bed):
|
||||
# returns true if the bed is occupied by a player
|
||||
bid = self.g.chunks.get_block_at(*bed)
|
||||
if blocks.PROPS[bid]['occupied'] == 'true':
|
||||
print('Checking bed occupancy:', bed, '-> occupied')
|
||||
return True
|
||||
else:
|
||||
print('Checking bed occupancy:', bed, '-> free')
|
||||
return False
|
||||
|
||||
def find_cache_openings(self, area):
|
||||
return self.find_bed_openings(area)
|
||||
|
||||
@@ -283,6 +312,7 @@ class World:
|
||||
if utils.phyp(center, pos) > distance:
|
||||
continue
|
||||
result.append(mob)
|
||||
result.sort(key=lambda mob: utils.phyp(center, (mob.x, mob.y, mob.z)))
|
||||
return result
|
||||
|
||||
def find_threats(self, center, distance):
|
||||
@@ -295,6 +325,7 @@ class World:
|
||||
if not self.check_air_column(pos, distance):
|
||||
continue
|
||||
result.append(mob)
|
||||
result.sort(key=lambda mob: utils.phyp(center, (mob.x, mob.y, mob.z)))
|
||||
return result
|
||||
|
||||
def find_villagers(self, center, distance):
|
||||
@@ -307,6 +338,7 @@ class World:
|
||||
if utils.phyp(center, pos) > distance:
|
||||
continue
|
||||
result.append(mob)
|
||||
result.sort(key=lambda mob: utils.phyp(center, (mob.x, mob.y, mob.z)))
|
||||
return result
|
||||
|
||||
def find_villager_openings(self, villager):
|
||||
|
@@ -1,39 +1,45 @@
|
||||
appdirs==1.4.3
|
||||
appdirs==1.4.4
|
||||
astar==0.93
|
||||
CacheControl==0.12.6
|
||||
certifi==2019.11.28
|
||||
certifi==2020.12.5
|
||||
cffi==1.14.5
|
||||
chardet==3.0.4
|
||||
chardet==4.0.0
|
||||
click==7.1.2
|
||||
colorama==0.4.3
|
||||
contextlib2==0.6.0
|
||||
colorama==0.4.4
|
||||
contextlib2==0.6.0.post1
|
||||
cryptography==3.4.7
|
||||
distlib==0.3.0
|
||||
distro==1.4.0
|
||||
distlib==0.3.1
|
||||
distro==1.5.0
|
||||
Flask==1.1.2
|
||||
html5lib==1.0.1
|
||||
idna==2.8
|
||||
Flask-Cors==3.0.10
|
||||
html5lib==1.1
|
||||
idna==2.10
|
||||
importlib-metadata==4.0.1
|
||||
ipaddr==2.2.0
|
||||
itsdangerous==1.1.0
|
||||
Jinja2==2.11.3
|
||||
lockfile==0.12.2
|
||||
MarkupSafe==1.1.1
|
||||
minecraft-data==2.82.2
|
||||
msgpack==0.6.2
|
||||
msgpack==1.0.2
|
||||
munch==2.5.0
|
||||
mutf8==1.0.3
|
||||
packaging==20.3
|
||||
pep517==0.8.2
|
||||
packaging==20.9
|
||||
pep517==0.10.0
|
||||
pkg-resources==0.0.0
|
||||
progress==1.5
|
||||
pycparser==2.20
|
||||
git+https://github.com/ammaraskar/pyCraft.git@2813d02ae7fb8182c3e5227a73de2240b09878d9
|
||||
PyNBT==3.1.0
|
||||
pyparsing==2.4.6
|
||||
pyparsing==2.4.7
|
||||
pytoml==0.1.21
|
||||
requests==2.22.0
|
||||
requests==2.25.1
|
||||
retrying==1.3.3
|
||||
six==1.14.0
|
||||
urllib3==1.25.8
|
||||
watchdog==2.0.2
|
||||
six==1.15.0
|
||||
toml==0.10.2
|
||||
typing-extensions==3.7.4.3
|
||||
urllib3==1.26.4
|
||||
watchdog==2.0.3
|
||||
webencodings==0.5.1
|
||||
Werkzeug==1.0.1
|
||||
zipp==3.4.1
|
||||
|
@@ -11,7 +11,7 @@ fi
|
||||
if ! command -v virtualenv &> /dev/null
|
||||
then
|
||||
echo "virtualenv could not be found, please install with:"
|
||||
echo "sudo apt install python3-virtualenv"
|
||||
echo "sudo apt install virtualenv"
|
||||
exit
|
||||
fi
|
||||
|
||||
@@ -49,7 +49,6 @@ fi
|
||||
if [ ! -d "minecraft_data" ]
|
||||
then
|
||||
echo "Grabbing minecraft data..."
|
||||
sleep 2
|
||||
|
||||
VERSION="1.16.4"
|
||||
|
||||
@@ -59,12 +58,14 @@ then
|
||||
rm mcdata.zip
|
||||
fi
|
||||
|
||||
# https://github.com/pypa/virtualenv/issues/1029
|
||||
PS1=${PS1:-}
|
||||
|
||||
# create virtual environment
|
||||
|
||||
if [ ! -d "env" ]
|
||||
then
|
||||
echo "Installing Python requirements..."
|
||||
sleep 2
|
||||
|
||||
virtualenv -p python3 env
|
||||
source env/bin/activate
|
||||
|
@@ -11,7 +11,7 @@ fi
|
||||
if ! command -v virtualenv &> /dev/null
|
||||
then
|
||||
echo "virtualenv could not be found, please install with:"
|
||||
echo "sudo apt install python3-virtualenv"
|
||||
echo "sudo apt install virtualenv"
|
||||
exit
|
||||
fi
|
||||
|
||||
@@ -54,7 +54,6 @@ fi
|
||||
# download minecraft data
|
||||
|
||||
echo "Grabbing minecraft data..."
|
||||
sleep 2
|
||||
|
||||
VERSION="1.16.4"
|
||||
|
||||
@@ -72,10 +71,12 @@ git pull --rebase
|
||||
git stash pop || true
|
||||
|
||||
|
||||
# https://github.com/pypa/virtualenv/issues/1029
|
||||
PS1=${PS1:-}
|
||||
|
||||
# create virtual environment
|
||||
|
||||
echo "Installing Python requirements..."
|
||||
sleep 2
|
||||
|
||||
rm -r env || true
|
||||
rm -r __pycache__ || true
|
||||
|
20
web_interface/.gitignore
vendored
Normal file
20
web_interface/.gitignore
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
17
web_interface/build/asset-manifest.json
Normal file
17
web_interface/build/asset-manifest.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"files": {
|
||||
"main.js": "/static/js/main.27336089.chunk.js",
|
||||
"main.js.map": "/static/js/main.27336089.chunk.js.map",
|
||||
"runtime-main.js": "/static/js/runtime-main.97b64705.js",
|
||||
"runtime-main.js.map": "/static/js/runtime-main.97b64705.js.map",
|
||||
"static/js/2.2b306052.chunk.js": "/static/js/2.2b306052.chunk.js",
|
||||
"static/js/2.2b306052.chunk.js.map": "/static/js/2.2b306052.chunk.js.map",
|
||||
"index.html": "/index.html",
|
||||
"static/js/2.2b306052.chunk.js.LICENSE.txt": "/static/js/2.2b306052.chunk.js.LICENSE.txt"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/js/runtime-main.97b64705.js",
|
||||
"static/js/2.2b306052.chunk.js",
|
||||
"static/js/main.27336089.chunk.js"
|
||||
]
|
||||
}
|
1
web_interface/build/index.html
Normal file
1
web_interface/build/index.html
Normal file
@@ -0,0 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><title>React App</title></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],p=0,s=[];p<i.length;p++)f=i[p],Object.prototype.hasOwnProperty.call(o,f)&&o[f]&&s.push(o[f][0]),o[f]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(c&&c(r);s.length;)s.shift()();return u.push.apply(u,a||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var l=t[i];0!==o[l]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var i=this.webpackJsonpweb_interface=this.webpackJsonpweb_interface||[],l=i.push.bind(i);i.push=r,i=i.slice();for(var a=0;a<i.length;a++)r(i[a]);var c=l;t()}([])</script><script src="/static/js/2.2b306052.chunk.js"></script><script src="/static/js/main.27336089.chunk.js"></script></body></html>
|
3
web_interface/build/static/js/2.2b306052.chunk.js
Normal file
3
web_interface/build/static/js/2.2b306052.chunk.js
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
object-assign
|
||||
(c) Sindre Sorhus
|
||||
@license MIT
|
||||
*/
|
||||
|
||||
/** @license React v0.20.2
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/** @license React v17.0.2
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
1
web_interface/build/static/js/2.2b306052.chunk.js.map
Normal file
1
web_interface/build/static/js/2.2b306052.chunk.js.map
Normal file
File diff suppressed because one or more lines are too long
2
web_interface/build/static/js/main.27336089.chunk.js
Normal file
2
web_interface/build/static/js/main.27336089.chunk.js
Normal file
@@ -0,0 +1,2 @@
|
||||
(this.webpackJsonpweb_interface=this.webpackJsonpweb_interface||[]).push([[0],{41:function(e,t,n){"use strict";n.r(t);var c=n(2),r=n.n(c),s=n(14),a=n.n(s),i=n(4),j=n.n(i),l=n(15),d=n(16),o=n(5),p=n.n(o),b=n(0);p.a.defaults.baseURL=Object({NODE_ENV:"production",PUBLIC_URL:"",WDS_SOCKET_HOST:void 0,WDS_SOCKET_PATH:void 0,WDS_SOCKET_PORT:void 0,FAST_REFRESH:!0}).REACT_APP_SERVER;var u=function(){var e=Object(c.useState)(!1),t=Object(d.a)(e,2),n=t[0],r=t[1];return Object(c.useEffect)((function(){var e=setInterval(function(){var e=Object(l.a)(j.a.mark((function e(){var t;return j.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,p.a.get("/api/global");case 3:t=e.sent,r(t.data),e.next=10;break;case 7:e.prev=7,e.t0=e.catch(0),r(!1);case 10:case"end":return e.stop()}}),e,null,[[0,7]])})));return function(){return e.apply(this,arguments)}}(),500);return function(){return clearInterval(e)}}),[]),Object(b.jsx)("div",{className:"app",children:n&&Object(b.jsxs)(b.Fragment,{children:[Object(b.jsxs)("p",{children:["Name: ",n.name]}),Object(b.jsxs)("p",{children:["Pos: ",n.pos]}),Object(b.jsxs)("p",{children:["Yaw: ",n.yaw]}),Object(b.jsxs)("p",{children:["Pitch: ",n.pitch]}),Object(b.jsxs)("p",{children:["Dimention: ",n.dimension]}),Object(b.jsxs)("p",{children:["Players:",Object.values(n.players).map((function(e){return Object(b.jsx)("div",{children:n.player_names[e.player_uuid]})}))]}),Object(b.jsxs)("p",{children:["Holding: ",n.holding]}),Object(b.jsxs)("p",{children:["AFK: ","".concat(n.afk)]}),Object(b.jsxs)("p",{children:["Health: ",n.health]}),Object(b.jsxs)("p",{children:["Food: ",n.food]}),Object(b.jsxs)("p",{children:["Time: ",n.time]})]})})};a.a.render(Object(b.jsx)(r.a.StrictMode,{children:Object(b.jsx)(u,{})}),document.getElementById("root"))}},[[41,1,2]]]);
|
||||
//# sourceMappingURL=main.27336089.chunk.js.map
|
1
web_interface/build/static/js/main.27336089.chunk.js.map
Normal file
1
web_interface/build/static/js/main.27336089.chunk.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["App.js","index.js"],"names":["axios","defaults","baseURL","process","REACT_APP_SERVER","App","useState","global","setGlobal","useEffect","interval","setInterval","a","get","res","data","clearInterval","className","name","pos","yaw","pitch","dimension","Object","values","players","map","x","player_names","player_uuid","holding","afk","health","food","time","ReactDOM","render","StrictMode","document","getElementById"],"mappings":"kNAGAA,IAAMC,SAASC,QAAUC,mIAAYC,iBAiDtBC,MA/Cf,WAAgB,IAAD,EACcC,oBAAS,GADvB,mBACPC,EADO,KACCC,EADD,KAiBd,OAdAC,qBAAU,WACT,IASMC,EAAWC,YATR,uCAAG,4BAAAC,EAAA,+EAEQZ,IAAMa,IAAI,eAFlB,OAEJC,EAFI,OAGVN,EAAUM,EAAIC,MAHJ,gDAKVP,GAAU,GALA,yDAAH,qDASyB,KAClC,OAAO,kBAAMQ,cAAcN,MACzB,IAGF,qBAAKO,UAAU,MAAf,SACEV,GACA,qCACC,uCAAUA,EAAOW,QACjB,sCAASX,EAAOY,OAChB,sCAASZ,EAAOa,OAChB,wCAAWb,EAAOc,SAElB,4CAAed,EAAOe,aAEtB,yCACEC,OAAOC,OAAOjB,EAAOkB,SAASC,KAAI,SAAAC,GAAC,OACnC,8BACEpB,EAAOqB,aAAaD,EAAEE,qBAK1B,0CAAatB,EAAOuB,WACpB,gDAAYvB,EAAOwB,QACnB,yCAAYxB,EAAOyB,UACnB,uCAAUzB,EAAO0B,QACjB,uCAAU1B,EAAO2B,cCzCtBC,IAASC,OACR,cAAC,IAAMC,WAAP,UACC,cAAC,EAAD,MAEDC,SAASC,eAAe,W","file":"static/js/main.27336089.chunk.js","sourcesContent":["import React, { useState, useEffect } from 'react';\nimport axios from 'axios';\n\naxios.defaults.baseURL = process.env.REACT_APP_SERVER;\n\nfunction App() {\n\tconst [global, setGlobal] = useState(false);\n\n\tuseEffect(() => {\n\t\tconst get = async() => {\n\t\t\ttry {\n\t\t\t\tconst res = await axios.get('/api/global');\n\t\t\t\tsetGlobal(res.data);\n\t\t\t} catch (error) {\n\t\t\t\tsetGlobal(false);\n\t\t\t}\n\t\t};\n\n\t\tconst interval = setInterval(get, 500);\n\t\treturn () => clearInterval(interval);\n\t}, []);\n\n\treturn (\n\t\t<div className=\"app\">\n\t\t\t{global &&\n\t\t\t\t<>\n\t\t\t\t\t<p>Name: {global.name}</p>\n\t\t\t\t\t<p>Pos: {global.pos}</p>\n\t\t\t\t\t<p>Yaw: {global.yaw}</p>\n\t\t\t\t\t<p>Pitch: {global.pitch}</p>\n\n\t\t\t\t\t<p>Dimention: {global.dimension}</p>\n\n\t\t\t\t\t<p>Players:\n\t\t\t\t\t\t{Object.values(global.players).map(x =>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t{global.player_names[x.player_uuid]}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</p>\n\n\t\t\t\t\t<p>Holding: {global.holding}</p>\n\t\t\t\t\t<p>AFK: {`${global.afk}`}</p>\n\t\t\t\t\t<p>Health: {global.health}</p>\n\t\t\t\t\t<p>Food: {global.food}</p>\n\t\t\t\t\t<p>Time: {global.time}</p>\n\t\t\t\t</>\n\t\t\t}\n\t\t</div>\n\t);\n}\n\nexport default App;\n","import React from 'react';\nimport ReactDOM from 'react-dom';\nimport App from './App';\n\nReactDOM.render(\n\t<React.StrictMode>\n\t\t<App />\n\t</React.StrictMode>,\n\tdocument.getElementById('root')\n);\n"],"sourceRoot":""}
|
2
web_interface/build/static/js/runtime-main.97b64705.js
Normal file
2
web_interface/build/static/js/runtime-main.97b64705.js
Normal file
@@ -0,0 +1,2 @@
|
||||
!function(e){function r(r){for(var n,f,i=r[0],l=r[1],a=r[2],p=0,s=[];p<i.length;p++)f=i[p],Object.prototype.hasOwnProperty.call(o,f)&&o[f]&&s.push(o[f][0]),o[f]=0;for(n in l)Object.prototype.hasOwnProperty.call(l,n)&&(e[n]=l[n]);for(c&&c(r);s.length;)s.shift()();return u.push.apply(u,a||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var l=t[i];0!==o[l]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var i=this.webpackJsonpweb_interface=this.webpackJsonpweb_interface||[],l=i.push.bind(i);i.push=r,i=i.slice();for(var a=0;a<i.length;a++)r(i[a]);var c=l;t()}([]);
|
||||
//# sourceMappingURL=runtime-main.97b64705.js.map
|
File diff suppressed because one or more lines are too long
39
web_interface/package.json
Normal file
39
web_interface/package.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "web_interface",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"axios": "^0.21.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-scripts": "4.0.3",
|
||||
"web-vitals": "^1.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
36
web_interface/public/index.html
Normal file
36
web_interface/public/index.html
Normal file
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
53
web_interface/src/App.js
Normal file
53
web_interface/src/App.js
Normal file
@@ -0,0 +1,53 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import axios from 'axios';
|
||||
|
||||
axios.defaults.baseURL = process.env.REACT_APP_SERVER;
|
||||
|
||||
function App() {
|
||||
const [global, setGlobal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const get = async() => {
|
||||
try {
|
||||
const res = await axios.get('/api/global');
|
||||
setGlobal(res.data);
|
||||
} catch (error) {
|
||||
setGlobal(false);
|
||||
}
|
||||
};
|
||||
|
||||
const interval = setInterval(get, 500);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="app">
|
||||
{global &&
|
||||
<>
|
||||
<p>Name: {global.name}</p>
|
||||
<p>Pos: {global.pos}</p>
|
||||
<p>Yaw: {global.yaw}</p>
|
||||
<p>Pitch: {global.pitch}</p>
|
||||
|
||||
<p>Dimention: {global.dimension}</p>
|
||||
|
||||
<p>Players:
|
||||
{Object.values(global.players).map(x =>
|
||||
<div>
|
||||
{global.player_names[x.player_uuid]}
|
||||
</div>
|
||||
)}
|
||||
</p>
|
||||
|
||||
<p>Holding: {global.holding}</p>
|
||||
<p>AFK: {`${global.afk}`}</p>
|
||||
<p>Health: {global.health}</p>
|
||||
<p>Food: {global.food}</p>
|
||||
<p>Time: {global.time}</p>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
10
web_interface/src/index.js
Normal file
10
web_interface/src/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
11418
web_interface/yarn.lock
Normal file
11418
web_interface/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user