1.7.7.0_02 and some other fixes
This commit is contained in:
parent
36b2264471
commit
7ac026dccc
|
@ -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')
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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')),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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')
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
from .base import Packet
|
||||||
|
|
||||||
|
class Packet143PhotoMode(Packet, packet_id=143):
|
||||||
|
__slots__ = ('disabled',)
|
||||||
|
FIELDS = [
|
||||||
|
('disabled', 'bool'),
|
||||||
|
]
|
|
@ -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])
|
Loading…
Reference in New Issue