Merge branch 'dev' of fantabos.co:treepuncher into dev
This commit is contained in:
commit
caeb5215cc
10 changed files with 148 additions and 62 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -127,3 +127,6 @@ dmypy.json
|
|||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# Auto generated version file
|
||||
src/treepuncher/__version__.py
|
||||
|
|
|
@ -1,7 +1,30 @@
|
|||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
"wheel"
|
||||
]
|
||||
requires = ["setuptools", "setuptools-scm"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "treepuncher"
|
||||
authors = [
|
||||
{name = "alemi", email = "me@alemi.dev"},
|
||||
]
|
||||
description = "An hackable Minecraft client, built with aiocraft"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.7"
|
||||
keywords = ["minecraft", "client", "bot", "hackable"]
|
||||
# license = {text = "MIT"}
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
dependencies = [
|
||||
"setproctitle",
|
||||
"termcolor",
|
||||
"apscheduler",
|
||||
"aioconsole",
|
||||
"aiocraft",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[tool.setuptools_scm]
|
||||
write_to = "src/treepuncher/__version__.py"
|
||||
|
|
30
setup.cfg
30
setup.cfg
|
@ -1,30 +0,0 @@
|
|||
[metadata]
|
||||
name = treepuncher
|
||||
version = 0.1.0
|
||||
author = alemi
|
||||
author_email = me@alemi.dev
|
||||
description = An hackable Minecraft client, built with aiocraft
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
url = https://github.com/alemidev/treepuncher
|
||||
project_urls =
|
||||
Bug Tracker = https://github.com/alemidev/treepuncher/issues
|
||||
classifiers =
|
||||
Programming Language :: Python :: 3
|
||||
License :: OSI Approved :: MIT License
|
||||
Operating System :: OS Independent
|
||||
|
||||
[options]
|
||||
install_requires =
|
||||
setproctitle
|
||||
termcolor
|
||||
apscheduler
|
||||
aioconsole
|
||||
aiocraft
|
||||
package_dir =
|
||||
= src
|
||||
packages = find:
|
||||
python_requires = >=3.6
|
||||
|
||||
[options.packages.find]
|
||||
where = src
|
|
@ -3,3 +3,4 @@ from .join_game import JoinGameEvent
|
|||
from .death import DeathEvent
|
||||
from .system import ConnectedEvent, DisconnectedEvent
|
||||
from .connection import PlayerJoinEvent, PlayerLeaveEvent
|
||||
from .block_update import BlockUpdateEvent
|
||||
|
|
13
src/treepuncher/events/block_update.py
Normal file
13
src/treepuncher/events/block_update.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from aiocraft.mc.definitions import BlockPos
|
||||
|
||||
from .base import BaseEvent
|
||||
|
||||
class BlockUpdateEvent(BaseEvent):
|
||||
SENTINEL = object()
|
||||
|
||||
location : BlockPos
|
||||
state : int
|
||||
|
||||
def __init__(self, location: BlockPos, state: int):
|
||||
self.location = location
|
||||
self.state = state
|
|
@ -4,3 +4,4 @@ from .tablist import GameTablist
|
|||
from .chat import GameChat
|
||||
from .world import GameWorld
|
||||
from .container import GameContainer
|
||||
from .position import GamePosition
|
||||
|
|
53
src/treepuncher/game/position.py
Normal file
53
src/treepuncher/game/position.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
from typing import Optional
|
||||
|
||||
from aiocraft.mc.definitions import BlockPos
|
||||
from aiocraft.mc.proto import PacketPosition, PacketSetPassengers, PacketEntityTeleport, PacketTeleportConfirm
|
||||
|
||||
from ..scaffold import Scaffold
|
||||
|
||||
class GamePosition(Scaffold):
|
||||
position : BlockPos
|
||||
vehicle_id : Optional[int]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.position = BlockPos(0, 0, 0)
|
||||
self.vehicle_id = None
|
||||
|
||||
@self.on_packet(PacketSetPassengers)
|
||||
async def player_enters_vehicle_cb(packet:PacketSetPassengers):
|
||||
if self.vehicle_id is None: # might get mounted on a vehicle
|
||||
for entity_id in packet.passengers:
|
||||
if entity_id == self.entity_id:
|
||||
self.vehicle_id = packet.entityId
|
||||
else: # might get dismounted from vehicle
|
||||
if packet.entityId == self.vehicle_id:
|
||||
if self.entity_id not in packet.passengers:
|
||||
self.vehicle_id = None
|
||||
|
||||
@self.on_packet(PacketEntityTeleport)
|
||||
async def entity_rubberband_cb(packet:PacketEntityTeleport):
|
||||
if self.vehicle_id is None:
|
||||
return
|
||||
if self.vehicle_id != packet.entityId:
|
||||
return
|
||||
self.position = BlockPos(packet.x, packet.y, packet.z)
|
||||
self.logger.info(
|
||||
"Position synchronized : (x:%.0f,y:%.0f,z:%.0f) (vehicle)",
|
||||
self.position.x, self.position.y, self.position.z
|
||||
)
|
||||
|
||||
@self.on_packet(PacketPosition)
|
||||
async def player_rubberband_cb(packet:PacketPosition):
|
||||
self.position = BlockPos(packet.x, packet.y, packet.z)
|
||||
self.logger.info(
|
||||
"Position synchronized : (x:%.0f,y:%.0f,z:%.0f)",
|
||||
self.position.x, self.position.y, self.position.z
|
||||
)
|
||||
await self.dispatcher.write(
|
||||
PacketTeleportConfirm(
|
||||
self.dispatcher.proto,
|
||||
teleportId=packet.teleportId
|
||||
)
|
||||
)
|
|
@ -1,10 +1,12 @@
|
|||
import asyncio
|
||||
import datetime
|
||||
import json
|
||||
|
||||
#from aiocraft.client import MinecraftClient
|
||||
from aiocraft.mc.definitions import Gamemode, Dimension, Difficulty
|
||||
from aiocraft.mc.proto import (
|
||||
PacketRespawn, PacketLogin, PacketUpdateHealth, PacketExperience, PacketSettings, PacketClientCommand, PacketAbilities
|
||||
PacketRespawn, PacketLogin, PacketUpdateHealth, PacketExperience, PacketSettings,
|
||||
PacketClientCommand, PacketAbilities, PacketDifficulty
|
||||
)
|
||||
|
||||
from ..events import JoinGameEvent, DeathEvent, DisconnectedEvent
|
||||
|
@ -37,9 +39,9 @@ class GameState(Scaffold):
|
|||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.in_game = False
|
||||
self.gamemode = Gamemode.SURVIVAL
|
||||
self.dimension = Dimension.OVERWORLD
|
||||
self.difficulty = Difficulty.HARD
|
||||
self.gamemode = Gamemode.UNKNOWN
|
||||
self.dimension = Dimension.UNKNOWN
|
||||
self.difficulty = Difficulty.UNKNOWN
|
||||
self.join_time = datetime.datetime(2011, 11, 18)
|
||||
|
||||
self.hp = 20.0
|
||||
|
@ -56,7 +58,8 @@ class GameState(Scaffold):
|
|||
async def on_player_respawning(packet:PacketRespawn):
|
||||
self.gamemode = Gamemode(packet.gamemode)
|
||||
if isinstance(packet.dimension, dict):
|
||||
self.dimension = Dimension.OVERWORLD # TODO wtf???
|
||||
self.logger.info("Received dimension data: %s", json.dumps(packet.dimensionCodec, indent=2))
|
||||
self.dimension = Dimension.from_str(packet.dimension['effects'])
|
||||
else:
|
||||
self.dimension = Dimension(packet.dimension)
|
||||
self.difficulty = Difficulty(packet.difficulty)
|
||||
|
@ -72,12 +75,19 @@ class GameState(Scaffold):
|
|||
self.gamemode.name
|
||||
)
|
||||
|
||||
@self.on_packet(PacketDifficulty)
|
||||
async def on_set_difficulty(packet:PacketDifficulty):
|
||||
self.difficulty = Difficulty(packet.difficulty)
|
||||
self.logger.info("Difficulty set to %s", self.difficulty.name)
|
||||
|
||||
@self.on_packet(PacketLogin)
|
||||
async def player_joining_cb(packet:PacketLogin):
|
||||
self.entity_id = packet.entityId
|
||||
self.gamemode = Gamemode(packet.gameMode)
|
||||
if isinstance(packet.dimension, dict):
|
||||
self.dimension = Dimension.OVERWORLD # TODO wtf???
|
||||
with open('world_codec.json', 'w') as f:
|
||||
json.dump(packet.dimensionCodec, f)
|
||||
self.dimension = Dimension.from_str(packet.dimension['effects'])
|
||||
else:
|
||||
self.dimension = Dimension(packet.dimension)
|
||||
self.difficulty = Difficulty(packet.difficulty)
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import json
|
||||
|
||||
from time import time
|
||||
from typing import Optional
|
||||
|
||||
from aiocraft.mc.definitions import BlockPos
|
||||
from aiocraft.mc.proto.play.clientbound import (
|
||||
PacketPosition, PacketMapChunk, PacketBlockChange, PacketMultiBlockChange, PacketSetPassengers,
|
||||
PacketEntityTeleport, PacketRelEntityMove
|
||||
from aiocraft.mc.proto import (
|
||||
PacketMapChunk, PacketBlockChange, PacketMultiBlockChange, PacketSetPassengers, PacketEntityTeleport,
|
||||
PacketSteerVehicle, PacketRelEntityMove, PacketTeleportConfirm, PacketPosition
|
||||
)
|
||||
from aiocraft.mc.proto.play.serverbound import PacketTeleportConfirm, PacketSteerVehicle
|
||||
from aiocraft.mc.types import twos_comp
|
||||
|
||||
from aiocraft import Chunk, World # TODO these imports will hopefully change!
|
||||
|
||||
from ..scaffold import Scaffold
|
||||
from ..events import BlockUpdateEvent
|
||||
|
||||
class GameWorld(Scaffold):
|
||||
position : BlockPos
|
||||
vehicle_id : Optional[int]
|
||||
vehicle_id : int | None
|
||||
world : World
|
||||
|
||||
_last_steer_vehicle : float
|
||||
|
@ -90,11 +90,10 @@ class GameWorld(Scaffold):
|
|||
)
|
||||
)
|
||||
|
||||
if not self.cfg.getboolean("process_world", fallback=False):
|
||||
# Since this might require more resources, allow to disable it
|
||||
if not self.cfg.getboolean("process_world", fallback=True):
|
||||
return
|
||||
|
||||
self.world = World()
|
||||
|
||||
@self.on_packet(PacketMapChunk)
|
||||
async def map_chunk_cb(packet:PacketMapChunk):
|
||||
assert isinstance(packet.bitMap, int)
|
||||
|
@ -105,18 +104,31 @@ class GameWorld(Scaffold):
|
|||
@self.on_packet(PacketBlockChange)
|
||||
async def block_change_cb(packet:PacketBlockChange):
|
||||
self.world.put_block(packet.location[0], packet.location[1], packet.location[2], packet.type)
|
||||
pos = BlockPos(packet.location[0], packet.location[1], packet.location[2])
|
||||
self.run_callbacks(BlockUpdateEvent, BlockUpdateEvent(pos, packet.type))
|
||||
|
||||
@self.on_packet(PacketMultiBlockChange)
|
||||
async def multi_block_change_cb(packet:PacketMultiBlockChange):
|
||||
return # holy shit this is LAME!
|
||||
if hasattr(packet, "chunkCoordinates"):
|
||||
chunk_x_off = (packet.chunkCoordinates & 0b1111111111111111111111) * 16
|
||||
chunk_z_off = ((packet.chunkCoordinates >> 22) & 0b1111111111111111111111) * 16
|
||||
chunk_y_off = ((packet.chunkCoordinates >> 44) & 0b11111111111111111111) * 16
|
||||
else:
|
||||
if self.dispatcher.proto < 751:
|
||||
chunk_x_off = packet.chunkX * 16
|
||||
chunk_z_off = packet.chunkZ * 16
|
||||
for entry in packet.records:
|
||||
x_off = (entry['horizontalPos'] >> 4 ) & 15
|
||||
z_off = entry['horizontalPos'] & 15
|
||||
self.world.put_block(x_off + chunk_x_off, entry['y'], z_off + chunk_z_off, entry['blockId'])
|
||||
for entry in packet.records:
|
||||
x_off = (entry['horizontalPos'] >> 4 ) & 15
|
||||
z_off = entry['horizontalPos'] & 15
|
||||
pos = BlockPos(x_off + chunk_x_off, entry['y'], z_off + chunk_z_off)
|
||||
self.world.put_block(pos.x,pos.y, pos.z, entry['blockId'])
|
||||
self.run_callbacks(BlockUpdateEvent, BlockUpdateEvent(pos, entry['blockId']))
|
||||
elif self.dispatcher.proto < 760:
|
||||
x = twos_comp((packet.chunkCoordinates >> 42) & 0x3FFFFF, 22)
|
||||
z = twos_comp((packet.chunkCoordinates >> 20) & 0x3FFFFF, 22)
|
||||
y = twos_comp((packet.chunkCoordinates ) & 0xFFFFF , 20)
|
||||
for loc in packet.records:
|
||||
state = loc >> 12
|
||||
dx = ((loc & 0x0FFF) >> 8 ) & 0x0F
|
||||
dz = ((loc & 0x0FFF) >> 4 ) & 0x0F
|
||||
dy = ((loc & 0x0FFF) ) & 0x0F
|
||||
pos = BlockPos(16*x + dx, 16*y + dy, 16*z + dz)
|
||||
self.world.put_block(pos.x, pos.y, pos.z, state)
|
||||
self.run_callbacks(BlockUpdateEvent, BlockUpdateEvent(pos, state))
|
||||
else:
|
||||
self.logger.error("Cannot process MultiBlockChange for protocol %d", self.dispatcher.proto)
|
||||
|
|
|
@ -15,7 +15,7 @@ from aiocraft.mc.auth import AuthInterface, AuthException, MojangAuthenticator,
|
|||
from aiocraft.mc.auth.microsoft import InvalidStateError
|
||||
|
||||
from .storage import StorageDriver, SystemState, AuthenticatorState
|
||||
from .game import GameState, GameChat, GameInventory, GameTablist, GameWorld, GameContainer
|
||||
from .game import GameState, GameChat, GameInventory, GameTablist, GameWorld, GameContainer, GamePosition
|
||||
from .addon import Addon
|
||||
from .notifier import Notifier, Provider
|
||||
|
||||
|
@ -223,7 +223,7 @@ class Treepuncher(
|
|||
|
||||
except AuthException as e:
|
||||
self.logger.error("Auth exception : [%s|%d] %s (%s)", e.endpoint, e.code, e.data, e.kwargs)
|
||||
except InvalidStateError as e:
|
||||
except InvalidStateError:
|
||||
self.logger.error("Invalid authenticator state")
|
||||
if isinstance(self.authenticator, MicrosoftAuthenticator):
|
||||
self.logger.info("Obtain an auth code by visiting %s", self.authenticator.url())
|
||||
|
|
Loading…
Reference in a new issue