More packets
This commit is contained in:
parent
7ef685df4f
commit
6116030f66
|
@ -1,3 +1,5 @@
|
|||
*.py[cow]
|
||||
__pycache__/
|
||||
venv/
|
||||
state
|
||||
packets.txt
|
||||
|
|
|
@ -7,29 +7,35 @@ from .debug import debug_client, debug_server
|
|||
MAX_SIZE = 0x400000
|
||||
|
||||
async def handle_server(writer: StreamWriter, server: socket.socket, fp):
|
||||
while (packet := await loop.sock_recv(server, MAX_SIZE)):
|
||||
try:
|
||||
debug_server(packet, fp)
|
||||
except Exception as e:
|
||||
print(f'[S] error: {e}')
|
||||
writer.write(packet)
|
||||
await writer.drain()
|
||||
try:
|
||||
while (packet := await loop.sock_recv(server, MAX_SIZE)):
|
||||
try:
|
||||
debug_server(packet, fp)
|
||||
except Exception as e:
|
||||
print(f'[S] error: {e}')
|
||||
writer.write(packet)
|
||||
await writer.drain()
|
||||
except Exception as e:
|
||||
print(f'handle_server(): {e}')
|
||||
|
||||
async def handle_client(reader: StreamReader, writer: StreamWriter):
|
||||
print(reader, writer)
|
||||
server = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
server.connect(('201:4f8c:4ea:0:71ec:6d7:6f1b:a4f9', 25565))
|
||||
server.setblocking(False)
|
||||
try:
|
||||
server = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
server.connect(('201:4f8c:4ea:0:71ec:6d7:6f1b:a4f9', 25565))
|
||||
server.setblocking(False)
|
||||
|
||||
with open("packets.txt", "w") as fp:
|
||||
loop.create_task(handle_server(writer, server, fp))
|
||||
with open("packets.txt", "w") as fp:
|
||||
loop.create_task(handle_server(writer, server, fp))
|
||||
|
||||
while (packet := await reader.read(MAX_SIZE)):
|
||||
try:
|
||||
debug_client(packet, fp)
|
||||
except Exception as e:
|
||||
print(f'[C] error: {e}')
|
||||
await loop.sock_sendall(server, packet)
|
||||
while (packet := await reader.read(MAX_SIZE)):
|
||||
try:
|
||||
debug_client(packet, fp)
|
||||
except Exception as e:
|
||||
print(f'[C] error: {e}')
|
||||
await loop.sock_sendall(server, packet)
|
||||
except Exception as e:
|
||||
print(f'handle_client(): {e}')
|
||||
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
from sys import argv
|
||||
from pathlib import Path
|
||||
from re import findall
|
||||
from typing import Any
|
||||
|
||||
def main(argv: list[str]):
|
||||
packetname, *fields = argv
|
||||
|
||||
packet_id = int(findall(r'\d+', packetname)[0])
|
||||
|
||||
with open(Path('bta_proxy/packets/') / f'{packetname.lower()}.py', 'w') as f:
|
||||
f.write(f'from .base import Packet\n')
|
||||
f.write(f'\n')
|
||||
f.write(f'class {packetname}(Packet, packet_id={packet_id}):\n')
|
||||
f.write(f' FIELDS = [\n')
|
||||
for field in fields:
|
||||
args: list[Any]
|
||||
name, typename, *args = field.split(':')
|
||||
for i, arg in enumerate(args):
|
||||
try:
|
||||
args[i] = int(arg)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if args:
|
||||
args_str = repr((typename, *args))
|
||||
f.write(f' ({name!r}, {args_str}),\n')
|
||||
else:
|
||||
f.write(f' ({name!r}, {typename!r}),\n')
|
||||
f.write(f' ]\n')
|
||||
|
||||
with open('bta_proxy/packets/__init__.py', 'a') as f:
|
||||
f.write(f'from .{packetname.lower()} import {packetname}\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(argv[1:])
|
|
@ -20,6 +20,7 @@ class DataInputStream:
|
|||
|
||||
def read_byte(self) -> int:
|
||||
if self._cursor >= len(self._buffer):
|
||||
print(f'\033[91mstream overread in {self._buffer}\033[0m')
|
||||
raise EOFError('stream overread')
|
||||
self._cursor += 1
|
||||
return self._buffer[self._cursor - 1]
|
||||
|
@ -69,6 +70,5 @@ class DataInputStream:
|
|||
|
||||
def read_string(self) -> str:
|
||||
size = self.read_short()
|
||||
print(f'read_bytes({size=})')
|
||||
return self.read_bytes(size).decode('utf-8')
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from collections.abc import Iterable
|
||||
|
||||
from bta_proxy.packets.base import Packet
|
||||
from bta_proxy.packets.packet50prechunk import Packet50PreChunk
|
||||
from bta_proxy.packets import *
|
||||
from .datainputstream import DataInputStream
|
||||
from typing import Generator, TextIO, TypeVar
|
||||
|
||||
|
@ -23,15 +23,13 @@ def debug_client(buffer: bytes, tmpfile: TextIO):
|
|||
while not stream.empty():
|
||||
try:
|
||||
packet = Packet.parse_packet(stream)
|
||||
match type(packet)():
|
||||
case Packet50PreChunk():
|
||||
continue
|
||||
match packet.packet_id:
|
||||
case _:
|
||||
print(packet)
|
||||
print('[C]', packet)
|
||||
except ValueError:
|
||||
# print(f'[C:rest] {stream.end()}')
|
||||
buf = stream.end()
|
||||
print("[C]", len(buf), buf, file=tmpfile)
|
||||
print(f"[C] {buf[0]=} {len(buf)=}, {buf=}", file=tmpfile)
|
||||
|
||||
|
||||
def debug_server(buffer: bytes, tmpfile: TextIO):
|
||||
|
@ -40,9 +38,15 @@ def debug_server(buffer: bytes, tmpfile: TextIO):
|
|||
while not stream.empty():
|
||||
try:
|
||||
packet = Packet.parse_packet(stream)
|
||||
print(packet)
|
||||
match packet.packet_id:
|
||||
case Packet50PreChunk.packet_id:
|
||||
continue
|
||||
case Packet38EntityStatus.packet_id:
|
||||
continue
|
||||
case _:
|
||||
print('[S]', packet)
|
||||
except ValueError:
|
||||
# print(f'[S:rest] {stream.end()}')
|
||||
buf = stream.end()
|
||||
print("[S]", len(buf), buf, file=tmpfile)
|
||||
print(f"[S] {buf[0]=} {len(buf)=}, {buf=}", file=tmpfile)
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
from typing import Any
|
||||
from bta_proxy.datainputstream import DataInputStream
|
||||
from enum import Enum
|
||||
from dataclasses import dataclass
|
||||
|
||||
from bta_proxy.itemstack import ItemStack
|
||||
|
||||
class DataItemType(Enum):
|
||||
BYTE = 0
|
||||
SHORT = 1
|
||||
INTEGER = 2
|
||||
FLOAT = 3
|
||||
STRING = 4
|
||||
ITEMSTACK = 5
|
||||
CHUNK_COORDINATES = 6
|
||||
|
||||
@dataclass
|
||||
class DataItem:
|
||||
type: DataItemType
|
||||
id: int
|
||||
value: Any
|
||||
|
||||
class EntityData:
|
||||
@classmethod
|
||||
def read_from(cls, dis: DataInputStream) -> list[DataItem]:
|
||||
items = []
|
||||
while (data := dis.read_byte()) != 0x7F:
|
||||
item_type = DataItemType((data & 0xE0) >> 5)
|
||||
item_id: int = data & 0x1F
|
||||
match item_type:
|
||||
case DataItemType.BYTE:
|
||||
items.append(DataItem(item_type, item_id, dis.read_byte()))
|
||||
case DataItemType.SHORT:
|
||||
items.append(DataItem(item_type, item_id, dis.read_short()))
|
||||
case DataItemType.FLOAT:
|
||||
items.append(DataItem(item_type, item_id, dis.read_float()))
|
||||
case DataItemType.STRING:
|
||||
items.append(DataItem(item_type, item_id, dis.read_string()))
|
||||
case DataItemType.ITEMSTACK:
|
||||
items.append(DataItem(item_type, item_id, ItemStack.read_from(dis)))
|
||||
case DataItemType.CHUNK_COORDINATES:
|
||||
x = dis.read_float()
|
||||
y = dis.read_float()
|
||||
z = dis.read_float()
|
||||
items.append(DataItem(item_type, item_id, (x, y, z)))
|
||||
return items
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
from bta_proxy.datainputstream import DataInputStream
|
||||
|
||||
|
||||
class ItemStack:
|
||||
__slots__ = ("item_id", "count", "data")
|
||||
def __init__(self, item_id: int, count: int, data: int):
|
||||
self.item_id = item_id
|
||||
self.count = count
|
||||
self.data = data
|
||||
|
||||
@classmethod
|
||||
def read_from(cls, stream: DataInputStream) -> 'ItemStack':
|
||||
item_id = stream.read_short()
|
||||
count = stream.read_byte()
|
||||
data = stream.read_ushort()
|
||||
return cls(item_id, count, data)
|
|
@ -5,3 +5,10 @@ from .packet3chat import Packet3Chat
|
|||
from .packet50prechunk import Packet50PreChunk
|
||||
from .packet73weatherstatus import Packet73WeatherStatus
|
||||
from .packet38entitystatus import Packet38EntityStatus
|
||||
from .packet136sendkey import Packet136SendKey
|
||||
from .packet6spawnposition import Packet6SpawnPosition
|
||||
from .packet25entitypainting import Packet25EntityPainting
|
||||
from .packet24mobspawn import Packet24MobSpawn
|
||||
from .packet4updatetime import Packet4UpdateTime
|
||||
from .packet138playerlist import Packet138PlayerList
|
||||
from .packet72updateplayerprofile import Packet72UpdatePlayerProfile
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
from typing import Any, ClassVar, Type
|
||||
|
||||
from bta_proxy.entitydata import EntityData
|
||||
from ..datainputstream import DataInputStream
|
||||
|
||||
class Packet:
|
||||
REGISTRY: ClassVar[dict[int, Type['Packet']]] = {}
|
||||
FIELDS: ClassVar[list[tuple[str, Any]]] = []
|
||||
PACKET_ID: ClassVar[int] = 0x00
|
||||
packet_id: int
|
||||
|
||||
def __init__(self, **params):
|
||||
for k, v in params.items():
|
||||
|
@ -46,11 +48,15 @@ class Packet:
|
|||
return stream.read_boolean()
|
||||
case 'bytes', length:
|
||||
return stream.read_bytes(length)
|
||||
case 'entitydata':
|
||||
return EntityData.read_from(stream)
|
||||
case _:
|
||||
raise ValueError(f'unknown type {datatype}')
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
Packet.REGISTRY[cls.PACKET_ID] = cls
|
||||
def __init_subclass__(cls, packet_id: int, **kwargs) -> None:
|
||||
Packet.REGISTRY[packet_id] = cls
|
||||
cls.packet_id = packet_id
|
||||
super().__init_subclass__(**kwargs)
|
||||
|
||||
@classmethod
|
||||
def parse_packet(cls, stream: DataInputStream) -> 'Packet':
|
||||
|
@ -61,7 +67,7 @@ class Packet:
|
|||
return cls.REGISTRY[packet_id].read_from(stream)
|
||||
|
||||
def __repr__(self):
|
||||
pkt_name = self.REGISTRY[self.PACKET_ID].__name__
|
||||
pkt_name = self.REGISTRY[self.packet_id].__name__
|
||||
fields = []
|
||||
for name, _ in self.FIELDS:
|
||||
fields.append(f'{name}={getattr(self, name)!r}')
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
from .base import Packet
|
||||
|
||||
class Packet136SendKey(Packet, packet_id=136):
|
||||
FIELDS = [
|
||||
('key', ('str', 344)),
|
||||
]
|
|
@ -0,0 +1,7 @@
|
|||
from .base import Packet
|
||||
|
||||
class Packet138PlayerList(Packet, packet_id=138):
|
||||
FIELDS = [
|
||||
('players', 'str'),
|
||||
('scores', 'str'),
|
||||
]
|
|
@ -1,9 +1,7 @@
|
|||
|
||||
from typing import ClassVar
|
||||
from .base import Packet
|
||||
|
||||
class Packet1Login(Packet):
|
||||
PACKET_ID: ClassVar[int] = 1
|
||||
class Packet1Login(Packet, packet_id=1):
|
||||
|
||||
FIELDS = [
|
||||
('entity_id_and_proto', 'uint'),
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
from .base import Packet
|
||||
|
||||
class Packet24MobSpawn(Packet, packet_id=24):
|
||||
FIELDS = [
|
||||
('entity_id', 'int'),
|
||||
('type', 'byte'),
|
||||
('x', 'int'),
|
||||
('y', 'int'),
|
||||
('z', 'int'),
|
||||
('yaw', 'byte'),
|
||||
('pitch', 'byte'),
|
||||
('metadata', 'entitydata'),
|
||||
('nickname', 'string'),
|
||||
('chatcolor', 'byte'),
|
||||
]
|
|
@ -0,0 +1,11 @@
|
|||
from .base import Packet
|
||||
|
||||
class Packet25EntityPainting(Packet, packet_id=25):
|
||||
FIELDS = [
|
||||
('entity_id', 'int'),
|
||||
('title', 'str'),
|
||||
('x', 'int'),
|
||||
('y', 'int'),
|
||||
('z', 'int'),
|
||||
('direction', 'int'),
|
||||
]
|
|
@ -2,8 +2,7 @@
|
|||
from typing import ClassVar
|
||||
from .base import Packet
|
||||
|
||||
class Packet2Handshake(Packet):
|
||||
PACKET_ID: ClassVar[int] = 2
|
||||
class Packet2Handshake(Packet, packet_id=2):
|
||||
|
||||
FIELDS = [
|
||||
('username', ('str', 16)),
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
from typing import ClassVar
|
||||
from .base import Packet
|
||||
|
||||
class Packet38EntityStatus(Packet):
|
||||
PACKET_ID: ClassVar[int] = 38
|
||||
class Packet38EntityStatus(Packet, packet_id=38):
|
||||
|
||||
FIELDS = [
|
||||
('entity_id', 'int'),
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
from typing import ClassVar
|
||||
from .base import Packet
|
||||
|
||||
class Packet3Chat(Packet):
|
||||
PACKET_ID: ClassVar[int] = 3
|
||||
class Packet3Chat(Packet, packet_id=3):
|
||||
|
||||
FIELDS = [
|
||||
('message', ('str', 1024)),
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
from .base import Packet
|
||||
|
||||
class Packet4UpdateTime(Packet, packet_id=4):
|
||||
FIELDS = [
|
||||
('time', 'long'),
|
||||
]
|
|
@ -2,8 +2,7 @@
|
|||
from typing import ClassVar
|
||||
from .base import Packet
|
||||
|
||||
class Packet50PreChunk(Packet):
|
||||
PACKET_ID: ClassVar[int] = 50
|
||||
class Packet50PreChunk(Packet, packet_id=50):
|
||||
|
||||
FIELDS = [
|
||||
('x', 'int'),
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
from .base import Packet
|
||||
|
||||
class Packet6SpawnPosition(Packet, packet_id=6):
|
||||
FIELDS = [
|
||||
('x', 'int'),
|
||||
('y', 'int'),
|
||||
('z', 'int'),
|
||||
]
|
|
@ -0,0 +1,11 @@
|
|||
from .base import Packet
|
||||
|
||||
class Packet72UpdatePlayerProfile(Packet, packet_id=72):
|
||||
FIELDS = [
|
||||
('username', ('str', 16)),
|
||||
('nickname', ('str', 32)),
|
||||
('score', 'int'),
|
||||
('color', 'byte'),
|
||||
('online', 'bool'),
|
||||
('op', 'bool'),
|
||||
]
|
|
@ -1,10 +1,7 @@
|
|||
|
||||
from typing import ClassVar
|
||||
from .base import Packet
|
||||
|
||||
class Packet73WeatherStatus(Packet):
|
||||
PACKET_ID: ClassVar[int] = 50
|
||||
|
||||
class Packet73WeatherStatus(Packet, packet_id=73):
|
||||
FIELDS = [
|
||||
('dim', 'int'),
|
||||
('id', 'int'),
|
||||
|
|
Loading…
Reference in New Issue