First 0.6 draft

No 0.6 code is tested yet
This commit is just about adding all 0.6 code without
breaking existing 0.7 tests
This commit is contained in:
ChillerDragon 2023-05-19 12:23:43 +02:00
parent d044e8dbad
commit 7c70e32725
72 changed files with 2828 additions and 9 deletions

View file

@ -1033,7 +1033,8 @@ def match_connless{self.protocol_version}(msg_id: bytes, data: bytes) -> Connles
raise ValueError(f"Enum not found '{enum_name}'")
def gen_enum_file(self) -> None:
enum_code: str = 'from enum import Enum\n\n'
enum_code = '# pylint: disable=duplicate-code\n' # TODO: remove
enum_code += 'from enum import Enum\n\n'
enum: GameEnumJson
for enum in self.game_enums:
base: str = name_to_camel(enum['name'])
@ -1100,12 +1101,11 @@ def main() -> None:
'../../libtw2/gamenet/generate/spec/teeworlds-0.7.5.json',
'7'
),
SpecInfo(
'../../libtw2/gamenet/generate/spec/teeworlds-0.6.json',
'6'
)
]
# SpecInfo(
# '../../libtw2/gamenet/generate/spec/teeworlds-0.6.json',
# '6'
# )
# ]
for spec_info in spec_infos:
spec_file = os.path.join(
dirname,

90
twnet_parser/enum6.py Normal file
View file

@ -0,0 +1,90 @@
# pylint: skip-file
from enum import Enum
class Emote(Enum):
NORMAL: int = 0
PAIN: int = 1
HAPPY: int = 2
SURPRISE: int = 3
ANGRY: int = 4
BLINK: int = 5
class Powerup(Enum):
HEALTH: int = 0
ARMOR: int = 1
WEAPON: int = 2
NINJA: int = 3
class Emoticon(Enum):
OOP: int = 0
EXCLAMATION: int = 1
HEARTS: int = 2
DROP: int = 3
DOTDOT: int = 4
MUSIC: int = 5
SORRY: int = 6
GHOST: int = 7
SUSHI: int = 8
SPLATTEE: int = 9
DEVILTEE: int = 10
ZOMG: int = 11
ZZZ: int = 12
WTF: int = 13
EYES: int = 14
QUESTION: int = 15
class Weapon(Enum):
HAMMER: int = 0
PISTOL: int = 1
SHOTGUN: int = 2
GRENADE: int = 3
RIFLE: int = 4
NINJA: int = 5
class Team(Enum):
SPECTATORS: int = -1
RED: int = 0
BLUE: int = 1
class Sound(Enum):
GUN_FIRE: int = 0
SHOTGUN_FIRE: int = 1
GRENADE_FIRE: int = 2
HAMMER_FIRE: int = 3
HAMMER_HIT: int = 4
NINJA_FIRE: int = 5
GRENADE_EXPLODE: int = 6
NINJA_HIT: int = 7
RIFLE_FIRE: int = 8
RIFLE_BOUNCE: int = 9
WEAPON_SWITCH: int = 10
PLAYER_PAIN_SHORT: int = 11
PLAYER_PAIN_LONG: int = 12
BODY_LAND: int = 13
PLAYER_AIRJUMP: int = 14
PLAYER_JUMP: int = 15
PLAYER_DIE: int = 16
PLAYER_SPAWN: int = 17
PLAYER_SKID: int = 18
TEE_CRY: int = 19
HOOK_LOOP: int = 20
HOOK_ATTACH_GROUND: int = 21
HOOK_ATTACH_PLAYER: int = 22
HOOK_NOATTACH: int = 23
PICKUP_HEALTH: int = 24
PICKUP_ARMOR: int = 25
PICKUP_GRENADE: int = 26
PICKUP_SHOTGUN: int = 27
PICKUP_NINJA: int = 28
WEAPON_SPAWN: int = 29
WEAPON_NOAMMO: int = 30
HIT: int = 31
CHAT_SERVER: int = 32
CHAT_CLIENT: int = 33
CHAT_HIGHLIGHT: int = 34
CTF_DROP: int = 35
CTF_RETURN: int = 36
CTF_GRAB_PL: int = 37
CTF_GRAB_EN: int = 38
CTF_CAPTURE: int = 39
MENU: int = 40

View file

@ -1,3 +1,4 @@
# pylint: skip-file
from enum import Enum
class Pickup(Enum):

View file

@ -0,0 +1,28 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.packer import pack_be_uint16
from typing import Literal
class MsgCount(PrettyPrint):
def __init__(
self,
count: int = 0
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.count'
self.message_id: list[int] = [255, 255, 255, 255, 115, 105, 122, 50]
self.count: int = count
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.count = unpacker.get_be_uint16()
return True
def pack(self) -> bytes:
return pack_be_uint16(self.count)

View file

@ -0,0 +1,22 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from typing import Literal
class MsgForwardCheck(PrettyPrint):
def __init__(
self
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.forward_check'
self.message_id: list[int] = [255, 255, 255, 255, 102, 119, 63, 63]
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,22 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from typing import Literal
class MsgForwardError(PrettyPrint):
def __init__(
self
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.forward_error'
self.message_id: list[int] = [255, 255, 255, 255, 102, 119, 101, 114]
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,22 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from typing import Literal
class MsgForwardOk(PrettyPrint):
def __init__(
self
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.forward_ok'
self.message_id: list[int] = [255, 255, 255, 255, 102, 119, 111, 107]
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,22 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from typing import Literal
class MsgForwardResponse(PrettyPrint):
def __init__(
self
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.forward_response'
self.message_id: list[int] = [255, 255, 255, 255, 102, 119, 33, 33]
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,28 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.packer import pack_be_uint16
from typing import Literal
class MsgHeartbeat(PrettyPrint):
def __init__(
self,
alt_port: int = 0
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.heartbeat'
self.message_id: list[int] = [255, 255, 255, 255, 98, 101, 97, 50]
self.alt_port: int = alt_port
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.alt_port = unpacker.get_be_uint16()
return True
def pack(self) -> bytes:
return pack_be_uint16(self.alt_port)

View file

@ -0,0 +1,68 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.packer import SANITIZE_CC, pack_str
from typing import Literal
class MsgInfo(PrettyPrint):
def __init__(
self,
token: int = 0,
version: str = 'default',
name: str = 'default',
map: str = 'default',
game_type: str = 'default',
flags: int = 0,
num_players: int = 0,
max_players: int = 0,
num_clients: int = 0,
max_clients: int = 0,
clients: bytes = b''
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.info'
self.message_id: list[int] = [255, 255, 255, 255, 105, 110, 102, 51]
self.token: int = token
self.version: str = version
self.name: str = name
self.map: str = map
self.game_type: str = game_type
self.flags: int = flags
self.num_players: int = num_players
self.max_players: int = max_players
self.num_clients: int = num_clients
self.max_clients: int = max_clients
self.clients: bytes = clients
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.token = int(unpacker.get_str())
self.version = unpacker.get_str(SANITIZE_CC)
self.name = unpacker.get_str(SANITIZE_CC)
self.map = unpacker.get_str(SANITIZE_CC)
self.game_type = unpacker.get_str(SANITIZE_CC)
self.flags = int(unpacker.get_str())
self.num_players = int(unpacker.get_str())
self.max_players = int(unpacker.get_str())
self.num_clients = int(unpacker.get_str())
self.max_clients = int(unpacker.get_str())
self.clients = unpacker.get_raw()
return True
def pack(self) -> bytes:
return pack_str(str(self.token)) + \
pack_str(self.version) + \
pack_str(self.name) + \
pack_str(self.map) + \
pack_str(self.game_type) + \
pack_str(str(self.flags)) + \
pack_str(str(self.num_players)) + \
pack_str(str(self.max_players)) + \
pack_str(str(self.num_clients)) + \
pack_str(str(self.max_clients)) + \
self.clients

View file

@ -0,0 +1,29 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.master_server import MastersrvAddr
from twnet_parser.packer import pack_packed_addresses
from typing import Literal
class MsgList(PrettyPrint):
def __init__(
self,
servers: list[MastersrvAddr] = []
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.list'
self.message_id: list[int] = [255, 255, 255, 255, 108, 105, 115, 50]
self.servers: list[MastersrvAddr] = servers
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.servers = unpacker.get_packed_addresses()
return True
def pack(self) -> bytes:
return pack_packed_addresses(self.servers)

View file

@ -0,0 +1,22 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from typing import Literal
class MsgRequestCount(PrettyPrint):
def __init__(
self
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.request_count'
self.message_id: list[int] = [255, 255, 255, 255, 99, 111, 117, 50]
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,28 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.packer import pack_uint8
from typing import Literal
class MsgRequestInfo(PrettyPrint):
def __init__(
self,
token: int = 0
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.request_info'
self.message_id: list[int] = [255, 255, 255, 255, 103, 105, 101, 51]
self.token: int = token
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.token = unpacker.get_uint8()
return True
def pack(self) -> bytes:
return pack_uint8(self.token)

View file

@ -0,0 +1,22 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from typing import Literal
class MsgRequestList(PrettyPrint):
def __init__(
self
) -> None:
self.message_type: Literal['connless'] = 'connless'
self.message_name: str = 'connless.request_list'
self.message_id: list[int] = [255, 255, 255, 255, 114, 101, 113, 50]
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,14 @@
from typing import Literal
from twnet_parser.pretty_print import PrettyPrint
class CtrlAccept(PrettyPrint):
def __init__(self) -> None:
self.message_type: Literal['control'] = 'control'
self.message_name: str = 'accept'
self.message_id: int = 3
def unpack(self, data: bytes, we_are_a_client: bool = True) -> bool:
return False
def pack(self, we_are_a_client: bool = True) -> bytes:
return b''

View file

@ -0,0 +1,29 @@
from typing import Optional, Literal
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.packer import pack_str
class CtrlClose(PrettyPrint):
def __init__(
self,
reason: Optional[str] = None
) -> None:
self.message_type: Literal['control'] = 'control'
self.message_name: str = 'close'
self.message_id: int = 4
self.reason: Optional[str] = reason
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes, we_are_a_client: bool = True) -> bool:
unpacker = Unpacker(data)
self.reason = unpacker.get_str() # TODO: this is an optional field
return True
def pack(self, we_are_a_client: bool = True) -> bytes:
if self.reason:
return pack_str(self.reason)
return b''

View file

@ -0,0 +1,30 @@
from typing import Literal
from twnet_parser.pretty_print import PrettyPrint
"""
CtrlConnect
with security token from 0.6.5
not compatible with 0.6.4 or earlier
also not compatible with 0.7 or later
"""
class CtrlConnect(PrettyPrint):
def __init__(
self,
response_token: bytes = b'\xff\xff\xff\xff'
) -> None:
self.message_type: Literal['control'] = 'control'
self.message_name: str = 'connect'
self.message_id: int = 1
self.response_token: bytes = response_token
def unpack(self, data: bytes, we_are_a_client: bool = True) -> bool:
# anti reflection attack
if len(data) < 512:
return False
self.response_token = data[4:8]
return True
def pack(self, we_are_a_client: bool = True) -> bytes:
return bytes(4) + self.response_token + bytes(504)

View file

@ -0,0 +1,26 @@
from typing import Literal
from twnet_parser.pretty_print import PrettyPrint
"""
CtrlConnectAccept
Only valid in 0.6.4 and earlier
got removed in 0.6.5
"""
class CtrlConnectAccept(PrettyPrint):
def __init__(
self,
response_token: bytes = b'\xff\xff\xff\xff'
) -> None:
self.message_type: Literal['control'] = 'control'
self.message_name: str = 'connect_accept'
self.message_id: int = 2
self.response_token: bytes = response_token
def unpack(self, data: bytes, we_are_a_client: bool = True) -> bool:
self.response_token = data[0:4]
return True
def pack(self, we_are_a_client: bool = True) -> bytes:
return self.response_token

View file

@ -0,0 +1,14 @@
from typing import Literal
from twnet_parser.pretty_print import PrettyPrint
class CtrlKeepAlive(PrettyPrint):
def __init__(self) -> None:
self.message_type: Literal['control'] = 'control'
self.message_name: str = 'keep_alive'
self.message_id: int = 0
def unpack(self, data: bytes, we_are_a_client: bool = True) -> bool:
return False
def pack(self, we_are_a_client: bool = True) -> bytes:
return b''

View file

@ -0,0 +1,26 @@
from typing import Literal
from twnet_parser.pretty_print import PrettyPrint
class CtrlToken(PrettyPrint):
def __init__(
self,
response_token: bytes = b'\xff\xff\xff\xff'
) -> None:
self.message_type: Literal['control'] = 'control'
self.message_name: str = 'token'
self.message_id: int = 5
self.response_token: bytes = response_token
def unpack(self, data: bytes, we_are_a_client: bool = True) -> bool:
if not we_are_a_client:
# anti reflection attack
if len(data) < 512:
return False
self.response_token = data[0:4]
return True
def pack(self, we_are_a_client: bool = True) -> bytes:
if we_are_a_client:
return self.response_token + bytes(508)
return self.response_token

View file

@ -0,0 +1,40 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import SANITIZE_CC, pack_str
from typing import Literal
class MsgClCallVote(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
type: str = 'default',
value: str = 'default',
reason: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'cl_call_vote'
self.system_message: bool = False
self.message_id: int = 25
self.header: ChunkHeader = chunk_header
self.type: str = type
self.value: str = value
self.reason: str = reason
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.type = unpacker.get_str(SANITIZE_CC)
self.value = unpacker.get_str(SANITIZE_CC)
self.reason = unpacker.get_str(SANITIZE_CC)
return True
def pack(self) -> bytes:
return pack_str(self.type) + \
pack_str(self.value) + \
pack_str(self.reason)

View file

@ -0,0 +1,56 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import SANITIZE_CC, pack_int, pack_str
from typing import Literal
class MsgClChangeInfo(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
name: str = 'default',
clan: str = 'default',
country: int = 0,
skin: str = 'default',
use_custom_color: bool = False,
color_body: int = 0,
color_feet: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'cl_change_info'
self.system_message: bool = False
self.message_id: int = 21
self.header: ChunkHeader = chunk_header
self.name: str = name
self.clan: str = clan
self.country: int = country
self.skin: str = skin
self.use_custom_color: bool = use_custom_color
self.color_body: int = color_body
self.color_feet: int = color_feet
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.name = unpacker.get_str(SANITIZE_CC)
self.clan = unpacker.get_str(SANITIZE_CC)
self.country = unpacker.get_int()
self.skin = unpacker.get_str(SANITIZE_CC)
self.use_custom_color = unpacker.get_int() == 1
self.color_body = unpacker.get_int()
self.color_feet = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_str(self.name) + \
pack_str(self.clan) + \
pack_int(self.country) + \
pack_str(self.skin) + \
pack_int(self.use_custom_color) + \
pack_int(self.color_body) + \
pack_int(self.color_feet)

View file

@ -0,0 +1,33 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
import twnet_parser.enum6 as enum6
class MsgClEmoticon(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
emoticon: int = enum6.Emoticon.OOP.value
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'cl_emoticon'
self.system_message: bool = False
self.message_id: int = 23
self.header: ChunkHeader = chunk_header
self.emoticon: int = emoticon
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.emoticon = unpacker.get_int() # enum EMOTICON
return True
def pack(self) -> bytes:
return pack_int(self.emoticon)

View file

@ -0,0 +1,26 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.chunk_header import ChunkHeader
from typing import Literal
class MsgClKill(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader()
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'cl_kill'
self.system_message: bool = False
self.message_id: int = 22
self.header: ChunkHeader = chunk_header
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,36 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import SANITIZE_CC, pack_int, pack_str
from typing import Literal
class MsgClSay(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
team: bool = False,
message: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'cl_say'
self.system_message: bool = False
self.message_id: int = 17
self.header: ChunkHeader = chunk_header
self.team: bool = team
self.message: str = message
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.team = unpacker.get_int() == 1
self.message = unpacker.get_str(SANITIZE_CC)
return True
def pack(self) -> bytes:
return pack_int(self.team) + \
pack_str(self.message)

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgClSetSpectatorMode(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
spectator_id: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'cl_set_spectator_mode'
self.system_message: bool = False
self.message_id: int = 19
self.header: ChunkHeader = chunk_header
self.spectator_id: int = spectator_id
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.spectator_id = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_int(self.spectator_id)

View file

@ -0,0 +1,33 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
import twnet_parser.enum6 as enum6
class MsgClSetTeam(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
team: int = enum6.Team.SPECTATORS.value
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'cl_set_team'
self.system_message: bool = False
self.message_id: int = 18
self.header: ChunkHeader = chunk_header
self.team: int = team
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.team = unpacker.get_int() # enum TEAM
return True
def pack(self) -> bytes:
return pack_int(self.team)

View file

@ -0,0 +1,56 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import SANITIZE_CC, pack_int, pack_str
from typing import Literal
class MsgClStartInfo(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
name: str = 'default',
clan: str = 'default',
country: int = 0,
skin: str = 'default',
use_custom_color: bool = False,
color_body: int = 0,
color_feet: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'cl_start_info'
self.system_message: bool = False
self.message_id: int = 20
self.header: ChunkHeader = chunk_header
self.name: str = name
self.clan: str = clan
self.country: int = country
self.skin: str = skin
self.use_custom_color: bool = use_custom_color
self.color_body: int = color_body
self.color_feet: int = color_feet
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.name = unpacker.get_str(SANITIZE_CC)
self.clan = unpacker.get_str(SANITIZE_CC)
self.country = unpacker.get_int()
self.skin = unpacker.get_str(SANITIZE_CC)
self.use_custom_color = unpacker.get_int() == 1
self.color_body = unpacker.get_int()
self.color_feet = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_str(self.name) + \
pack_str(self.clan) + \
pack_int(self.country) + \
pack_str(self.skin) + \
pack_int(self.use_custom_color) + \
pack_int(self.color_body) + \
pack_int(self.color_feet)

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgClVote(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
vote: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'cl_vote'
self.system_message: bool = False
self.message_id: int = 24
self.header: ChunkHeader = chunk_header
self.vote: int = vote
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.vote = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_int(self.vote)

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_str
from typing import Literal
class MsgSvBroadcast(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
message: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_broadcast'
self.system_message: bool = False
self.message_id: int = 2
self.header: ChunkHeader = chunk_header
self.message: str = message
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.message = unpacker.get_str()
return True
def pack(self) -> bytes:
return pack_str(self.message)

View file

@ -0,0 +1,40 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import SANITIZE_CC, pack_int, pack_str
from typing import Literal
class MsgSvChat(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
team: bool = False,
client_id: int = 0,
message: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_chat'
self.system_message: bool = False
self.message_id: int = 3
self.header: ChunkHeader = chunk_header
self.team: bool = team
self.client_id: int = client_id
self.message: str = message
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.team = unpacker.get_int() == 1
self.client_id = unpacker.get_int()
self.message = unpacker.get_str(SANITIZE_CC)
return True
def pack(self) -> bytes:
return pack_int(self.team) + \
pack_int(self.client_id) + \
pack_str(self.message)

View file

@ -0,0 +1,37 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
import twnet_parser.enum6 as enum6
class MsgSvEmoticon(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
client_id: int = 0,
emoticon: int = enum6.Emoticon.OOP.value
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_emoticon'
self.system_message: bool = False
self.message_id: int = 10
self.header: ChunkHeader = chunk_header
self.client_id: int = client_id
self.emoticon: int = emoticon
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.client_id = unpacker.get_int()
self.emoticon = unpacker.get_int() # enum EMOTICON
return True
def pack(self) -> bytes:
return pack_int(self.client_id) + \
pack_int(self.emoticon)

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgSvExtraProjectile(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
projectile: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_extra_projectile'
self.system_message: bool = False
self.message_id: int = 7
self.header: ChunkHeader = chunk_header
self.projectile: int = projectile
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.projectile = unpacker.get_int() # TODO: this is a snapshot object
return True
def pack(self) -> bytes:
return pack_int(self.projectile)

View file

@ -0,0 +1,44 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgSvKillMsg(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
killer: int = 0,
victim: int = 0,
weapon: int = 0,
mode_special: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_kill_msg'
self.system_message: bool = False
self.message_id: int = 4
self.header: ChunkHeader = chunk_header
self.killer: int = killer
self.victim: int = victim
self.weapon: int = weapon
self.mode_special: int = mode_special
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.killer = unpacker.get_int()
self.victim = unpacker.get_int()
self.weapon = unpacker.get_int()
self.mode_special = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_int(self.killer) + \
pack_int(self.victim) + \
pack_int(self.weapon) + \
pack_int(self.mode_special)

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_str
from typing import Literal
class MsgSvMotd(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
message: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_motd'
self.system_message: bool = False
self.message_id: int = 1
self.header: ChunkHeader = chunk_header
self.message: str = message
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.message = unpacker.get_str()
return True
def pack(self) -> bytes:
return pack_str(self.message)

View file

@ -0,0 +1,26 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.chunk_header import ChunkHeader
from typing import Literal
class MsgSvReadyToEnter(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader()
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_ready_to_enter'
self.system_message: bool = False
self.message_id: int = 8
self.header: ChunkHeader = chunk_header
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,33 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
import twnet_parser.enum6 as enum6
class MsgSvSoundGlobal(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
sound_id: int = enum6.Sound.GUN_FIRE.value
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_sound_global'
self.system_message: bool = False
self.message_id: int = 5
self.header: ChunkHeader = chunk_header
self.sound_id: int = sound_id
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.sound_id = unpacker.get_int() # enum SOUND
return True
def pack(self) -> bytes:
return pack_int(self.sound_id)

View file

@ -0,0 +1,160 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgSvTuneParams(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
ground_control_speed: float = 10,
ground_control_accel: float = 2,
ground_friction: float = 0.5,
ground_jump_impulse: float = 13.2,
air_jump_impulse: float = 12,
air_control_speed: float = 5,
air_control_accel: float = 1.5,
air_friction: float = 0.95,
hook_length: float = 380,
hook_fire_speed: float = 80,
hook_drag_accel: float = 3,
hook_drag_speed: float = 15,
gravity: float = 0.5,
velramp_start: float = 550,
velramp_range: float = 2000,
velramp_curvature: float = 1.4,
gun_curvature: float = 1.25,
gun_speed: float = 2200,
gun_lifetime: float = 2,
shotgun_curvature: float = 1.25,
shotgun_speed: float = 2750,
shotgun_speeddiff: float = 0.8,
shotgun_lifetime: float = 0.2,
grenade_curvature: float = 7,
grenade_speed: float = 1000,
grenade_lifetime: float = 2,
laser_reach: float = 800,
laser_bounce_delay: float = 150,
laser_bounce_num: float = 1,
laser_bounce_cost: float = 0,
laser_damage: float = 0.0,
player_collision: float = 1,
player_hooking: float = 1
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_tune_params'
self.system_message: bool = False
self.message_id: int = 6
self.header: ChunkHeader = chunk_header
self.ground_control_speed: float = ground_control_speed
self.ground_control_accel: float = ground_control_accel
self.ground_friction: float = ground_friction
self.ground_jump_impulse: float = ground_jump_impulse
self.air_jump_impulse: float = air_jump_impulse
self.air_control_speed: float = air_control_speed
self.air_control_accel: float = air_control_accel
self.air_friction: float = air_friction
self.hook_length: float = hook_length
self.hook_fire_speed: float = hook_fire_speed
self.hook_drag_accel: float = hook_drag_accel
self.hook_drag_speed: float = hook_drag_speed
self.gravity: float = gravity
self.velramp_start: float = velramp_start
self.velramp_range: float = velramp_range
self.velramp_curvature: float = velramp_curvature
self.gun_curvature: float = gun_curvature
self.gun_speed: float = gun_speed
self.gun_lifetime: float = gun_lifetime
self.shotgun_curvature: float = shotgun_curvature
self.shotgun_speed: float = shotgun_speed
self.shotgun_speeddiff: float = shotgun_speeddiff
self.shotgun_lifetime: float = shotgun_lifetime
self.grenade_curvature: float = grenade_curvature
self.grenade_speed: float = grenade_speed
self.grenade_lifetime: float = grenade_lifetime
self.laser_reach: float = laser_reach
self.laser_bounce_delay: float = laser_bounce_delay
self.laser_bounce_num: float = laser_bounce_num
self.laser_bounce_cost: float = laser_bounce_cost
self.laser_damage: float = laser_damage
self.player_collision: float = player_collision
self.player_hooking: float = player_hooking
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.ground_control_speed = unpacker.get_int() / 100.0
self.ground_control_accel = unpacker.get_int() / 100.0
self.ground_friction = unpacker.get_int() / 100.0
self.ground_jump_impulse = unpacker.get_int() / 100.0
self.air_jump_impulse = unpacker.get_int() / 100.0
self.air_control_speed = unpacker.get_int() / 100.0
self.air_control_accel = unpacker.get_int() / 100.0
self.air_friction = unpacker.get_int() / 100.0
self.hook_length = unpacker.get_int() / 100.0
self.hook_fire_speed = unpacker.get_int() / 100.0
self.hook_drag_accel = unpacker.get_int() / 100.0
self.hook_drag_speed = unpacker.get_int() / 100.0
self.gravity = unpacker.get_int() / 100.0
self.velramp_start = unpacker.get_int() / 100.0
self.velramp_range = unpacker.get_int() / 100.0
self.velramp_curvature = unpacker.get_int() / 100.0
self.gun_curvature = unpacker.get_int() / 100.0
self.gun_speed = unpacker.get_int() / 100.0
self.gun_lifetime = unpacker.get_int() / 100.0
self.shotgun_curvature = unpacker.get_int() / 100.0
self.shotgun_speed = unpacker.get_int() / 100.0
self.shotgun_speeddiff = unpacker.get_int() / 100.0
self.shotgun_lifetime = unpacker.get_int() / 100.0
self.grenade_curvature = unpacker.get_int() / 100.0
self.grenade_speed = unpacker.get_int() / 100.0
self.grenade_lifetime = unpacker.get_int() / 100.0
self.laser_reach = unpacker.get_int() / 100.0
self.laser_bounce_delay = unpacker.get_int() / 100.0
self.laser_bounce_num = unpacker.get_int() / 100.0
self.laser_bounce_cost = unpacker.get_int() / 100.0
self.laser_damage = unpacker.get_int() / 100.0
self.player_collision = unpacker.get_int() / 100.0
self.player_hooking = unpacker.get_int() / 100.0
return True
def pack(self) -> bytes:
return pack_int(int(self.ground_control_speed * 100.0)) + \
pack_int(int(self.ground_control_accel * 100.0)) + \
pack_int(int(self.ground_friction * 100.0)) + \
pack_int(int(self.ground_jump_impulse * 100.0)) + \
pack_int(int(self.air_jump_impulse * 100.0)) + \
pack_int(int(self.air_control_speed * 100.0)) + \
pack_int(int(self.air_control_accel * 100.0)) + \
pack_int(int(self.air_friction * 100.0)) + \
pack_int(int(self.hook_length * 100.0)) + \
pack_int(int(self.hook_fire_speed * 100.0)) + \
pack_int(int(self.hook_drag_accel * 100.0)) + \
pack_int(int(self.hook_drag_speed * 100.0)) + \
pack_int(int(self.gravity * 100.0)) + \
pack_int(int(self.velramp_start * 100.0)) + \
pack_int(int(self.velramp_range * 100.0)) + \
pack_int(int(self.velramp_curvature * 100.0)) + \
pack_int(int(self.gun_curvature * 100.0)) + \
pack_int(int(self.gun_speed * 100.0)) + \
pack_int(int(self.gun_lifetime * 100.0)) + \
pack_int(int(self.shotgun_curvature * 100.0)) + \
pack_int(int(self.shotgun_speed * 100.0)) + \
pack_int(int(self.shotgun_speeddiff * 100.0)) + \
pack_int(int(self.shotgun_lifetime * 100.0)) + \
pack_int(int(self.grenade_curvature * 100.0)) + \
pack_int(int(self.grenade_speed * 100.0)) + \
pack_int(int(self.grenade_lifetime * 100.0)) + \
pack_int(int(self.laser_reach * 100.0)) + \
pack_int(int(self.laser_bounce_delay * 100.0)) + \
pack_int(int(self.laser_bounce_num * 100.0)) + \
pack_int(int(self.laser_bounce_cost * 100.0)) + \
pack_int(int(self.laser_damage * 100.0)) + \
pack_int(int(self.player_collision * 100.0)) + \
pack_int(int(self.player_hooking * 100.0))

View file

@ -0,0 +1,26 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.chunk_header import ChunkHeader
from typing import Literal
class MsgSvVoteClearOptions(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader()
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_vote_clear_options'
self.system_message: bool = False
self.message_id: int = 11
self.header: ChunkHeader = chunk_header
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import SANITIZE_CC, pack_str
from typing import Literal
class MsgSvVoteOptionAdd(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
description: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_vote_option_add'
self.system_message: bool = False
self.message_id: int = 13
self.header: ChunkHeader = chunk_header
self.description: str = description
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.description = unpacker.get_str(SANITIZE_CC)
return True
def pack(self) -> bytes:
return pack_str(self.description)

View file

@ -0,0 +1,38 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import SANITIZE_CC, pack_int, pack_str
from typing import Annotated, Literal
class MsgSvVoteOptionListAdd(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
num_options: int = 0,
description: Annotated[list[str], 15] = \
['', '', '', '', '', '', '', '', '', '', '', '', '', '', '']
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_vote_option_list_add'
self.system_message: bool = False
self.message_id: int = 12
self.header: ChunkHeader = chunk_header
self.num_options: int = num_options
self.description = description
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.num_options = unpacker.get_int()
for i in range(0, 15):
self.description[i] = unpacker.get_str(SANITIZE_CC)
return True
def pack(self) -> bytes:
return pack_int(self.num_options) + \
b''.join([pack_str(x) for x in self.description])

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import SANITIZE_CC, pack_str
from typing import Literal
class MsgSvVoteOptionRemove(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
description: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_vote_option_remove'
self.system_message: bool = False
self.message_id: int = 14
self.header: ChunkHeader = chunk_header
self.description: str = description
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.description = unpacker.get_str(SANITIZE_CC)
return True
def pack(self) -> bytes:
return pack_str(self.description)

View file

@ -0,0 +1,40 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import SANITIZE_CC, pack_int, pack_str
from typing import Literal
class MsgSvVoteSet(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
timeout: int = 0,
description: str = 'default',
reason: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_vote_set'
self.system_message: bool = False
self.message_id: int = 15
self.header: ChunkHeader = chunk_header
self.timeout: int = timeout
self.description: str = description
self.reason: str = reason
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.timeout = unpacker.get_int()
self.description = unpacker.get_str(SANITIZE_CC)
self.reason = unpacker.get_str(SANITIZE_CC)
return True
def pack(self) -> bytes:
return pack_int(self.timeout) + \
pack_str(self.description) + \
pack_str(self.reason)

View file

@ -0,0 +1,44 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgSvVoteStatus(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
yes: int = 0,
no: int = 0,
pass_: int = 0,
total: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_vote_status'
self.system_message: bool = False
self.message_id: int = 16
self.header: ChunkHeader = chunk_header
self.yes: int = yes
self.no: int = no
self.pass_: int = pass_
self.total: int = total
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.yes = unpacker.get_int()
self.no = unpacker.get_int()
self.pass_ = unpacker.get_int()
self.total = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_int(self.yes) + \
pack_int(self.no) + \
pack_int(self.pass_) + \
pack_int(self.total)

View file

@ -0,0 +1,33 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
import twnet_parser.enum6 as enum6
class MsgSvWeaponPickup(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
weapon: int = enum6.Weapon.HAMMER.value
) -> None:
self.message_type: Literal['system', 'game'] = 'game'
self.message_name: str = 'sv_weapon_pickup'
self.system_message: bool = False
self.message_id: int = 9
self.header: ChunkHeader = chunk_header
self.weapon: int = weapon
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.weapon = unpacker.get_int() # enum WEAPON
return True
def pack(self) -> bytes:
return pack_int(self.weapon)

View file

@ -0,0 +1,26 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.chunk_header import ChunkHeader
from typing import Literal
class MsgConReady(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader()
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'con_ready'
self.system_message: bool = True
self.message_id: int = 4
self.header: ChunkHeader = chunk_header
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,26 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.chunk_header import ChunkHeader
from typing import Literal
class MsgEnterGame(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader()
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'enter_game'
self.system_message: bool = True
self.message_id: int = 15
self.header: ChunkHeader = chunk_header
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,36 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_str
from typing import Literal
class MsgInfo(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
version: str = '0.7 802f1be60a05665f',
password: str = ''
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'info'
self.system_message: bool = True
self.message_id: int = 1
self.header: ChunkHeader = chunk_header
self.version: str = version
self.password: str = password
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.version = unpacker.get_str()
self.password = unpacker.get_str() # TODO: optionals
return True
def pack(self) -> bytes:
return pack_str(self.version) + \
pack_str(self.password)

View file

@ -0,0 +1,44 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgInput(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
ack_snapshot: int = 0,
intended_tick: int = 0,
input_size: int = 0,
input: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'input'
self.system_message: bool = True
self.message_id: int = 16
self.header: ChunkHeader = chunk_header
self.ack_snapshot: int = ack_snapshot
self.intended_tick: int = intended_tick
self.input_size: int = input_size
self.input: int = input
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.ack_snapshot = unpacker.get_int()
self.intended_tick = unpacker.get_int()
self.input_size = unpacker.get_int()
self.input = unpacker.get_int() # TODO: this is a snapshot object
return True
def pack(self) -> bytes:
return pack_int(self.ack_snapshot) + \
pack_int(self.intended_tick) + \
pack_int(self.input_size) + \
pack_int(self.input)

View file

@ -0,0 +1,36 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgInputTiming(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
input_pred_tick: int = 0,
time_left: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'input_timing'
self.system_message: bool = True
self.message_id: int = 9
self.header: ChunkHeader = chunk_header
self.input_pred_tick: int = input_pred_tick
self.time_left: int = time_left
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.input_pred_tick = unpacker.get_int()
self.time_left = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_int(self.input_pred_tick) + \
pack_int(self.time_left)

View file

@ -0,0 +1,40 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int, pack_str
from typing import Literal
class MsgMapChange(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
name: str = 'default',
crc: int = 0,
size: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'map_change'
self.system_message: bool = True
self.message_id: int = 2
self.header: ChunkHeader = chunk_header
self.name: str = name
self.crc: int = crc
self.size: int = size
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.name = unpacker.get_str()
self.crc = unpacker.get_int()
self.size = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_str(self.name) + \
pack_int(self.crc) + \
pack_int(self.size)

View file

@ -0,0 +1,48 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal, Optional
class MsgMapData(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
last: int = 0,
crc: int = 0,
chunk: int = 0,
data_size: Optional[int] = None,
data: bytes = b'\x00'
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'map_data'
self.system_message: bool = True
self.message_id: int = 3
self.header: ChunkHeader = chunk_header
self.last: int = last
self.crc: int = crc
self.chunk: int = chunk
self.data_size: int = data_size if data_size else len(data)
self.data: bytes = data
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.last = unpacker.get_int()
self.crc = unpacker.get_int()
self.chunk = unpacker.get_int()
self.data_size = unpacker.get_int()
self.data = unpacker.get_raw(self.data_size)
return True
def pack(self) -> bytes:
return pack_int(self.last) + \
pack_int(self.crc) + \
pack_int(self.chunk) + \
pack_int(self.data_size) + \
self.data

View file

@ -0,0 +1,26 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.chunk_header import ChunkHeader
from typing import Literal
class MsgPing(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader()
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'ping'
self.system_message: bool = True
self.message_id: int = 20
self.header: ChunkHeader = chunk_header
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,26 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.chunk_header import ChunkHeader
from typing import Literal
class MsgPingReply(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader()
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'ping_reply'
self.system_message: bool = True
self.message_id: int = 21
self.header: ChunkHeader = chunk_header
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,40 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int, pack_str
from typing import Literal
class MsgRconAuth(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
_unused: str = 'default',
password: str = 'default',
request_commands: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'rcon_auth'
self.system_message: bool = True
self.message_id: int = 18
self.header: ChunkHeader = chunk_header
self._unused: str = _unused
self.password: str = password
self.request_commands: int = request_commands
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self._unused = unpacker.get_str()
self.password = unpacker.get_str()
self.request_commands = unpacker.get_int() # TODO: optionals
return True
def pack(self) -> bytes:
return pack_str(self._unused) + \
pack_str(self.password) + \
pack_int(self.request_commands)

View file

@ -0,0 +1,36 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgRconAuthStatus(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
auth_level: int = 0,
receive_commands: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'rcon_auth_status'
self.system_message: bool = True
self.message_id: int = 10
self.header: ChunkHeader = chunk_header
self.auth_level: int = auth_level
self.receive_commands: int = receive_commands
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.auth_level = unpacker.get_int() # TODO: optionals
self.receive_commands = unpacker.get_int() # TODO: optionals
return True
def pack(self) -> bytes:
return pack_int(self.auth_level) + \
pack_int(self.receive_commands)

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_str
from typing import Literal
class MsgRconCmd(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
cmd: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'rcon_cmd'
self.system_message: bool = True
self.message_id: int = 17
self.header: ChunkHeader = chunk_header
self.cmd: str = cmd
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.cmd = unpacker.get_str()
return True
def pack(self) -> bytes:
return pack_str(self.cmd)

View file

@ -0,0 +1,40 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_str
from typing import Literal
class MsgRconCmdAdd(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
name: str = 'default',
help: str = 'default',
params: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'rcon_cmd_add'
self.system_message: bool = True
self.message_id: int = 25
self.header: ChunkHeader = chunk_header
self.name: str = name
self.help: str = help
self.params: str = params
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.name = unpacker.get_str()
self.help = unpacker.get_str()
self.params = unpacker.get_str()
return True
def pack(self) -> bytes:
return pack_str(self.name) + \
pack_str(self.help) + \
pack_str(self.params)

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_str
from typing import Literal
class MsgRconCmdRemove(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
name: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'rcon_cmd_remove'
self.system_message: bool = True
self.message_id: int = 26
self.header: ChunkHeader = chunk_header
self.name: str = name
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.name = unpacker.get_str()
return True
def pack(self) -> bytes:
return pack_str(self.name)

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_str
from typing import Literal
class MsgRconLine(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
line: str = 'default'
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'rcon_line'
self.system_message: bool = True
self.message_id: int = 11
self.header: ChunkHeader = chunk_header
self.line: str = line
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.line = unpacker.get_str()
return True
def pack(self) -> bytes:
return pack_str(self.line)

View file

@ -0,0 +1,26 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.chunk_header import ChunkHeader
from typing import Literal
class MsgReady(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader()
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'ready'
self.system_message: bool = True
self.message_id: int = 14
self.header: ChunkHeader = chunk_header
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
return True
def pack(self) -> bytes:
return b''

View file

@ -0,0 +1,32 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgRequestMapData(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
chunk: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'request_map_data'
self.system_message: bool = True
self.message_id: int = 19
self.header: ChunkHeader = chunk_header
self.chunk: int = chunk
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.chunk = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_int(self.chunk)

View file

@ -0,0 +1,58 @@
from typing import Literal, Optional
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from twnet_parser.snapshot7 import Snapshot
class MsgSnap(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
tick: int = 0,
delta_tick: int = 0,
num_parts: int = 0,
part: int = 0,
crc: int = 0,
data_size: Optional[int] = None,
data: bytes = b'\x00'
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'snap'
self.system_message: bool = True
self.message_id: int = 5
self.header: ChunkHeader = chunk_header
self.tick: int = tick
self.delta_tick: int = delta_tick
self.num_parts: int = num_parts
self.part: int = part
self.crc: int = crc
self.data_size: int = data_size if data_size else len(data)
self.data: bytes = data
self.snapshot = Snapshot()
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.tick = unpacker.get_int()
self.delta_tick = unpacker.get_int()
self.num_parts = unpacker.get_int()
self.part = unpacker.get_int()
self.crc = unpacker.get_int()
self.data_size = unpacker.get_int()
self.data = unpacker.get_raw(self.data_size)
self.snapshot.unpack(self.data)
return True
def pack(self) -> bytes:
return pack_int(self.tick) + \
pack_int(self.delta_tick) + \
pack_int(self.num_parts) + \
pack_int(self.part) + \
pack_int(self.crc) + \
pack_int(self.data_size) + \
self.snapshot.pack()

View file

@ -0,0 +1,36 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal
class MsgSnapEmpty(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
tick: int = 0,
delta_tick: int = 0
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'snap_empty'
self.system_message: bool = True
self.message_id: int = 6
self.header: ChunkHeader = chunk_header
self.tick: int = tick
self.delta_tick: int = delta_tick
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.tick = unpacker.get_int()
self.delta_tick = unpacker.get_int()
return True
def pack(self) -> bytes:
return pack_int(self.tick) + \
pack_int(self.delta_tick)

View file

@ -0,0 +1,48 @@
# generated by scripts/generate_messages.py
from twnet_parser.pretty_print import PrettyPrint
from twnet_parser.packer import Unpacker
from twnet_parser.chunk_header import ChunkHeader
from twnet_parser.packer import pack_int
from typing import Literal, Optional
class MsgSnapSingle(PrettyPrint):
def __init__(
self,
chunk_header: ChunkHeader = ChunkHeader(),
tick: int = 0,
delta_tick: int = 0,
crc: int = 0,
data_size: Optional[int] = None,
data: bytes = b'\x00'
) -> None:
self.message_type: Literal['system', 'game'] = 'system'
self.message_name: str = 'snap_single'
self.system_message: bool = True
self.message_id: int = 7
self.header: ChunkHeader = chunk_header
self.tick: int = tick
self.delta_tick: int = delta_tick
self.crc: int = crc
self.data_size: int = data_size if data_size else len(data)
self.data: bytes = data
# first byte of data
# has to be the first byte of the message payload
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.tick = unpacker.get_int()
self.delta_tick = unpacker.get_int()
self.crc = unpacker.get_int()
self.data_size = unpacker.get_int()
self.data = unpacker.get_raw(self.data_size)
return True
def pack(self) -> bytes:
return pack_int(self.tick) + \
pack_int(self.delta_tick) + \
pack_int(self.crc) + \
pack_int(self.data_size) + \
self.data

80
twnet_parser/msg6.py Normal file
View file

@ -0,0 +1,80 @@
# connless
# pylint: disable=duplicate-code
CONNLESS_HEARTBEAT = b'\xff\xff\xff\xffbea2'
CONNLESS_REQUEST_LIST = b'\xff\xff\xff\xffreq2'
CONNLESS_LIST = b'\xff\xff\xff\xfflis2'
CONNLESS_REQUEST_COUNT = b'\xff\xff\xff\xffcou2'
CONNLESS_COUNT = b'\xff\xff\xff\xffsiz2'
CONNLESS_REQUEST_INFO = b'\xff\xff\xff\xffgie3'
CONNLESS_INFO = b'\xff\xff\xff\xffinf3'
CONNLESS_FORWARD_CHECK = b'\xff\xff\xff\xfffw??'
CONNLESS_FORWARD_RESPONSE = b'\xff\xff\xff\xfffw!!'
CONNLESS_FORWARD_OK = b'\xff\xff\xff\xfffwok'
CONNLESS_FORWARD_ERROR = b'\xff\xff\xff\xfffwer'
# control
CTRL_KEEPALIVE = 0
CTRL_CONNECT = 1
CTRL_CONNECT_ACCEPT = 2
CTRL_ACCEPT = 2 # got remove in 0.6.5
CTRL_CLOSE = 4
# system
INFO = 1
MAP_CHANGE = 2 # sent when client should switch map
MAP_DATA = 3 # map transfer, contains a chunk of the map file
CON_READY = 4 # connection is ready, client should send start info
SNAP = 5 # normal snapshot, multiple parts
SNAP_EMPTY = 6 # empty snapshot
SNAP_SINGLE = 7
SNAP_SMALL = 8
INPUT_TIMING = 9 # reports how off the input was
RCON_AUTH_STATUS = 10# result of the authentication
RCON_LINE = 11 # line that should be printed to the remote console
AUTH_CHALLANGE = 12
AUTH_RESULT = 13
READY = 14
ENTER_GAME = 15
INPUT = 16 # contains the inputdata from the client
RCON_CMD = 17
RCON_AUTH = 18
REQUEST_MAP_DATA = 19
AUTH_START = 20
AUTH_RESPONSE = 21
PING = 22
PING_REPLY = 23
ERROR = 24
RCON_CMD_ADD = 25
RCON_CMD_REMOVE = 26
# game
INVALID = 0
SV_MOTD = 1
SV_BROADCAST = 2
SV_CHAT = 3
SV_KILL_MSG = 4
SV_SOUND_GLOBAL = 5
SV_TUNE_PARAMS = 6
SV_EXTRA_PROJECTILE = 7
SV_READY_TO_ENTER = 8
SV_WEAPON_PICKUP = 9
SV_EMOTICON = 10
SV_VOTE_CLEAR_OPTIONS = 11
SV_VOTE_OPTION_LIST_ADD = 12
SV_VOTE_OPTION_ADD = 13
SV_VOTE_OPTION_REMOVE = 14
SV_VOTE_SET = 15
SV_VOTE_STATUS = 16
CL_SAY = 17
CL_SET_TEAM = 18
CL_SET_SPECTATOR_MODE = 19
CL_START_INFO = 20
CL_CHANGE_INFO = 21
CL_KILL = 22
CL_EMOTICON = 23
CL_VOTE = 24
CL_CALL_VOTE = 25
NUM_GAME_MESSAGES = 26

View file

@ -1,4 +1,5 @@
# connless
# pylint: disable=duplicate-code
CONNLESS_HEARTBEAT = b'\xff\xff\xff\xffbea2'
CONNLESS_REQUEST_LIST = b'\xff\xff\xff\xffreq2'

View file

@ -0,0 +1,62 @@
# generated by scripts/generate_messages.py
from typing import Optional
import twnet_parser.msg6
from twnet_parser.connless_message import ConnlessMessage
import twnet_parser.messages6.connless.request_list as \
connless_request_list
import twnet_parser.messages6.connless.list as \
connless_list
import twnet_parser.messages6.connless.request_count as \
connless_request_count
import twnet_parser.messages6.connless.count as \
connless_count
import twnet_parser.messages6.connless.request_info as \
connless_request_info
import twnet_parser.messages6.connless.info as \
connless_info
import twnet_parser.messages6.connless.heartbeat as \
connless_heartbeat
import twnet_parser.messages6.connless.forward_check as \
connless_forward_check
import twnet_parser.messages6.connless.forward_response as \
connless_forward_response
import twnet_parser.messages6.connless.forward_ok as \
connless_forward_ok
import twnet_parser.messages6.connless.forward_error as \
connless_forward_error
def match_connless6(msg_id: bytes, data: bytes) -> ConnlessMessage:
msg: Optional[ConnlessMessage] = None
if msg_id == twnet_parser.msg6.CONNLESS_REQUEST_LIST:
msg = connless_request_list.MsgRequestList()
elif msg_id == twnet_parser.msg6.CONNLESS_LIST:
msg = connless_list.MsgList()
elif msg_id == twnet_parser.msg6.CONNLESS_REQUEST_COUNT:
msg = connless_request_count.MsgRequestCount()
elif msg_id == twnet_parser.msg6.CONNLESS_COUNT:
msg = connless_count.MsgCount()
elif msg_id == twnet_parser.msg6.CONNLESS_REQUEST_INFO:
msg = connless_request_info.MsgRequestInfo()
elif msg_id == twnet_parser.msg6.CONNLESS_INFO:
msg = connless_info.MsgInfo()
elif msg_id == twnet_parser.msg6.CONNLESS_HEARTBEAT:
msg = connless_heartbeat.MsgHeartbeat()
elif msg_id == twnet_parser.msg6.CONNLESS_FORWARD_CHECK:
msg = connless_forward_check.MsgForwardCheck()
elif msg_id == twnet_parser.msg6.CONNLESS_FORWARD_RESPONSE:
msg = connless_forward_response.MsgForwardResponse()
elif msg_id == twnet_parser.msg6.CONNLESS_FORWARD_OK:
msg = connless_forward_ok.MsgForwardOk()
elif msg_id == twnet_parser.msg6.CONNLESS_FORWARD_ERROR:
msg = connless_forward_error.MsgForwardError()
if msg is None:
raise ValueError(
f"Error: unknown conless message id={msg_id!r} data={data[0]}"
)
msg.unpack(data)
return msg

View file

@ -0,0 +1,30 @@
from typing import Optional
import twnet_parser.msg6
from twnet_parser.ctrl_message import CtrlMessage
import twnet_parser.messages6.control.keep_alive as keep_alive6
import twnet_parser.messages6.control.connect as connect6
import twnet_parser.messages6.control.connect_accept as connect_accept6
import twnet_parser.messages6.control.accept as accept6
import twnet_parser.messages6.control.close as close6
def match_control6(msg_id: int, data: bytes, client: bool) -> CtrlMessage:
msg: Optional[CtrlMessage] = None
if msg_id == twnet_parser.msg6.CTRL_KEEPALIVE:
msg = keep_alive6.CtrlKeepAlive()
elif msg_id == twnet_parser.msg6.CTRL_CONNECT:
msg = connect6.CtrlConnect()
elif msg_id == twnet_parser.msg6.CTRL_CONNECT_ACCEPT:
msg = connect_accept6.CtrlConnectAccept()
elif msg_id == twnet_parser.msg6.CTRL_ACCEPT:
msg = accept6.CtrlAccept()
elif msg_id == twnet_parser.msg6.CTRL_CLOSE:
msg = close6.CtrlClose()
if msg is None:
raise ValueError(f"Error: unknown control message id={msg_id} data={data[0]}")
msg.unpack(data, client)
return msg

View file

@ -0,0 +1,116 @@
# generated by scripts/generate_messages.py
from typing import Optional
import twnet_parser.msg6
from twnet_parser.net_message import NetMessage
import twnet_parser.messages6.game.sv_motd as \
game6_sv_motd
import twnet_parser.messages6.game.sv_broadcast as \
game6_sv_broadcast
import twnet_parser.messages6.game.sv_chat as \
game6_sv_chat
import twnet_parser.messages6.game.sv_kill_msg as \
game6_sv_kill_msg
import twnet_parser.messages6.game.sv_sound_global as \
game6_sv_sound_global
import twnet_parser.messages6.game.sv_tune_params as \
game6_sv_tune_params
import twnet_parser.messages6.game.sv_extra_projectile as \
game6_sv_extra_projectile
import twnet_parser.messages6.game.sv_ready_to_enter as \
game6_sv_ready_to_enter
import twnet_parser.messages6.game.sv_weapon_pickup as \
game6_sv_weapon_pickup
import twnet_parser.messages6.game.sv_emoticon as \
game6_sv_emoticon
import twnet_parser.messages6.game.sv_vote_clear_options as \
game6_sv_vote_clear_options
import twnet_parser.messages6.game.sv_vote_option_list_add as \
game6_sv_vote_option_list_add
import twnet_parser.messages6.game.sv_vote_option_add as \
game6_sv_vote_option_add
import twnet_parser.messages6.game.sv_vote_option_remove as \
game6_sv_vote_option_remove
import twnet_parser.messages6.game.sv_vote_set as \
game6_sv_vote_set
import twnet_parser.messages6.game.sv_vote_status as \
game6_sv_vote_status
import twnet_parser.messages6.game.cl_say as \
game6_cl_say
import twnet_parser.messages6.game.cl_set_team as \
game6_cl_set_team
import twnet_parser.messages6.game.cl_set_spectator_mode as \
game6_cl_set_spectator_mode
import twnet_parser.messages6.game.cl_start_info as \
game6_cl_start_info
import twnet_parser.messages6.game.cl_change_info as \
game6_cl_change_info
import twnet_parser.messages6.game.cl_kill as \
game6_cl_kill
import twnet_parser.messages6.game.cl_emoticon as \
game6_cl_emoticon
import twnet_parser.messages6.game.cl_vote as \
game6_cl_vote
import twnet_parser.messages6.game.cl_call_vote as \
game6_cl_call_vote
def match_game6(msg_id: int, data: bytes) -> NetMessage:
msg: Optional[NetMessage] = None
if msg_id == twnet_parser.msg6.SV_MOTD:
msg = game6_sv_motd.MsgSvMotd()
elif msg_id == twnet_parser.msg6.SV_BROADCAST:
msg = game6_sv_broadcast.MsgSvBroadcast()
elif msg_id == twnet_parser.msg6.SV_CHAT:
msg = game6_sv_chat.MsgSvChat()
elif msg_id == twnet_parser.msg6.SV_KILL_MSG:
msg = game6_sv_kill_msg.MsgSvKillMsg()
elif msg_id == twnet_parser.msg6.SV_SOUND_GLOBAL:
msg = game6_sv_sound_global.MsgSvSoundGlobal()
elif msg_id == twnet_parser.msg6.SV_TUNE_PARAMS:
msg = game6_sv_tune_params.MsgSvTuneParams()
elif msg_id == twnet_parser.msg6.SV_EXTRA_PROJECTILE:
msg = game6_sv_extra_projectile.MsgSvExtraProjectile()
elif msg_id == twnet_parser.msg6.SV_READY_TO_ENTER:
msg = game6_sv_ready_to_enter.MsgSvReadyToEnter()
elif msg_id == twnet_parser.msg6.SV_WEAPON_PICKUP:
msg = game6_sv_weapon_pickup.MsgSvWeaponPickup()
elif msg_id == twnet_parser.msg6.SV_EMOTICON:
msg = game6_sv_emoticon.MsgSvEmoticon()
elif msg_id == twnet_parser.msg6.SV_VOTE_CLEAR_OPTIONS:
msg = game6_sv_vote_clear_options.MsgSvVoteClearOptions()
elif msg_id == twnet_parser.msg6.SV_VOTE_OPTION_LIST_ADD:
msg = game6_sv_vote_option_list_add.MsgSvVoteOptionListAdd()
elif msg_id == twnet_parser.msg6.SV_VOTE_OPTION_ADD:
msg = game6_sv_vote_option_add.MsgSvVoteOptionAdd()
elif msg_id == twnet_parser.msg6.SV_VOTE_OPTION_REMOVE:
msg = game6_sv_vote_option_remove.MsgSvVoteOptionRemove()
elif msg_id == twnet_parser.msg6.SV_VOTE_SET:
msg = game6_sv_vote_set.MsgSvVoteSet()
elif msg_id == twnet_parser.msg6.SV_VOTE_STATUS:
msg = game6_sv_vote_status.MsgSvVoteStatus()
elif msg_id == twnet_parser.msg6.CL_SAY:
msg = game6_cl_say.MsgClSay()
elif msg_id == twnet_parser.msg6.CL_SET_TEAM:
msg = game6_cl_set_team.MsgClSetTeam()
elif msg_id == twnet_parser.msg6.CL_SET_SPECTATOR_MODE:
msg = game6_cl_set_spectator_mode.MsgClSetSpectatorMode()
elif msg_id == twnet_parser.msg6.CL_START_INFO:
msg = game6_cl_start_info.MsgClStartInfo()
elif msg_id == twnet_parser.msg6.CL_CHANGE_INFO:
msg = game6_cl_change_info.MsgClChangeInfo()
elif msg_id == twnet_parser.msg6.CL_KILL:
msg = game6_cl_kill.MsgClKill()
elif msg_id == twnet_parser.msg6.CL_EMOTICON:
msg = game6_cl_emoticon.MsgClEmoticon()
elif msg_id == twnet_parser.msg6.CL_VOTE:
msg = game6_cl_vote.MsgClVote()
elif msg_id == twnet_parser.msg6.CL_CALL_VOTE:
msg = game6_cl_call_vote.MsgClCallVote()
if msg is None:
raise ValueError(f"Error: unknown game message id={msg_id} data={data[0]}")
msg.unpack(data)
return msg

View file

@ -0,0 +1,96 @@
# generated by scripts/generate_messages.py
from typing import Optional
import twnet_parser.msg6
from twnet_parser.net_message import NetMessage
import twnet_parser.messages6.system.info as \
system6_info
import twnet_parser.messages6.system.map_change as \
system6_map_change
import twnet_parser.messages6.system.map_data as \
system6_map_data
import twnet_parser.messages6.system.con_ready as \
system6_con_ready
import twnet_parser.messages6.system.snap as \
system6_snap
import twnet_parser.messages6.system.snap_empty as \
system6_snap_empty
import twnet_parser.messages6.system.snap_single as \
system6_snap_single
import twnet_parser.messages6.system.input_timing as \
system6_input_timing
import twnet_parser.messages6.system.rcon_auth_status as \
system6_rcon_auth_status
import twnet_parser.messages6.system.rcon_line as \
system6_rcon_line
import twnet_parser.messages6.system.ready as \
system6_ready
import twnet_parser.messages6.system.enter_game as \
system6_enter_game
import twnet_parser.messages6.system.input as \
system6_input
import twnet_parser.messages6.system.rcon_cmd as \
system6_rcon_cmd
import twnet_parser.messages6.system.rcon_auth as \
system6_rcon_auth
import twnet_parser.messages6.system.request_map_data as \
system6_request_map_data
import twnet_parser.messages6.system.ping as \
system6_ping
import twnet_parser.messages6.system.ping_reply as \
system6_ping_reply
import twnet_parser.messages6.system.rcon_cmd_add as \
system6_rcon_cmd_add
import twnet_parser.messages6.system.rcon_cmd_remove as \
system6_rcon_cmd_remove
def match_system6(msg_id: int, data: bytes) -> NetMessage:
msg: Optional[NetMessage] = None
if msg_id == twnet_parser.msg6.INFO:
msg = system6_info.MsgInfo()
elif msg_id == twnet_parser.msg6.MAP_CHANGE:
msg = system6_map_change.MsgMapChange()
elif msg_id == twnet_parser.msg6.MAP_DATA:
msg = system6_map_data.MsgMapData()
elif msg_id == twnet_parser.msg6.CON_READY:
msg = system6_con_ready.MsgConReady()
elif msg_id == twnet_parser.msg6.SNAP:
msg = system6_snap.MsgSnap()
elif msg_id == twnet_parser.msg6.SNAP_EMPTY:
msg = system6_snap_empty.MsgSnapEmpty()
elif msg_id == twnet_parser.msg6.SNAP_SINGLE:
msg = system6_snap_single.MsgSnapSingle()
elif msg_id == twnet_parser.msg6.INPUT_TIMING:
msg = system6_input_timing.MsgInputTiming()
elif msg_id == twnet_parser.msg6.RCON_AUTH_STATUS:
msg = system6_rcon_auth_status.MsgRconAuthStatus()
elif msg_id == twnet_parser.msg6.RCON_LINE:
msg = system6_rcon_line.MsgRconLine()
elif msg_id == twnet_parser.msg6.READY:
msg = system6_ready.MsgReady()
elif msg_id == twnet_parser.msg6.ENTER_GAME:
msg = system6_enter_game.MsgEnterGame()
elif msg_id == twnet_parser.msg6.INPUT:
msg = system6_input.MsgInput()
elif msg_id == twnet_parser.msg6.RCON_CMD:
msg = system6_rcon_cmd.MsgRconCmd()
elif msg_id == twnet_parser.msg6.RCON_AUTH:
msg = system6_rcon_auth.MsgRconAuth()
elif msg_id == twnet_parser.msg6.REQUEST_MAP_DATA:
msg = system6_request_map_data.MsgRequestMapData()
elif msg_id == twnet_parser.msg6.PING:
msg = system6_ping.MsgPing()
elif msg_id == twnet_parser.msg6.PING_REPLY:
msg = system6_ping_reply.MsgPingReply()
elif msg_id == twnet_parser.msg6.RCON_CMD_ADD:
msg = system6_rcon_cmd_add.MsgRconCmdAdd()
elif msg_id == twnet_parser.msg6.RCON_CMD_REMOVE:
msg = system6_rcon_cmd_remove.MsgRconCmdRemove()
if msg is None:
raise ValueError(f"Error: unknown system message id={msg_id} data={data[0]}")
msg.unpack(data)
return msg

View file

@ -11,6 +11,8 @@ from twnet_parser.connless_message import ConnlessMessage
from twnet_parser.chunk_header import ChunkHeader, ChunkFlags
from twnet_parser.msg_matcher.control7 import match_control7
from twnet_parser.msg_matcher.connless7 import match_connless7
from twnet_parser.msg_matcher.control6 import match_control6
from twnet_parser.msg_matcher.connless6 import match_connless6
from twnet_parser.constants import NET_MAX_SEQUENCE, NET_PACKETVERSION
from twnet_parser.external.huffman import huffman
@ -22,6 +24,12 @@ PACKETFLAG7_RESEND = 2
PACKETFLAG7_COMPRESSION = 4
PACKETFLAG7_CONNLESS = 8
PACKETFLAG6_TOKEN = 1
PACKETFLAG6_CONTROL = 2
PACKETFLAG6_CONNLESS = 4
PACKETFLAG6_RESEND = 8
PACKETFLAG6_COMPRESSION = 16
CHUNKFLAG7_VITAL = 1
CHUNKFLAG7_RESEND = 2
@ -43,6 +51,73 @@ class PacketFlags6(PrettyPrint):
self.compression: Optional[bool] = None
self.connless: Optional[bool] = None
class PacketHeader6(PrettyPrint):
def __init__(
self,
flags: Optional[PacketFlags6] = None,
ack: int = 0,
token: bytes = b'\xff\xff\xff\xff',
num_chunks: Optional[int] = None
) -> None:
"""
If num_chunks is not set it will count
the messages it was given when
the pack() method is called
"""
if not flags:
flags = PacketFlags6()
self.flags: PacketFlags6 = flags
self.ack: int = ack % NET_MAX_SEQUENCE
self.token: bytes = token
self.num_chunks: Optional[int] = num_chunks
# connless only
self.connless_version: int = NET_PACKETVERSION
self.response_token: bytes = b'\xff\xff\xff\xff'
def pack(self) -> bytes:
"""
Generate 7 byte teeworlds 0.6.5 packet header
based on the current instance variable
values.
The layout is as follows
6bit flags, 2bit ack
8bit ack
8bit chunks
32bit token
ffffffaa
aaaaaaaa
NNNNNNNN
TTTTTTTT
TTTTTTTT
TTTTTTTT
TTTTTTTT
"""
flags = 0
if self.flags.token:
flags |= PACKETFLAG6_TOKEN
if self.flags.control:
flags |= PACKETFLAG6_CONTROL
if self.flags.connless:
flags |= PACKETFLAG6_CONNLESS
if self.flags.resend:
flags |= PACKETFLAG6_RESEND
if self.flags.compression:
flags |= PACKETFLAG6_COMPRESSION
if self.num_chunks is None:
self.num_chunks = 0
if self.flags.connless:
return bytes([ \
((PACKETFLAG6_CONNLESS<<2)&0xfc) | (self.connless_version&0x03)
]) + self.token + self.response_token
return bytes([ \
((flags << 2)&0xfc) | ((self.ack>>8)&0x03), \
self.ack&0xff, \
self.num_chunks \
]) + self.token
class PacketHeader(PrettyPrint):
def __init__(
self,
@ -113,7 +188,7 @@ class TwPacket(PrettyPrint):
self.version: str = 'unknown'
self.payload_raw: bytes = b''
self.payload_decompressed: bytes = b''
self.header: PacketHeader = PacketHeader()
self.header: Union[PacketHeader, PacketHeader6] = PacketHeader()
self.messages: list[Union[CtrlMessage, NetMessage, ConnlessMessage]] = []
def pack(self, we_are_a_client = True) -> bytes:
@ -156,6 +231,48 @@ class TwPacket(PrettyPrint):
self.header.flags.connless = True
return self.header.pack() + payload
class PacketHeaderParser6():
def parse_flags6(self, data: bytes) -> PacketFlags6:
# FFFF FFaa
flag_bits = data[0] >> 2
flags = PacketFlags6()
flags.token = (flag_bits & PACKETFLAG6_TOKEN) != 0
flags.control = (flag_bits & PACKETFLAG6_CONTROL) != 0
flags.connless = (flag_bits & PACKETFLAG6_CONNLESS) != 0
flags.resend = (flag_bits & PACKETFLAG6_RESEND) != 0
flags.compression = (flag_bits & PACKETFLAG6_COMPRESSION) != 0
return flags
def parse_ack(self, header_bytes: bytes) -> int:
# ffAA AAAA AAAA
return ((header_bytes[0] & 0x3) << 8) | header_bytes[1]
def parse_num_chunks(self, header_bytes: bytes) -> int:
# TODO: not sure if this is correct
return header_bytes[2]
def parse_token(self, header_bytes: bytes) -> bytes:
return header_bytes[3:7]
def parse_header(self, data: bytes) -> PacketHeader6:
header = PacketHeader6()
# bits 1..5
header.flags = self.parse_flags6(data)
if header.flags.connless:
# TODO: do not hardcode version field
# but actually read the bits
header.connless_version = NET_PACKETVERSION
header.token = data[1:5]
header.response_token = data[5:9]
else:
# bits 6..16
header.ack = self.parse_ack(data)
# bits 17..25
header.num_chunks = self.parse_num_chunks(data)
# bits 16..57
header.token = self.parse_token(data)
return header
class PacketHeaderParser7():
def parse_flags7(self, data: bytes) -> PacketFlags7:
# FFFF FFaa
@ -256,6 +373,35 @@ class PacketParser():
msg.header = chunk_header
return msg
def parse6(self, data: bytes, client: bool) -> TwPacket:
pck = TwPacket()
pck.version = '0.6'
# TODO: what is the most performant way in python to do this?
# heap allocating a PacketHeaderParser7 just to bundle a bunch of
# methods that do not share state seems like a waste of performance
# would this be nicer with class methods?
pck.header = PacketHeaderParser6().parse_header(data)
header_size = PACKET_HEADER7_SIZE
if pck.header.flags.connless:
header_size = CONNLESS_PACKET_HEADER7_SIZE
pck.payload_raw = data[header_size:]
pck.payload_decompressed = pck.payload_raw
if pck.header.flags.control:
ctrl_msg: CtrlMessage = match_control6(data[7], data[8:], client)
pck.messages.append(ctrl_msg)
return pck
if pck.header.flags.connless:
connless_msg: ConnlessMessage = match_connless6(data[9:17], data[17:])
pck.messages.append(connless_msg)
return pck
if pck.header.flags.compression:
payload = bytearray(pck.payload_raw)
pck.payload_decompressed = huffman.decompress(payload)
pck.messages = cast(
list[Union[CtrlMessage, NetMessage, ConnlessMessage]],
self.get_messages(pck.payload_decompressed))
return pck
def parse7(self, data: bytes, client: bool) -> TwPacket:
pck = TwPacket()
pck.version = '0.7'
@ -285,8 +431,8 @@ class PacketParser():
self.get_messages(pck.payload_decompressed))
return pck
def parse6(data: bytes) -> TwPacket:
raise NotImplementedError()
def parse6(data: bytes, we_are_a_client: bool = True) -> TwPacket:
return PacketParser().parse6(data, we_are_a_client)
def parse7(data: bytes, we_are_a_client: bool = True) -> TwPacket:
return PacketParser().parse7(data, we_are_a_client)