1.7.7.0_02 and some other fixes

This commit is contained in:
Casey 2023-08-28 22:07:33 +03:00
parent 36b2264471
commit 7ac026dccc
Signed by: hkc
GPG Key ID: F0F6CFE11CDB0960
8 changed files with 160 additions and 45 deletions

View File

@ -1,27 +1,39 @@
from asyncio.queues import Queue from asyncio.queues import Queue
from logging import getLogger
import struct import struct
logger = getLogger(__name__)
class AsyncDataInputStream: class AsyncDataInputStream:
def __init__(self, queue: Queue): def __init__(self, queue: Queue):
self._queue = queue self.queue = queue
self._buffer = b'' self.buffer = b''
self._last = b'' self.last = b''
self.offset = 0
def peek_rest(self):
return self.buffer
def read_rest(self): def read_rest(self):
out = self._buffer out = self.buffer
self._buffer = b'' self.buffer = b''
return out return out
async def read_bytes(self, n: int) -> bytes: async def read_bytes(self, n: int) -> bytes:
if len(self._buffer) < n: logger.debug(f"trying to read {n} bytes")
self._last = (await self._queue.get()) if len(self.buffer) < n:
if not self._last: self.last = (await self.queue.get())
logger.debug(f"new packet from the queue {self.last!r}")
if not self.last:
raise EOFError('empty packet was received') raise EOFError('empty packet was received')
self._buffer += self._last self.buffer += self.last
out, self._buffer = self._buffer[:n], self._buffer[n:] self.offset -= len(self.last)
out, self.buffer = self.buffer[:n], self.buffer[n:]
self.offset += n
return out return out
async def read(self) -> int: async def read(self) -> int:
self.offset += 1
return (await self.read_bytes(1))[0] return (await self.read_bytes(1))[0]
read_ubyte = read read_ubyte = read
@ -73,7 +85,7 @@ class AsyncDataInputStream:
return value return value
async def read_string(self) -> str: async def read_string(self) -> str:
last = self._last last = self.last
size = await self.read_short() size = await self.read_short()
try: try:
return (await self.read_bytes(size)).decode('utf-8') return (await self.read_bytes(size)).decode('utf-8')

View File

@ -43,14 +43,14 @@ async def inspect_client(queue: Queue, addr: tuple[str, int]):
stats[pkt.packet_id] = stats.get(pkt.packet_id, 0) + 1 stats[pkt.packet_id] = stats.get(pkt.packet_id, 0) + 1
match pkt.packet_id: match pkt.packet_id:
case Packet10Flying.packet_id: # case Packet10Flying.packet_id:
continue # continue
case Packet11PlayerPosition.packet_id: # case Packet11PlayerPosition.packet_id:
continue # continue
case Packet12PlayerLook.packet_id: # case Packet12PlayerLook.packet_id:
continue # continue
case Packet13LookMove.packet_id: # case Packet13LookMove.packet_id:
continue # continue
case _: case _:
print(f"C {delta*1000:+8.1f}ms {pkt}") print(f"C {delta*1000:+8.1f}ms {pkt}")
if pkt.packet_id == Packet255KickDisconnect.packet_id: if pkt.packet_id == Packet255KickDisconnect.packet_id:
@ -86,26 +86,26 @@ async def inspect_server(queue: Queue, addr: tuple[str, int]):
stats[pkt.packet_id] = stats.get(pkt.packet_id, 0) + 1 stats[pkt.packet_id] = stats.get(pkt.packet_id, 0) + 1
match pkt.packet_id: match pkt.packet_id:
case Packet53BlockChange.packet_id: # case Packet53BlockChange.packet_id:
continue # continue
case Packet50PreChunk.packet_id: # case Packet50PreChunk.packet_id:
continue # continue
case Packet51MapChunk.packet_id: # case Packet51MapChunk.packet_id:
continue # continue
case Packet34EntityTeleport.packet_id: # case Packet34EntityTeleport.packet_id:
continue # continue
case Packet28EntityVelocity.packet_id: # case Packet28EntityVelocity.packet_id:
continue # continue
case Packet31RelEntityMove.packet_id: # case Packet31RelEntityMove.packet_id:
continue # continue
case Packet32EntityLook.packet_id: # case Packet32EntityLook.packet_id:
continue # continue
case Packet33RelEntityMoveLook.packet_id: # case Packet33RelEntityMoveLook.packet_id:
continue # continue
case Packet73WeatherStatus.packet_id: # case Packet73WeatherStatus.packet_id:
continue # continue
case Packet52MultiBlockChange.packet_id: # case Packet52MultiBlockChange.packet_id:
continue # continue
case _: case _:
print(f"S {delta*1000:+8.1f}ms {pkt}") print(f"S {delta*1000:+8.1f}ms {pkt}")
finally: finally:

View File

@ -80,3 +80,4 @@ from .packet108sethotbaroffset import Packet108SetHotbarOffset
from .packet5playerinventory import Packet5PlayerInventory from .packet5playerinventory import Packet5PlayerInventory
from .packet5playerinventory import Packet5PlayerInventory from .packet5playerinventory import Packet5PlayerInventory
from .packet5playerinventory import Packet5PlayerInventory from .packet5playerinventory import Packet5PlayerInventory
from .packet143photomode import Packet143PhotoMode

View File

@ -4,7 +4,9 @@ import gzip
from bta_proxy.entitydata import EntityData from bta_proxy.entitydata import EntityData
from bta_proxy.itemstack import ItemStack from bta_proxy.itemstack import ItemStack
from ..datainputstream import AsyncDataInputStream from ..datainputstream import AsyncDataInputStream
from logging import getLogger
logger = getLogger(__name__)
def try_int(v: str) -> Union[str, int]: def try_int(v: str) -> Union[str, int]:
try: try:
@ -24,6 +26,7 @@ class Packet:
@classmethod @classmethod
async def read_data_from(cls, stream: AsyncDataInputStream) -> "Packet": async def read_data_from(cls, stream: AsyncDataInputStream) -> "Packet":
logger.debug("Packet.read_data_from(%r)", stream)
fields: dict = {} fields: dict = {}
for key, datatype in cls.FIELDS: for key, datatype in cls.FIELDS:
if "?" in key: if "?" in key:
@ -35,6 +38,7 @@ class Packet:
elif not fields[cond]: elif not fields[cond]:
continue continue
try: try:
logger.debug(f"reading {key=} of type {datatype!r} ({fields=})")
fields[key] = await cls.read_field(stream, datatype, fields) fields[key] = await cls.read_field(stream, datatype, fields)
except Exception as e: except Exception as e:
raise ValueError(f"Failed getting key {key} on {cls}") from e raise ValueError(f"Failed getting key {key} on {cls}") from e
@ -46,10 +50,12 @@ class Packet:
datatype: Any, datatype: Any,
fields: dict[str, Any] = {}, fields: dict[str, Any] = {},
): ):
logger.debug(f"Packet.read_field(_, {datatype=}, {fields=})")
match datatype: match datatype:
case "list", sizekey, *args: case "list", sizekey, *args:
args = args[0] if len(args) == 1 else tuple(args) args = args[0] if len(args) == 1 else tuple(args)
length = sizekey if isinstance(try_int(sizekey), int) else fields[sizekey] size = try_int(sizekey)
length = size if isinstance(size, int) else fields[sizekey]
return [ return [
await Packet.read_field(stream, args, fields) await Packet.read_field(stream, args, fields)
for _ in range(length) for _ in range(length)
@ -156,6 +162,7 @@ class Packet:
raise ValueError(f"unknown type {datatype}") raise ValueError(f"unknown type {datatype}")
def __init_subclass__(cls, packet_id: int, **kwargs) -> None: def __init_subclass__(cls, packet_id: int, **kwargs) -> None:
logger.debug(f"registered packet {cls} with id = {packet_id}")
Packet.REGISTRY[packet_id] = cls Packet.REGISTRY[packet_id] = cls
cls.packet_id = packet_id cls.packet_id = packet_id
super().__init_subclass__(**kwargs) super().__init_subclass__(**kwargs)
@ -166,13 +173,15 @@ class Packet:
@classmethod @classmethod
async def read_packet(cls, stream: AsyncDataInputStream) -> "Packet": async def read_packet(cls, stream: AsyncDataInputStream) -> "Packet":
packet_id: int = await stream.read() packet_id: int = await stream.read()
logger.debug(f"incoming {packet_id=}")
if packet_id not in cls.REGISTRY: if packet_id not in cls.REGISTRY:
raise ValueError( raise ValueError(
f"invalid packet 0x{packet_id:02x} ({packet_id}) (rest: {stream.read_rest()[:16]}...)" f"invalid packet 0x{packet_id:02x} ({packet_id}) (rest: {stream.peek_rest()[:16]}...)"
) )
pkt = await cls.REGISTRY[packet_id].read_data_from(stream) pkt = await cls.REGISTRY[packet_id].read_data_from(stream)
pkt.packet_id = packet_id pkt.packet_id = packet_id
pkt.post_creation() pkt.post_creation()
logger.debug(f"received {pkt}")
return pkt return pkt
def __repr__(self): def __repr__(self):

View File

@ -2,6 +2,7 @@ from .base import Packet
class Packet138PlayerList(Packet, packet_id=138): class Packet138PlayerList(Packet, packet_id=138):
FIELDS = [ FIELDS = [
('players', 'str'), ('n_players', 'int'),
('scores', 'str'), ('players', ('list', 'n_players', 'str')),
('scores', ('list', 'n_players', 'int')),
] ]

View File

@ -1,12 +1,11 @@
from .base import Packet from .base import Packet
class Packet141UpdateFlag(Packet, packet_id=141): class Packet141UpdateFlag(Packet, packet_id=141):
__slots__ = ('x', 'y', 'z', 'colors', 'items') __slots__ = ('x', 'y', 'z', 'colors')
FIELDS = [ FIELDS = [
('x', 'int'), ('x', 'int'),
('y', 'short'), ('y', 'short'),
('z', 'int'), ('z', 'int'),
('colors', ('bytes', 384)), ('colors', ('bytes', 384)),
('items', ('list', 3, 'nbt')),
('owner', 'string') ('owner', 'string')
] ]

View File

@ -0,0 +1,7 @@
from .base import Packet
class Packet143PhotoMode(Packet, packet_id=143):
__slots__ = ('disabled',)
FIELDS = [
('disabled', 'bool'),
]

86
tools/packetreader.py Normal file
View File

@ -0,0 +1,86 @@
# x-run: PYTHONPATH=.. python packetreader.py ../packets-127.0.0.1-54356-server.txt.gz
from asyncio.queues import Queue
import asyncio
from bta_proxy.datainputstream import AsyncDataInputStream
from bta_proxy.packets import Packet
from sys import argv
from gzip import open as open_gzip
from json import loads
import logging
loggers = [
logging.getLogger(name)
for name in logging.root.manager.loggerDict
if name.startswith("bta_proxy")
]
class CustomFormatter(logging.Formatter):
grey = "\x1b[38;20m"
yellow = "\x1b[93;20m"
red = "\x1b[91;20m"
bold_red = "\x1b[91;1m"
reset = "\x1b[0m"
fmt = "(%(filename)s:%(lineno)d) %(name)s - %(message)s"
FORMATS = {
logging.DEBUG: "\x1b[92m" + fmt + reset,
logging.INFO: "\x1b[94m" + fmt + reset,
logging.WARNING: yellow + fmt + reset,
logging.ERROR: red + fmt + reset,
logging.CRITICAL: bold_red + fmt + reset
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
streamhandler = logging.StreamHandler()
streamhandler.setLevel(logging.INFO)
streamhandler.setFormatter(CustomFormatter())
for logger in loggers:
logger.setLevel(logging.DEBUG)
logger = logging.getLogger("packetreader")
logger.setLevel(logging.DEBUG)
logging.getLogger().addHandler(streamhandler)
async def amain(stream: AsyncDataInputStream):
while True:
try:
pkt = await Packet.read_packet(stream)
logger.info(f"we just got a package {pkt.__class__.__name__}")
for key, _ in pkt.FIELDS:
logger.info(f"=== pkt.{key} = {getattr(pkt, key)!r}")
except EOFError:
logger.warning("EOFError")
break
except Exception as e:
logger.error(e)
logger.warning("ignoring it :)")
logger.info("exiting")
def main(filename: str):
queue = Queue()
with open_gzip(filename, "rt") as fp:
for line in fp:
data = loads(line.strip())
queue.put_nowait(bytes.fromhex(data["b"]))
queue.put_nowait(None)
stream = AsyncDataInputStream(queue)
loop = asyncio.get_event_loop()
loop.run_until_complete(amain(stream))
while True:
pass
if __name__ == "__main__":
main(argv[1])