feat: split world and position, added BlockUpdateEvent

This commit is contained in:
əlemi 2023-03-17 11:07:13 +01:00
parent e6a1ecc5c4
commit f920bbc838
6 changed files with 105 additions and 88 deletions

View file

@ -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

View 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

View file

@ -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

View 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
)
)

View file

@ -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)

View file

@ -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())