chore: updated aiocraft

This commit is contained in:
əlemi 2023-11-20 17:52:49 +01:00
parent bd97b891f4
commit f9c97c0c34
Signed by: alemi
GPG key ID: A4895B84D311642C
16 changed files with 80 additions and 151 deletions

View file

@ -1,8 +1,5 @@
#!/usr/bin/env python
import os
import re
import sys
import asyncio
import logging
import argparse
import inspect
@ -20,8 +17,8 @@ from .scaffold import ConfigObject
from .helpers import configure_logging
def main():
root = Path(os.getcwd())
# TODO would be cool if it was possible to configure addons path, but we need to load addons before doing argparse so we can do helptext
#root = Path(os.getcwd())
#addon_path = Path(args.path) if args.addon_path else ( root/'addons' )
addon_path = Path('addons')
addons : Set[Type[Addon]] = set()
@ -34,7 +31,7 @@ def main():
obj = getattr(m, obj_name)
if obj != Addon and inspect.isclass(obj) and issubclass(obj, Addon):
addons.add(obj)
except Exception as e:
except Exception:
print(f"Exception importing addon {py_path}")
traceback.print_exc()
pass

View file

@ -1,4 +1,4 @@
from aiocraft.mc.definitions import BlockPos
from aiocraft.types import BlockPos
from .base import BaseEvent

View file

@ -1,4 +1,4 @@
from aiocraft.mc.definitions import Player
from aiocraft.types import Player
from .base import BaseEvent
class PlayerJoinEvent(BaseEvent):

View file

@ -1,4 +1,4 @@
from aiocraft.mc.definitions import Dimension, Difficulty, Gamemode
from aiocraft.types import Dimension, Difficulty, Gamemode
from .base import BaseEvent

View file

@ -1,4 +1,4 @@
from aiocraft.mc.packet import Packet
from aiocraft.packet import Packet
from .base import BaseEvent

View file

@ -4,4 +4,3 @@ from .tablist import GameTablist
from .chat import GameChat
from .world import GameWorld
from .container import GameContainer
from .position import GamePosition

View file

@ -1,9 +1,7 @@
from typing import Union
from aiocraft.proto.play.clientbound import PacketChat as PacketChatMessage
from aiocraft.proto.play.serverbound import PacketChat
from aiocraft.mc.proto.play.clientbound import PacketChat as PacketChatMessage
from aiocraft.mc.proto.play.serverbound import PacketChat
from ..events.chat import ChatEvent, MessageType
from ..events.chat import ChatEvent
from ..scaffold import Scaffold
class GameChat(Scaffold):
@ -15,14 +13,11 @@ class GameChat(Scaffold):
async def chat_event_callback(packet:PacketChatMessage):
self.run_callbacks(ChatEvent, ChatEvent(packet.message))
async def chat(self, message:str, whisper:str=None, wait:bool=False):
async def chat(self, message:str, whisper:str="", wait:bool=False):
if whisper:
message = f"/w {whisper} {message}"
await self.dispatcher.write(
PacketChat(
self.dispatcher.proto,
message=message
),
PacketChat(message=message),
wait=wait
)

View file

@ -1,27 +1,22 @@
import asyncio
import datetime
from typing import List, Optional
#from aiocraft.client import MinecraftClient
from aiocraft.mc.definitions import Item
from aiocraft.mc.proto.play.clientbound import PacketTransaction
from aiocraft.mc.proto.play.serverbound import PacketTransaction as PacketTransactionServerbound
from aiocraft.mc.proto import (
from aiocraft.types import Item
from aiocraft.proto.play.clientbound import PacketTransaction
from aiocraft.proto.play.serverbound import PacketTransaction as PacketTransactionServerbound
from aiocraft.proto import (
PacketOpenWindow, PacketCloseWindow, PacketSetSlot
)
from ..events import JoinGameEvent, DeathEvent, ConnectedEvent, DisconnectedEvent
from ..events import DisconnectedEvent
from ..scaffold import Scaffold
class WindowContainer:
id: int
title: str
type: str
entity_id: Optional[int]
entity_id: int | None
transaction_id: int
inventory: List[Optional[Item]]
inventory: list[Item | None]
def __init__(self, id:int, title: str, type: str, entity_id:int = None, slot_count:int = 27):
def __init__(self, id:int, title: str, type: str, entity_id:int | None = None, slot_count:int = 27):
self.id = id
self.title = title
self.type = type
@ -37,7 +32,7 @@ class WindowContainer:
return self.transaction_id
class GameContainer(Scaffold):
window: Optional[WindowContainer]
window: WindowContainer | None
@property
def is_container_open(self) -> bool:
@ -85,7 +80,6 @@ class GameContainer(Scaffold):
if not packet.accepted: # apologize to server automatically
await self.dispatcher.write(
PacketTransactionServerbound(
self.dispatcher.proto,
windowId=packet.windowId,
action=packet.action,
accepted=packet.accepted,

View file

@ -1,8 +1,8 @@
from typing import List
from aiocraft.mc.definitions import Item
from aiocraft.mc.proto.play.clientbound import PacketSetSlot, PacketHeldItemSlot as PacketHeldItemChange
from aiocraft.mc.proto.play.serverbound import PacketHeldItemSlot
from aiocraft.types import Item
from aiocraft.proto.play.clientbound import PacketSetSlot, PacketHeldItemSlot as PacketHeldItemChange
from aiocraft.proto.play.serverbound import PacketHeldItemSlot
from ..scaffold import Scaffold
@ -13,7 +13,7 @@ class GameInventory(Scaffold):
async def set_slot(self, slot:int):
self.slot = slot
await self.dispatcher.write(PacketHeldItemSlot(self.dispatcher.proto, slotId=slot))
await self.dispatcher.write(PacketHeldItemSlot(slotId=slot))
@property
def hotbar(self) -> List[Item]:

View file

@ -1,53 +0,0 @@
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
)
)

View file

@ -3,8 +3,8 @@ import datetime
import json
#from aiocraft.client import MinecraftClient
from aiocraft.mc.definitions import Gamemode, Dimension, Difficulty
from aiocraft.mc.proto import (
from aiocraft.types import Gamemode, Dimension, Difficulty
from aiocraft.proto import (
PacketRespawn, PacketLogin, PacketUpdateHealth, PacketExperience, PacketSettings,
PacketClientCommand, PacketAbilities, PacketDifficulty
)
@ -58,7 +58,7 @@ class GameState(Scaffold):
async def on_player_respawning(packet:PacketRespawn):
self.gamemode = Gamemode(packet.gamemode)
if isinstance(packet.dimension, dict):
self.logger.info("Received dimension data: %s", json.dumps(packet.dimensionCodec, indent=2))
self.logger.info("Received dimension data: %s", json.dumps(packet.dimension, indent=2))
self.dimension = Dimension.from_str(packet.dimension['effects'])
else:
self.dimension = Dimension(packet.dimension)
@ -106,7 +106,6 @@ class GameState(Scaffold):
self.run_callbacks(JoinGameEvent, JoinGameEvent(self.dimension, self.difficulty, self.gamemode))
await self.dispatcher.write(
PacketSettings(
self.dispatcher.proto,
locale="en_US",
viewDistance=4,
chatFlags=0,
@ -115,7 +114,7 @@ class GameState(Scaffold):
mainHand=0,
)
)
await self.dispatcher.write(PacketClientCommand(self.dispatcher.proto, actionId=0))
await self.dispatcher.write(PacketClientCommand(actionId=0))
@self.on_packet(PacketUpdateHealth)
async def player_hp_cb(packet:PacketUpdateHealth):
@ -132,7 +131,7 @@ class GameState(Scaffold):
self.logger.warning("Died, attempting to respawn")
await asyncio.sleep(0.5) # TODO make configurable
await self.dispatcher.write(
PacketClientCommand(self.dispatcher.proto, actionId=0) # respawn
PacketClientCommand(actionId=0) # respawn
)
@self.on_packet(PacketExperience)

View file

@ -2,10 +2,9 @@ import uuid
import datetime
from enum import Enum
from typing import Dict, List
from aiocraft.mc.definitions import Player
from aiocraft.mc.proto import PacketPlayerInfo
from aiocraft.types import Player
from aiocraft.proto import PacketPlayerInfo
from ..scaffold import Scaffold
from ..events import ConnectedEvent, PlayerJoinEvent, PlayerLeaveEvent
@ -18,7 +17,7 @@ class ActionType(Enum): # TODO move this in aiocraft
REMOVE_PLAYER = 4
class GameTablist(Scaffold):
tablist : Dict[uuid.UUID, Player]
tablist : dict[uuid.UUID, Player]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

View file

@ -1,13 +1,13 @@
import json
from time import time
from aiocraft.mc.definitions import BlockPos
from aiocraft.mc.proto import (
from aiocraft.types import BlockPos
from aiocraft.proto import (
PacketMapChunk, PacketBlockChange, PacketMultiBlockChange, PacketSetPassengers, PacketEntityTeleport,
PacketSteerVehicle, PacketRelEntityMove, PacketTeleportConfirm
)
from aiocraft.mc.proto.play.clientbound import PacketPosition
from aiocraft.mc.types import twos_comp
from aiocraft.proto.play.clientbound import PacketPosition
from aiocraft.primitives import twos_comp
from aiocraft import Chunk, World # TODO these imports will hopefully change!
@ -70,12 +70,7 @@ class GameWorld(Scaffold):
if time() - self._last_steer_vehicle >= 5:
self._last_steer_vehicle = time()
await self.dispatcher.write(
PacketSteerVehicle(
self.dispatcher.proto,
forward=0,
sideways=0,
jump=0
)
PacketSteerVehicle(forward=0, sideways=0, jump=0)
)
@self.on_packet(PacketPosition)
@ -86,10 +81,7 @@ class GameWorld(Scaffold):
self.position.x, self.position.y, self.position.z
)
await self.dispatcher.write(
PacketTeleportConfirm(
self.dispatcher.proto,
teleportId=packet.teleportId
)
PacketTeleportConfirm(teleportId=packet.teleportId)
)
# Since this might require more resources, allow to disable it
@ -118,7 +110,7 @@ class GameWorld(Scaffold):
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.world.put_block(pos.i_x, pos.i_y, pos.i_z, entry['blockId'])
self.run_callbacks(BlockUpdateEvent, BlockUpdateEvent(pos, entry['blockId']))
elif self.dispatcher.proto < 760:
x = twos_comp((packet.chunkCoordinates >> 42) & 0x3FFFFF, 22)
@ -130,7 +122,7 @@ class GameWorld(Scaffold):
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.world.put_block(pos.i_x, pos.i_y, pos.i_z, state)
self.run_callbacks(BlockUpdateEvent, BlockUpdateEvent(pos, state))
else:
self.logger.error("Cannot process MultiBlockChange for protocol %d", self.dispatcher.proto)

View file

@ -2,13 +2,13 @@ from configparser import ConfigParser, SectionProxy
from typing import Type, Any
from aiocraft.client import MinecraftClient
from aiocraft.client import AbstractMinecraftClient
from aiocraft.util import helpers
from aiocraft.mc.packet import Packet
from aiocraft.mc.definitions import ConnectionState
from aiocraft.mc.proto import PacketKickDisconnect, PacketSetCompression
from aiocraft.mc.proto.play.clientbound import PacketKeepAlive
from aiocraft.mc.proto.play.serverbound import PacketKeepAlive as PacketKeepAliveResponse
from aiocraft.packet import Packet
from aiocraft.types import ConnectionState
from aiocraft.proto import PacketKickDisconnect, PacketSetCompression
from aiocraft.proto.play.clientbound import PacketKeepAlive
from aiocraft.proto.play.serverbound import PacketKeepAlive as PacketKeepAliveResponse
from .traits import CallbacksHolder, Runnable
from .events import ConnectedEvent, DisconnectedEvent
@ -21,7 +21,7 @@ class ConfigObject:
class Scaffold(
CallbacksHolder,
Runnable,
MinecraftClient,
AbstractMinecraftClient,
):
entity_id : int
@ -43,16 +43,17 @@ class Scaffold(
#Override
async def _play(self) -> bool:
self.dispatcher.set_state(ConnectionState.PLAY)
assert self.dispatcher is not None
self.dispatcher.promote(ConnectionState.PLAY)
self.run_callbacks(ConnectedEvent, ConnectedEvent())
async for packet in self.dispatcher.packets():
self.logger.debug("[ * ] Processing %s", packet.__class__.__name__)
if isinstance(packet, PacketSetCompression):
self.logger.info("Compression updated")
self.dispatcher.set_compression(packet.threshold)
self.dispatcher.update_compression_threshold(packet.threshold)
elif isinstance(packet, PacketKeepAlive):
if self.cfg.getboolean("send_keep_alive", fallback=True):
keep_alive_packet = PacketKeepAliveResponse(self.dispatcher._proto, keepAliveId=packet.keepAliveId)
keep_alive_packet = PacketKeepAliveResponse(keepAliveId=packet.keepAliveId)
await self.dispatcher.write(keep_alive_packet)
elif isinstance(packet, PacketKickDisconnect):
self.logger.error("Kicked while in game : %s", helpers.parse_chat(packet.reason))

View file

@ -5,8 +5,6 @@ import logging
from inspect import isclass
from typing import Dict, List, Set, Any, Callable, Type
from ..events.base import BaseEvent
class CallbacksHolder:
_callbacks : Dict[Any, List[Callable]]
@ -17,7 +15,7 @@ class CallbacksHolder:
self._callbacks = {}
self._tasks = {}
def callback_keys(self, filter:Type = None) -> Set[Any]:
def callback_keys(self, filter:Type | None = None) -> Set[Any]:
return set(x for x in self._callbacks.keys() if not filter or (isclass(x) and issubclass(x, filter)))
def register(self, key:Any, callback:Callable):

View file

@ -4,18 +4,18 @@ import asyncio
import datetime
import pkg_resources
from typing import List, Dict, Any, Type, Optional
from typing import Any, Type
from time import time
from configparser import ConfigParser
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from aiocraft.mc.packet import Packet
from aiocraft.mc.auth import AuthInterface, AuthException, MojangAuthenticator, MicrosoftAuthenticator, OfflineAuthenticator
from aiocraft.mc.auth.microsoft import InvalidStateError
from aiocraft.packet import Packet
from aiocraft.auth import AuthInterface, AuthException, MojangAuthenticator, MicrosoftAuthenticator, OfflineAuthenticator
from aiocraft.auth.microsoft import InvalidStateError
from .storage import StorageDriver, SystemState, AuthenticatorState
from .game import GameState, GameChat, GameInventory, GameTablist, GameWorld, GameContainer, GamePosition
from .game import GameState, GameChat, GameInventory, GameTablist, GameWorld, GameContainer
from .addon import Addon
from .notifier import Notifier, Provider
@ -42,10 +42,13 @@ class Treepuncher(
notifier: Notifier
scheduler: AsyncIOScheduler
modules: List[Addon]
ctx: Dict[Any, Any]
modules: list[Addon]
ctx: dict[Any, Any]
_processing: bool
_proto_override: int
_host: str
_port: int
def __init__(
self,
@ -61,7 +64,7 @@ class Treepuncher(
authenticator : AuthInterface
def opt(k:str, required=False, default=None, t:type=str) -> Optional[Any]:
def opt(k, required=False, default=None, t=str):
v = kwargs.get(k)
if v is None:
v = self.cfg.get(k)
@ -95,13 +98,19 @@ class Treepuncher(
)
super().__init__(
opt('server', required=True),
authenticator=authenticator,
online_mode=opt('online_mode', default=True, t=bool),
force_port=opt('force_port', default=0, t=int),
resolve_srv=opt('resolve_srv', default=True, t=bool),
)
self._proto_override = opt('force_proto', t=int)
self._host = opt('server', required=True)
if ":" in self._host:
h, p = self._host.split(":", 1)
self._host = h
self._port = int(p)
else:
self._host, self._port = self.resolve_srv(self._host)
self.storage = StorageDriver(opt('session_file') or f"data/{name}.session") # TODO wrap with pathlib
self.notifier = Notifier(self)
@ -199,22 +208,21 @@ class Treepuncher(
async def _work(self):
self.logger.debug("Worker started")
try:
if "force_proto" in self.cfg:
self.dispatcher.set_proto(self.cfg.getint('force_proto'))
log_ignored_packets = self.cfg.getboolean('log_ignored_packets', fallback=False)
whitelist = self.callback_keys(filter=Packet)
if self._proto_override:
proto = self._proto_override
else:
try:
server_data = await self.info()
server_data = await self.info(self._host, self._port, whitelist=whitelist, log_ignored_packets=log_ignored_packets)
if "version" in server_data and "protocol" in server_data["version"]:
self.dispatcher.set_proto(server_data['version']['protocol'])
proto = server_data['version']['protocol']
except OSError as e:
self.logger.error("Connection error : %s", str(e))
self.dispatcher.whitelist(self.callback_keys(filter=Packet))
self.dispatcher.log_ignored_packets(self.cfg.getboolean('log_ignored_packets', fallback=False))
while self._processing:
try:
await self.join()
await self.join(self._host, self._port, proto, whitelist=whitelist, log_ignored_packets=log_ignored_packets)
except OSError as e:
self.logger.error("Connection error : %s", str(e))