feat: split world and position, added BlockUpdateEvent
This commit is contained in:
parent
e6a1ecc5c4
commit
f920bbc838
6 changed files with 105 additions and 88 deletions
|
@ -3,3 +3,4 @@ from .join_game import JoinGameEvent
|
||||||
from .death import DeathEvent
|
from .death import DeathEvent
|
||||||
from .system import ConnectedEvent, DisconnectedEvent
|
from .system import ConnectedEvent, DisconnectedEvent
|
||||||
from .connection import PlayerJoinEvent, PlayerLeaveEvent
|
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 .chat import GameChat
|
||||||
from .world import GameWorld
|
from .world import GameWorld
|
||||||
from .container import GameContainer
|
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,98 +1,27 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from time import time
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from aiocraft.mc.definitions import BlockPos
|
from aiocraft.mc.definitions import BlockPos
|
||||||
from aiocraft.mc.proto.play.clientbound import (
|
from aiocraft.mc.proto import PacketMapChunk, PacketBlockChange, PacketMultiBlockChange
|
||||||
PacketPosition, PacketMapChunk, PacketBlockChange, PacketMultiBlockChange, PacketSetPassengers,
|
from aiocraft.mc.types import twos_comp
|
||||||
PacketEntityTeleport, PacketRelEntityMove
|
|
||||||
)
|
|
||||||
from aiocraft.mc.proto.play.serverbound import PacketTeleportConfirm, PacketSteerVehicle
|
|
||||||
from aiocraft import Chunk, World # TODO these imports will hopefully change!
|
from aiocraft import Chunk, World # TODO these imports will hopefully change!
|
||||||
|
|
||||||
from ..scaffold import Scaffold
|
from ..scaffold import Scaffold
|
||||||
|
from ..events import BlockUpdateEvent
|
||||||
|
|
||||||
class GameWorld(Scaffold):
|
class GameWorld(Scaffold):
|
||||||
position : BlockPos
|
|
||||||
vehicle_id : Optional[int]
|
|
||||||
world : World
|
world : World
|
||||||
|
|
||||||
_last_steer_vehicle : float
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.position = BlockPos(0, 0, 0)
|
self.world = World()
|
||||||
self.vehicle_id = None
|
|
||||||
self._last_steer_vehicle = time()
|
|
||||||
|
|
||||||
@self.on_packet(PacketSetPassengers)
|
# Since this might require more resources, allow to disable it
|
||||||
async def player_enters_vehicle_cb(packet:PacketSetPassengers):
|
if not self.cfg.getboolean("process_world", fallback=True):
|
||||||
if self.vehicle_id is None: # might get mounted on a vehicle
|
return
|
||||||
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(PacketRelEntityMove)
|
|
||||||
async def entity_relative_move_cb(packet:PacketRelEntityMove):
|
|
||||||
if self.vehicle_id is None:
|
|
||||||
return
|
|
||||||
if self.vehicle_id != packet.entityId:
|
|
||||||
return
|
|
||||||
self.position = BlockPos(
|
|
||||||
self.position.x + packet.dX,
|
|
||||||
self.position.y + packet.dY,
|
|
||||||
self.position.z + packet.dZ
|
|
||||||
)
|
|
||||||
self.logger.debug(
|
|
||||||
"Position synchronized : (x:%.0f,y:%.0f,z:%.0f) (relMove vehicle)",
|
|
||||||
self.position.x, self.position.y, self.position.z
|
|
||||||
)
|
|
||||||
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
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@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
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.cfg.getboolean("process_world", fallback=False):
|
|
||||||
self.world = World()
|
|
||||||
|
|
||||||
|
if self.proto == 340: # Chunk parsing is only implemented for 1.12
|
||||||
@self.on_packet(PacketMapChunk)
|
@self.on_packet(PacketMapChunk)
|
||||||
async def map_chunk_cb(packet:PacketMapChunk):
|
async def map_chunk_cb(packet:PacketMapChunk):
|
||||||
assert isinstance(packet.bitMap, int)
|
assert isinstance(packet.bitMap, int)
|
||||||
|
@ -100,15 +29,34 @@ class GameWorld(Scaffold):
|
||||||
c.read(packet.chunkData)
|
c.read(packet.chunkData)
|
||||||
self.world.put(c, packet.x, packet.z, not packet.groundUp)
|
self.world.put(c, packet.x, packet.z, not packet.groundUp)
|
||||||
|
|
||||||
@self.on_packet(PacketBlockChange)
|
@self.on_packet(PacketBlockChange)
|
||||||
async def block_change_cb(packet:PacketBlockChange):
|
async def block_change_cb(packet:PacketBlockChange):
|
||||||
self.world.put_block(packet.location[0], packet.location[1], packet.location[2], packet.type)
|
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])
|
||||||
|
await self.run_callbacks(BlockUpdateEvent, BlockUpdateEvent(pos, packet.type))
|
||||||
|
|
||||||
@self.on_packet(PacketMultiBlockChange)
|
@self.on_packet(PacketMultiBlockChange)
|
||||||
async def multi_block_change_cb(packet:PacketMultiBlockChange):
|
async def multi_block_change_cb(packet:PacketMultiBlockChange):
|
||||||
|
if self.proto < 751:
|
||||||
chunk_x_off = packet.chunkX * 16
|
chunk_x_off = packet.chunkX * 16
|
||||||
chunk_z_off = packet.chunkZ * 16
|
chunk_z_off = packet.chunkZ * 16
|
||||||
for entry in packet.records:
|
for entry in packet.records:
|
||||||
x_off = (entry['horizontalPos'] >> 4 ) & 15
|
x_off = (entry['horizontalPos'] >> 4 ) & 15
|
||||||
z_off = entry['horizontalPos'] & 15
|
z_off = entry['horizontalPos'] & 15
|
||||||
self.world.put_block(x_off + chunk_x_off, entry['y'], z_off + chunk_z_off, entry['blockId'])
|
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'])
|
||||||
|
await self.run_callbacks(BlockUpdateEvent, BlockUpdateEvent(pos, entry['blockId']))
|
||||||
|
elif self.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)
|
||||||
|
await self.run_callbacks(BlockUpdateEvent, BlockUpdateEvent(pos, state))
|
||||||
|
else:
|
||||||
|
self.logger.error("Cannot process MultiBlockChange for protocol %d", self.proto)
|
||||||
|
|
|
@ -15,7 +15,7 @@ from aiocraft.mc.auth import AuthInterface, AuthException, MojangAuthenticator,
|
||||||
from aiocraft.mc.auth.microsoft import InvalidStateError
|
from aiocraft.mc.auth.microsoft import InvalidStateError
|
||||||
|
|
||||||
from .storage import StorageDriver, SystemState, AuthenticatorState
|
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 .addon import Addon
|
||||||
from .notifier import Notifier, Provider
|
from .notifier import Notifier, Provider
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ class Treepuncher(
|
||||||
GameInventory,
|
GameInventory,
|
||||||
GameContainer,
|
GameContainer,
|
||||||
GameTablist,
|
GameTablist,
|
||||||
|
GamePosition,
|
||||||
GameWorld
|
GameWorld
|
||||||
):
|
):
|
||||||
name: str
|
name: str
|
||||||
|
@ -222,7 +223,7 @@ class Treepuncher(
|
||||||
|
|
||||||
except AuthException as e:
|
except AuthException as e:
|
||||||
self.logger.error("Auth exception : [%s|%d] %s (%s)", e.endpoint, e.code, e.data, e.kwargs)
|
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")
|
self.logger.error("Invalid authenticator state")
|
||||||
if isinstance(self.authenticator, MicrosoftAuthenticator):
|
if isinstance(self.authenticator, MicrosoftAuthenticator):
|
||||||
self.logger.info("Obtain an auth code by visiting %s", self.authenticator.url())
|
self.logger.info("Obtain an auth code by visiting %s", self.authenticator.url())
|
||||||
|
|
Loading…
Reference in a new issue