From 58f9fb7603a9a92f0dc4fe094fd3bd89fcda0304 Mon Sep 17 00:00:00 2001 From: Moonlit Jolteon Date: Thu, 5 Dec 2024 15:02:24 -0500 Subject: [PATCH] Last update of the day Signed-off-by: Moonlit Jolteon --- README.md | 32 ++++++++++++++++++++++ __pycache__/manager.cpython-311.pyc | Bin 2054 -> 2278 bytes __pycache__/mcbot.cpython-311.pyc | Bin 8608 -> 9442 bytes __pycache__/task_funcs.cpython-311.pyc | Bin 8362 -> 7171 bytes main.py | 28 ++++++++++++++------ manager.py | 13 +++++---- mcbot.py | 33 +++++++++++++++-------- requirements.txt | 8 ++++++ task_funcs.py | 35 ++++--------------------- 9 files changed, 93 insertions(+), 56 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..8fc376f --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# HiveMine: A Minecraft hivemind +HiveMine is a work in progress bot that employs mineflayer as a primary backend for working within a minecraft world. While MineFlayer is a javascript library, HiveMine uses the javascript package for python to be able to load it + +### Prerequirements +- Python 3.8+ +- Required dependencies listed in requirements.txt +### Installation +Clone the Repository +```bash +git https://github.com/MoonlitJolteon/hivemine +cd hivemine +``` +Create the virtual environment for it to run in +```bash +python -m venv venv +source venv/bin/activate +# On Windows, use `venv\Scripts\activate` instead +``` +Install dependencies +```bash +pip install -r requirements.txt +``` +To start the program, first ensure that you are running a 1.20.1 minecraft server in offline mode on your local machine. Alternatively, you can also launch a single player world and open it to lan specifying 25565 as the port.
+Next, simply start the bot by running. +```bash +python main.py +``` +You can access the web server based controls at the following addresses (heavily WIP, currently going to the URL activates it) + +- List the tasks currently in queue: [127.0.0.1:5000/tasks](127.0.0.1:5000/tasks) +- Add an additional demo task to the queue: [127.0.0.1:5000/add_task](127.0.0.1:5000/add_task) +- Add 20 demo tasks to the queue: [127.0.0.1:5000/add_many_tasks](127.0.0.1:5000/add_many_tasks) \ No newline at end of file diff --git a/__pycache__/manager.cpython-311.pyc b/__pycache__/manager.cpython-311.pyc index 531eb8c21264d22de4b34ddb7cc0f93a7d2fb7a8..d20ae58d54500fa93803072914b6c1db4b3a81fd 100644 GIT binary patch delta 560 zcmZn@cqYiVoR^o20SK1v4@_Ucw2?2GiIIJC7855UBiG~-<_C-dlS5hNGIDJG%p%Ij zC^DIoO*5Veq#g=bfHX4@f9?Ph(-}A!m>6mpvY?U-3^k1Dj6gmkOkfdX5JMjWBSQ*9 zFoQBf2}m~-s4x^M)H2mDEMT7epHVb}X&F$@YPdc|hDe~ETBbTCkWL7zoUxptf;p0* zoUw=wZt#e9xb%2VNWZ|NwSZ*_Cy+uzKko8Nd|+VYL?UkR z2=se&c}xiG^zQNQK!HDi-~%&IHIObA0@~9gEY2{U%aM_Jp{Nb>9$8i(%TJTFNCqfX zBnu+sKm;F%(3<>_O@>i;G9SCLI14M#K1^b=2fH<+_T(0J)mVN;nGXz@#7B_W7leco U7bDOlOyVO*>$YbN~PV delta 334 zcmaDR*e1ZYoR^o20SNjW1Jm1?H}XX@G4gEAV&Y_EyN`j9A%!8Bp@0*5Rd74ZXQ{WMvNWPo&$EQkPUD&hkX zYLhRr%P>kze#fpX&d4hFfdP}4tifT;s6IJ`Lp4@}QRV{!Ch-v@_5~rK!_Eja36uB; L68nNh0_;QpZlyrR diff --git a/__pycache__/mcbot.cpython-311.pyc b/__pycache__/mcbot.cpython-311.pyc index f5e90237680f50a0162806c9f28b31e7a0b8257c..a0e51ce79bcc50f2bd622c3f5f2db6a6eb7402be 100644 GIT binary patch delta 2645 zcmah~|8LvI6~7}T#S-;}wy1BmWz(%=NnYi~h1&p~8)bEpIzwuEF_I?QVn^1|wj5c^ zNIGc*cgYX=VFm_N&Z%&=B!G%y>42cvI&av1D7O3wWWXVYfELF3%YXrcvZg?{pLRz@ zlI676>Fy)mdwiew?(XqjW#aEAy&ro#E+v$*Z?cd8M5O6m!Eaq?7&~T68JB!gLstd z!ESB{D_l1|#qszq$3r*%gbQ(=sj59MFXakZh&v;~_Ltl&gs*HL_7Iof$C5V@t=hEQ zs+4WgljvD8{A7HU$A9#W+e7QH!r)G~z-%yh*CXIQ-=;4`^y-@gzPSN*{UQ9&7rGwb z;f2Tiz$1R3GHCEIlaJ|*USpvrUIPS_7m1PbJpx(kr_tz1hsgNqH#Pu1fLmb0zS7VE zl|f(@tV1Ge#<|km=_^p_*|1kw8X{ii`y7Z}XDT*pTI%pRt^nn<#^EpcZU>~a4*amI zZw%Sn&lUoWYdQq`Gpc2Xf7#6sLJ~{~DxX*(bu`Q_SqmcakBEOK^&dtZ?OurTeWG+x zWgP8GxA$1&T4zYOqk9)x;mFH@Sft?v0K~1sJK!Fpb+=^e@ILYWfW;qnhl9v%(Hwc6 z<_CLeJnJm-wgh(o^4(*#-aVvk`n?W#%R%ya2mLKRfI79l7FqrCsK?i3nZNP8d1|k@ ze}8;r$zqGJUE7MT7YP>qNffvV-m^XLWNfdKU^@-jgwQ>9-KNnreb%fu@MuTK(Q@KO zVav&zgx*T15fo`ZYEq`UH|+CW4Ho)htc~P zA{Kj><*Oo+#cT4KxF|_^F|#1AY2sq8kjbk{b)J8uiq1*7AgNmZTI!kXn^f`TSxq5a z5*iUFG6nIZxU!~dqIxNJS=3~4Vp++l+G<7>a|KnC*bGYH2mZ5oNa%mzY9^;;o8J|2 zLKE7m1n0=Z7SJHxVS4dH<^}wPCvvtJnWQwDxFAUdF)tUEM5!RJEiI=860G7OCQ3gA z`mraN>VXAWo0k_>sxGSt&eOW1Qg5QD43NJntF2xx9>zcO4PicDyFN0Wm$PKGM@EQz zfxMt53ETjmhoiSwjBvsXC-iV)C)|IRdmN5G3di-tF(Z833?J7U-6}Rk_d8@=ITGEX ztcXRIs7hJ6fYgzZs_mz1Ijz|7mMo)%Ytq1pgrLy$S7;^btJ+AwsuCy9w1{4nl)Y=h zbHd?(o9ftHK~rc-DKi8T4*Xjo;+v*(k$_4MUBrek8Is9j>*caMPHSOkhYyw}>LPp+7}`AN{od5B<9DDCwW`ffCx}%3O&fWwqR~<+P3vlb_M~nO$Gt2?*ikz&ZYk5t}k&Q-&~Y3e&nUjUNRL3F#l3XMSSDE|{?k zhA?LebGk5xBf+T8h@CcLrww7&6lQf{7QY)zUZ1~crZYxt!Hg{!Le> z^GdVV;@kfTeh6WH`%d_MhELNIdV>PZPvt0ny(bDU;&VN(Ojx-;VWrn^v=_L>Pb8KL?NEp!jNXnJ$Bhl^g|HHmxoAZ^!tm yjMnnf5k)34dT*;Q;djN;V<&X*fBFsx>g^r>Ta;4;2uXO~j!!K4)9=t)Vf_!>{Bd0X delta 1951 zcma)6+i%lW7(d5xQW7_g8=u_TGzq=AffZVWn1nhypjNKh2fj1y%L)yb6c8*H|9&DO@{Ik#Z zeZSv#ZlCYJQ{2vf<@I_1xn}pjH?km1^I_&MFx~!c^faI1Az*+4W#=3OV4gfHSIRei z5l#QdO%*`^X00VXbx%Lu}y>kpnM4sn~ zf--*yBz?FOZh}rc0=t4s=*+2fMj6Ve>8zF;8yV8l!>5$Hcc26MOJA_tA#5r=aCSi0 zT0%8F3{2ua_vi2@{MbDcbyjo%key$uJfWkA~3u4DT zvBMD8o8tP*y^B7v44}7VQ9ujA>U+X!LufXIX1&tN4&t;T@OQqDJ8XEPm#TIM|{C8-XD15~587I0e-Lb)94@;U5wS0Ek^JFhwT`4TK!F zHKo2m;&V|S{#5YspEzV^fjI}%4MYgyheB+PR#(v@yW%RqTi`aM`Cr&3*#ev+IpZeWV-f}*a4)rff*!?d?-Xhmoy6v)mk{#kJ6q2P{oJFB}sy;>m zDB`OqfQ7)3)(Orkrw*%%CV~e^ZIPl%RH`ds*>N@ykDUD|r|nGEsZpY@(j&CQ4v%Yj zwH-Hv-t6{LYb^!3`f8lQUIIy{x|zI|TzO?oR#a7@9X73ws#T>+e9AELh`^F(G(9GtQijMTsQuL1O`(UvPW&tug@ZU6Uk69=p7^5;4waVp${xDNSF*<1 zD@#coq^?&avWZStC-LOZ;n{?GLA;4{cg%3dOn2;8H+Q-B%I>MG;chhDjb+B+CUdpH z69@2+&_?=>6H1vS?x!B$skJjBlxc$sv&<0l(@oDA`26aJunuRN29rCWv!e`Nk7_fW z3;osXPloWrra^qLxr-aF7}X5E(j4~WNT2?)^9lS-^ZV_`b@0C$-qO8ZqrF-c9T1^ zTZ_VOF=&;7lq+hVsxOsRK_oAtAU^q`52-{51EmPQhzctR{yd21+|`sqFLS>+_uMn* z|IF3HuTEt@%w&=T)~>IfUbHe7vW3=raC(=x#HBt7Xqg5|S)q7Vd^OO@nxs`<4~()Q zY0Wo-L^&a8-M7k?LYU?nmnk71!S1edQgY2(xl5_-`;?nlBjvO^Zow<#CIPc<)=jU` za*vyVQ?IK(OYEMX@C#ds)zGWOYPB9-xHMtJT9Z}xf4W&vXj1&DM5QbU#rg@qUR~yW zK=U4ePy<*fm1cRbIIH&S1vnwl?3l#YqKKC3t0RNv6VHChm%{@IYLeXL(r zAr#o%MalW5OTZUHIL30(rpIi`QJ1aoL3nXA9x4FsHdy-EIQsAia_#{5vrWFAXB#KI zjpdbV^Q)Wlt8Kz_aN)iG8YMQ3&BzN_y%E_KPl<~8h-T|NBR(+m12PYD zs$XB^hR9kaIwJO26EExrBi+a_i)fzr0uiUHb>C;zsB*eqcliXc24II50U+3|WZ(Hc z?YWL(>S*!yC_cA1lwfaiozkrMAvHu(;-A#cq5Ysknv7Mi){Lnq3kar55cq6*K%WDF z-J+cS#K08t$@TGEPNP#|F28dRc9yO2%(f28i&#*pIRWDbfZ5g6mL+;SKSK|Q@A6AD zCuRm`=%jdgaJo0nR=IOB@@k8f$T{IN@x|aYeO&x8SRBO^@o|7Sg<8WSXO5xXhmehi z`D0?da942}$wLT_0>l~4RvR8?;d7{t&kCdY{6r&UTxMV&@}#HO zn!G5!8GfL(1IbZ@dl2qLxDUWKIeHlzA*;EJcS~Y)ZNx_8@I@B!T_|A%^(G5gEeb<) zM7oaE!aJ>6hZHd&kHZds0od-3>zZ|Lhj?k^MhgoG<1~q{zE}&K=oH7v;2r zVU>F;5yvTr)1eov_^eVr<@n1o(pbX@;%J`tBNS0iI}ZN`Ii#{hId+cBpmgX;cLu>v zu={u7U{H4(K0CyHkitJ$cmtrLQcAbTog24%#E->ia$9D$efnJUY-_ExY2Mw|I*OPo ZjpcKrbfQCUg{P=^tCSa)N~VSp{|orAN)7-3 delta 2514 zcmai0-ESMm5#Qx~@JJph)0QIIl&rIx+ND2aJFNt#`iNyCj@$%<9knqcprB8@i*nSF zC-0ps$e==@4iHHY;27&de(-}^6i(6rsRQ+;|3IGda55MqxT1j4x4aYr+M+=oiq0%U z*>VeXb^KxWYj$S#H?z8R_V;6%57Ox*f%e#(n(5Dj=8a_ zG${#La^^DD=TjSdXf8$*ydVWC)^1q@3|9Cpb*AqlS}ag;Tp`9fVH-nheN?uz#L_$+zeIca z)p&0j4f#d^C?uK2*Wxe!go&QQF!)hQ_;=ZJYJnd~v!4HyHe6?btEliF@iFx|==-1W zA?<`FC%U!?0KcyN^6;k4nmP-0_7^?-{-Krewee;6+fK9>u@3%kZ6qb)DY@P>lljGj zL3{XgV&IJ4Rir*ZAhGt+uMJsFfWKR(7t{qWjxRNJVOki*!ZhDfLi; z?!M;5SUqONW-t_l-3E8(xMD{F!-W9 zKo;*b{!)i_y8aB;7L~ff&eoL*Qd~4knGp`LlPl_j48POe^)f2`Kkiy&S$&H_GHMUO zp^t@As1DP2f`FzD{%Nu^X2rSL-3j{IeGU7KLZTRD%FKK#aB9<~z?^bzhDNXffP%yh zp?nzO2LO>|I;^~75RBk)m{5Oz9^lt>odi$K*2xOpoqiL9;YC3u$jrKC*>04|rt40b zS*v-0Aru;ownjs^e!_sP&BJHI}Ic3MTAj=lL$S8G|00NR^~nCT9Gzw2j{UU zMsf}IM@eC?7-7iDLY!fiMcKApt-C8ZR@C{-P}!TS1?-2oIvJP@6qmruNJqH=9pPpx z?#O;j)1CUz`M=`MF2k}@@bkX~cv2^}a{2puGj}>XGuF(VZ{}VO<*u*t2k(!3neTs` z?+;I%+suzO^JAf$+luG!nUCXrjWFJ~mC3GEnwdk((iSWpc;NqT?!)W9S$O3CaW3o{ zfp0mvt&&{+?!uSdg~#26507nj4>Y?6mUS4`b66pNM(@4W>^b?U>v7Lu*fY4Lcib+n z7VrHObk&>>3g?B>z`wusbua1XZ085JC6MF?E!6Q6{jquXG1AT&0 z!`MFy5T%)Yea>OF|8tSaqx{~!-m9V<{{q*b3G5AAD5G6bTv!L3QJGzKX6=aDEh1pC zGp=2GnhHrls}k;DQ!qWgp?AI~gVlF)$A*QK_>x6&V9bKee0B_a4BchNaa^18Z6@L% zxG!9Q1R$)Jf0yeo^r2`V?5;R%o~yoHvuwsv{L22$6N1c%s&~V#!VBX2Pk8(N3nQ03 zi1;k%^Z1?p|7>9D!&5GbS|&J^JcB%Zf>COnFBROzD1G>ca zAoL>Oe;(qM6fc&@0}%^x(nqZdBdXSS+Hlpg=3IM}&B2}w7DfLvfNl6Mh4SJ2>#=Q_ fKggeWe$R7sWSi{33y0~zHrWkD{&oJKj4u2KOBq}D diff --git a/main.py b/main.py index 1d56728..30bd911 100644 --- a/main.py +++ b/main.py @@ -12,25 +12,37 @@ def root(): @app.route('/tasks') def tasks(): - return "
".join([task.name for task in manager.queue.queue]) + return "
".join([task.name for task in manager.tasks.queue]) num = 0 @app.route('/add_task') -def add_tasks(): +def add_task(): global num - manager.queue(Task(f"Task {num}", demo_task, num)) + manager.queue(Task(f"Demo Task {num}", demo_task, num)) num += 1 return "Done" +@app.route('/add_many_tasks') +def add_many_tasks(): + global num + i = 0 + while i < 20: + manager.queue(Task(f"Demo Task {num}", demo_task, num)) + i += 1 + num += 1 + return "Done" + def run(): - for i in range(1): - MCBot(f"bot{i}", manager, is_king = (i == 0)) + MCBot(f"HiveMineAgent", manager, is_king = True) + print("You can access the task list at 127.0.0.1:5000/tasks") # TODO: figure out how to read url/port dynamically + print("You can add a single demo task at 127.0.0.1:5000/add_task") + print("You can add 20 demo tasks at 127.0.0.1:5000/add_many_tasks") app.run() -def demo_task(num): - sleep(1) - print(f"Task {num}") +def demo_task(bot_obj, num): + sleep(0.2) + bot_obj.bot.chat(f"I have completed the demo task! ({num})") if __name__ == '__main__': run() \ No newline at end of file diff --git a/manager.py b/manager.py index fc5b9cf..15fef40 100644 --- a/manager.py +++ b/manager.py @@ -3,11 +3,6 @@ class Manager: def __init__(self): self.bots = set() self.tasks = Queue() - # self.tasks.put(Task("Task A", lambda: print("Task A"))) - # self.tasks.put(Task("Task B", lambda: print("Task B"))) - # self.tasks.put(Task("Task C", lambda: print("Task C"))) - # self.tasks.put(Task("Task D", lambda: print("Task D"))) - # self.tasks.put(Task("Task E", lambda: print("Task E"))) def queue(self, task): self.tasks.put(task) @@ -27,5 +22,9 @@ class Task: self.func = func self.args = args - def perform_task(self): - self.func(*self.args) \ No newline at end of file + def perform_task(self, bot_obj): + try: + self.func(bot_obj, *self.args) + except Exception as e: + self.log(e) + \ No newline at end of file diff --git a/mcbot.py b/mcbot.py index 6edefc1..8820ded 100644 --- a/mcbot.py +++ b/mcbot.py @@ -1,7 +1,8 @@ from javascript import require, On, off, AsyncTask from simple_chalk import chalk from manager import Task -from task_funcs import come, collect_wood, print_inventory, craft_item, place_crafting_table +from task_funcs import come, collect_wood, print_inventory, craft_item +from time import sleep mineflayer = require('mineflayer') pathfinder = require('mineflayer-pathfinder').pathfinder mineflayerViewer = require('prismarine-viewer').mineflayer @@ -59,38 +60,48 @@ class MCBot: def handle_spawn(this): self.log("I spawned 👋") - @AsyncTask(start=True) @On(self.bot, 'time') def handle_tick_update(this): task = self.manager.get_next_task() if task: - task.perform_task() + task.perform_task(self) @On(self.bot, 'chat') def handle_chat(this, sender, message, *args): if not self.is_king: - return + return # The king is here to read chat, no one else should be doing so + if sender not in self.manager.bots: self.log(f"chat message: {sender}> {message}") + if 'how do you feel about finals' in message.lower(): + sleep(0.75) + self.bot.chat("Honestly?") + sleep(1.5) + self.bot.chat("Stressed. Can I just skip to Christmas instead?") + if sender and (sender != self.bot_name) and (message.startswith(f"~")): args = message.split(' ') if 'come' in message: - self.manager.queue(Task(f"Come to {sender}", come, self, sender)) + self.manager.queue(Task(f"Come to {sender}", come, sender)) if 'collect-wood' in message: - self.manager.queue(Task(f"Collect {args[1]} wood", collect_wood, self, args[1])) + self.manager.queue(Task(f"Collect {args[1]} wood", collect_wood, args[1])) if 'list-inv' in message: - self.manager.queue(Task(f"Listing inventory", print_inventory, self)) - - if 'place-table' in message: - self.manager.queue(Task(f"Placing crafting table", place_crafting_table, self)) + self.manager.queue(Task(f"Listing inventory", print_inventory)) if 'craft' in message: - self.manager.queue(Task(f"Attempting to craft {args[1]}", craft_item, self, args[1])) + self.manager.queue(Task(f"Attempting to craft {args[1]}", craft_item, args[1])) + + if 'wait' in message: + def tmp(bot_obj, sleep_time): + sleep(sleep_time) + bot_obj.bot.chat("Has it been long enough?!") + self.manager.queue(Task(f"Waiting for {args[1]} seconds..", tmp, int(args[1]))) if 'quit' in message: + self.bot.chat("Goodbye!") self.reconnect = False self.bot.quit() diff --git a/requirements.txt b/requirements.txt index 719f455..0c34843 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,9 @@ +blinker==1.9.0 +click==8.1.7 +Flask==3.1.0 +itsdangerous==2.2.0 javascript==1!1.2.1 +Jinja2==3.1.4 +MarkupSafe==3.0.2 +simple-chalk==0.1.0 +Werkzeug==3.1.3 diff --git a/task_funcs.py b/task_funcs.py index fb929b9..7303bbb 100644 --- a/task_funcs.py +++ b/task_funcs.py @@ -1,9 +1,10 @@ +from math import sin, cos from javascript import require from simple_chalk import chalk from time import sleep import threading pathfinder = require('mineflayer-pathfinder') - +Vec3 = require('vec3').Vec3 def dig_with_wait(bot, block): finished = threading.Event() def on_dig_complete(err): @@ -26,8 +27,8 @@ def collect_wood(bot_obj, quantity): for block in blockPoints: bot.chat(f"Found oak log at {block.position}") pos = block.position - bot.pathfinder.goto(pathfinder.goals.GoalNear(pos.x, pos.y, pos.z), lambda: dig_with_wait(bot, block)) - sleep(5) + bot.pathfinder.goto(pathfinder.goals.GoalNear(pos.x, pos.y, pos.z), lambda: dig_with_wait(bot, block), timeout=100000) + bot_obj.log(chalk.magenta(f"Collecting {quantity} oak wood...")) @@ -44,30 +45,6 @@ def print_inventory(bot_obj): bot.chat(f"Item {i+1}: {inv[i].displayName} x{inv[i].count}") bot.chat("]") -def place_crafting_table(bot_obj): - bot = bot_obj.bot - # Check if the bot has a crafting table in its inventory - crafting_table_id = bot.registry.itemsByName["crafting_table"].id - crafting_table_count = bot.inventory.count(crafting_table_id) - - if crafting_table_count > 0: - # Get the bot's current position - pos = bot.entity.position - # Define the position directly below the bot (y - 1) - target_pos = {'x': pos.x, 'y': pos.y - 1, 'z': pos.z} - - # Get the block at the target position - target_block = bot.world.getBlock(target_pos) - - # If the block below is empty (not a solid block), place the crafting table - if target_block and target_block.name == 'air': - # Place the crafting table at the target position - bot.placeBlock(target_pos, bot.registry.itemsByName["crafting_table"], None, lambda err: place_callback(err, bot)) - else: - bot.chat("Cannot place crafting table, position is blocked.") - else: - bot.chat("No crafting table in inventory to place.") - def craft_item(bot_obj, item_name, quantity=1): bot = bot_obj.bot @@ -105,8 +82,6 @@ def craft_item(bot_obj, item_name, quantity=1): except Exception as e: bot.chat(f"Error during crafting: {str(e)}") - - def come(bot_obj, sender): bot = bot_obj.bot @@ -121,4 +96,4 @@ def come(bot_obj, sender): movements = pathfinder.Movements(bot) movements.canDig = False bot.pathfinder.setMovements(movements) - bot.pathfinder.setGoal(pathfinder.goals.GoalNear(pos.x, pos.y, pos.z, 2)) \ No newline at end of file + bot.pathfinder.setGoal(pathfinder.goals.GoalNear(pos.x, pos.y, pos.z, 2))