Compare commits
69 Commits
ef68956e63
...
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 | |||
ae2b0f4875 | |||
23891066c0 | |||
9874e23aa6 | |||
221d497204 | |||
433e35e79b | |||
3c90a09d82 | |||
390c3405c3 | |||
1f621333de | |||
38f3b0ed67 | |||
d099ae6965 | |||
3c373ccfe3 | |||
4d2b09e578 | |||
f88809af64 | |||
87bb638270 | |||
bdfec0f9a4 | |||
679030ff9a | |||
3aa766926b | |||
8c9a28beac | |||
5ff0394dc3 | |||
fac4309e43 | |||
fc929db658 | |||
7d0cef0e2e | |||
daf152389a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,7 +8,6 @@ __pycache__/
|
|||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
build/
|
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
dist/
|
dist/
|
||||||
downloads/
|
downloads/
|
||||||
|
234
README.md
234
README.md
@@ -1,110 +1,192 @@
|
|||||||
# Minecraft Bot
|
# Mosfet Minecraft Bot
|
||||||
|
|
||||||
## Setup
|
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, 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
|
||||||
|
|
||||||
Assuming Debian / Ubuntu based distro:
|
Assuming Debian / Ubuntu based distro:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ sudo apt update
|
$ sudo apt update
|
||||||
$ sudo apt install build-essential python3 python3-dev python3-pip python3-virtualenv git
|
$ sudo apt install build-essential python3 python3-dev python3-pip virtualenv git wget unzip
|
||||||
|
|
||||||
$ git clone https://git.tannercollin.com/tanner/minecraft-bot.git
|
$ git clone https://git.tannercollin.com/tanner/minecraft-bot.git
|
||||||
$ cd minecraft-bot/
|
$ cd minecraft-bot/
|
||||||
$ bash download_mcdata.sh
|
|
||||||
$ virtualenv -p python3 env
|
|
||||||
$ source env/bin/activate
|
|
||||||
(env) $ pip install -r requirements.txt
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Always make sure the virtual environment is running `(env)`.
|
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
|
If you want to use the built-in burner account (Minecraft name `mattstack`):
|
||||||
|
|
||||||
```
|
```
|
||||||
(env) $ USERNAME=you@domain.com PASSWORD=supersecret SERVER=example.com python main.py
|
$ 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
|
## Commands
|
||||||
|
|
||||||
Talking to bot (All commands can be found in "game.py" under "public commands", starting around line 494)
|
The command prefix character is the last character of the bot's name. For
|
||||||
|
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.
|
||||||
|
|
||||||
Use prefix (in this case, "1") to call to attention before giving command. (EX. 1ping, 1here, etc.)
|
In the following examples, we'll assume the bot's name is `mosfet1`, so commands
|
||||||
|
would be ran like `1farm wood` or `1pos`.
|
||||||
|
|
||||||
# ping
|
The exception are the below public commands, they can optionally be prefixed with `!`
|
||||||
Will respond "pong" if active and listening
|
and all bots will run the command.
|
||||||
|
|
||||||
# pos
|
Commands can be issued in public chat or private message like `/msg mosfet1
|
||||||
Will respond with current position
|
1farm wood`. The bot will reply the same way the command was issued.
|
||||||
|
|
||||||
# afk
|
Use `echo` to make Mosfet run Minecraft commands. For example, `1echo /sethome
|
||||||
Will afk (if not already)
|
home` will cause him to issue the `/sethome` command.
|
||||||
|
|
||||||
# unafk
|
### Public Commands
|
||||||
Will unafk (if afk)
|
|
||||||
|
|
||||||
# error
|
These can be ran by anyone, all bots will reply.
|
||||||
---
|
|
||||||
|
|
||||||
# inv
|
`!help` - prints this whole help message to console
|
||||||
Will list inventory (in terminal)
|
|
||||||
|
`!help [command]` - replies in-game explaining command
|
||||||
|
|
||||||
|
`!ping` - replies with "pong"
|
||||||
|
|
||||||
|
`!echo [data]` - replies with "data"
|
||||||
|
|
||||||
|
`!pos` - replies with position and dimension
|
||||||
|
|
||||||
|
`!afk` - goes AFK with /afk
|
||||||
|
|
||||||
|
`!unafk` - goes not AFK with /afk
|
||||||
|
|
||||||
|
`!error` - raises an error
|
||||||
|
|
||||||
|
`!inv` - replies and prints current inventory
|
||||||
|
|
||||||
|
`!time` - replies with Minecraft world time
|
||||||
|
|
||||||
|
`!count [id]` - counts the number of items with that id
|
||||||
|
|
||||||
|
`!loaded` - replies with the current loaded area
|
||||||
|
|
||||||
|
`!players` - prints the current players
|
||||||
|
|
||||||
|
`!players clear` - clears the current player list
|
||||||
|
|
||||||
|
`!objects` - prints the current items on ground
|
||||||
|
|
||||||
|
`!objects clear` - clears the current object list
|
||||||
|
|
||||||
|
`!mobs` - prints the current mobs
|
||||||
|
|
||||||
|
`!mobs clear` - clears the current mob list
|
||||||
|
|
||||||
|
`!monsters` - prints the current monsters
|
||||||
|
|
||||||
|
`!villagers` - prints the current villagers
|
||||||
|
|
||||||
|
`!threats` - prints the dangerous monsters within 20 blocks
|
||||||
|
|
||||||
|
`!threats [num]` - prints the dangerous monsters within num blocks
|
||||||
|
|
||||||
|
`"zzz" or !zzz` - bot does /afk to let others sleep
|
||||||
|
|
||||||
|
`!tree` - replies with the closest tree
|
||||||
|
|
||||||
|
`!block x y z` - replies what block is at (x, y, z)
|
||||||
|
|
||||||
|
### Bot-specific Commands
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
`1gather wood` - gathers wood from the world
|
||||||
|
|
||||||
|
`1gather sand` - gathers sand from the world
|
||||||
|
|
||||||
|
`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
|
||||||
|
|
||||||
|
`1loiter` - stands still but eats, sleeps, and flees
|
||||||
|
|
||||||
|
`1trade` - sells items to villagers to get emeralds
|
||||||
|
|
||||||
|
`1stop` - stops the current job and resets bot
|
||||||
|
|
||||||
|
`1drop` - drops the current stack its holding
|
||||||
|
|
||||||
|
`1select [id]` - moves item with id into main hand
|
||||||
|
|
||||||
|
`1dump [id]` - drops all items matching id
|
||||||
|
|
||||||
|
`1drain` - drops all items in inventory
|
||||||
|
|
||||||
|
`1fill [x] [y] [z] [x] [y] [z]` - fills the cuboid with the block at the first coordinate
|
||||||
|
|
||||||
|
`1here` - bot comes to your location
|
||||||
|
|
||||||
|
`1goto [x] [y] [z]` - sends the bot to coordinate (x, y, z)
|
||||||
|
|
||||||
|
`1close` - closes the current Minecraft window
|
||||||
|
|
||||||
|
`1click [slot] [button] [mode]` - clicks the current window
|
||||||
|
|
||||||
|
`1use` - use the item it's currently holding
|
||||||
|
|
||||||
|
`1interact [entity id]` - interacts with that entity
|
||||||
|
|
||||||
|
### Authorized Commands
|
||||||
|
|
||||||
|
These dangerous commands can only be ran by the bot owner.
|
||||||
|
|
||||||
|
`1print [expression]` - replies with Python eval(expression)
|
||||||
|
|
||||||
|
`1exit` - exits the program
|
||||||
|
|
||||||
|
|
||||||
# time
|
## License
|
||||||
---
|
|
||||||
|
|
||||||
# count
|
This program is free and open-source software licensed under the MIT License.
|
||||||
---
|
Please see the `LICENSE` file for details.
|
||||||
|
|
||||||
# loaded
|
That means you have the right to study, change, and distribute the software and
|
||||||
---
|
source code to anyone and for any purpose. You deserve these rights. Please take
|
||||||
|
advantage of them because I like pull requests and would love to see this code
|
||||||
# players
|
put to use.
|
||||||
---
|
|
||||||
|
|
||||||
# objects
|
|
||||||
---
|
|
||||||
|
|
||||||
# mobs
|
|
||||||
---
|
|
||||||
|
|
||||||
# monsters
|
|
||||||
---
|
|
||||||
|
|
||||||
# villagers
|
|
||||||
---
|
|
||||||
|
|
||||||
# threats
|
|
||||||
---
|
|
||||||
|
|
||||||
# spiral
|
|
||||||
---
|
|
||||||
|
|
||||||
# sand slice
|
|
||||||
---
|
|
||||||
|
|
||||||
# zzz
|
|
||||||
Will afk if in overworld, and if not already afk
|
|
||||||
|
|
||||||
# tree
|
|
||||||
---
|
|
||||||
|
|
||||||
# block
|
|
||||||
---
|
|
||||||
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## temporary workarounds
|
|
||||||
|
|
||||||
# Add "?" to line 457, which is "match1 = re.match(r'<(\w+)> (.*)', text)"
|
|
||||||
It should now look like this "match1 = re.match(r'<?(\w+)> (.*)', text)"
|
|
||||||
This allows the bot to react to commands made by someone else than the creator of code (Tanner6)
|
|
||||||
|
|
||||||
|
## Acknowledgements
|
||||||
|
|
||||||
|
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 |
@@ -1,10 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
VERSION="1.16.4"
|
|
||||||
|
|
||||||
wget -O/tmp/mcdata.zip https://apimon.de/mcdata/$VERSION/$VERSION.zip
|
|
||||||
rm -rf minecraft_data
|
|
||||||
mkdir minecraft_data
|
|
||||||
unzip /tmp/mcdata.zip -d minecraft_data
|
|
||||||
rm /tmp/mcdata.zip
|
|
||||||
|
|
39
main.py
39
main.py
@@ -4,14 +4,21 @@ import time
|
|||||||
import traceback
|
import traceback
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
import logging
|
||||||
|
log = logging.getLogger('werkzeug')
|
||||||
|
log.setLevel(logging.ERROR)
|
||||||
|
|
||||||
from flask import Flask
|
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 munch import Munch
|
||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
from watchdog.events import PatternMatchingEventHandler
|
from watchdog.events import PatternMatchingEventHandler
|
||||||
|
|
||||||
import bot
|
from mosfet import bot
|
||||||
|
|
||||||
global_state = Munch()
|
global_state = Munch()
|
||||||
g = global_state
|
g = global_state
|
||||||
@@ -20,6 +27,7 @@ g.name = None
|
|||||||
g.mcdata = False
|
g.mcdata = False
|
||||||
g.pos = False
|
g.pos = False
|
||||||
g.dimension = None
|
g.dimension = None
|
||||||
|
g.item_lock = False
|
||||||
g.inv = {}
|
g.inv = {}
|
||||||
g.objects = {}
|
g.objects = {}
|
||||||
g.mobs = {}
|
g.mobs = {}
|
||||||
@@ -32,9 +40,10 @@ g.holding = 0
|
|||||||
g.afk = False
|
g.afk = False
|
||||||
g.health = 20
|
g.health = 20
|
||||||
g.food = 20
|
g.food = 20
|
||||||
|
g.sand_origin = None
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/api/global')
|
||||||
def hello_world():
|
def api_global():
|
||||||
data = json.dumps(g, default=lambda o: str(o), indent=4)
|
data = json.dumps(g, default=lambda o: str(o), indent=4)
|
||||||
|
|
||||||
response = app.response_class(
|
response = app.response_class(
|
||||||
@@ -44,6 +53,10 @@ def hello_world():
|
|||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def root():
|
||||||
|
return app.send_static_file('index.html')
|
||||||
|
|
||||||
reload_timeout = time.time()
|
reload_timeout = time.time()
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -58,7 +71,7 @@ def main():
|
|||||||
event_handler.on_any_event = reload_bot
|
event_handler.on_any_event = reload_bot
|
||||||
|
|
||||||
observer = Observer()
|
observer = Observer()
|
||||||
observer.schedule(event_handler, '.', recursive=True)
|
observer.schedule(event_handler, 'mosfet', recursive=True)
|
||||||
observer.start()
|
observer.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -78,8 +91,22 @@ def main():
|
|||||||
observer.stop()
|
observer.stop()
|
||||||
observer.join()
|
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__':
|
if __name__ == '__main__':
|
||||||
threading.Thread(target=app.run).start()
|
threading.Thread(target=run_api).start()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
@@ -3,47 +3,57 @@ if __name__ == '__main__':
|
|||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
import importlib
|
import importlib
|
||||||
from math import floor, ceil
|
from math import floor, ceil
|
||||||
from copy import copy
|
from copy import copy
|
||||||
|
|
||||||
USERNAME = os.environ['USERNAME']
|
from . import monkey_patch # must be before any possible pyCraft imports
|
||||||
PASSWORD = os.environ['PASSWORD']
|
|
||||||
SERVER = os.environ['SERVER']
|
|
||||||
PORT = int(os.environ.get('PORT', 25565))
|
|
||||||
|
|
||||||
import monkey_patch # must be before any possible pyCraft imports
|
|
||||||
|
|
||||||
|
|
||||||
from minecraft import authentication
|
from minecraft import authentication
|
||||||
from minecraft.exceptions import YggdrasilError
|
from minecraft.exceptions import YggdrasilError
|
||||||
from minecraft.networking.connection import Connection
|
from minecraft.networking.connection import Connection
|
||||||
from minecraft.networking.packets import Packet, clientbound, serverbound
|
from minecraft.networking.packets import Packet, clientbound, serverbound
|
||||||
|
|
||||||
from protocol.managers import DataManager, ChunksManager, ChatManager, ChunkNotLoadedException
|
from mosfet.protocol.managers import DataManager, ChunksManager, ChatManager, ChunkNotLoadedException
|
||||||
|
|
||||||
from munch import Munch
|
from munch import Munch
|
||||||
from vector import Point3D, Vector3D
|
|
||||||
|
|
||||||
import game
|
from mosfet import commands
|
||||||
importlib.reload(game)
|
from mosfet import game
|
||||||
import blocks
|
from mosfet import job
|
||||||
importlib.reload(blocks)
|
from mosfet import path
|
||||||
import utils
|
from mosfet import print_help
|
||||||
importlib.reload(utils)
|
from mosfet import utils
|
||||||
import path
|
from mosfet import vector
|
||||||
importlib.reload(path)
|
from mosfet import world
|
||||||
import jobs
|
from mosfet.info import blocks
|
||||||
importlib.reload(jobs)
|
from mosfet.info import items
|
||||||
import mcdata
|
from mosfet.info import mcdata
|
||||||
importlib.reload(mcdata)
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
for module in [
|
||||||
|
blocks,
|
||||||
|
commands,
|
||||||
|
game,
|
||||||
|
items,
|
||||||
|
job,
|
||||||
|
mcdata,
|
||||||
|
mobs,
|
||||||
|
path,
|
||||||
|
print_help,
|
||||||
|
utils,
|
||||||
|
vector,
|
||||||
|
world,
|
||||||
|
]:
|
||||||
|
importlib.reload(module)
|
||||||
|
|
||||||
last_tick = time.time()
|
last_tick = time.time()
|
||||||
|
|
||||||
PITCH_ANGLE_DIR = Vector3D((0, 1, 0))
|
PITCH_ANGLE_DIR = vector.Vector3D((0, 1, 0))
|
||||||
YAW_ANGLE_DIR = Vector3D((0, 0, -1))
|
YAW_ANGLE_DIR = vector.Vector3D((0, 0, -1))
|
||||||
YAW_ANGLE_REF = Vector3D((0, 1, 0))
|
YAW_ANGLE_REF = vector.Vector3D((0, 1, 0))
|
||||||
YAW_LOOK_AHEAD = 4
|
YAW_LOOK_AHEAD = 4
|
||||||
|
|
||||||
|
|
||||||
@@ -55,21 +65,19 @@ def tick(global_state):
|
|||||||
target = None
|
target = None
|
||||||
|
|
||||||
# make sure current chunks are loaded for physics
|
# 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:
|
if not g.chunks.loading:
|
||||||
print('Loading chunks', end='', flush=True)
|
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)
|
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)
|
g.connection.write_packet(packet, force=True)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if g.chunks.loading:
|
if g.chunks.loading:
|
||||||
print()
|
print()
|
||||||
print('Chunks loaded.')
|
print('Chunks loaded in', round(time.time() - g.chunks.loading, 2), 's')
|
||||||
g.chunks.loading = False
|
g.chunks.loading = False
|
||||||
|
|
||||||
g.chunks.unload_chunks(p)
|
|
||||||
|
|
||||||
########## object physics ##########
|
########## object physics ##########
|
||||||
# note: it's possible the chunk data is out of date when this runs
|
# note: it's possible the chunk data is out of date when this runs
|
||||||
|
|
||||||
@@ -105,7 +113,7 @@ def tick(global_state):
|
|||||||
########## player physics ##########
|
########## player physics ##########
|
||||||
|
|
||||||
if g.path and len(g.path):
|
if g.path and len(g.path):
|
||||||
target = Point3D(g.path[0])
|
target = vector.Point3D(g.path[0])
|
||||||
target.x += 0.5
|
target.x += 0.5
|
||||||
target.z += 0.5
|
target.z += 0.5
|
||||||
|
|
||||||
@@ -158,11 +166,11 @@ def tick(global_state):
|
|||||||
g.y_a = 0
|
g.y_a = 0
|
||||||
|
|
||||||
if g.look_at:
|
if g.look_at:
|
||||||
look_at = Point3D(g.look_at)
|
look_at = vector.Point3D(g.look_at)
|
||||||
elif g.path and len(g.path) > YAW_LOOK_AHEAD:
|
elif g.path and len(g.path) > YAW_LOOK_AHEAD:
|
||||||
look_at = Point3D(g.path[YAW_LOOK_AHEAD])
|
look_at = vector.Point3D(g.path[YAW_LOOK_AHEAD])
|
||||||
elif g.path and len(g.path):
|
elif g.path and len(g.path):
|
||||||
look_at = Point3D(g.path[-1])
|
look_at = vector.Point3D(g.path[-1])
|
||||||
else:
|
else:
|
||||||
look_at = None
|
look_at = None
|
||||||
|
|
||||||
@@ -192,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))
|
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.connection.write_packet(packet)
|
||||||
|
|
||||||
g.game.tick()
|
|
||||||
g.job.tick()
|
g.job.tick()
|
||||||
|
g.game.tick() # order important for correction_count
|
||||||
|
|
||||||
|
|
||||||
def init(global_state):
|
def init(global_state):
|
||||||
@@ -219,28 +227,68 @@ def init(global_state):
|
|||||||
|
|
||||||
g.trades = []
|
g.trades = []
|
||||||
|
|
||||||
g.job = jobs.JobStates(g)
|
g.job = job.JobStates(g)
|
||||||
g.chopped_tree = False
|
g.chopped_tree = False
|
||||||
|
|
||||||
g.afk_timeout = 0
|
g.afk_timeout = 0
|
||||||
|
|
||||||
g.filling = False
|
g.filling = False
|
||||||
|
|
||||||
|
g.minimum_cache_slots = 27
|
||||||
|
g.maximum_supply_slots = 33
|
||||||
|
|
||||||
def bot(global_state):
|
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
|
g = global_state
|
||||||
|
|
||||||
if not g.mcdata:
|
if not g.mcdata:
|
||||||
g.mcdata = DataManager('./minecraft_data')
|
g.mcdata = DataManager('./minecraft_data')
|
||||||
|
|
||||||
if not g.connection:
|
if not SERVER:
|
||||||
auth_token = authentication.AuthenticationToken()
|
print()
|
||||||
try:
|
print('You must specify a server to connect to. For example:')
|
||||||
auth_token.authenticate(USERNAME, PASSWORD)
|
print('SERVER=minecraft.example.com ./run_linux.sh')
|
||||||
except YggdrasilError as e:
|
print('SERVER=localhost PORT=12345 ./run_linux.sh')
|
||||||
print(e)
|
print()
|
||||||
sys.exit()
|
print('If you want to use your own account:')
|
||||||
print("Logged in as %s..." % auth_token.username)
|
print('EMAIL=you@domain.com PASSWORD=supersecret SERVER=minecraft.example.com ./run_linux.sh')
|
||||||
g.connection = Connection(SERVER, PORT, auth_token=auth_token)
|
os._exit(0)
|
||||||
|
elif not g.connection:
|
||||||
|
if EMAIL and PASSWORD:
|
||||||
|
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)
|
||||||
|
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)
|
g.chunks = ChunksManager(g.mcdata)
|
||||||
|
|
||||||
@@ -251,7 +299,8 @@ def bot(global_state):
|
|||||||
g.chat = ChatManager(g)
|
g.chat = ChatManager(g)
|
||||||
|
|
||||||
g.game = game.Game(g)
|
g.game = game.Game(g)
|
||||||
g.world = game.MCWorld(g)
|
g.world = world.World(g)
|
||||||
|
g.commands = commands.Commands(g)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while not g.pos:
|
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)
|
||||||
|
|
570
mosfet/game.py
Normal file
570
mosfet/game.py
Normal file
@@ -0,0 +1,570 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from munch import Munch
|
||||||
|
|
||||||
|
from minecraft.networking.packets import Packet, clientbound, serverbound
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.packets import (
|
||||||
|
SetSlotPacket, PlayerDiggingPacket,
|
||||||
|
BlockBreakAnimationPacket, AcknowledgePlayerDiggingPacket,
|
||||||
|
HeldItemChangePacket, PickItemPacket, OpenWindowPacket,
|
||||||
|
ClickWindowPacket, CloseWindowPacket, ServerWindowConfirmationPacket,
|
||||||
|
ClientWindowConfirmationPacket, EntityMetadataPacket,
|
||||||
|
SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket,
|
||||||
|
EntityActionPacket, EntityTeleport, InteractEntityPacket, TradeListPacket,
|
||||||
|
SelectTradePacket, DisconnectPacket, UnloadChunkPacket,
|
||||||
|
)
|
||||||
|
|
||||||
|
from mosfet.protocol.types import Slot
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet import bot
|
||||||
|
from mosfet import vector
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class Game:
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
|
||||||
|
register = self.g.connection.register_packet_listener
|
||||||
|
register(self.handle_login_success, clientbound.login.LoginSuccessPacket)
|
||||||
|
register(self.handle_block_change, clientbound.play.BlockChangePacket)
|
||||||
|
register(self.handle_join_game, clientbound.play.JoinGamePacket)
|
||||||
|
register(self.handle_position_and_look, clientbound.play.PlayerPositionAndLookPacket)
|
||||||
|
register(self.handle_time_update, clientbound.play.TimeUpdatePacket)
|
||||||
|
register(self.handle_set_slot, SetSlotPacket)
|
||||||
|
register(self.handle_break_animation, BlockBreakAnimationPacket)
|
||||||
|
register(self.handle_break_ack, AcknowledgePlayerDiggingPacket)
|
||||||
|
register(self.handle_window, OpenWindowPacket)
|
||||||
|
register(self.handle_window_confirmation, ClientWindowConfirmationPacket)
|
||||||
|
register(self.handle_spawn_object, clientbound.play.SpawnObjectPacket)
|
||||||
|
register(self.handle_entity_metadata, EntityMetadataPacket)
|
||||||
|
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)
|
||||||
|
register(self.handle_player_list, clientbound.play.PlayerListItemPacket)
|
||||||
|
register(self.handle_entity_teleport, EntityTeleport)
|
||||||
|
register(self.handle_update_health, clientbound.play.UpdateHealthPacket)
|
||||||
|
#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)
|
||||||
|
|
||||||
|
def handle_login_success(self, packet):
|
||||||
|
print(packet)
|
||||||
|
self.g.name = packet.Username
|
||||||
|
|
||||||
|
def handle_join_game(self, packet):
|
||||||
|
print('Received join game packet')
|
||||||
|
print('Connected.')
|
||||||
|
self.g.info = packet
|
||||||
|
self.g.eid = packet.entity_id
|
||||||
|
self.g.dimension = packet.world_name.replace('minecraft:', '')
|
||||||
|
|
||||||
|
def handle_block_change(self, packet):
|
||||||
|
if packet.block_state_id == blocks.SOUL_TORCH:
|
||||||
|
try:
|
||||||
|
self.g.goal = vector.Point3D((packet.location[0], packet.location[1], packet.location[2]))
|
||||||
|
print('new waypoint:', self.g.goal)
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
solution = path.Pathfinder(self.g).astar(utils.pint(self.g.pos), utils.pint(self.g.goal))
|
||||||
|
if solution:
|
||||||
|
solution = list(solution)
|
||||||
|
self.g.path = solution
|
||||||
|
if self.g.job:
|
||||||
|
self.g.job.stop()
|
||||||
|
print(len(solution))
|
||||||
|
print(solution)
|
||||||
|
print(round(time.time() - start, 3), 'seconds')
|
||||||
|
else:
|
||||||
|
print('No path found')
|
||||||
|
#say(connection, 'No path found')
|
||||||
|
|
||||||
|
#g.y_v = 10.0
|
||||||
|
#g.y_a = -36.0
|
||||||
|
except BaseException as e:
|
||||||
|
import traceback
|
||||||
|
print(traceback.format_exc())
|
||||||
|
|
||||||
|
#print(packet)
|
||||||
|
|
||||||
|
def handle_position_and_look(self, packet):
|
||||||
|
print(packet)
|
||||||
|
p = vector.Point3D((packet.x, packet.y, packet.z))
|
||||||
|
self.g.pos = p
|
||||||
|
|
||||||
|
confirm_packet = serverbound.play.TeleportConfirmPacket()
|
||||||
|
confirm_packet.teleport_id = packet.teleport_id
|
||||||
|
self.g.connection.write_packet(confirm_packet)
|
||||||
|
|
||||||
|
self.g.correction_count += 1
|
||||||
|
|
||||||
|
if self.g.get('path', None) and self.g.correction_count > 5:
|
||||||
|
self.g.correction_count = 0
|
||||||
|
dest = self.g.path[-1]
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
new_path = w.path_to_place(p, dest)
|
||||||
|
|
||||||
|
if new_path:
|
||||||
|
self.g.path = new_path
|
||||||
|
|
||||||
|
def handle_time_update(self, packet):
|
||||||
|
self.g.time = packet.time_of_day % 24000
|
||||||
|
|
||||||
|
def handle_set_slot(self, packet):
|
||||||
|
g = self.g
|
||||||
|
print(packet)
|
||||||
|
if packet.window_id == 0:
|
||||||
|
g.inv[packet.slot] = packet.slot_data
|
||||||
|
elif g.window:
|
||||||
|
g.window.contents[packet.slot] = packet.slot_data
|
||||||
|
|
||||||
|
if g.item_lock and packet.window_id >= 0 and not packet.slot_data.present:
|
||||||
|
print('Unlocking item lock')
|
||||||
|
g.item_lock = False
|
||||||
|
|
||||||
|
def break_block(self, location):
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
#if utils.phyp(p, location) > blocks.BREAK_DISTANCE + 1:
|
||||||
|
# return False
|
||||||
|
|
||||||
|
bid = self.g.chunks.get_block_at(*location)
|
||||||
|
if bid == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
packet = PlayerDiggingPacket()
|
||||||
|
packet.status = 0
|
||||||
|
packet.location = location
|
||||||
|
packet.face = 1
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
self.g.breaking = location
|
||||||
|
self.g.break_time = time.time() + utils.break_time(bid, self.g.holding)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def break_finish(self):
|
||||||
|
packet = PlayerDiggingPacket()
|
||||||
|
packet.status = 2
|
||||||
|
packet.location = self.g.breaking
|
||||||
|
packet.face = 1
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
#self.g.chunks.set_block_at(*self.g.breaking, 0)
|
||||||
|
|
||||||
|
if self.g.chunks.get_block_at(*self.g.breaking) == 0:
|
||||||
|
self.g.breaking = None
|
||||||
|
|
||||||
|
def handle_break_animation(self, packet):
|
||||||
|
return
|
||||||
|
print(packet)
|
||||||
|
|
||||||
|
def handle_break_ack(self, packet):
|
||||||
|
#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
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
def place_block(self, location, face):
|
||||||
|
packet = serverbound.play.PlayerBlockPlacementPacket()
|
||||||
|
packet.hand = 0
|
||||||
|
packet.location = location
|
||||||
|
packet.face = face
|
||||||
|
packet.x = 0.5
|
||||||
|
packet.y = 0.5
|
||||||
|
packet.z = 0.5
|
||||||
|
packet.inside_block = False
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
def pick(self, slot):
|
||||||
|
packet = PickItemPacket()
|
||||||
|
packet.slot_to_use = slot
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
def hold(self, slot):
|
||||||
|
packet = HeldItemChangePacket()
|
||||||
|
packet.slot = slot
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
def choose_slot(self, slot):
|
||||||
|
if slot >= 36:
|
||||||
|
slot -= 36
|
||||||
|
self.hold(slot)
|
||||||
|
else:
|
||||||
|
self.pick(slot)
|
||||||
|
|
||||||
|
def count_items(self, items):
|
||||||
|
# count how many items are in inv
|
||||||
|
count = 0
|
||||||
|
for slot, item in self.g.inv.items():
|
||||||
|
if item.item_id in items:
|
||||||
|
count += item.item_count
|
||||||
|
return count
|
||||||
|
|
||||||
|
def count_inventory_slots(self):
|
||||||
|
# count how many inventory slots are filled
|
||||||
|
# excludes armour, crafting slots, off-hand
|
||||||
|
count = 0
|
||||||
|
for slot, item in self.g.inv.items():
|
||||||
|
if item.present and slot >= 9 and slot <= 45:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
def count_window_slots(self):
|
||||||
|
# count how many window slots are filled
|
||||||
|
# excludes player inventory
|
||||||
|
w = self.g.window
|
||||||
|
w_info = mcdata.WINDOWS[w.data.window_type]
|
||||||
|
w_container_slots = w_info.container
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
for slot, item in w.contents.items():
|
||||||
|
if item.present and slot in w_container_slots:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
def get_window_slot(self, item_id):
|
||||||
|
# get the first slot that matches item of a window
|
||||||
|
window_items = list(self.g.window.contents.items())
|
||||||
|
for slot, item in window_items:
|
||||||
|
if not item.present: continue
|
||||||
|
if item.item_id == item_id:
|
||||||
|
return slot, item
|
||||||
|
else: #for
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
def select_item(self, items):
|
||||||
|
# select the first match from items of inv
|
||||||
|
# uses smallest stack of that match
|
||||||
|
# and optionally the most damaged item
|
||||||
|
inv_items = list(self.g.inv.items())
|
||||||
|
inv_items.sort(key=lambda x: (x[1].nbt or {}).get('Damage', 0), reverse=True)
|
||||||
|
inv_items.sort(key=lambda x: x[1].item_count or 0)
|
||||||
|
for slot, item in inv_items:
|
||||||
|
if item.item_id in items:
|
||||||
|
self.g.game.choose_slot(slot)
|
||||||
|
self.g.holding = item.item_id
|
||||||
|
return True
|
||||||
|
else: #for
|
||||||
|
return False
|
||||||
|
|
||||||
|
def select_random_item(self, items):
|
||||||
|
# select a random match from items of inv
|
||||||
|
# this is random per item type
|
||||||
|
# example: 5 stacks wood, 1 stack glass
|
||||||
|
# -> still 50/50 chance between them
|
||||||
|
|
||||||
|
matches = set()
|
||||||
|
for slot, item in self.g.inv.items():
|
||||||
|
if item.item_id in items:
|
||||||
|
matches.add(item.item_id)
|
||||||
|
|
||||||
|
if matches:
|
||||||
|
return self.select_item([random.choice(list(matches))])
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def select_next_item(self):
|
||||||
|
# select the next item slot that has an item
|
||||||
|
for slot, item in self.g.inv.items():
|
||||||
|
if slot < 9: continue # skip armour slots
|
||||||
|
if item.present:
|
||||||
|
print('slot:', slot, 'item:', item)
|
||||||
|
self.g.game.choose_slot(slot)
|
||||||
|
self.g.holding = item.item_id
|
||||||
|
return True
|
||||||
|
else: # for
|
||||||
|
return False
|
||||||
|
|
||||||
|
def drop_stack(self):
|
||||||
|
packet = PlayerDiggingPacket()
|
||||||
|
packet.status = 3
|
||||||
|
packet.location = utils.pint(self.g.pos)
|
||||||
|
packet.face = 1
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
def open_container(self, location):
|
||||||
|
bid = self.g.chunks.get_block_at(*location)
|
||||||
|
# TODO: check if block is a chest??
|
||||||
|
self.place_block(location, BlockFace.TOP)
|
||||||
|
|
||||||
|
def handle_window(self, packet):
|
||||||
|
print(packet)
|
||||||
|
self.g.window = Munch(data=packet, contents=dict(), count=0)
|
||||||
|
|
||||||
|
def click_window(self, slot, button, mode, item):
|
||||||
|
w = self.g.window
|
||||||
|
|
||||||
|
packet = ClickWindowPacket()
|
||||||
|
packet.window_id = w.data.window_id
|
||||||
|
packet.slot = slot
|
||||||
|
packet.button = button
|
||||||
|
packet.action_number = w.count
|
||||||
|
packet.mode = mode
|
||||||
|
packet.clicked_item = item
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
print('<--', packet)
|
||||||
|
|
||||||
|
w.count += 1
|
||||||
|
|
||||||
|
def close_window(self):
|
||||||
|
if self.g.window:
|
||||||
|
packet = CloseWindowPacket()
|
||||||
|
packet.window_id = self.g.window.data.window_id
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
self.g.window = None
|
||||||
|
|
||||||
|
def handle_window_confirmation(self, packet):
|
||||||
|
print(packet)
|
||||||
|
packet2 = ServerWindowConfirmationPacket()
|
||||||
|
packet2.window_id = packet.window_id
|
||||||
|
packet2.action_number = packet.action_number
|
||||||
|
packet2.accepted = packet.accepted
|
||||||
|
self.g.connection.write_packet(packet2)
|
||||||
|
|
||||||
|
def handle_spawn_player(self, packet):
|
||||||
|
print(packet)
|
||||||
|
self.g.players[packet.entity_id] = Munch(
|
||||||
|
entity_id=packet.entity_id,
|
||||||
|
player_uuid=packet.player_UUID,
|
||||||
|
x=packet.x,
|
||||||
|
y=packet.y,
|
||||||
|
z=packet.z,
|
||||||
|
yaw=packet.yaw,
|
||||||
|
pitch=packet.pitch,
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_spawn_object(self, packet):
|
||||||
|
#return
|
||||||
|
if packet.type_id != 37: return
|
||||||
|
#print(packet)
|
||||||
|
self.g.objects[packet.entity_id] = Munch(
|
||||||
|
entity_id=packet.entity_id,
|
||||||
|
x=packet.x,
|
||||||
|
y=packet.y,
|
||||||
|
z=packet.z,
|
||||||
|
velocity_x=packet.velocity_x,
|
||||||
|
velocity_y=packet.velocity_y,
|
||||||
|
velocity_z=packet.velocity_z,
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_gapple(self, packet):
|
||||||
|
current_gapple_chest = self.g.job.find_gapple_states.current_chest
|
||||||
|
if current_gapple_chest:
|
||||||
|
for entry in packet.metadata:
|
||||||
|
if entry.type != 6:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if entry.value.item_id in items.GAPPLE_ID:
|
||||||
|
self.g.chat.send('gapple found: ' + str(current_gapple_chest)[1:-1])
|
||||||
|
print('gapple found:', str(current_gapple_chest)[1:-1])
|
||||||
|
|
||||||
|
def handle_entity_metadata(self, packet):
|
||||||
|
if not packet.metadata:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.g.job and self.g.job.state == self.g.job.find_gapple_states:
|
||||||
|
self.check_gapple(packet)
|
||||||
|
|
||||||
|
obj = self.g.objects.get(packet.entity_id, None)
|
||||||
|
if obj:
|
||||||
|
for entry in packet.metadata:
|
||||||
|
if entry.type != 6:
|
||||||
|
continue
|
||||||
|
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
|
||||||
|
|
||||||
|
def handle_spawn_living(self, packet):
|
||||||
|
self.g.mobs[packet.entity_id] = Munch(
|
||||||
|
entity_id=packet.entity_id,
|
||||||
|
entity_uuid=packet.entity_uuid,
|
||||||
|
type=packet.type,
|
||||||
|
x=packet.x,
|
||||||
|
y=packet.y,
|
||||||
|
z=packet.z,
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_entity_position(self, packet):
|
||||||
|
mob = self.g.mobs.get(packet.entity_id, None)
|
||||||
|
if mob:
|
||||||
|
mob.x += packet.delta_x / 4096.0
|
||||||
|
mob.y += packet.delta_y / 4096.0
|
||||||
|
mob.z += packet.delta_z / 4096.0
|
||||||
|
|
||||||
|
player = self.g.players.get(packet.entity_id, None)
|
||||||
|
if player:
|
||||||
|
player.x += packet.delta_x / 4096.0
|
||||||
|
player.y += packet.delta_y / 4096.0
|
||||||
|
player.z += packet.delta_z / 4096.0
|
||||||
|
|
||||||
|
#if player.player_uuid == '0c123cfa-1697-4427-9413-4b645dee7ec0': print(packet)
|
||||||
|
|
||||||
|
def handle_entity_position_rotation(self, packet):
|
||||||
|
mob = self.g.mobs.get(packet.entity_id, None)
|
||||||
|
if mob:
|
||||||
|
mob.x += packet.delta_x / 4096.0
|
||||||
|
mob.y += packet.delta_y / 4096.0
|
||||||
|
mob.z += packet.delta_z / 4096.0
|
||||||
|
|
||||||
|
player = self.g.players.get(packet.entity_id, None)
|
||||||
|
if player:
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
if mob:
|
||||||
|
mob.x = packet.x
|
||||||
|
mob.y = packet.y
|
||||||
|
mob.z = packet.z
|
||||||
|
|
||||||
|
player = self.g.players.get(packet.entity_id, None)
|
||||||
|
if player:
|
||||||
|
player.x = packet.x
|
||||||
|
player.y = packet.y
|
||||||
|
player.z = packet.z
|
||||||
|
|
||||||
|
#if player.player_uuid == '0c123cfa-1697-4427-9413-4b645dee7ec0': print(packet)
|
||||||
|
|
||||||
|
def handle_entity_velocity(self, packet):
|
||||||
|
obj = self.g.objects.get(packet.entity_id, None)
|
||||||
|
if obj:
|
||||||
|
print(packet)
|
||||||
|
#obj.velocity_x = packet.velocity_x
|
||||||
|
#obj.velocity_y = packet.velocity_y
|
||||||
|
#obj.velocity_z = packet.velocity_z
|
||||||
|
|
||||||
|
def handle_destroy_entities(self, packet):
|
||||||
|
for eid in packet.entity_ids:
|
||||||
|
if eid in self.g.objects:
|
||||||
|
del self.g.objects[eid]
|
||||||
|
if eid in self.g.mobs:
|
||||||
|
del self.g.mobs[eid]
|
||||||
|
if eid in self.g.players:
|
||||||
|
del self.g.players[eid]
|
||||||
|
|
||||||
|
def leave_bed(self):
|
||||||
|
packet = EntityActionPacket()
|
||||||
|
packet.entity_id = self.g.eid
|
||||||
|
packet.action_id = 2
|
||||||
|
packet.jump_boost = 0
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
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
|
||||||
|
self.g.player_names[action.name.lower()] = action.uuid # porque no los dos?
|
||||||
|
|
||||||
|
def handle_update_health(self, packet):
|
||||||
|
print(packet)
|
||||||
|
self.g.health = packet.health
|
||||||
|
self.g.food = packet.food
|
||||||
|
|
||||||
|
if packet.health == 0:
|
||||||
|
print('Died, stopping')
|
||||||
|
print('Use 1respawn to respawn the bot')
|
||||||
|
self.close_window()
|
||||||
|
bot.init(self.g)
|
||||||
|
|
||||||
|
def use_item(self, hand):
|
||||||
|
packet = serverbound.play.UseItemPacket()
|
||||||
|
packet.hand = hand
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
def interact(self, eid):
|
||||||
|
packet = InteractEntityPacket()
|
||||||
|
packet.entity_id = eid
|
||||||
|
packet.type = 0
|
||||||
|
packet.hand = 0
|
||||||
|
packet.sneaking = False
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
def handle_trade_list(self, packet):
|
||||||
|
print(packet)
|
||||||
|
self.g.trades = packet.trades
|
||||||
|
|
||||||
|
def select_trade(self, num):
|
||||||
|
packet = SelectTradePacket()
|
||||||
|
packet.selected_slot = num
|
||||||
|
self.g.connection.write_packet(packet)
|
||||||
|
|
||||||
|
def handle_disconnect(self, packet):
|
||||||
|
print(packet)
|
||||||
|
print('Client disconnected!')
|
||||||
|
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()
|
||||||
|
|
||||||
|
if time.time() >= self.g.break_time: #- 2*utils.TICK:
|
||||||
|
self.break_finish()
|
||||||
|
|
||||||
|
if self.g.dumping and not self.g.item_lock:
|
||||||
|
if self.select_item([self.g.dumping]):
|
||||||
|
self.drop_stack()
|
||||||
|
self.g.item_lock = True
|
||||||
|
else:
|
||||||
|
self.g.dumping = None
|
||||||
|
|
||||||
|
if self.g.draining and not self.g.item_lock:
|
||||||
|
if self.select_next_item():
|
||||||
|
self.drop_stack()
|
||||||
|
self.g.item_lock = True
|
||||||
|
else:
|
||||||
|
self.g.draining = False
|
||||||
|
|
||||||
|
if not self.g.path:
|
||||||
|
self.g.correction_count = 0
|
@@ -1,8 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
import mcdata
|
from mosfet.info import mcdata
|
||||||
importlib.reload(mcdata)
|
|
||||||
|
|
||||||
MCD_BLOCKS = {}
|
MCD_BLOCKS = {}
|
||||||
for d in mcdata.mcd.blocks.values():
|
for d in mcdata.mcd.blocks.values():
|
||||||
@@ -16,6 +15,11 @@ for name, data in JSON_BLOCKS.items():
|
|||||||
for state in data['states']:
|
for state in data['states']:
|
||||||
BLOCKS[state['id']] = name.replace('minecraft:', '')
|
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
|
BREAK_DISTANCE = 6
|
||||||
|
|
||||||
AIR = 0
|
AIR = 0
|
||||||
@@ -28,10 +32,6 @@ EMERALD_BLOCK = 5407
|
|||||||
TEST_BLOCK = (616, 78, 496)
|
TEST_BLOCK = (616, 78, 496)
|
||||||
|
|
||||||
|
|
||||||
WATER = [
|
|
||||||
'water',
|
|
||||||
]
|
|
||||||
|
|
||||||
AVOID = [
|
AVOID = [
|
||||||
'lava',
|
'lava',
|
||||||
'water',
|
'water',
|
||||||
@@ -263,89 +263,37 @@ INDEXED = [
|
|||||||
'barrel',
|
'barrel',
|
||||||
] + BEDS
|
] + BEDS
|
||||||
|
|
||||||
|
def get_set(ids):
|
||||||
|
result = set()
|
||||||
|
for block_name in ids:
|
||||||
|
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
||||||
|
result.add(state['id'])
|
||||||
|
return result
|
||||||
|
|
||||||
NON_SOLID_IDS = set([SINGLE_SNOW])
|
NON_SOLID_IDS = get_set(NON_SOLID)
|
||||||
for block_name in NON_SOLID:
|
NON_SOLID_IDS.add(SINGLE_SNOW)
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
NON_SOLID_IDS.add(state['id'])
|
|
||||||
|
|
||||||
AVOID_IDS = set()
|
|
||||||
for block_name in AVOID:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
AVOID_IDS.add(state['id'])
|
|
||||||
|
|
||||||
WATER_IDS = set()
|
|
||||||
for block_name in WATER:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
WATER_IDS.add(state['id'])
|
|
||||||
|
|
||||||
LOG_IDS = set()
|
|
||||||
for block_name in LOGS:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
LOG_IDS.add(state['id'])
|
|
||||||
|
|
||||||
LEAF_IDS = set()
|
|
||||||
for block_name in LEAVES:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
LEAF_IDS.add(state['id'])
|
|
||||||
|
|
||||||
CHEST_IDS = set()
|
|
||||||
for block_name in ['chest']:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
CHEST_IDS.add(state['id'])
|
|
||||||
|
|
||||||
BARREL_IDS = set()
|
|
||||||
for block_name in ['barrel']:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
BARREL_IDS.add(state['id'])
|
|
||||||
|
|
||||||
TRAPPED_CHEST_IDS = set()
|
|
||||||
for block_name in ['trapped_chest']:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
TRAPPED_CHEST_IDS.add(state['id'])
|
|
||||||
|
|
||||||
NETHERWART_IDS = set()
|
|
||||||
for block_name in ['nether_wart']:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
NETHERWART_IDS.add(state['id'])
|
|
||||||
|
|
||||||
WHEAT_IDS = set()
|
|
||||||
for block_name in ['wheat']:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
WHEAT_IDS.add(state['id'])
|
|
||||||
|
|
||||||
POTATO_IDS = set()
|
|
||||||
for block_name in ['potatoes']:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
POTATO_IDS.add(state['id'])
|
|
||||||
|
|
||||||
CARROT_IDS = set()
|
|
||||||
for block_name in ['carrots']:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
CARROT_IDS.add(state['id'])
|
|
||||||
|
|
||||||
BEETROOT_IDS = set()
|
|
||||||
for block_name in ['beetroots']:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
BEETROOT_IDS.add(state['id'])
|
|
||||||
|
|
||||||
SAPLING_IDS = set()
|
|
||||||
for block_name in SAPLINGS:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
SAPLING_IDS.add(state['id'])
|
|
||||||
|
|
||||||
BED_IDS = set()
|
|
||||||
for block_name in BEDS:
|
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
|
||||||
BED_IDS.add(state['id'])
|
|
||||||
|
|
||||||
|
|
||||||
|
AVOID_IDS = get_set(AVOID)
|
||||||
|
WATER_IDS = get_set(['water'])
|
||||||
|
LOG_IDS = get_set(LOGS)
|
||||||
|
LEAF_IDS = get_set(LEAVES)
|
||||||
|
CHEST_IDS = get_set(['chest'])
|
||||||
|
BARREL_IDS = get_set(['barrel'])
|
||||||
|
TRAPPED_CHEST_IDS = get_set(['trapped_chest'])
|
||||||
|
NETHERWART_IDS = get_set(['nether_wart'])
|
||||||
|
WHEAT_IDS = get_set(['wheat'])
|
||||||
|
POTATO_IDS = get_set(['potatoes'])
|
||||||
|
CARROT_IDS = get_set(['carrots'])
|
||||||
|
BEETROOT_IDS = get_set(['beetroots'])
|
||||||
|
SAPLING_IDS = get_set(SAPLINGS)
|
||||||
|
BED_IDS = get_set(BEDS)
|
||||||
|
|
||||||
INDEXED_IDS = set()
|
INDEXED_IDS = set()
|
||||||
for block_name in INDEXED:
|
for block_name in INDEXED:
|
||||||
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
for state in JSON_BLOCKS['minecraft:' + block_name]['states']:
|
||||||
INDEXED_IDS.add(state['id'])
|
INDEXED_IDS.add(state['id'])
|
||||||
|
|
||||||
|
|
||||||
MATURE_WHEAT_ID = max(WHEAT_IDS)
|
MATURE_WHEAT_ID = max(WHEAT_IDS)
|
||||||
MATURE_POTATO_ID = max(POTATO_IDS)
|
MATURE_POTATO_ID = max(POTATO_IDS)
|
||||||
MATURE_CARROT_ID = max(CARROT_IDS)
|
MATURE_CARROT_ID = max(CARROT_IDS)
|
@@ -67,37 +67,31 @@ LOGS = [
|
|||||||
'dark_oak_log',
|
'dark_oak_log',
|
||||||
]
|
]
|
||||||
|
|
||||||
BED_IDS = set()
|
|
||||||
for item_name in BEDS:
|
|
||||||
BED_IDS.add(ITEMS['minecraft:'+item_name]['protocol_id'])
|
|
||||||
|
|
||||||
SHOVEL_IDS = set()
|
def get_set(ids):
|
||||||
for item_name in SHOVELS:
|
result = set()
|
||||||
SHOVEL_IDS.add(ITEMS['minecraft:'+item_name]['protocol_id'])
|
for item_name in ids:
|
||||||
|
result.add(ITEMS['minecraft:'+item_name]['protocol_id'])
|
||||||
|
return result
|
||||||
|
|
||||||
AXE_IDS = set()
|
BED_IDS = get_set(BEDS)
|
||||||
for item_name in AXES:
|
SHOVEL_IDS = get_set(SHOVELS)
|
||||||
AXE_IDS.add(ITEMS['minecraft:'+item_name]['protocol_id'])
|
AXE_IDS = get_set(AXES)
|
||||||
|
FOOD_IDS = get_set(FOOD)
|
||||||
FOOD_IDS = set()
|
SAPLING_IDS = get_set(SAPLINGS)
|
||||||
for item_name in FOOD:
|
LOG_IDS = get_set(LOGS)
|
||||||
FOOD_IDS.add(ITEMS['minecraft:'+item_name]['protocol_id'])
|
|
||||||
|
|
||||||
SAPLING_IDS = set()
|
|
||||||
for item_name in SAPLINGS:
|
|
||||||
SAPLING_IDS.add(ITEMS['minecraft:'+item_name]['protocol_id'])
|
|
||||||
|
|
||||||
LOG_IDS = set()
|
|
||||||
for item_name in LOGS:
|
|
||||||
LOG_IDS.add(ITEMS['minecraft:'+item_name]['protocol_id'])
|
|
||||||
|
|
||||||
ITEM_NAMES = {}
|
ITEM_NAMES = {}
|
||||||
for item_name, item in ITEMS.items():
|
for item_name, item in ITEMS.items():
|
||||||
ITEM_NAMES[ITEMS[item_name]['protocol_id']] = item_name.replace('minecraft:', '')
|
ITEM_NAMES[ITEMS[item_name]['protocol_id']] = item_name.replace('minecraft:', '')
|
||||||
|
|
||||||
|
|
||||||
def get_id(name):
|
def get_id(name):
|
||||||
return ITEMS['minecraft:' + name]['protocol_id']
|
return ITEMS['minecraft:' + name]['protocol_id']
|
||||||
|
|
||||||
|
def get_name(idx):
|
||||||
|
return ITEM_NAMES[idx]
|
||||||
|
|
||||||
CHEST_ID = get_id('chest')
|
CHEST_ID = get_id('chest')
|
||||||
GAPPLE_ID = get_id('enchanted_golden_apple')
|
GAPPLE_ID = get_id('enchanted_golden_apple')
|
||||||
SAND_ID = get_id('sand')
|
SAND_ID = get_id('sand')
|
||||||
@@ -105,13 +99,30 @@ NETHERWART_ID = get_id('nether_wart')
|
|||||||
|
|
||||||
CARROT_ID = get_id('carrot')
|
CARROT_ID = get_id('carrot')
|
||||||
POTATO_ID = get_id('potato')
|
POTATO_ID = get_id('potato')
|
||||||
|
WHEAT_ID = get_id('wheat')
|
||||||
WHEAT_SEEDS_ID = get_id('wheat_seeds')
|
WHEAT_SEEDS_ID = get_id('wheat_seeds')
|
||||||
BEETROOT_SEEDS_ID = get_id('beetroot_seeds')
|
BEETROOT_SEEDS_ID = get_id('beetroot_seeds')
|
||||||
PUMPKIN_ID = get_id('pumpkin')
|
PUMPKIN_ID = get_id('pumpkin')
|
||||||
|
BEETROOT_ID = get_id('beetroot')
|
||||||
|
|
||||||
EMERALD_ID = get_id('emerald')
|
EMERALD_ID = get_id('emerald')
|
||||||
BERRIES_ID = get_id('sweet_berries')
|
BERRIES_ID = get_id('sweet_berries')
|
||||||
IRON_INGOT_ID = get_id('iron_ingot')
|
IRON_INGOT_ID = get_id('iron_ingot')
|
||||||
|
|
||||||
NEEDED_ITEMS = BED_IDS | SHOVEL_IDS | AXE_IDS | FOOD_IDS | set([CHEST_ID, PUMPKIN_ID, BERRIES_ID, IRON_INGOT_ID])
|
|
||||||
WANTED_ITEMS = SAPLING_IDS | set([NETHERWART_ID, CARROT_ID, POTATO_ID, WHEAT_SEEDS_ID, BEETROOT_SEEDS_ID])
|
INIT_NEEDED_ITEMS = BED_IDS | FOOD_IDS
|
||||||
|
INIT_NEEDED_ITEMS.add(CHEST_ID)
|
||||||
|
|
||||||
|
NEEDED_ITEMS = INIT_NEEDED_ITEMS
|
||||||
|
|
||||||
|
INIT_WANTED_ITEMS = set()
|
||||||
|
WANTED_ITEMS = INIT_WANTED_ITEMS
|
||||||
|
|
||||||
|
def set_needed(items):
|
||||||
|
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
|
import json
|
||||||
|
|
||||||
with open('minecraft_data/registries.json') as f:
|
with open('minecraft_data/registries.json') as f:
|
||||||
MOBS = json.load(f)['minecraft:entity_type']['entries']
|
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 = [
|
EVIL = [
|
||||||
'blaze',
|
'blaze',
|
||||||
'cave_spider',
|
'cave_spider',
|
264
mosfet/job.py
Normal file
264
mosfet/job.py
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
from mosfet.jobs import (
|
||||||
|
cache_items,
|
||||||
|
check_threats,
|
||||||
|
clear_leaves,
|
||||||
|
eat_food,
|
||||||
|
fill_blocks,
|
||||||
|
find_gapple,
|
||||||
|
gather_crop,
|
||||||
|
gather_sand,
|
||||||
|
gather_wart,
|
||||||
|
gather_wood,
|
||||||
|
grab_sand,
|
||||||
|
grab_sapling,
|
||||||
|
grab_supplies,
|
||||||
|
plant_tree,
|
||||||
|
sell_to_villager,
|
||||||
|
sleep_with_bed,
|
||||||
|
)
|
||||||
|
|
||||||
|
for module in [
|
||||||
|
cache_items,
|
||||||
|
check_threats,
|
||||||
|
clear_leaves,
|
||||||
|
eat_food,
|
||||||
|
fill_blocks,
|
||||||
|
find_gapple,
|
||||||
|
gather_crop,
|
||||||
|
gather_sand,
|
||||||
|
gather_wart,
|
||||||
|
gather_wood,
|
||||||
|
grab_sand,
|
||||||
|
grab_sapling,
|
||||||
|
grab_supplies,
|
||||||
|
plant_tree,
|
||||||
|
sell_to_villager,
|
||||||
|
sleep_with_bed,
|
||||||
|
]:
|
||||||
|
importlib.reload(module)
|
||||||
|
|
||||||
|
|
||||||
|
class JobStates:
|
||||||
|
def idle(self):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def init_machines(self):
|
||||||
|
self.gather_wood_states = gather_wood.GatherWoodStates(self.g)
|
||||||
|
self.gather_sand_states = gather_sand.GatherSandStates(self.g)
|
||||||
|
self.sleep_with_bed_states = sleep_with_bed.SleepWithBedStates(self.g)
|
||||||
|
self.cache_items_states = cache_items.CacheItemsStates(self.g)
|
||||||
|
self.grab_supplies_states = grab_supplies.GrabSuppliesStates(self.g)
|
||||||
|
self.find_gapple_states = find_gapple.FindGappleStates(self.g)
|
||||||
|
self.plant_tree_states = plant_tree.PlantTreeStates(self.g)
|
||||||
|
self.clear_leaves_states = clear_leaves.ClearLeavesStates(self.g)
|
||||||
|
self.grab_sapling_states = grab_sapling.GrabSaplingStates(self.g)
|
||||||
|
self.grab_sand_states = grab_sand.GrabSandStates(self.g)
|
||||||
|
self.fill_blocks_states = fill_blocks.FillBlocksStates(self.g)
|
||||||
|
self.check_threats_states = check_threats.CheckThreatsStates(self.g)
|
||||||
|
self.gather_wart_states = gather_wart.GatherWartStates(self.g)
|
||||||
|
self.gather_crop_states = gather_crop.GatherCropStates(self.g)
|
||||||
|
self.eat_food_states = eat_food.EatFoodStates(self.g)
|
||||||
|
self.sell_to_villager_states = sell_to_villager.SellToVillagerStates(self.g)
|
||||||
|
|
||||||
|
def run_machines(self, machines):
|
||||||
|
for m in machines:
|
||||||
|
if m.state == m.idle:
|
||||||
|
continue
|
||||||
|
if m.state != m.done:
|
||||||
|
m.run()
|
||||||
|
return
|
||||||
|
# if we went through them all
|
||||||
|
for m in machines:
|
||||||
|
m.state = m.init
|
||||||
|
|
||||||
|
def gather_sand(self):
|
||||||
|
machines = [
|
||||||
|
self.gather_sand_states,
|
||||||
|
self.grab_sand_states,
|
||||||
|
self.cache_items_states,
|
||||||
|
self.sleep_with_bed_states,
|
||||||
|
self.eat_food_states,
|
||||||
|
]
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def farm_sand(self):
|
||||||
|
machines = [
|
||||||
|
self.grab_supplies_states,
|
||||||
|
self.check_threats_states,
|
||||||
|
self.gather_sand_states,
|
||||||
|
self.grab_sand_states,
|
||||||
|
self.cache_items_states,
|
||||||
|
self.sleep_with_bed_states,
|
||||||
|
self.eat_food_states,
|
||||||
|
]
|
||||||
|
self.sleep_with_bed_states.silent = True
|
||||||
|
self.cache_items_states.silent = True
|
||||||
|
self.grab_supplies_states.supplies = {
|
||||||
|
tuple(items.SHOVEL_IDS): (1, 9),
|
||||||
|
}
|
||||||
|
|
||||||
|
items.set_needed(items.SHOVEL_IDS)
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def cache_items(self):
|
||||||
|
machines = [
|
||||||
|
self.cache_items_states,
|
||||||
|
]
|
||||||
|
return machines
|
||||||
|
|
||||||
|
|
||||||
|
def find_gapple(self):
|
||||||
|
machines = [
|
||||||
|
self.find_gapple_states,
|
||||||
|
]
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def gather_wood(self):
|
||||||
|
machines = [
|
||||||
|
self.gather_wood_states,
|
||||||
|
self.sleep_with_bed_states,
|
||||||
|
self.eat_food_states,
|
||||||
|
self.cache_items_states,
|
||||||
|
]
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def farm_wood(self):
|
||||||
|
machines = [
|
||||||
|
self.grab_supplies_states,
|
||||||
|
self.gather_wood_states,
|
||||||
|
self.clear_leaves_states,
|
||||||
|
self.plant_tree_states,
|
||||||
|
self.grab_sapling_states,
|
||||||
|
self.sleep_with_bed_states,
|
||||||
|
self.eat_food_states,
|
||||||
|
self.cache_items_states,
|
||||||
|
]
|
||||||
|
self.sleep_with_bed_states.silent = True
|
||||||
|
self.cache_items_states.silent = True
|
||||||
|
self.grab_supplies_states.supplies = {
|
||||||
|
tuple(items.AXE_IDS): (1, 9),
|
||||||
|
}
|
||||||
|
|
||||||
|
items.set_needed(items.AXE_IDS)
|
||||||
|
items.set_wanted(items.SAPLING_IDS)
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def farm_wart(self):
|
||||||
|
machines = [
|
||||||
|
self.gather_wart_states,
|
||||||
|
self.sleep_with_bed_states,
|
||||||
|
self.eat_food_states,
|
||||||
|
self.cache_items_states,
|
||||||
|
]
|
||||||
|
self.sleep_with_bed_states.silent = True
|
||||||
|
self.cache_items_states.silent = True
|
||||||
|
|
||||||
|
items.set_wanted(set([items.NETHERWART_ID]))
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def farm_crop(self):
|
||||||
|
machines = [
|
||||||
|
self.gather_crop_states,
|
||||||
|
self.sleep_with_bed_states,
|
||||||
|
self.eat_food_states,
|
||||||
|
self.cache_items_states,
|
||||||
|
]
|
||||||
|
self.sleep_with_bed_states.silent = True
|
||||||
|
self.cache_items_states.silent = True
|
||||||
|
|
||||||
|
items.set_wanted(set([
|
||||||
|
items.CARROT_ID,
|
||||||
|
items.POTATO_ID,
|
||||||
|
items.WHEAT_SEEDS_ID,
|
||||||
|
items.BEETROOT_SEEDS_ID,
|
||||||
|
]))
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def fill_blocks(self):
|
||||||
|
machines = [
|
||||||
|
self.grab_supplies_states,
|
||||||
|
self.fill_blocks_states,
|
||||||
|
self.sleep_with_bed_states,
|
||||||
|
self.eat_food_states,
|
||||||
|
]
|
||||||
|
self.sleep_with_bed_states.silent = True
|
||||||
|
|
||||||
|
f = self.g.filling
|
||||||
|
if f:
|
||||||
|
name = blocks.BLOCKS[f.block]
|
||||||
|
item = items.ITEMS['minecraft:'+name]['protocol_id']
|
||||||
|
|
||||||
|
self.grab_supplies_states.supplies = {
|
||||||
|
tuple([item]): (1, 0),
|
||||||
|
}
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def loiter(self):
|
||||||
|
machines = [
|
||||||
|
self.check_threats_states,
|
||||||
|
self.sleep_with_bed_states,
|
||||||
|
self.eat_food_states,
|
||||||
|
]
|
||||||
|
self.sleep_with_bed_states.silent = True
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def trade(self):
|
||||||
|
machines = [
|
||||||
|
self.grab_supplies_states,
|
||||||
|
self.sell_to_villager_states,
|
||||||
|
self.sleep_with_bed_states,
|
||||||
|
self.eat_food_states,
|
||||||
|
self.cache_items_states,
|
||||||
|
]
|
||||||
|
self.sleep_with_bed_states.silent = True
|
||||||
|
self.cache_items_states.silent = True
|
||||||
|
self.grab_supplies_states.supplies = {
|
||||||
|
tuple([items.PUMPKIN_ID]): (64, 3),
|
||||||
|
tuple([items.BERRIES_ID]): (64, 3),
|
||||||
|
tuple([items.IRON_INGOT_ID]): (64, 3),
|
||||||
|
tuple([items.WHEAT_ID]): (64, 3),
|
||||||
|
tuple([items.POTATO_ID]): (64, 3),
|
||||||
|
tuple([items.CARROT_ID]): (64, 3),
|
||||||
|
tuple([items.BEETROOT_ID]): (64, 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
items.set_needed(set([
|
||||||
|
items.PUMPKIN_ID,
|
||||||
|
items.BERRIES_ID,
|
||||||
|
items.IRON_INGOT_ID,
|
||||||
|
items.WHEAT_ID,
|
||||||
|
items.POTATO_ID,
|
||||||
|
items.CARROT_ID,
|
||||||
|
items.BEETROOT_ID,
|
||||||
|
]))
|
||||||
|
return machines
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.init_machines()
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
|
||||||
|
self.init_machines()
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
def tick(self):
|
||||||
|
self.run_machines(self.state())
|
0
mosfet/jobs/__init__.py
Normal file
0
mosfet/jobs/__init__.py
Normal file
228
mosfet/jobs/cache_items.py
Normal file
228
mosfet/jobs/cache_items.py
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class CacheItemsStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.skip_slots = []
|
||||||
|
self.skip_items = []
|
||||||
|
|
||||||
|
num_stacks = self.g.game.count_inventory_slots()
|
||||||
|
print('Inventory amount:', num_stacks)
|
||||||
|
if num_stacks >= self.g.minimum_cache_slots:
|
||||||
|
self.state = self.find_trapped_chests
|
||||||
|
else:
|
||||||
|
print('Aborting caching, not full')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def find_trapped_chests(self):
|
||||||
|
print('Finding trapped chests...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
self.trapped_chests = w.find_blocks_indexed(p, blocks.TRAPPED_CHEST_IDS)
|
||||||
|
print('Found:', self.trapped_chests)
|
||||||
|
self.state = self.choose_trapped_chest
|
||||||
|
|
||||||
|
def choose_trapped_chest(self):
|
||||||
|
print('Choosing a trapped chest...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
c = self.g.chunks
|
||||||
|
|
||||||
|
if not len(self.trapped_chests):
|
||||||
|
print('No trapped chests')
|
||||||
|
self.state = self.select_chest
|
||||||
|
return
|
||||||
|
|
||||||
|
chest = self.trapped_chests[0]
|
||||||
|
|
||||||
|
navpath = w.path_to_place_faked(p, chest)
|
||||||
|
print('navpath:', navpath)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath[:-1]
|
||||||
|
self.opening = self.g.path[-1]
|
||||||
|
self.area = chest
|
||||||
|
self.state = self.going_to_trapped_chest
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.trapped_chests.pop(0)
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
def going_to_trapped_chest(self):
|
||||||
|
if utils.pint(self.g.pos) == self.opening:
|
||||||
|
self.g.look_at = self.area
|
||||||
|
self.state = self.open_chest
|
||||||
|
|
||||||
|
def select_chest(self):
|
||||||
|
if self.g.game.select_item([items.CHEST_ID]):
|
||||||
|
self.state = self.find_cache_spot
|
||||||
|
else:
|
||||||
|
print('No chest, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def find_cache_spot(self):
|
||||||
|
print('Finding a chest spot...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
for area in w.find_cache_areas(p, 100):
|
||||||
|
print('Found area:', area)
|
||||||
|
if area not in self.bad_areas:
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
print('Unable to find area')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
self.area = area
|
||||||
|
openings = w.find_cache_openings(self.area)
|
||||||
|
|
||||||
|
for o in openings:
|
||||||
|
navpath = w.path_to_place(p, o)
|
||||||
|
self.opening = o
|
||||||
|
if navpath: break
|
||||||
|
else: # for
|
||||||
|
print('Unable to get to cache area', self.area)
|
||||||
|
self.bad_areas.append(self.area)
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
self.g.path = navpath
|
||||||
|
self.state = self.going_to_area
|
||||||
|
|
||||||
|
def going_to_area(self):
|
||||||
|
if utils.pint(self.g.pos) == self.opening:
|
||||||
|
self.g.look_at = self.area
|
||||||
|
self.state = self.place_chest
|
||||||
|
|
||||||
|
def place_chest(self):
|
||||||
|
self.g.game.place_block(self.area, BlockFace.TOP)
|
||||||
|
self.state = self.open_chest
|
||||||
|
|
||||||
|
def open_chest(self):
|
||||||
|
print('Opening chest')
|
||||||
|
self.g.game.open_container(self.area)
|
||||||
|
self.wait_time = 1
|
||||||
|
self.state = self.wait
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
# wait for server to send us chest contents
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.move_items
|
||||||
|
|
||||||
|
def move_items(self):
|
||||||
|
if self.g.item_lock: return
|
||||||
|
w = self.g.window
|
||||||
|
|
||||||
|
if not w:
|
||||||
|
print('Didnt get a window, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
if w.data.window_type != mcdata.SINGLE_CHEST:
|
||||||
|
print('Got wrong window, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
w_info = mcdata.WINDOWS[w.data.window_type]
|
||||||
|
w_inventory_slots = w_info.inventory
|
||||||
|
w_container_slots = w_info.container
|
||||||
|
|
||||||
|
used_slots = self.g.game.count_window_slots()
|
||||||
|
print('used:', used_slots, 'total:', len(w_container_slots))
|
||||||
|
if used_slots >= len(w_container_slots):
|
||||||
|
print('Container is too full, aborting')
|
||||||
|
self.g.game.close_window()
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
slot_list = []
|
||||||
|
|
||||||
|
for slot_num in w_inventory_slots:
|
||||||
|
if slot_num not in w.contents:
|
||||||
|
continue
|
||||||
|
|
||||||
|
slot = w.contents[slot_num]
|
||||||
|
|
||||||
|
if not slot.present:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if slot.item_id in items.NEEDED_ITEMS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if slot_num in self.skip_slots:
|
||||||
|
continue
|
||||||
|
|
||||||
|
slot_list.append((slot_num, slot))
|
||||||
|
|
||||||
|
slot_list.sort(key=lambda x: x[1].item_count, reverse=True)
|
||||||
|
|
||||||
|
for slot_num, slot in slot_list:
|
||||||
|
if slot.item_id in items.WANTED_ITEMS and slot.item_id not in self.skip_items:
|
||||||
|
print('skipping wanted item', slot)
|
||||||
|
self.skip_slots.append(slot_num)
|
||||||
|
self.skip_items.append(slot.item_id)
|
||||||
|
continue
|
||||||
|
|
||||||
|
print('moving', slot)
|
||||||
|
|
||||||
|
self.g.item_lock = True
|
||||||
|
self.g.game.click_window(slot_num, 0, 1, slot)
|
||||||
|
return
|
||||||
|
|
||||||
|
print('nothing left to move')
|
||||||
|
self.state = self.close_chest
|
||||||
|
|
||||||
|
def close_chest(self):
|
||||||
|
print('closing chest')
|
||||||
|
self.g.game.close_window()
|
||||||
|
if not self.silent:
|
||||||
|
self.g.chat.send('cache at ' + str(self.area)[1:-1])
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.silent = False
|
||||||
|
|
||||||
|
self.skip_slots = []
|
||||||
|
self.skip_items = []
|
||||||
|
|
||||||
|
self.area = None
|
||||||
|
self.opening = None
|
||||||
|
self.trapped_chests = []
|
||||||
|
self.bad_areas = []
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
109
mosfet/jobs/check_threats.py
Normal file
109
mosfet/jobs/check_threats.py
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class CheckThreatsStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.state = self.find_threats
|
||||||
|
print('Checking for threats')
|
||||||
|
|
||||||
|
def find_threats(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
threats = w.find_threats(p, 40)
|
||||||
|
|
||||||
|
if threats:
|
||||||
|
print('Found', len(threats), 'threats, fleeing:')
|
||||||
|
print(threats)
|
||||||
|
self.state = self.find_safety
|
||||||
|
else:
|
||||||
|
print('Aborting, no threats')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def find_safety(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
safety = w.find_blocks_indexed(p, [blocks.EMERALD_BLOCK])
|
||||||
|
|
||||||
|
if not safety:
|
||||||
|
print('No emerald blocks found, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
safety.sort(key=lambda s: utils.phyp(p, s))
|
||||||
|
print('Found emerald blocks:', safety)
|
||||||
|
|
||||||
|
for s in safety:
|
||||||
|
s = utils.padd(s, path.BLOCK_ABOVE)
|
||||||
|
navpath = w.path_to_place(p, s)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath
|
||||||
|
self.state = self.going_to_safety
|
||||||
|
self.safety = s
|
||||||
|
print('Going to safety', self.safety)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print('Cant get to safety', self.safety)
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
print('Cant get to safety, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def going_to_safety(self):
|
||||||
|
if utils.pint(self.g.pos) == self.safety:
|
||||||
|
print('At safety spot, waiting to be moved')
|
||||||
|
self.state = self.wait_for_move
|
||||||
|
|
||||||
|
def wait_for_move(self):
|
||||||
|
# wait for the server to move the bot when it's safe
|
||||||
|
# ie. a piston + daylight sensor
|
||||||
|
if utils.pint(self.g.pos) != self.safety:
|
||||||
|
print('Moved, resuming job')
|
||||||
|
self.state = self.wait
|
||||||
|
self.wait_time = 3
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
# wait to land, etc
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.safety = None
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
||||||
|
|
91
mosfet/jobs/clear_leaves.py
Normal file
91
mosfet/jobs/clear_leaves.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class ClearLeavesStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
if not self.g.chopped_tree:
|
||||||
|
print('Didnt chop tree, clearing leaves')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
sapling_type = self.g.chopped_tree + '_sapling'
|
||||||
|
sapling_item = items.get_id(sapling_type)
|
||||||
|
num_saplings = self.g.game.count_items([sapling_item])
|
||||||
|
print('Have', num_saplings, sapling_type, 'in inventory')
|
||||||
|
|
||||||
|
if num_saplings > 8:
|
||||||
|
print('Have enough saplings, aborting clearing leaves')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
self.state = self.select_log
|
||||||
|
print('Clearing leaves...')
|
||||||
|
|
||||||
|
def select_log(self):
|
||||||
|
# select a log to avoid using tools
|
||||||
|
self.g.game.select_item(items.LOG_IDS)
|
||||||
|
self.state = self.find_leaves
|
||||||
|
|
||||||
|
def find_leaves(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
pos = utils.padd(p, path.BLOCK_ABOVE)
|
||||||
|
|
||||||
|
for l in w.find_leaves(pos, blocks.BREAK_DISTANCE):
|
||||||
|
self.leaves.append(l)
|
||||||
|
|
||||||
|
self.state = self.break_leaves
|
||||||
|
|
||||||
|
def break_leaves(self):
|
||||||
|
if not self.g.breaking:
|
||||||
|
if self.leaves:
|
||||||
|
leaf = self.leaves.pop(0)
|
||||||
|
self.g.look_at = leaf
|
||||||
|
self.g.game.break_block(leaf)
|
||||||
|
print('Breaking leaf', leaf)
|
||||||
|
else:
|
||||||
|
self.wait_time = 1
|
||||||
|
self.state = self.wait
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
# wait for the items to drop
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.leaves = []
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
72
mosfet/jobs/eat_food.py
Normal file
72
mosfet/jobs/eat_food.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class EatFoodStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
if self.g.food < 12:
|
||||||
|
print('Hungry, eating')
|
||||||
|
self.state = self.select_food
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.g.health < 20 and self.g.food < 18:
|
||||||
|
print('Low health, eating')
|
||||||
|
self.state = self.select_food
|
||||||
|
return
|
||||||
|
|
||||||
|
print('Don\'t need to eat, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def select_food(self):
|
||||||
|
if self.g.game.select_item(items.FOOD_IDS):
|
||||||
|
self.state = self.eat_food
|
||||||
|
else:
|
||||||
|
print('No food, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def eat_food(self):
|
||||||
|
self.g.game.use_item(0)
|
||||||
|
|
||||||
|
print('Eating food')
|
||||||
|
self.wait_time = 3
|
||||||
|
self.state = self.wait
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
||||||
|
|
215
mosfet/jobs/fill_blocks.py
Normal file
215
mosfet/jobs/fill_blocks.py
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class FillBlocksStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
f = self.g.filling
|
||||||
|
|
||||||
|
if not f:
|
||||||
|
self.state = self.cleanup
|
||||||
|
print('Aborting, nothing to fill')
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.last_block:
|
||||||
|
self.state = self.select_item
|
||||||
|
else:
|
||||||
|
self.state = self.find_last_block
|
||||||
|
|
||||||
|
def find_last_block(self):
|
||||||
|
w = self.g.world
|
||||||
|
f = self.g.filling
|
||||||
|
print('Finding last block')
|
||||||
|
|
||||||
|
b1, b2 = utils.pboundingbox(f.coord1, f.coord2)
|
||||||
|
box = utils.psub(b2, b1)
|
||||||
|
xz_distance = utils.hypot(box[0]+1, box[2]+1)
|
||||||
|
y_start = f.coord1[1]
|
||||||
|
y_end = f.coord2[1]
|
||||||
|
|
||||||
|
for y in range(y_start, y_end+1):
|
||||||
|
for offset in utils.search_2d(xz_distance):
|
||||||
|
check = utils.padd(f.coord1, offset)
|
||||||
|
check = (check[0], y, check[2])
|
||||||
|
|
||||||
|
# ensure block is within fill area
|
||||||
|
if check[0] < b1[0] or check[0] > b2[0]:
|
||||||
|
continue
|
||||||
|
if check[2] < b1[2] or check[2] > b2[2]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if w.block_at(*check) == blocks.AIR:
|
||||||
|
self.state = self.select_item
|
||||||
|
return
|
||||||
|
|
||||||
|
self.last_block = check
|
||||||
|
else: # for
|
||||||
|
self.state = self.cleanup
|
||||||
|
print('Aborting, no air left')
|
||||||
|
return
|
||||||
|
|
||||||
|
def select_item(self):
|
||||||
|
f = self.g.filling
|
||||||
|
name = blocks.BLOCKS[f.block]
|
||||||
|
item = items.ITEMS['minecraft:'+name]['protocol_id']
|
||||||
|
|
||||||
|
if self.g.game.select_item([item]):
|
||||||
|
#self.g.look_at = utils.padd(self.area, path.BLOCK_BELOW)
|
||||||
|
self.state = self.find_next_block
|
||||||
|
else:
|
||||||
|
print('No blocks, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def find_next_block(self):
|
||||||
|
w = self.g.world
|
||||||
|
f = self.g.filling
|
||||||
|
print('Finding next block, last:', self.last_block)
|
||||||
|
|
||||||
|
b1, b2 = utils.pboundingbox(f.coord1, f.coord2)
|
||||||
|
box = utils.psub(b2, b1)
|
||||||
|
xz_distance = utils.hypot(box[0]+1, box[2]+1)
|
||||||
|
y_start = f.coord1[1]
|
||||||
|
y_end = f.coord2[1]
|
||||||
|
|
||||||
|
for y in range(y_start, y_end+1):
|
||||||
|
if y not in self.iterators:
|
||||||
|
self.iterators[y] = utils.search_2d(xz_distance)
|
||||||
|
|
||||||
|
for offset in self.iterators[y]:
|
||||||
|
check = utils.padd(f.coord1, offset)
|
||||||
|
check = (check[0], y, check[2])
|
||||||
|
|
||||||
|
# ensure block is within fill area
|
||||||
|
if check[0] < b1[0] or check[0] > b2[0]:
|
||||||
|
continue
|
||||||
|
if check[2] < b1[2] or check[2] > b2[2]:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if w.block_at(*check) == blocks.AIR:
|
||||||
|
print('Found next block:', check)
|
||||||
|
self.next_block = check
|
||||||
|
self.state = self.check_block_distance
|
||||||
|
return
|
||||||
|
|
||||||
|
# if there's nothing left to fill
|
||||||
|
self.g.filling = None
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def check_block_distance(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
head = utils.padd(p, path.BLOCK_ABOVE)
|
||||||
|
|
||||||
|
if utils.phyp(head, self.next_block) < 4:
|
||||||
|
self.state = self.fill_block
|
||||||
|
else:
|
||||||
|
self.state = self.nav_to_block
|
||||||
|
|
||||||
|
def nav_to_block(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
c = self.g.chunks
|
||||||
|
|
||||||
|
tmp = c.get_block_at(*self.next_block)
|
||||||
|
c.set_block_at(*self.next_block, blocks.STONE)
|
||||||
|
pos = utils.padd(self.next_block, path.BLOCK_ABOVE)
|
||||||
|
navpath = w.path_to_place(p, pos)
|
||||||
|
c.set_block_at(*self.next_block, tmp)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath[:-1]
|
||||||
|
self.state = self.going_to_block
|
||||||
|
else:
|
||||||
|
print('Cant get to that block')
|
||||||
|
self.state = self.cleanup
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
def going_to_block(self):
|
||||||
|
if not len(self.g.path):
|
||||||
|
self.state = self.fill_block
|
||||||
|
|
||||||
|
def fill_block(self):
|
||||||
|
print('Filling block', self.next_block)
|
||||||
|
|
||||||
|
self.g.game.place_block(self.next_block, BlockFace.TOP)
|
||||||
|
self.g.look_at = self.next_block
|
||||||
|
|
||||||
|
self.wait_time = 0.25
|
||||||
|
self.state = self.wait_for_block
|
||||||
|
|
||||||
|
def wait_for_block(self):
|
||||||
|
w = self.g.world
|
||||||
|
if w.block_at(*self.next_block) != blocks.AIR:
|
||||||
|
self.last_block = self.next_block
|
||||||
|
self.state = self.check_obstruction
|
||||||
|
elif self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
print('Block didnt appear')
|
||||||
|
self.state = self.check_obstruction
|
||||||
|
|
||||||
|
def check_obstruction(self):
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
f = self.g.filling
|
||||||
|
print('last', self.last_block)
|
||||||
|
print('p', p)
|
||||||
|
if self.last_block[1] >= p[1] and f.block not in blocks.NON_SOLID_IDS:
|
||||||
|
print('Obstructed, going to last block')
|
||||||
|
self.state = self.nav_to_last_block
|
||||||
|
else:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def nav_to_last_block(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
c = self.g.chunks
|
||||||
|
|
||||||
|
pos = utils.padd(self.last_block, path.BLOCK_ABOVE)
|
||||||
|
navpath = w.path_to_place(p, pos)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath
|
||||||
|
self.state = self.going_to_last_block
|
||||||
|
else:
|
||||||
|
print('Cant get to that block')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def going_to_last_block(self):
|
||||||
|
if not len(self.g.path):
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.iterators = {}
|
||||||
|
self.wait_time = 0
|
||||||
|
self.last_block = None
|
||||||
|
self.next_block = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
117
mosfet/jobs/find_gapple.py
Normal file
117
mosfet/jobs/find_gapple.py
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class FindGappleStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.state = self.go_spectator
|
||||||
|
|
||||||
|
def go_spectator(self):
|
||||||
|
print('Going spectator...')
|
||||||
|
self.g.chat.send('/gamemode spectator')
|
||||||
|
|
||||||
|
self.state = self.tp_to_coord
|
||||||
|
|
||||||
|
def tp_to_coord(self):
|
||||||
|
step = utils.spiral(self.count)
|
||||||
|
step_scaled = utils.pmul(step, 192)
|
||||||
|
self.coord = utils.padd(self.origin, step_scaled)
|
||||||
|
self.coord = (self.coord[0], 50, self.coord[2])
|
||||||
|
|
||||||
|
print('count:', self.count, 'teleporting to:', self.coord)
|
||||||
|
self.g.chat.send('/tp {} {} {}'.format(*self.coord))
|
||||||
|
|
||||||
|
self.g.command_lock = True
|
||||||
|
self.state = self.wait_for_load
|
||||||
|
|
||||||
|
def wait_for_load(self):
|
||||||
|
if self.g.command_lock:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.g.chunks.check_loaded(self.g.pos, 169):
|
||||||
|
print('chunks have been loaded')
|
||||||
|
self.state = self.pick_chest
|
||||||
|
|
||||||
|
def pick_chest(self):
|
||||||
|
chest_list = []
|
||||||
|
for chest_id in blocks.CHEST_IDS:
|
||||||
|
chest_list.extend(self.g.chunks.index.get(chest_id, []))
|
||||||
|
|
||||||
|
for chest in chest_list:
|
||||||
|
if chest in self.checked_chests:
|
||||||
|
# slow but simple
|
||||||
|
continue
|
||||||
|
|
||||||
|
if utils.phyp_king(self.coord, chest) > 96:
|
||||||
|
# skip because we can't detect item drops
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.current_chest = chest
|
||||||
|
self.checked_chests.append(self.current_chest)
|
||||||
|
self.state = self.break_chest
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
print('exhausted chest list')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def break_chest(self):
|
||||||
|
print('Breaking chest', self.current_chest)
|
||||||
|
self.g.command_lock = True
|
||||||
|
self.g.item_lock = True
|
||||||
|
self.g.chat.send('/setblock {} {} {} air destroy'.format(*self.current_chest))
|
||||||
|
|
||||||
|
self.wait_time = 0.5
|
||||||
|
self.state = self.wait_for_items
|
||||||
|
|
||||||
|
def wait_for_items(self):
|
||||||
|
# wait for command to execute
|
||||||
|
if self.g.command_lock:
|
||||||
|
return
|
||||||
|
|
||||||
|
# wait for items to drop
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
print('done waiting for items')
|
||||||
|
self.state = self.pick_chest
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.count += 1
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.origin = utils.pint(self.g.pos)
|
||||||
|
self.count = 0
|
||||||
|
self.coord = None
|
||||||
|
self.current_chest = None
|
||||||
|
self.checked_chests = []
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
||||||
|
|
||||||
|
|
131
mosfet/jobs/gather_crop.py
Normal file
131
mosfet/jobs/gather_crop.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class GatherCropStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.state = self.find_new_crop
|
||||||
|
|
||||||
|
def find_new_crop(self):
|
||||||
|
print('Finding new crop...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
mature_crops = [
|
||||||
|
blocks.MATURE_WHEAT_ID,
|
||||||
|
blocks.MATURE_POTATO_ID,
|
||||||
|
blocks.MATURE_CARROT_ID,
|
||||||
|
blocks.MATURE_BEETROOT_ID,
|
||||||
|
]
|
||||||
|
|
||||||
|
for crop in w.find_blocks_3d(p, mature_crops, 50, 20, True):
|
||||||
|
if crop not in self.bad_crops:
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
print('No good crops left, aborting.')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
print('Found crop:', crop)
|
||||||
|
self.crop = crop
|
||||||
|
self.type_id = w.block_at(*crop)
|
||||||
|
self.state = self.nav_to_crop
|
||||||
|
|
||||||
|
def nav_to_crop(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
navpath = w.path_to_place(p, self.crop)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
print('Going to crop', self.crop)
|
||||||
|
self.g.path = navpath
|
||||||
|
self.g.look_at = utils.padd(self.crop, path.BLOCK_BELOW)
|
||||||
|
self.state = self.going_to_crop
|
||||||
|
else:
|
||||||
|
print('Cant get to it, blacklisting')
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.bad_crops.append(self.crop)
|
||||||
|
self.state = self.find_new_crop
|
||||||
|
|
||||||
|
def going_to_crop(self):
|
||||||
|
if utils.pint(self.g.pos) == self.crop:
|
||||||
|
print('At the crop')
|
||||||
|
self.state = self.break_crop
|
||||||
|
|
||||||
|
def break_crop(self):
|
||||||
|
self.g.game.break_block(self.crop)
|
||||||
|
self.state = self.select_seed
|
||||||
|
|
||||||
|
def select_seed(self):
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
crop_seeds = {
|
||||||
|
blocks.MATURE_WHEAT_ID: items.WHEAT_SEEDS_ID,
|
||||||
|
blocks.MATURE_POTATO_ID: items.POTATO_ID,
|
||||||
|
blocks.MATURE_CARROT_ID: items.CARROT_ID,
|
||||||
|
blocks.MATURE_BEETROOT_ID: items.BEETROOT_SEEDS_ID,
|
||||||
|
}
|
||||||
|
self.target_seed = crop_seeds[self.type_id]
|
||||||
|
|
||||||
|
if self.g.game.select_item([self.target_seed]):
|
||||||
|
self.state = self.wait_select
|
||||||
|
else:
|
||||||
|
print('Havent picked up seed yet')
|
||||||
|
return
|
||||||
|
|
||||||
|
def wait_select(self):
|
||||||
|
if self.target_seed != self.g.holding:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.state = self.place_crop
|
||||||
|
|
||||||
|
def place_crop(self):
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
self.g.game.place_block(p, BlockFace.TOP)
|
||||||
|
print('Placed crop')
|
||||||
|
self.state = self.wait_place
|
||||||
|
|
||||||
|
def wait_place(self):
|
||||||
|
w = self.g.world
|
||||||
|
if w.block_at(*self.crop) == blocks.AIR:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.crop = None
|
||||||
|
self.type_id = None
|
||||||
|
self.target_seed = None
|
||||||
|
self.bad_crops = []
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
131
mosfet/jobs/gather_sand.py
Normal file
131
mosfet/jobs/gather_sand.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class GatherSandStates:
|
||||||
|
def bair(self, p):
|
||||||
|
return self.g.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS
|
||||||
|
|
||||||
|
def bsand(self, p):
|
||||||
|
return self.g.chunks.get_block_at(*p) == blocks.SAND
|
||||||
|
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
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):
|
||||||
|
self.g.game.select_item(items.SHOVEL_IDS)
|
||||||
|
self.state = self.find_new_slice
|
||||||
|
|
||||||
|
def find_new_slice(self):
|
||||||
|
print('Finding new slice...')
|
||||||
|
w = self.g.world
|
||||||
|
origin = self.g.sand_origin
|
||||||
|
|
||||||
|
print('using origin', self.g.sand_origin)
|
||||||
|
start = time.time()
|
||||||
|
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:
|
||||||
|
self.slice = s
|
||||||
|
self.bad_slices.append(s)
|
||||||
|
self.state = self.find_new_sand
|
||||||
|
else:
|
||||||
|
print('No slices remaining.')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def find_new_sand(self):
|
||||||
|
print('Finding new sand...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
head = utils.padd(p, path.BLOCK_ABOVE)
|
||||||
|
|
||||||
|
for sand in w.find_sand(self.slice, 2, p):
|
||||||
|
if sand not in self.bad_sand:
|
||||||
|
print('Found sand:', sand)
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
print('No good sands left, aborting.')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
self.sand = sand
|
||||||
|
|
||||||
|
if utils.phyp(head, self.sand) < blocks.BREAK_DISTANCE:
|
||||||
|
self.state = self.dig_sand
|
||||||
|
else:
|
||||||
|
self.state = self.nav_to_sand
|
||||||
|
|
||||||
|
def nav_to_sand(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
c = self.g.chunks
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
def going_to_sand(self):
|
||||||
|
if not len(self.g.path):
|
||||||
|
self.g.look_at = self.sand
|
||||||
|
self.state = self.dig_sand
|
||||||
|
|
||||||
|
def dig_sand(self):
|
||||||
|
if not self.g.breaking:
|
||||||
|
if self.bsand(self.sand):
|
||||||
|
self.g.look_at = self.sand
|
||||||
|
self.g.game.break_block(self.sand)
|
||||||
|
print('digging sand')
|
||||||
|
else:
|
||||||
|
self.state = self.find_new_sand
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.slice = None
|
||||||
|
self.bad_slices = []
|
||||||
|
self.prev_layer = 0
|
||||||
|
self.sand = None
|
||||||
|
self.bad_sand = []
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
126
mosfet/jobs/gather_wart.py
Normal file
126
mosfet/jobs/gather_wart.py
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class GatherWartStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.state = self.find_new_wart
|
||||||
|
|
||||||
|
def find_new_wart(self):
|
||||||
|
print('Finding new wart...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
mature_wart = max(blocks.NETHERWART_IDS)
|
||||||
|
for wart in w.find_blocks_3d(p, [mature_wart], 50, 20, True):
|
||||||
|
if wart not in self.bad_warts:
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
print('No good warts left, aborting.')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
print('Found wart:', wart)
|
||||||
|
self.wart = wart
|
||||||
|
self.state = self.nav_to_wart
|
||||||
|
|
||||||
|
def nav_to_wart(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
navpath = w.path_to_place(p, self.wart)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
print('Going to wart', self.wart)
|
||||||
|
self.g.path = navpath
|
||||||
|
self.g.look_at = utils.padd(self.wart, path.BLOCK_BELOW)
|
||||||
|
self.state = self.going_to_wart
|
||||||
|
else:
|
||||||
|
print('Cant get to it, blacklisting')
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.bad_warts.append(wart)
|
||||||
|
self.state = self.find_new_wart
|
||||||
|
|
||||||
|
def going_to_wart(self):
|
||||||
|
if utils.pint(self.g.pos) == self.wart:
|
||||||
|
print('At the wart')
|
||||||
|
self.state = self.break_wart
|
||||||
|
|
||||||
|
def break_wart(self):
|
||||||
|
self.g.game.break_block(self.wart)
|
||||||
|
self.wait_time = 0.5
|
||||||
|
self.state = self.wait
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
# wait for the item
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.select_wart
|
||||||
|
|
||||||
|
def select_wart(self):
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
if self.g.game.select_item([items.NETHERWART_ID]):
|
||||||
|
self.state = self.wait_select
|
||||||
|
self.wait_time = 0.5
|
||||||
|
else:
|
||||||
|
print('Aborting planting, no wart')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def wait_select(self):
|
||||||
|
# wait a bit to select
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.place_wart
|
||||||
|
|
||||||
|
def place_wart(self):
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
self.g.game.place_block(p, BlockFace.TOP)
|
||||||
|
print('Placed wart')
|
||||||
|
self.state = self.wait_place
|
||||||
|
self.wait_time = 0.5
|
||||||
|
|
||||||
|
def wait_place(self):
|
||||||
|
# wait a bit for chunk data to update
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.wart = None
|
||||||
|
self.bad_warts = []
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
191
mosfet/jobs/gather_wood.py
Normal file
191
mosfet/jobs/gather_wood.py
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class GatherWoodStates:
|
||||||
|
def bair(self, p):
|
||||||
|
return self.g.chunks.get_block_at(*p) in blocks.NON_SOLID_IDS
|
||||||
|
|
||||||
|
def blog(self, p):
|
||||||
|
return self.g.chunks.get_block_at(*p) in blocks.LOG_IDS
|
||||||
|
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.g.chopped_tree = False
|
||||||
|
self.state = self.select_axe
|
||||||
|
|
||||||
|
def select_axe(self):
|
||||||
|
self.g.game.select_item(items.AXE_IDS)
|
||||||
|
self.state = self.find_new_tree
|
||||||
|
|
||||||
|
def find_new_tree(self):
|
||||||
|
print('Finding new tree...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
for tree in w.find_trees(p, 100):
|
||||||
|
print('Found tree:', tree)
|
||||||
|
if tree not in self.bad_trees:
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
print('No good trees left, aborting.')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
self.tree = tree
|
||||||
|
self.type = blocks.BLOCKS[w.block_at(*tree)].replace('_log', '')
|
||||||
|
print('Type:', self.type)
|
||||||
|
|
||||||
|
self.state = self.find_openings
|
||||||
|
|
||||||
|
def find_openings(self):
|
||||||
|
w = self.g.world
|
||||||
|
self.openings = w.find_tree_openings(self.tree)
|
||||||
|
self.state = self.choose_opening
|
||||||
|
|
||||||
|
def choose_opening(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
print('openings:', self.openings)
|
||||||
|
|
||||||
|
if not len(self.openings):
|
||||||
|
print('Unable to get to tree', self.tree)
|
||||||
|
if self.tree not in self.good_trees:
|
||||||
|
self.bad_trees.append(self.tree)
|
||||||
|
print('Added to bad trees list')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
navpath = w.path_to_place(p, self.openings[0])
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath
|
||||||
|
self.state = self.going_to_tree
|
||||||
|
else:
|
||||||
|
self.openings.pop(0)
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
def going_to_tree(self):
|
||||||
|
if utils.pint(self.g.pos) == self.openings[0]:
|
||||||
|
self.g.look_at = self.tree
|
||||||
|
self.state = self.clear_leaves
|
||||||
|
|
||||||
|
def clear_leaves(self):
|
||||||
|
if not self.g.breaking:
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
diff = utils.psub(self.tree, p)
|
||||||
|
|
||||||
|
for x in utils.diffrange(diff[0]):
|
||||||
|
for z in utils.diffrange(diff[2]):
|
||||||
|
for y in range(2):
|
||||||
|
check = utils.padd(p, (x, y, z))
|
||||||
|
if check == self.tree:
|
||||||
|
break
|
||||||
|
if not self.bair(check):
|
||||||
|
print('Breaking leaf')
|
||||||
|
self.g.game.break_block(check)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.state = self.clear_trunk_base
|
||||||
|
|
||||||
|
def clear_trunk_base(self):
|
||||||
|
if not self.g.breaking:
|
||||||
|
base = self.tree
|
||||||
|
above = utils.padd(self.tree, path.BLOCK_ABOVE)
|
||||||
|
|
||||||
|
if self.blog(base):
|
||||||
|
self.g.game.break_block(base)
|
||||||
|
print('breaking base')
|
||||||
|
elif self.blog(above):
|
||||||
|
self.g.game.break_block(above)
|
||||||
|
print('breaking above')
|
||||||
|
else:
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
navpath = w.path_to_place(p, self.tree)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath
|
||||||
|
self.state = self.going_to_trunk_base
|
||||||
|
else:
|
||||||
|
self.openings.pop(0)
|
||||||
|
self.state = self.choose_opening
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
def going_to_trunk_base(self):
|
||||||
|
if utils.pint(self.g.pos) == self.tree:
|
||||||
|
self.g.look_at = utils.padd(self.tree, path.BLOCK_ABOVE2)
|
||||||
|
self.state = self.clear_trunk
|
||||||
|
|
||||||
|
def clear_trunk(self):
|
||||||
|
if not self.g.breaking:
|
||||||
|
check = self.tree
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
while self.bair(check) and count < 6:
|
||||||
|
check = utils.padd(check, path.BLOCK_ABOVE)
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
if self.blog(check):
|
||||||
|
print('breaking log', check)
|
||||||
|
self.g.game.break_block(check)
|
||||||
|
else:
|
||||||
|
print('Finished clearing tree')
|
||||||
|
self.wait_time = 0.5
|
||||||
|
self.state = self.wait
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
# wait for the last log to fall
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.g.chopped_tree = self.type
|
||||||
|
self.good_trees.append(self.tree)
|
||||||
|
self.state = self.check_pos
|
||||||
|
|
||||||
|
def check_pos(self):
|
||||||
|
# make sure we are at base of trunk
|
||||||
|
# doesn't always happen, for some reason
|
||||||
|
if utils.pint(self.g.pos) == self.tree:
|
||||||
|
self.state = self.cleanup
|
||||||
|
else:
|
||||||
|
self.state = self.find_openings
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.tree = None
|
||||||
|
self.type = None
|
||||||
|
self.openings = []
|
||||||
|
self.bad_trees = []
|
||||||
|
self.good_trees = []
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
89
mosfet/jobs/grab_sand.py
Normal file
89
mosfet/jobs/grab_sand.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class GrabSandStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.state = self.find_sand
|
||||||
|
print('Trying to grab sand')
|
||||||
|
|
||||||
|
def find_sand(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
sand = w.find_objects([items.SAND_ID])
|
||||||
|
|
||||||
|
if not sand:
|
||||||
|
print('No sand objects found, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
sand.sort(key=lambda s: utils.phyp(p, (s.x, s.y, s.z)))
|
||||||
|
|
||||||
|
for s in sand:
|
||||||
|
s_pos = utils.pint((s.x, s.y, s.z))
|
||||||
|
check = utils.padd(s_pos, path.BLOCK_BELOW)
|
||||||
|
|
||||||
|
if utils.phyp(p, s_pos) > 6:
|
||||||
|
continue
|
||||||
|
# skip if the sand is floating
|
||||||
|
if self.g.chunks.get_block_at(*check) in {0}:
|
||||||
|
continue
|
||||||
|
if s.entity_id in self.eid_blacklist:
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.eid_blacklist.append(s.entity_id)
|
||||||
|
|
||||||
|
navpath = w.path_to_place(p, s_pos)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath
|
||||||
|
self.state = self.going_to_sand
|
||||||
|
self.sand = s_pos
|
||||||
|
print('Going to sand', self.sand)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print('Cant get to sand', self.sand)
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
print('Cant get to any more sand, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def going_to_sand(self):
|
||||||
|
if utils.pint(self.g.pos) == self.sand:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.sand = None
|
||||||
|
self.eid_blacklist = []
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
85
mosfet/jobs/grab_sapling.py
Normal file
85
mosfet/jobs/grab_sapling.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class GrabSaplingStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.state = self.find_saplings
|
||||||
|
print('Trying to grab a sapling')
|
||||||
|
|
||||||
|
def find_saplings(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
saplings = w.find_objects(items.SAPLING_IDS)
|
||||||
|
|
||||||
|
if not saplings:
|
||||||
|
print('No sapling objects found, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
saplings.sort(key=lambda s: utils.phyp(p, (s.x, s.y, s.z)))
|
||||||
|
|
||||||
|
for s in saplings:
|
||||||
|
s_pos = utils.pint((s.x, s.y, s.z))
|
||||||
|
|
||||||
|
check = utils.padd(s_pos, path.BLOCK_BELOW)
|
||||||
|
|
||||||
|
if s.entity_id in self.eid_blacklist:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# skip if the sapling is floating
|
||||||
|
if self.g.chunks.get_block_at(*check) in blocks.LEAF_IDS | {0}:
|
||||||
|
continue
|
||||||
|
|
||||||
|
navpath = w.path_to_place(p, s_pos)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath
|
||||||
|
self.state = self.going_to_sapling
|
||||||
|
self.sapling = s_pos
|
||||||
|
self.eid_blacklist.append(s.entity_id)
|
||||||
|
print('Going to sapling', self.sapling)
|
||||||
|
return
|
||||||
|
|
||||||
|
print('Cant get to any more saplings, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def going_to_sapling(self):
|
||||||
|
if utils.pint(self.g.pos) == self.sapling:
|
||||||
|
self.state = self.find_saplings
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.sapling = None
|
||||||
|
self.eid_blacklist = []
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
236
mosfet/jobs/grab_supplies.py
Normal file
236
mosfet/jobs/grab_supplies.py
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class GrabSuppliesStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
print('Started grab supplies states')
|
||||||
|
self.checked_barrels = []
|
||||||
|
|
||||||
|
used_slots = self.g.game.count_inventory_slots()
|
||||||
|
print('used:', used_slots, 'total:', self.g.maximum_supply_slots)
|
||||||
|
if used_slots >= self.g.maximum_supply_slots:
|
||||||
|
print('Inventory is too full, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.supplies:
|
||||||
|
self.state = self.check_supplies
|
||||||
|
else:
|
||||||
|
print('Aborting getting supplies, none specified')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def check_supplies(self):
|
||||||
|
# check if we need to grab anything
|
||||||
|
for items, limits in self.supplies.items():
|
||||||
|
minimum, maximum = limits
|
||||||
|
print('Checking items:', items)
|
||||||
|
num_items = self.g.game.count_items(items)
|
||||||
|
print('Have:', num_items)
|
||||||
|
|
||||||
|
if num_items >= minimum:
|
||||||
|
print('Have enough, skipping')
|
||||||
|
continue
|
||||||
|
|
||||||
|
print('Need at least one item')
|
||||||
|
self.state = self.find_barrels
|
||||||
|
return
|
||||||
|
|
||||||
|
print('Aborting, dont need any supplies')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def find_barrels(self):
|
||||||
|
print('Finding barrels...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
self.barrels = w.find_blocks_indexed(p, blocks.BARREL_IDS, 80)
|
||||||
|
print('Found:', self.barrels)
|
||||||
|
self.state = self.choose_barrel
|
||||||
|
|
||||||
|
def choose_barrel(self):
|
||||||
|
print('Choosing a barrel...')
|
||||||
|
for barrel in self.barrels:
|
||||||
|
if barrel in self.checked_barrels:
|
||||||
|
continue
|
||||||
|
if barrel in self.bad_barrels:
|
||||||
|
continue
|
||||||
|
|
||||||
|
print('Chose:', barrel)
|
||||||
|
self.barrel = barrel
|
||||||
|
self.state = self.path_to_barrel
|
||||||
|
return
|
||||||
|
|
||||||
|
print('No barrels')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def path_to_barrel(self):
|
||||||
|
print('Finding path to barrel')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
c = self.g.chunks
|
||||||
|
|
||||||
|
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(self.barrel)
|
||||||
|
self.area = self.barrel
|
||||||
|
self.state = self.going_to_barrel
|
||||||
|
self.checked_supplies = []
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
print('No path, blacklisting 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):
|
||||||
|
if utils.pint(self.g.pos) == self.opening:
|
||||||
|
self.g.look_at = self.area
|
||||||
|
self.state = self.open_barrel
|
||||||
|
|
||||||
|
def open_barrel(self):
|
||||||
|
print('Opening barrel')
|
||||||
|
self.g.game.open_container(self.area)
|
||||||
|
self.wait_time = 1
|
||||||
|
self.state = self.wait
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
# wait for server to send us inventory contents
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.choose_items
|
||||||
|
|
||||||
|
def choose_items(self):
|
||||||
|
print('Selecting next item')
|
||||||
|
for items, limits in self.supplies.items():
|
||||||
|
minimum_items, maximum_stacks = limits
|
||||||
|
print('Checking items:', items)
|
||||||
|
num_items = self.g.game.count_items(items)
|
||||||
|
print('Have:', num_items)
|
||||||
|
|
||||||
|
if num_items >= minimum_items:
|
||||||
|
print('Have enough, skipping')
|
||||||
|
continue
|
||||||
|
|
||||||
|
if items in self.checked_supplies:
|
||||||
|
print('Already checked, skipping')
|
||||||
|
continue
|
||||||
|
|
||||||
|
print('Need some')
|
||||||
|
self.checked_supplies.append(items)
|
||||||
|
self.target_items = items
|
||||||
|
self.maximum_stacks = maximum_stacks
|
||||||
|
self.count = 0
|
||||||
|
self.state = self.grab_items
|
||||||
|
return
|
||||||
|
|
||||||
|
print('Aborting, dont need any more supplies')
|
||||||
|
self.state = self.close_barrel
|
||||||
|
|
||||||
|
def grab_items(self):
|
||||||
|
if self.g.item_lock: return
|
||||||
|
w = self.g.window
|
||||||
|
|
||||||
|
if not w:
|
||||||
|
print('Didnt get a window, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
if w.data.window_type != mcdata.SINGLE_CHEST:
|
||||||
|
print('Got wrong window, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
used_slots = self.g.game.count_inventory_slots()
|
||||||
|
print('used:', used_slots, 'total:', self.g.maximum_supply_slots)
|
||||||
|
if used_slots >= self.g.maximum_supply_slots:
|
||||||
|
print('Inventory is too full, aborting')
|
||||||
|
self.g.game.close_window()
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
w_info = mcdata.WINDOWS[w.data.window_type]
|
||||||
|
w_container_slots = w_info.container
|
||||||
|
|
||||||
|
for slot_num in w_container_slots:
|
||||||
|
if slot_num not in w.contents:
|
||||||
|
continue
|
||||||
|
|
||||||
|
slot = w.contents[slot_num]
|
||||||
|
|
||||||
|
if not slot.present:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if slot.item_id not in self.target_items:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.maximum_stacks and self.count >= self.maximum_stacks:
|
||||||
|
break
|
||||||
|
self.count += 1
|
||||||
|
|
||||||
|
print('Moving', slot)
|
||||||
|
|
||||||
|
self.g.item_lock = True
|
||||||
|
self.g.game.click_window(slot_num, 0, 1, slot)
|
||||||
|
return
|
||||||
|
|
||||||
|
print('None left to move')
|
||||||
|
self.wait_time = 0.25
|
||||||
|
self.state = self.wait
|
||||||
|
|
||||||
|
def close_barrel(self):
|
||||||
|
print('Closing barrel')
|
||||||
|
self.g.game.close_window()
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.choose_barrel
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.supplies = {}
|
||||||
|
self.barrels = []
|
||||||
|
self.checked_barrels = []
|
||||||
|
self.bad_barrels = []
|
||||||
|
self.barrel = None
|
||||||
|
self.checked_supplies = []
|
||||||
|
self.target_items = None
|
||||||
|
self.maximum_stacks = 0
|
||||||
|
self.count = 0
|
||||||
|
self.area = None
|
||||||
|
self.opening = None
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
110
mosfet/jobs/plant_tree.py
Normal file
110
mosfet/jobs/plant_tree.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class PlantTreeStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
if self.g.chopped_tree:
|
||||||
|
self.state = self.check_feet
|
||||||
|
else:
|
||||||
|
print('Aborting planting, did not plant')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def check_feet(self):
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
# check for air at feet
|
||||||
|
if self.g.chunks.get_block_at(*p) in [0]:
|
||||||
|
self.state = self.select_sapling
|
||||||
|
else:
|
||||||
|
print('Aborting planting, feet not air')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def select_sapling(self):
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
sapling_type = self.g.chopped_tree + '_sapling'
|
||||||
|
sapling_item = items.get_id(sapling_type)
|
||||||
|
|
||||||
|
if self.g.game.select_item([sapling_item]):
|
||||||
|
self.g.look_at = utils.padd(p, path.BLOCK_BELOW)
|
||||||
|
self.state = self.wait_select
|
||||||
|
self.wait_time = 1
|
||||||
|
else:
|
||||||
|
print('Aborting planting, no', sapling_type)
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def wait_select(self):
|
||||||
|
# wait a bit to look down
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.place_sapling
|
||||||
|
|
||||||
|
def place_sapling(self):
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
self.g.game.place_block(p, BlockFace.TOP)
|
||||||
|
print('Placed sapling')
|
||||||
|
self.state = self.wait_place
|
||||||
|
self.wait_time = 1
|
||||||
|
|
||||||
|
def wait_place(self):
|
||||||
|
# wait a bit for chunk data to update
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.find_open_spot
|
||||||
|
|
||||||
|
def find_open_spot(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
for opening in w.find_tree_openings(p)[::-1]:
|
||||||
|
print('trying sapling opening', opening)
|
||||||
|
navpath = w.path_to_place(p, opening)
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath
|
||||||
|
self.area = opening
|
||||||
|
self.state = self.going_to_area
|
||||||
|
return
|
||||||
|
else: # for
|
||||||
|
print('cant escape sapling')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def going_to_area(self):
|
||||||
|
if utils.pint(self.g.pos) == self.area:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.wait_time = 0
|
||||||
|
self.area = None
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
234
mosfet/jobs/sell_to_villager.py
Normal file
234
mosfet/jobs/sell_to_villager.py
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class SellToVillagerStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.trade = None
|
||||||
|
self.state = self.find_villager
|
||||||
|
|
||||||
|
def find_villager(self):
|
||||||
|
print('Finding new villager...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
for v in w.find_villagers(p, 100):
|
||||||
|
print('Found villager:', v)
|
||||||
|
|
||||||
|
if v 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.')
|
||||||
|
self.spent_villagers = []
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
self.villager = v
|
||||||
|
self.villager_pos = utils.pint((v.x, v.y, v.z))
|
||||||
|
self.state = self.find_openings
|
||||||
|
|
||||||
|
def find_openings(self):
|
||||||
|
w = self.g.world
|
||||||
|
self.openings = w.find_villager_openings(self.villager_pos)
|
||||||
|
self.state = self.choose_opening
|
||||||
|
|
||||||
|
def choose_opening(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
print('openings:', self.openings)
|
||||||
|
|
||||||
|
if not len(self.openings):
|
||||||
|
print('Unable to get to villager:', self.villager)
|
||||||
|
if self.villager not in self.good_villagers:
|
||||||
|
self.bad_villagers.append(self.villager)
|
||||||
|
print('Added to bad villager list')
|
||||||
|
self.state = self.find_villager
|
||||||
|
return
|
||||||
|
|
||||||
|
navpath = w.path_to_place(p, self.openings[0])
|
||||||
|
|
||||||
|
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]:
|
||||||
|
print('Arrived at villager')
|
||||||
|
self.g.look_at = self.villager_pos
|
||||||
|
self.wait_time = 0.5
|
||||||
|
self.state = self.wait_to_interact
|
||||||
|
|
||||||
|
def wait_to_interact(self):
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.interact_villager
|
||||||
|
|
||||||
|
def interact_villager(self):
|
||||||
|
print('Interacting with villager')
|
||||||
|
self.g.game.interact(self.villager.entity_id)
|
||||||
|
self.g.game.animate()
|
||||||
|
self.wait_time = 0.5
|
||||||
|
self.state = self.wait_for_window
|
||||||
|
|
||||||
|
def wait_for_window(self):
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.choose_trade
|
||||||
|
|
||||||
|
def choose_trade(self):
|
||||||
|
g = self.g.game
|
||||||
|
w = self.g.window
|
||||||
|
|
||||||
|
if not w or not self.g.trades:
|
||||||
|
print('Didnt get a trade window, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
if w.data.window_type != mcdata.VILLAGER_TRADE:
|
||||||
|
print('Got wrong window, aborting')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
for trade_num, trade in enumerate(self.g.trades):
|
||||||
|
in_id = trade.input_item_1.item_id
|
||||||
|
in_num = trade.input_item_1.item_count
|
||||||
|
out_id = trade.output_item.item_id
|
||||||
|
|
||||||
|
price = in_num \
|
||||||
|
+ floor(in_num * trade.price_multiplier * trade.demand) \
|
||||||
|
+ trade.special_price
|
||||||
|
if price < 1: price = 1
|
||||||
|
|
||||||
|
if g.count_items([in_id]) < price:
|
||||||
|
continue
|
||||||
|
|
||||||
|
print('Checking trade #', trade_num)
|
||||||
|
if trade.trade_disabled:
|
||||||
|
print('Trade disabled, skipping')
|
||||||
|
continue
|
||||||
|
if out_id != items.EMERALD_ID:
|
||||||
|
print('Not for emeralds, skipping')
|
||||||
|
continue
|
||||||
|
if price > in_num:
|
||||||
|
print('Trade in demand, skipping')
|
||||||
|
continue
|
||||||
|
|
||||||
|
print('Found trade:', trade)
|
||||||
|
print('Adjusted price:', price)
|
||||||
|
self.trade = trade
|
||||||
|
self.trade_num = trade_num
|
||||||
|
self.state = self.click_trade
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
print('Villager has been spent, aborting')
|
||||||
|
self.g.game.close_window()
|
||||||
|
self.spent_villagers.append(self.villager)
|
||||||
|
self.state = self.find_villager
|
||||||
|
return
|
||||||
|
|
||||||
|
def click_trade(self):
|
||||||
|
print('Clicking trade')
|
||||||
|
self.g.item_lock = True
|
||||||
|
self.g.game.select_trade(self.trade_num)
|
||||||
|
self.state = self.execute_trade
|
||||||
|
|
||||||
|
def execute_trade(self):
|
||||||
|
if self.g.item_lock:
|
||||||
|
return
|
||||||
|
|
||||||
|
w = self.g.window
|
||||||
|
w_info = mcdata.WINDOWS[w.data.window_type]
|
||||||
|
slot_num = w_info.output
|
||||||
|
|
||||||
|
if slot_num in w.contents:
|
||||||
|
print('Executing trade')
|
||||||
|
slot = w.contents[slot_num]
|
||||||
|
self.g.game.click_window(slot_num, 0, 1, slot)
|
||||||
|
else:
|
||||||
|
print('Bad trade, aborting')
|
||||||
|
|
||||||
|
self.state = self.end_trade
|
||||||
|
|
||||||
|
def end_trade(self):
|
||||||
|
print('Trading complete')
|
||||||
|
self.g.game.close_window()
|
||||||
|
self.wait_time = 1
|
||||||
|
self.state = self.wait_for_trade
|
||||||
|
|
||||||
|
def wait_for_trade(self):
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.interact_villager
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.villager = None
|
||||||
|
self.villager_pos = None
|
||||||
|
self.bad_villagers = []
|
||||||
|
self.good_villagers = []
|
||||||
|
self.spent_villagers = []
|
||||||
|
self.openings = []
|
||||||
|
self.trade = None
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
229
mosfet/jobs/sleep_with_bed.py
Normal file
229
mosfet/jobs/sleep_with_bed.py
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
import re
|
||||||
|
import time
|
||||||
|
import importlib
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from math import floor
|
||||||
|
|
||||||
|
from minecraft.networking.types import BlockFace
|
||||||
|
|
||||||
|
from mosfet.protocol.managers import ChunkNotLoadedException
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import items
|
||||||
|
from mosfet.info import mcdata
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class SleepWithBedStates:
|
||||||
|
def idle(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
if self.g.time < 12000:
|
||||||
|
print('Aborting sleep, not night')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.g.dimension != 'overworld':
|
||||||
|
print('Aborting sleep, not in overworld')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
self.state = self.find_beds
|
||||||
|
|
||||||
|
def find_beds(self):
|
||||||
|
print('Finding beds...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
result = w.find_blocks_indexed(p, blocks.BED_IDS, 80)
|
||||||
|
|
||||||
|
self.beds = []
|
||||||
|
for bed in result:
|
||||||
|
if bed in self.bad_beds:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if w.check_bed_occupied(bed):
|
||||||
|
continue
|
||||||
|
|
||||||
|
self.beds.append(bed)
|
||||||
|
|
||||||
|
print('Found:', self.beds)
|
||||||
|
self.state = self.choose_bed
|
||||||
|
|
||||||
|
def choose_bed(self):
|
||||||
|
print('Choosing a bed...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
c = self.g.chunks
|
||||||
|
|
||||||
|
if not len(self.beds):
|
||||||
|
print('No beds')
|
||||||
|
self.state = self.select_bed
|
||||||
|
return
|
||||||
|
|
||||||
|
bed = self.beds[0]
|
||||||
|
print('Chose:', bed)
|
||||||
|
|
||||||
|
navpath = w.path_to_place_faked(p, bed)
|
||||||
|
print('navpath:', navpath)
|
||||||
|
|
||||||
|
if navpath:
|
||||||
|
self.g.path = navpath[:-1]
|
||||||
|
self.opening = self.g.path[-1]
|
||||||
|
self.g.look_at = self.opening
|
||||||
|
self.area = bed
|
||||||
|
self.state = self.going_to_bed
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.beds.pop(0)
|
||||||
|
self.bad_beds.append(bed)
|
||||||
|
print('Cant get to bed, blacklisting')
|
||||||
|
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
|
||||||
|
|
||||||
|
def select_bed(self):
|
||||||
|
if self.g.game.select_item(items.BED_IDS):
|
||||||
|
self.state = self.find_bed_spot
|
||||||
|
else:
|
||||||
|
print('No bed, aborting.')
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def find_bed_spot(self):
|
||||||
|
print('Finding a bed spot...')
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
|
||||||
|
for area in w.find_bed_areas(p, 100):
|
||||||
|
print('Found area:', area)
|
||||||
|
if area not in self.bad_areas:
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
print('Unable to find area')
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
self.area = area
|
||||||
|
openings = w.find_bed_openings(self.area)
|
||||||
|
|
||||||
|
for o in openings:
|
||||||
|
navpath = w.path_to_place(p, o)
|
||||||
|
self.opening = o
|
||||||
|
if navpath: break
|
||||||
|
else: # for
|
||||||
|
print('Unable to get to bed area', self.area)
|
||||||
|
self.bad_areas.append(self.area)
|
||||||
|
self.state = self.cleanup
|
||||||
|
return
|
||||||
|
|
||||||
|
self.g.path = navpath
|
||||||
|
self.g.look_at = self.opening
|
||||||
|
self.state = self.going_to_area
|
||||||
|
|
||||||
|
def going_to_area(self):
|
||||||
|
if utils.pint(self.g.pos) == self.opening:
|
||||||
|
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):
|
||||||
|
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')
|
||||||
|
self.state = self.sleep_bed
|
||||||
|
|
||||||
|
def sleep_bed(self):
|
||||||
|
w = self.g.world
|
||||||
|
p = utils.pint(self.g.pos)
|
||||||
|
threats = w.find_threats(p, 10)
|
||||||
|
|
||||||
|
if threats:
|
||||||
|
print('Waking up due to threats:')
|
||||||
|
print(threats)
|
||||||
|
self.g.game.leave_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:
|
||||||
|
self.g.game.break_block(self.area)
|
||||||
|
self.state = self.collect_bed
|
||||||
|
else:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def collect_bed(self):
|
||||||
|
if not self.g.breaking:
|
||||||
|
self.g.path = [utils.padd(self.area, utils.spiral(n)) for n in range(9)]
|
||||||
|
self.wait_time = 2
|
||||||
|
self.state = self.wait
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
# wait to pick up bed
|
||||||
|
if self.wait_time > 0:
|
||||||
|
self.wait_time -= utils.TICK
|
||||||
|
else:
|
||||||
|
self.state = self.cleanup
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.g.look_at = None
|
||||||
|
self.state = self.done
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
# never gets ran, placeholder
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
self.state = self.idle
|
||||||
|
|
||||||
|
self.silent = False
|
||||||
|
|
||||||
|
self.my_bed = False
|
||||||
|
self.beds = []
|
||||||
|
self.bad_beds = []
|
||||||
|
self.area = None
|
||||||
|
self.opening = None
|
||||||
|
self.bad_areas = []
|
||||||
|
self.wait_time = 0
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.state()
|
@@ -1,5 +1,5 @@
|
|||||||
import minecraft.networking.packets
|
import minecraft.networking.packets
|
||||||
from protocol import packets
|
from .protocol import packets
|
||||||
|
|
||||||
def get_packets(old_get_packets):
|
def get_packets(old_get_packets):
|
||||||
def wrapper(func, context):
|
def wrapper(func, context):
|
||||||
@@ -20,6 +20,7 @@ def get_packets(old_get_packets):
|
|||||||
mc_packets.add(packets.EntityTeleport)
|
mc_packets.add(packets.EntityTeleport)
|
||||||
mc_packets.add(packets.TradeListPacket)
|
mc_packets.add(packets.TradeListPacket)
|
||||||
mc_packets.add(packets.DisconnectPacket)
|
mc_packets.add(packets.DisconnectPacket)
|
||||||
|
mc_packets.add(packets.UnloadChunkPacket)
|
||||||
|
|
||||||
|
|
||||||
return mc_packets
|
return mc_packets
|
@@ -1,14 +1,11 @@
|
|||||||
import importlib
|
import importlib
|
||||||
import functools
|
import functools
|
||||||
import time
|
import time
|
||||||
from math import hypot, sqrt
|
|
||||||
|
|
||||||
from astar import AStar
|
from astar import AStar
|
||||||
|
|
||||||
import blocks
|
from mosfet.info import blocks
|
||||||
importlib.reload(blocks)
|
from mosfet import utils
|
||||||
import utils
|
|
||||||
importlib.reload(utils)
|
|
||||||
|
|
||||||
class AStarTimeout(Exception):
|
class AStarTimeout(Exception):
|
||||||
pass
|
pass
|
||||||
@@ -308,7 +305,7 @@ class Pathfinder(AStar):
|
|||||||
def distance_between(self, n1, n2):
|
def distance_between(self, n1, n2):
|
||||||
(x1, y1, z1) = n1
|
(x1, y1, z1) = n1
|
||||||
(x2, y2, z2) = n2
|
(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):
|
def heuristic_cost_estimate(self, n1, n2):
|
||||||
(x1, y1, z1) = n1
|
(x1, y1, z1) = n1
|
16
mosfet/print_help.py
Normal file
16
mosfet/print_help.py
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
HELP_LINES = []
|
||||||
|
|
||||||
|
with open('mosfet/commands.py', 'r') as f:
|
||||||
|
for line in f.readlines():
|
||||||
|
if line.strip().startswith('## '):
|
||||||
|
HELP_LINES.append(line.strip()[3:])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
for line in HELP_LINES:
|
||||||
|
if ' - ' in line:
|
||||||
|
command, doc = line.split(' - ')
|
||||||
|
print('`{}` - {}\n'.format(command, doc))
|
||||||
|
else:
|
||||||
|
print(line)
|
||||||
|
print()
|
0
mosfet/protocol/__init__.py
Normal file
0
mosfet/protocol/__init__.py
Normal file
@@ -4,9 +4,10 @@ import json
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from minecraft.networking.packets import clientbound, serverbound
|
from minecraft.networking.packets import clientbound, serverbound
|
||||||
from protocol import packets
|
from mosfet.protocol import packets
|
||||||
|
|
||||||
import utils
|
from mosfet import utils
|
||||||
|
from mosfet.info import blocks
|
||||||
|
|
||||||
class DataManager:
|
class DataManager:
|
||||||
def __init__(self, directory):
|
def __init__(self, directory):
|
||||||
@@ -42,7 +43,7 @@ class ChunksManager:
|
|||||||
def __init__(self, data_manager):
|
def __init__(self, data_manager):
|
||||||
self.data = data_manager
|
self.data = data_manager
|
||||||
self.chunks = {}
|
self.chunks = {}
|
||||||
self.biomes = {}
|
#self.biomes = {}
|
||||||
self.index = {}
|
self.index = {}
|
||||||
self.loading = False
|
self.loading = False
|
||||||
|
|
||||||
@@ -68,13 +69,12 @@ class ChunksManager:
|
|||||||
|
|
||||||
for item_id, locations in chunk.sub_index.items():
|
for item_id, locations in chunk.sub_index.items():
|
||||||
if item_id not in self.index:
|
if item_id not in self.index:
|
||||||
self.index[item_id] = []
|
self.index[item_id] = set()
|
||||||
|
|
||||||
for l in locations:
|
for l in locations:
|
||||||
coords = (dx + l%16, dy + l//256, dz + l%256//16)
|
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:
|
if self.loading:
|
||||||
print('.', end='', flush=True)
|
print('.', end='', flush=True)
|
||||||
|
|
||||||
@@ -116,28 +116,26 @@ class ChunksManager:
|
|||||||
if not c: return None
|
if not c: return None
|
||||||
c.set_block_at(x%16, y%16, z%16, block)
|
c.set_block_at(x%16, y%16, z%16, block)
|
||||||
|
|
||||||
def check_loaded(self, position, steps):
|
if block in blocks.INDEXED_IDS:
|
||||||
x, y, z = utils.pint(position)
|
if block not in self.index:
|
||||||
player_chunk = (x//16, 1, z//16)
|
self.index[block] = set()
|
||||||
for i in range(steps): # TODO: base off render_distance?
|
self.index[block].add((x, y, z))
|
||||||
offset = utils.spiral(i)
|
|
||||||
check = utils.padd(player_chunk, offset)
|
|
||||||
|
|
||||||
if check not in self.chunks:
|
def check_loaded(self, chunk_distance):
|
||||||
return False
|
num = (chunk_distance * 2 + 1) ** 2
|
||||||
|
num_subchunks = num * 16
|
||||||
|
return len(self.chunks) >= num_subchunks
|
||||||
|
|
||||||
return True
|
def unload_chunk(self, x, z):
|
||||||
|
for y in range(16):
|
||||||
def unload_chunks(self, position):
|
try:
|
||||||
x, y, z = utils.pint(position)
|
del self.chunks[(x, y, z)]
|
||||||
player_chunk = (x//16, 0, z//16)
|
except KeyError:
|
||||||
loaded_chunks = list(self.chunks.keys())
|
pass
|
||||||
|
|
||||||
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_all_chunks(self):
|
||||||
|
self.chunks = {}
|
||||||
|
self.index = {}
|
||||||
|
|
||||||
class ChunkNotLoadedException(Exception):
|
class ChunkNotLoadedException(Exception):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@@ -155,24 +153,25 @@ class ChatManager:
|
|||||||
def translate_chat(self, data):
|
def translate_chat(self, data):
|
||||||
if isinstance(data, str):
|
if isinstance(data, str):
|
||||||
return data
|
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:
|
elif 'extra' in data:
|
||||||
return ''.join([self.translate_chat(x) for x in data['extra']])
|
result += ''.join([self.translate_chat(x) for x in data['extra']])
|
||||||
elif 'text' in data:
|
|
||||||
return data['text']
|
return result
|
||||||
elif 'with' in data:
|
|
||||||
if len(data['with']) >= 2:
|
|
||||||
return '<{}> {}'.format(*[self.translate_chat(x) for x in data['with']])
|
|
||||||
else:
|
|
||||||
return self.translate_chat(data['with'][0])
|
|
||||||
elif 'translate' in data:
|
|
||||||
return data['translate']
|
|
||||||
else:
|
|
||||||
print(data)
|
|
||||||
return '?'
|
|
||||||
|
|
||||||
def print_chat(self, chat_packet):
|
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:
|
try:
|
||||||
source = chat_packet.field_string('position')
|
source = chat_packet.field_string('position')
|
||||||
|
sender = chat_packet.sender
|
||||||
text = self.translate_chat(json.loads(chat_packet.json_data))
|
text = self.translate_chat(json.loads(chat_packet.json_data))
|
||||||
print('[%s] %s'%(source, text))
|
print('[%s] %s'%(source, text))
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
@@ -180,7 +179,7 @@ class ChatManager:
|
|||||||
return
|
return
|
||||||
|
|
||||||
if self.handler:
|
if self.handler:
|
||||||
self.handler((source, text))
|
self.handler((source, sender, text))
|
||||||
|
|
||||||
def set_handler(self, func):
|
def set_handler(self, func):
|
||||||
self.handler = func
|
self.handler = func
|
@@ -8,9 +8,9 @@ from minecraft.networking.types import (
|
|||||||
Float, Direction, PositionAndLook
|
Float, Direction, PositionAndLook
|
||||||
)
|
)
|
||||||
|
|
||||||
from protocol.types import Nbt, Slot, Entry, Trade
|
from .types import Nbt, Slot, Entry, Trade
|
||||||
|
|
||||||
import blocks
|
from mosfet.info import blocks
|
||||||
|
|
||||||
|
|
||||||
class ChunkDataPacket(Packet):
|
class ChunkDataPacket(Packet):
|
||||||
@@ -31,10 +31,10 @@ class ChunkDataPacket(Packet):
|
|||||||
self.biomes.append(VarInt.read(file_object))
|
self.biomes.append(VarInt.read(file_object))
|
||||||
size = VarInt.read(file_object)
|
size = VarInt.read(file_object)
|
||||||
self.data = file_object.read(size)
|
self.data = file_object.read(size)
|
||||||
size_entities = VarInt.read(file_object)
|
#size_entities = VarInt.read(file_object)
|
||||||
self.entities = []
|
#self.entities = []
|
||||||
for i in range(size_entities):
|
#for i in range(size_entities):
|
||||||
self.entities.append(Nbt.read(file_object))
|
# self.entities.append(Nbt.read(file_object))
|
||||||
|
|
||||||
self.decode_chunk_data()
|
self.decode_chunk_data()
|
||||||
|
|
||||||
@@ -49,9 +49,9 @@ class ChunkDataPacket(Packet):
|
|||||||
Integer.send(self.biomes[i], packet_buffer)
|
Integer.send(self.biomes[i], packet_buffer)
|
||||||
VarInt.send(len(self.data), packet_buffer)
|
VarInt.send(len(self.data), packet_buffer)
|
||||||
packet_buffer.send(self.data)
|
packet_buffer.send(self.data)
|
||||||
VarInt.send(len(self.entities), packet_buffer)
|
#VarInt.send(len(self.entities), packet_buffer)
|
||||||
for e in self.entities:
|
#for e in self.entities:
|
||||||
Nbt.send(e, packet_buffer)
|
# Nbt.send(e, packet_buffer)
|
||||||
|
|
||||||
def decode_chunk_data(self):
|
def decode_chunk_data(self):
|
||||||
packet_data = PacketBuffer()
|
packet_data = PacketBuffer()
|
||||||
@@ -64,9 +64,9 @@ class ChunkDataPacket(Packet):
|
|||||||
if self.bit_mask_y & (1 << i):
|
if self.bit_mask_y & (1 << i):
|
||||||
self.chunks[i].read(packet_data)
|
self.chunks[i].read(packet_data)
|
||||||
|
|
||||||
for e in self.entities:
|
#for e in self.entities:
|
||||||
y = e['y']
|
# y = e['y']
|
||||||
self.chunks[floor(y/16)].entities.append(e)
|
# self.chunks[floor(y/16)].entities.append(e)
|
||||||
|
|
||||||
class Chunk:
|
class Chunk:
|
||||||
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
position = multi_attribute_alias(Vector, 'x', 'y', 'z')
|
||||||
@@ -304,7 +304,7 @@ class EntityMetadataPacket(Packet):
|
|||||||
self.metadata = []
|
self.metadata = []
|
||||||
for _ in range(99):
|
for _ in range(99):
|
||||||
entry = Entry.read(file_object, self.context)
|
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)
|
self.metadata.append(entry)
|
||||||
|
|
||||||
|
|
||||||
@@ -458,3 +458,15 @@ class DisconnectPacket(Packet):
|
|||||||
definition = [
|
definition = [
|
||||||
{'reason': String},
|
{'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):
|
class Entry(Type):
|
||||||
types = {
|
simple_types = {
|
||||||
0: Byte,
|
0: Byte,
|
||||||
1: VarInt,
|
1: VarInt,
|
||||||
2: Float,
|
2: Float,
|
||||||
@@ -169,12 +169,19 @@ class Entry(Type):
|
|||||||
index = UnsignedByte.read(file_object)
|
index = UnsignedByte.read(file_object)
|
||||||
if index == 0xff: return None
|
if index == 0xff: return None
|
||||||
type = VarInt.read(file_object)
|
type = VarInt.read(file_object)
|
||||||
try:
|
|
||||||
value = Entry.types[type].read(file_object)
|
if type == 10: # optional position
|
||||||
except TypeError:
|
present = Boolean.read(file_object)
|
||||||
value = Entry.types[type].read_with_context(file_object, context)
|
value = Position.read_with_context(file_object, context) if present else None
|
||||||
except KeyError:
|
elif type == 16: # villager data
|
||||||
return None
|
value = (VarInt.read(file_object), VarInt.read(file_object), VarInt.read(file_object))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
value = Entry.simple_types[type].read(file_object)
|
||||||
|
except TypeError:
|
||||||
|
value = Entry.simple_types[type].read_with_context(file_object, context)
|
||||||
|
except KeyError:
|
||||||
|
return None # unimplemented data type, stops parsing entries
|
||||||
return Entry(index, type, value)
|
return Entry(index, type, value)
|
||||||
|
|
||||||
|
|
@@ -1,14 +1,16 @@
|
|||||||
import importlib
|
import importlib
|
||||||
import collections
|
import collections
|
||||||
from math import floor, ceil, sqrt, hypot
|
from math import floor, ceil, sqrt
|
||||||
|
|
||||||
import blocks
|
from mosfet.info import blocks
|
||||||
importlib.reload(blocks)
|
from mosfet.info import mcdata
|
||||||
import mcdata
|
|
||||||
importlib.reload(mcdata)
|
|
||||||
|
|
||||||
TICK = 0.05
|
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):
|
def padd(p1, p2):
|
||||||
return (p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2])
|
return (p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2])
|
||||||
|
|
||||||
@@ -135,49 +137,32 @@ def search_2d(distance=0):
|
|||||||
visited.add(cur)
|
visited.add(cur)
|
||||||
yield cur
|
yield cur
|
||||||
|
|
||||||
def search_3d(distance=0, y_limit=0):
|
def get_neighbors_3d(x,y,z):
|
||||||
def get_neighbors(x,y,z):
|
return [
|
||||||
return [
|
#(x+1, y+1, z+0),
|
||||||
(x+1, y+1, z+0),
|
#(x+1, y-1, z+0),
|
||||||
(x+1, y-1, z+0),
|
#(x+1, y+1, z+1),
|
||||||
(x+1, y+1, z+1),
|
#(x+1, y+0, z+1),
|
||||||
(x+1, y+0, z+1),
|
#(x+1, y-1, z+1),
|
||||||
(x+1, y-1, z+1),
|
#(x+1, y+1, z-1),
|
||||||
(x+1, y+1, z-1),
|
#(x+1, y+0, z-1),
|
||||||
(x+1, y+0, z-1),
|
#(x+1, y-1, z-1),
|
||||||
(x+1, y-1, z-1),
|
(x+1, y+0, z+0),
|
||||||
(x+1, y+0, z+0),
|
(x+0, y+1, z+0),
|
||||||
(x+0, y+1, z+0),
|
(x+0, y-1, z+0),
|
||||||
(x+0, y-1, z+0),
|
#(x+0, y+1, z+1),
|
||||||
(x+0, y+1, z+1),
|
(x+0, y+0, z+1),
|
||||||
(x+0, y+0, z+1),
|
#(x+0, y-1, z+1),
|
||||||
(x+0, y-1, z+1),
|
#(x+0, y+1, z-1),
|
||||||
(x+0, y+1, z-1),
|
(x+0, y+0, z-1),
|
||||||
(x+0, y+0, z-1),
|
#(x+0, y-1, z-1),
|
||||||
(x+0, y-1, z-1),
|
#(x-1, y+1, z+0),
|
||||||
(x-1, y+1, z+0),
|
#(x-1, y-1, z+0),
|
||||||
(x-1, y-1, z+0),
|
#(x-1, y+1, z+1),
|
||||||
(x-1, y+1, z+1),
|
#(x-1, y+0, z+1),
|
||||||
(x-1, y+0, z+1),
|
#(x-1, y-1, z+1),
|
||||||
(x-1, y-1, z+1),
|
#(x-1, y+1, z-1),
|
||||||
(x-1, y+1, z-1),
|
#(x-1, y+0, z-1),
|
||||||
(x-1, y+0, z-1),
|
#(x-1, y-1, z-1),
|
||||||
(x-1, y-1, z-1),
|
(x-1, y+0, z+0),
|
||||||
(x-1, y+0, z+0),
|
]
|
||||||
]
|
|
||||||
|
|
||||||
to_visit = collections.deque([(0, 0, 0)])
|
|
||||||
visited = set()
|
|
||||||
|
|
||||||
while to_visit:
|
|
||||||
cur = to_visit.pop()
|
|
||||||
if cur in visited:
|
|
||||||
continue
|
|
||||||
if y_limit and abs(cur[1]) > y_limit:
|
|
||||||
continue
|
|
||||||
if distance and hypot(*cur) > distance:
|
|
||||||
continue
|
|
||||||
for neighbor in get_neighbors(*cur):
|
|
||||||
to_visit.appendleft(neighbor)
|
|
||||||
visited.add(cur)
|
|
||||||
yield cur
|
|
@@ -1,4 +1,5 @@
|
|||||||
import math
|
import math
|
||||||
|
from mosfet import utils
|
||||||
|
|
||||||
class Vector3D:
|
class Vector3D:
|
||||||
def __init__(self, vector):
|
def __init__(self, vector):
|
||||||
@@ -17,7 +18,7 @@ class Vector3D:
|
|||||||
return self.tuple()[key]
|
return self.tuple()[key]
|
||||||
|
|
||||||
def length(self):
|
def length(self):
|
||||||
return math.hypot(self.x, self.y, self.z)
|
return utils.hypot(self.x, self.y, self.z)
|
||||||
|
|
||||||
def normalized(self):
|
def normalized(self):
|
||||||
x = self.x / self.length()
|
x = self.x / self.length()
|
365
mosfet/world.py
Normal file
365
mosfet/world.py
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
import collections
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import random
|
||||||
|
from itertools import count
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
from mosfet import utils
|
||||||
|
from mosfet import path
|
||||||
|
from mosfet.info import blocks
|
||||||
|
from mosfet.info import mobs
|
||||||
|
|
||||||
|
class World:
|
||||||
|
def __init__(self, global_state):
|
||||||
|
self.g = global_state
|
||||||
|
|
||||||
|
def block_at(self, x, y, z):
|
||||||
|
return self.g.chunks.get_block_at(x, y, z)
|
||||||
|
|
||||||
|
def check_air_column(self, pos, distance):
|
||||||
|
for i in range(distance):
|
||||||
|
check = utils.padd(pos, (0, i, 0))
|
||||||
|
if self.block_at(*check) not in blocks.NON_SOLID_IDS:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def find_blocks_3d(self, center, block_ids, distance=0, y_limit=0, thru_air=False):
|
||||||
|
to_visit = collections.deque([(0, 0, 0)])
|
||||||
|
visited = set()
|
||||||
|
|
||||||
|
while to_visit:
|
||||||
|
cur = to_visit.pop()
|
||||||
|
if cur in visited:
|
||||||
|
continue
|
||||||
|
if y_limit and abs(cur[1]) > y_limit:
|
||||||
|
continue
|
||||||
|
if distance and utils.hypot(*cur) > distance:
|
||||||
|
continue
|
||||||
|
|
||||||
|
check = utils.padd(center, cur)
|
||||||
|
|
||||||
|
if not thru_air or self.block_at(*check) in blocks.NON_SOLID_IDS:
|
||||||
|
for neighbor in utils.get_neighbors_3d(*cur):
|
||||||
|
to_visit.appendleft(neighbor)
|
||||||
|
|
||||||
|
visited.add(cur)
|
||||||
|
|
||||||
|
if self.block_at(*check) in block_ids:
|
||||||
|
yield check
|
||||||
|
|
||||||
|
def find_blocks_indexed(self, center, block_ids, distance=0):
|
||||||
|
print('finding', block_ids)
|
||||||
|
index = []
|
||||||
|
for bid in block_ids:
|
||||||
|
index.extend(self.g.chunks.index.get(bid, []))
|
||||||
|
|
||||||
|
print('index', index)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for block in index:
|
||||||
|
if self.block_at(*block) not in block_ids:
|
||||||
|
continue
|
||||||
|
if distance and utils.phyp(center, block) > distance:
|
||||||
|
continue
|
||||||
|
if block not in result:
|
||||||
|
result.append(block)
|
||||||
|
|
||||||
|
result.sort(key=lambda x: utils.phyp(center, x))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def find_blocks(self, center, distance, block_ids, limit=0):
|
||||||
|
# search in a spiral from center to all blocks with ID
|
||||||
|
result = []
|
||||||
|
for n in count():
|
||||||
|
offset = utils.spiral(n)
|
||||||
|
check = utils.padd(center, offset)
|
||||||
|
if self.block_at(*check) in block_ids:
|
||||||
|
if utils.hypot(*offset) < distance:
|
||||||
|
result.append(check)
|
||||||
|
if limit and len(result) == limit:
|
||||||
|
return result
|
||||||
|
if offset[0] > distance:
|
||||||
|
return result
|
||||||
|
|
||||||
|
def find_trees(self, center, distance):
|
||||||
|
found_trees = []
|
||||||
|
for log in self.find_blocks_3d(center, blocks.LOG_IDS, distance, 15):
|
||||||
|
# crawl to the bottom log
|
||||||
|
while self.block_at(*utils.padd(log, path.BLOCK_BELOW)) in blocks.LOG_IDS:
|
||||||
|
log = utils.padd(log, path.BLOCK_BELOW)
|
||||||
|
base = log
|
||||||
|
|
||||||
|
if base in found_trees:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# make sure we are on the ground
|
||||||
|
if self.block_at(*utils.padd(base, path.BLOCK_BELOW)) in blocks.NON_SOLID_IDS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# crawl to the top log to count and check leaves
|
||||||
|
log_count = 1
|
||||||
|
good_leaves = False
|
||||||
|
while self.block_at(*utils.padd(log, path.BLOCK_ABOVE)) in blocks.LOG_IDS:
|
||||||
|
log = utils.padd(log, path.BLOCK_ABOVE)
|
||||||
|
log_count += 1
|
||||||
|
|
||||||
|
for offset in path.CHECK_DIRECTIONS:
|
||||||
|
if self.block_at(*utils.padd(log, offset)) in blocks.LEAF_IDS:
|
||||||
|
good_leaves = True
|
||||||
|
|
||||||
|
# make sure it's a good tree
|
||||||
|
if not good_leaves or log_count < 3:
|
||||||
|
continue
|
||||||
|
|
||||||
|
found_trees.append(base)
|
||||||
|
|
||||||
|
yield base
|
||||||
|
|
||||||
|
def find_tree_openings(self, tree):
|
||||||
|
# returns coords in a cardinal direction where we can stand by tree
|
||||||
|
maze_solver = path.Pathfinder(self.g)
|
||||||
|
result = []
|
||||||
|
|
||||||
|
# TODO: make sure only non-solid and leaves between
|
||||||
|
# make sure traversable too and non-avoid
|
||||||
|
|
||||||
|
for distance in range(5):
|
||||||
|
for direction in path.CHECK_DIRECTIONS:
|
||||||
|
offset = utils.pmul(direction, distance+1)
|
||||||
|
if maze_solver.check_traverse(tree, offset):
|
||||||
|
result.append(utils.padd(tree, offset))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def path_to_place(self, start, place):
|
||||||
|
maze_solver = path.Pathfinder(self.g)
|
||||||
|
|
||||||
|
try:
|
||||||
|
s = maze_solver.astar(start, place)
|
||||||
|
return list(s) if s else None
|
||||||
|
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
|
||||||
|
|
||||||
|
for a in self.find_blocks_3d(center, [0], distance, 50):
|
||||||
|
# check for air around the area
|
||||||
|
if len(self.find_blocks(a, clear_distance, [0], bed_clearance)) < bed_clearance:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# check for ground around the area
|
||||||
|
if len(self.find_blocks(utils.padd(a, path.BLOCK_BELOW), clear_distance, blocks.NON_SOLID_IDS, bed_clearance)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# check for air above the area
|
||||||
|
if len(self.find_blocks(utils.padd(a, path.BLOCK_ABOVE), clear_distance, [0], bed_clearance)) < bed_clearance:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# ensure there's no monsters within 20 blocks
|
||||||
|
# can't sleep if they are within 10, good to have a buffer
|
||||||
|
if self.find_monsters(a, 20):
|
||||||
|
continue
|
||||||
|
|
||||||
|
yield a
|
||||||
|
|
||||||
|
def find_cache_areas(self, center, distance):
|
||||||
|
return self.find_bed_areas(center, distance)
|
||||||
|
|
||||||
|
def sand_adjacent_safe(self, sand):
|
||||||
|
for direction in path.CHECK_DIRECTIONS:
|
||||||
|
if self.block_at(*utils.padd(sand, direction)) in blocks.AVOID_IDS:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def find_sand(self, center, distance, player):
|
||||||
|
sand = []
|
||||||
|
sand.extend(self.find_blocks(center, distance, [blocks.SAND], 25))
|
||||||
|
|
||||||
|
safe_sand = []
|
||||||
|
for s in sand:
|
||||||
|
# make sure it has solid below
|
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_BELOW)) in blocks.NON_SOLID_IDS:
|
||||||
|
continue
|
||||||
|
# make sure it has solid two below - prevent hanging sand
|
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_BELOW2)) in blocks.NON_SOLID_IDS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# and walkable air above
|
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_ABOVE)) not in blocks.NON_SOLID_IDS:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not self.sand_adjacent_safe(s):
|
||||||
|
continue
|
||||||
|
|
||||||
|
safe_sand.append(s)
|
||||||
|
|
||||||
|
safe_sand.sort(key=lambda x: utils.phyp(player, x))
|
||||||
|
return safe_sand
|
||||||
|
|
||||||
|
def check_sand_slice(self, center):
|
||||||
|
# checks if a 5x5x1 slice has sand in it
|
||||||
|
for i in range(9):
|
||||||
|
s = utils.padd(center, utils.spiral(i))
|
||||||
|
if self.block_at(*s) != blocks.SAND:
|
||||||
|
continue
|
||||||
|
# make sure it has solid below
|
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_BELOW)) in blocks.NON_SOLID_IDS:
|
||||||
|
continue
|
||||||
|
# make sure it has solid two below - prevent hanging sand
|
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_BELOW2)) in blocks.NON_SOLID_IDS:
|
||||||
|
continue
|
||||||
|
# and walkable air above
|
||||||
|
if self.block_at(*utils.padd(s, path.BLOCK_ABOVE)) not in blocks.NON_SOLID_IDS:
|
||||||
|
continue
|
||||||
|
if not self.sand_adjacent_safe(s):
|
||||||
|
continue
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def find_sand_slice(self, center, distance, y_limit=0, bad_slices=[], prev_layer=0):
|
||||||
|
# returns the centre coord of the next 5x5x1 slice that still has
|
||||||
|
# diggable sand in it. lower slices are only valid if there's an
|
||||||
|
# adjacent slice farther at the same level. this should ensure an
|
||||||
|
# upside down pyramid gets excavated so the edges are still climbable
|
||||||
|
for v in count(prev_layer):
|
||||||
|
peak = utils.padd(center, (0, 10-v, 0))
|
||||||
|
|
||||||
|
slices = []
|
||||||
|
layer = 0
|
||||||
|
for step in count():
|
||||||
|
offset = utils.spiral(step)
|
||||||
|
layer = max(layer, *offset)
|
||||||
|
offset = utils.pmul(offset, 3)
|
||||||
|
check = utils.padd(peak, offset)
|
||||||
|
check = utils.padd(check, (0, layer, 0))
|
||||||
|
|
||||||
|
if y_limit and check[1] - center[1] > y_limit:
|
||||||
|
break
|
||||||
|
if utils.phyp_king(center, check) > distance:
|
||||||
|
break
|
||||||
|
|
||||||
|
if self.check_sand_slice(check) and check not in bad_slices:
|
||||||
|
slices.append(check)
|
||||||
|
|
||||||
|
if len(slices):
|
||||||
|
return v, slices[-1]
|
||||||
|
elif v > 40:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def find_bed_openings(self, area):
|
||||||
|
# returns coords in a cardinal direction where we can stand by bed
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for direction in path.CHECK_DIRECTIONS:
|
||||||
|
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)
|
||||||
|
|
||||||
|
def find_objects(self, object_ids):
|
||||||
|
result = []
|
||||||
|
for eid, obj in copy(self.g.objects).items():
|
||||||
|
if obj.get('item_id', None) in object_ids:
|
||||||
|
result.append(obj)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def find_leaves(self, center, distance):
|
||||||
|
for a in self.find_blocks_3d(center, blocks.LEAF_IDS, distance, 10):
|
||||||
|
yield a
|
||||||
|
|
||||||
|
def find_monsters(self, center, distance):
|
||||||
|
# finds monsters within distance
|
||||||
|
result = []
|
||||||
|
for eid, mob in copy(self.g.mobs).items():
|
||||||
|
if mob.type not in mobs.EVIL_IDS:
|
||||||
|
continue
|
||||||
|
pos = utils.pint((mob.x, mob.y, mob.z))
|
||||||
|
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):
|
||||||
|
# finds monsters on the surface within distance
|
||||||
|
monsters = self.find_monsters(center, distance)
|
||||||
|
result = []
|
||||||
|
for mob in monsters:
|
||||||
|
pos = utils.pint((mob.x, mob.y, mob.z))
|
||||||
|
# check distance number of blocks above, close enough?
|
||||||
|
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):
|
||||||
|
# finds villagers within distance
|
||||||
|
result = []
|
||||||
|
for eid, mob in copy(self.g.mobs).items():
|
||||||
|
type_name = mobs.MOB_NAMES[mob.type]
|
||||||
|
if type_name != 'villager' : continue
|
||||||
|
pos = utils.pint((mob.x, mob.y, mob.z))
|
||||||
|
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):
|
||||||
|
# returns coords in a cardinal direction where we can stand by a villager
|
||||||
|
maze_solver = path.Pathfinder(self.g)
|
||||||
|
result = []
|
||||||
|
|
||||||
|
for distance in range(3):
|
||||||
|
for direction in path.CHECK_DIRECTIONS:
|
||||||
|
offset = utils.pmul(direction, distance+1)
|
||||||
|
|
||||||
|
if not maze_solver.check_traverse(villager, offset):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# check for line of sight
|
||||||
|
for check in range(distance+1):
|
||||||
|
offset2 = utils.pmul(direction, check+1)
|
||||||
|
offset2 = utils.padd(offset2, path.BLOCK_ABOVE)
|
||||||
|
check = utils.padd(villager, offset2)
|
||||||
|
if self.block_at(*check) not in blocks.NON_SOLID_IDS:
|
||||||
|
break
|
||||||
|
else: # for
|
||||||
|
result.append(utils.padd(villager, offset))
|
||||||
|
return result
|
@@ -1,40 +1,45 @@
|
|||||||
appdirs==1.4.3
|
appdirs==1.4.4
|
||||||
astar==0.92
|
astar==0.93
|
||||||
CacheControl==0.12.6
|
CacheControl==0.12.6
|
||||||
certifi==2020.6.20
|
certifi==2020.12.5
|
||||||
cffi==1.14.2
|
cffi==1.14.5
|
||||||
chardet==3.0.4
|
chardet==4.0.0
|
||||||
click==7.1.2
|
click==7.1.2
|
||||||
colorama==0.4.3
|
colorama==0.4.4
|
||||||
contextlib2==0.6.0
|
contextlib2==0.6.0.post1
|
||||||
cryptography==3.1
|
cryptography==3.4.7
|
||||||
distlib==0.3.0
|
distlib==0.3.1
|
||||||
distro==1.4.0
|
distro==1.5.0
|
||||||
Flask==1.1.2
|
Flask==1.1.2
|
||||||
html5lib==1.0.1
|
Flask-Cors==3.0.10
|
||||||
|
html5lib==1.1
|
||||||
idna==2.10
|
idna==2.10
|
||||||
|
importlib-metadata==4.0.1
|
||||||
ipaddr==2.2.0
|
ipaddr==2.2.0
|
||||||
itsdangerous==1.1.0
|
itsdangerous==1.1.0
|
||||||
Jinja2==2.11.2
|
Jinja2==2.11.3
|
||||||
lockfile==0.12.2
|
lockfile==0.12.2
|
||||||
MarkupSafe==1.1.1
|
MarkupSafe==1.1.1
|
||||||
minecraft-data==2.70.1
|
minecraft-data==2.82.2
|
||||||
msgpack==0.6.2
|
msgpack==1.0.2
|
||||||
munch==2.5.0
|
munch==2.5.0
|
||||||
packaging==20.3
|
mutf8==1.0.3
|
||||||
panda3d==1.10.6.post2
|
packaging==20.9
|
||||||
pathtools==0.1.2
|
pep517==0.10.0
|
||||||
pep517==0.8.2
|
pkg-resources==0.0.0
|
||||||
progress==1.5
|
progress==1.5
|
||||||
pycparser==2.20
|
pycparser==2.20
|
||||||
git+https://github.com/ammaraskar/pyCraft.git@2813d02ae7fb8182c3e5227a73de2240b09878d9
|
git+https://github.com/ammaraskar/pyCraft.git@2813d02ae7fb8182c3e5227a73de2240b09878d9
|
||||||
PyNBT==3.0.0
|
PyNBT==3.1.0
|
||||||
pyparsing==2.4.6
|
pyparsing==2.4.7
|
||||||
pytoml==0.1.21
|
pytoml==0.1.21
|
||||||
requests==2.24.0
|
requests==2.25.1
|
||||||
retrying==1.3.3
|
retrying==1.3.3
|
||||||
six==1.15.0
|
six==1.15.0
|
||||||
urllib3==1.25.10
|
toml==0.10.2
|
||||||
watchdog==0.10.3
|
typing-extensions==3.7.4.3
|
||||||
|
urllib3==1.26.4
|
||||||
|
watchdog==2.0.3
|
||||||
webencodings==0.5.1
|
webencodings==0.5.1
|
||||||
Werkzeug==1.0.1
|
Werkzeug==1.0.1
|
||||||
|
zipp==3.4.1
|
||||||
|
79
run_linux.sh
Executable file
79
run_linux.sh
Executable file
@@ -0,0 +1,79 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
if [ ! -d "mosfet" ]
|
||||||
|
then
|
||||||
|
echo "You must run this from the mosfet-minecraft-bot directory."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v virtualenv &> /dev/null
|
||||||
|
then
|
||||||
|
echo "virtualenv could not be found, please install with:"
|
||||||
|
echo "sudo apt install virtualenv"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v wget &> /dev/null
|
||||||
|
then
|
||||||
|
echo "wget could not be found, please install with:"
|
||||||
|
echo "sudo apt install wget"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v unzip &> /dev/null
|
||||||
|
then
|
||||||
|
echo "unzip could not be found, please install with:"
|
||||||
|
echo "sudo apt install unzip"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v python3 &> /dev/null
|
||||||
|
then
|
||||||
|
echo "python3 could not be found, please install with:"
|
||||||
|
echo "sudo apt install python3"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v pip3 &> /dev/null
|
||||||
|
then
|
||||||
|
echo "pip3 could not be found, please install with:"
|
||||||
|
echo "sudo apt install pip3"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# download minecraft data
|
||||||
|
|
||||||
|
if [ ! -d "minecraft_data" ]
|
||||||
|
then
|
||||||
|
echo "Grabbing minecraft data..."
|
||||||
|
|
||||||
|
VERSION="1.16.4"
|
||||||
|
|
||||||
|
wget -Omcdata.zip "https://apimon.de/mcdata/$VERSION/$VERSION.zip"
|
||||||
|
mkdir minecraft_data
|
||||||
|
unzip mcdata.zip -d minecraft_data
|
||||||
|
rm mcdata.zip
|
||||||
|
fi
|
||||||
|
|
||||||
|
# https://github.com/pypa/virtualenv/issues/1029
|
||||||
|
PS1=${PS1:-}
|
||||||
|
|
||||||
|
# create virtual environment
|
||||||
|
|
||||||
|
if [ ! -d "env" ]
|
||||||
|
then
|
||||||
|
echo "Installing Python requirements..."
|
||||||
|
|
||||||
|
virtualenv -p python3 env
|
||||||
|
source env/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
else
|
||||||
|
source env/bin/activate
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Running bot..."
|
||||||
|
|
||||||
|
python main.py
|
157
test_pycraft.py
157
test_pycraft.py
@@ -1,157 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import getpass
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
from optparse import OptionParser
|
|
||||||
|
|
||||||
from custom.managers import DataManager, ChunksManager
|
|
||||||
from custom.networking.packets.clientbound.play import chunk_data, block_change_packet
|
|
||||||
|
|
||||||
import minecraft.networking.packets
|
|
||||||
|
|
||||||
def get_packets(old_get_packets):
|
|
||||||
def wrapper(func, context):
|
|
||||||
print('Monkey-patched.')
|
|
||||||
packets = func(context)
|
|
||||||
packets.add(chunk_data.ChunkDataPacket)
|
|
||||||
packets.add(block_change_packet.BlockChangePacket)
|
|
||||||
packets.add(block_change_packet.MultiBlockChangePacket)
|
|
||||||
return packets
|
|
||||||
return lambda x: wrapper(old_get_packets, x)
|
|
||||||
|
|
||||||
minecraft.networking.packets.clientbound.play.get_packets = get_packets(minecraft.networking.packets.clientbound.play.get_packets)
|
|
||||||
|
|
||||||
from minecraft import authentication
|
|
||||||
from minecraft.exceptions import YggdrasilError
|
|
||||||
from minecraft.networking.connection import Connection
|
|
||||||
from minecraft.networking.packets import Packet, clientbound, serverbound
|
|
||||||
|
|
||||||
|
|
||||||
def get_options():
|
|
||||||
parser = OptionParser()
|
|
||||||
|
|
||||||
parser.add_option("-u", "--username", dest="username", default=None,
|
|
||||||
help="username to log in with")
|
|
||||||
|
|
||||||
parser.add_option("-p", "--password", dest="password", default=None,
|
|
||||||
help="password to log in with")
|
|
||||||
|
|
||||||
parser.add_option("-s", "--server", dest="server", default=None,
|
|
||||||
help="server host or host:port "
|
|
||||||
"(enclose IPv6 addresses in square brackets)")
|
|
||||||
|
|
||||||
parser.add_option("-o", "--offline", dest="offline", action="store_true",
|
|
||||||
help="connect to a server in offline mode "
|
|
||||||
"(no password required)")
|
|
||||||
|
|
||||||
parser.add_option("-d", "--dump-packets", dest="dump_packets",
|
|
||||||
action="store_true",
|
|
||||||
help="print sent and received packets to standard error")
|
|
||||||
|
|
||||||
parser.add_option("-v", "--dump-unknown-packets", dest="dump_unknown",
|
|
||||||
action="store_true",
|
|
||||||
help="include unknown packets in --dump-packets output")
|
|
||||||
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
|
|
||||||
if not options.username:
|
|
||||||
options.username = input("Enter your username: ")
|
|
||||||
|
|
||||||
if not options.password and not options.offline:
|
|
||||||
options.password = getpass.getpass("Enter your password (leave "
|
|
||||||
"blank for offline mode): ")
|
|
||||||
options.offline = options.offline or (options.password == "")
|
|
||||||
|
|
||||||
if not options.server:
|
|
||||||
options.server = input("Enter server host or host:port "
|
|
||||||
"(enclose IPv6 addresses in square brackets): ")
|
|
||||||
# Try to split out port and address
|
|
||||||
match = re.match(r"((?P<host>[^\[\]:]+)|\[(?P<addr>[^\[\]]+)\])"
|
|
||||||
r"(:(?P<port>\d+))?$", options.server)
|
|
||||||
if match is None:
|
|
||||||
raise ValueError("Invalid server address: '%s'." % options.server)
|
|
||||||
options.address = match.group("host") or match.group("addr")
|
|
||||||
options.port = int(match.group("port") or 25565)
|
|
||||||
|
|
||||||
return options
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
options = get_options()
|
|
||||||
|
|
||||||
mcdata = DataManager('./mcdata')
|
|
||||||
|
|
||||||
if options.offline:
|
|
||||||
print("Connecting in offline mode...")
|
|
||||||
connection = Connection(
|
|
||||||
options.address, options.port, username=options.username)
|
|
||||||
else:
|
|
||||||
auth_token = authentication.AuthenticationToken()
|
|
||||||
try:
|
|
||||||
auth_token.authenticate(options.username, options.password)
|
|
||||||
except YggdrasilError as e:
|
|
||||||
print(e)
|
|
||||||
sys.exit()
|
|
||||||
print("Logged in as %s..." % auth_token.username)
|
|
||||||
connection = Connection(
|
|
||||||
options.address, options.port, auth_token=auth_token)
|
|
||||||
|
|
||||||
if options.dump_packets:
|
|
||||||
def print_incoming(packet):
|
|
||||||
if type(packet) is Packet:
|
|
||||||
# This is a direct instance of the base Packet type, meaning
|
|
||||||
# that it is a packet of unknown type, so we do not print it
|
|
||||||
# unless explicitly requested by the user.
|
|
||||||
if options.dump_unknown:
|
|
||||||
print('--> [unknown packet] %s' % packet, file=sys.stderr)
|
|
||||||
else:
|
|
||||||
print('--> %s' % packet, file=sys.stderr)
|
|
||||||
|
|
||||||
def print_outgoing(packet):
|
|
||||||
print('<-- %s' % packet, file=sys.stderr)
|
|
||||||
|
|
||||||
connection.register_packet_listener(
|
|
||||||
print_incoming, Packet, early=True)
|
|
||||||
connection.register_packet_listener(
|
|
||||||
print_outgoing, Packet, outgoing=True)
|
|
||||||
|
|
||||||
def handle_join_game(join_game_packet):
|
|
||||||
print('Connected.')
|
|
||||||
|
|
||||||
connection.register_packet_listener(
|
|
||||||
handle_join_game, clientbound.play.JoinGamePacket)
|
|
||||||
|
|
||||||
def print_chat(chat_packet):
|
|
||||||
print("Message (%s): %s" % (
|
|
||||||
chat_packet.field_string('position'), chat_packet.json_data))
|
|
||||||
|
|
||||||
chunks = ChunksManager(mcdata)
|
|
||||||
chunks.register(connection)
|
|
||||||
|
|
||||||
connection.register_packet_listener(
|
|
||||||
print_chat, clientbound.play.ChatMessagePacket)
|
|
||||||
|
|
||||||
connection.connect()
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
text = input()
|
|
||||||
if text == "/respawn":
|
|
||||||
print("respawning...")
|
|
||||||
packet = serverbound.play.ClientStatusPacket()
|
|
||||||
packet.action_id = serverbound.play.ClientStatusPacket.RESPAWN
|
|
||||||
connection.write_packet(packet)
|
|
||||||
elif text == '!test':
|
|
||||||
print(chunks.get_block_at(91, 65, 57))
|
|
||||||
else:
|
|
||||||
packet = serverbound.play.ChatPacket()
|
|
||||||
packet.message = text
|
|
||||||
connection.write_packet(packet)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print("Bye!")
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
88
update_linux.sh
Executable file
88
update_linux.sh
Executable file
@@ -0,0 +1,88 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
if [ ! -d "mosfet" ]
|
||||||
|
then
|
||||||
|
echo "You must run this from the mosfet-minecraft-bot directory."
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v virtualenv &> /dev/null
|
||||||
|
then
|
||||||
|
echo "virtualenv could not be found, please install with:"
|
||||||
|
echo "sudo apt install virtualenv"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v wget &> /dev/null
|
||||||
|
then
|
||||||
|
echo "wget could not be found, please install with:"
|
||||||
|
echo "sudo apt install wget"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v unzip &> /dev/null
|
||||||
|
then
|
||||||
|
echo "unzip could not be found, please install with:"
|
||||||
|
echo "sudo apt install unzip"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v python3 &> /dev/null
|
||||||
|
then
|
||||||
|
echo "python3 could not be found, please install with:"
|
||||||
|
echo "sudo apt install python3"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v pip3 &> /dev/null
|
||||||
|
then
|
||||||
|
echo "pip3 could not be found, please install with:"
|
||||||
|
echo "sudo apt install pip3"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v git &> /dev/null
|
||||||
|
then
|
||||||
|
echo "git could not be found, please install with:"
|
||||||
|
echo "sudo apt install pip3"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# download minecraft data
|
||||||
|
|
||||||
|
echo "Grabbing minecraft data..."
|
||||||
|
|
||||||
|
VERSION="1.16.4"
|
||||||
|
|
||||||
|
wget -Omcdata.zip "https://apimon.de/mcdata/$VERSION/$VERSION.zip"
|
||||||
|
rm -r minecraft_data || true
|
||||||
|
mkdir minecraft_data
|
||||||
|
unzip mcdata.zip -d minecraft_data
|
||||||
|
rm mcdata.zip
|
||||||
|
|
||||||
|
|
||||||
|
# update code
|
||||||
|
|
||||||
|
git stash
|
||||||
|
git pull --rebase
|
||||||
|
git stash pop || true
|
||||||
|
|
||||||
|
|
||||||
|
# https://github.com/pypa/virtualenv/issues/1029
|
||||||
|
PS1=${PS1:-}
|
||||||
|
|
||||||
|
# create virtual environment
|
||||||
|
|
||||||
|
echo "Installing Python requirements..."
|
||||||
|
|
||||||
|
rm -r env || true
|
||||||
|
rm -r __pycache__ || true
|
||||||
|
virtualenv -p python3 env
|
||||||
|
source env/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
|
||||||
|
echo "Done."
|
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