Use named default for enums and move enum file

This commit is contained in:
ChillerDragon 2023-04-16 09:28:36 +02:00
parent 58051469f2
commit 90b43608b9
13 changed files with 98 additions and 51 deletions

View file

@ -46,6 +46,10 @@ class ArrayMemberTypeJson(TypedDict):
class NetMessageMemberTypeJson(TypedDict):
kind: KIND
inner: InnerNetMessageMemberTypeJson
# enums
enum: list[str]
# strings
disallow_cc: bool
@ -164,7 +168,8 @@ def gen_unpack_members(msg: NetMessageJson) -> str:
raise ValueError(f"Error: unknown data size {member['type']}")
# {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}},
elif member['type']['kind'] == 'enum':
unpacker = 'int() # TODO: this is a enum'
enum_name: str = name_to_camel(member['type']['enum']).upper()
unpacker = f"int() # enum {enum_name}"
elif member['type']['kind'] in ('int32', 'tick'):
unpacker = 'int()'
elif member['type']['kind'] == 'boolean':
@ -187,6 +192,8 @@ def gen_unpack_members(msg: NetMessageJson) -> str:
else:
unpacker = 'str()'
elif arr_member['kind'] == 'enum':
# TODO: can we represent the enum as text
# instead of as magic number?
unpacker = 'int() # TODO: this is a enum'
elif arr_member['kind'] == 'boolean':
unpacker = 'int() == 1'
@ -220,6 +227,7 @@ def gen_unpack_members(msg: NetMessageJson) -> str:
def get_dependencies(msg: NetMessageJson) -> str:
packer_deps: list[str] = []
typing_deps: list[str] = []
need_enums: bool = False
for member in msg['members']:
if member['type']['kind'] == 'string':
packer_deps.append('pack_str')
@ -236,6 +244,7 @@ def get_dependencies(msg: NetMessageJson) -> str:
raise ValueError(f"Error: unknown data size {member['type']}")
# {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}},
elif member['type']['kind'] == 'enum':
need_enums = True
packer_deps.append('pack_int')
elif member['type']['kind'] in ('int32', 'tick'):
packer_deps.append('pack_int')
@ -255,6 +264,7 @@ def get_dependencies(msg: NetMessageJson) -> str:
if arr_member['disallow_cc']:
packer_deps.append('SANITIZE_CC')
elif arr_member['kind'] == 'enum':
need_enums = True
packer_deps.append('pack_int')
elif arr_member['kind'] == 'boolean':
packer_deps.append('pack_int')
@ -280,6 +290,8 @@ def get_dependencies(msg: NetMessageJson) -> str:
if len(typing_deps) > 0:
res += 'from typing import ' + \
', '.join(sorted(set(typing_deps))) + '\n'
if need_enums:
res += 'import twnet_parser.enum7 as enum7\n'
return res
def pack_field(member: NetMessageMemberJson) -> str:
@ -382,31 +394,10 @@ def get_default(field_path: str) -> Optional[str]:
print(f" please check {def_file} for errors")
exit(1)
def gen_enum_file7(enums: list[GameEnumJson]):
enum_code: str = ''
enum: GameEnumJson
for enum in enums:
base: str = name_to_snake(enum['name']).upper()
val: GameEnumValueJson
for val in enum['values']:
sub: str = name_to_snake(val['name']).upper()
enum_code += f"{base}_{sub}: int = {val['value']}\n"
enum_code += "\n"
# cut off last doubled newline
# because we do not split a section anymore
enum_code = enum_code[:-1]
dirname = os.path.dirname(__file__)
file_path= os.path.join(
dirname,
'../twnet_parser/msg_matcher/enum7.py')
# if os.path.exists(file_path):
# print(f"Warning: file already exists! {file_path}")
# return
with open(file_path, 'w') as out_file:
print(f"Generating {file_path} ...")
out_file.write(enum_code)
class CodeGenerator():
def __init__(self) -> None:
self.game_enums: list[GameEnumJson] = []
def generate_msg(
self,
msg: NetMessageJson,
@ -465,8 +456,9 @@ class CodeGenerator():
# {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}},
elif member['type']['kind'] == 'enum':
ftype = 'int'
default = '0'
# TODO: use ENUM_NAME_SOME_VALUE as default here
enum_name: str = name_to_snake(member['type']['enum'])
default = self.get_default_enum7(enum_name)
default = f"enum7.{default}"
elif member['type']['kind'] in ('int32', 'tick'):
ftype = 'int'
default = '0'
@ -602,15 +594,59 @@ class CodeGenerator():
out_file.write(' def pack(self) -> bytes:\n')
out_file.write(gen_pack_return(msg))
def get_default_enum7(self, enum_name: str) -> str:
"""
enum_name has to be snake case
can be lower or upper case does not matter
If for example enum_name 'chat' is given
it returns 'CHAT_NONE'
"""
enum_name = enum_name.upper()
enum: GameEnumJson
for enum in self.game_enums:
base: str = name_to_snake(enum['name']).upper()
if base != enum_name:
continue
val: GameEnumValueJson
for val in enum['values']:
sub: str = name_to_snake(val['name']).upper()
return f"{base}_{sub}"
raise ValueError(f"Enum not found '{enum_name}'")
def gen_enum_file7(self) -> None:
enum_code: str = ''
enum: GameEnumJson
for enum in self.game_enums:
base: str = name_to_snake(enum['name']).upper()
val: GameEnumValueJson
for val in enum['values']:
sub: str = name_to_snake(val['name']).upper()
enum_code += f"{base}_{sub}: int = {val['value']}\n"
enum_code += "\n"
# cut off last doubled newline
# because we do not split a section anymore
enum_code = enum_code[:-1]
dirname = os.path.dirname(__file__)
file_path= os.path.join(
dirname,
'../twnet_parser/enum7.py')
# if os.path.exists(file_path):
# print(f"Warning: file already exists! {file_path}")
# return
with open(file_path, 'w') as out_file:
print(f"Generating {file_path} ...")
out_file.write(enum_code)
def generate(self, spec: str) -> None:
print(f"generating classes from {spec} ...")
with open(spec) as spec_io:
spec_data: SpecJson = json.load(spec_io)
# for msg in [spec_data['game_messages'][1]]:
game_enums: list[GameEnumJson] = spec_data['game_enumerations']
self.game_enums = spec_data['game_enumerations']
game_messages: list[NetMessageJson] = spec_data['game_messages']
system_messages: list[NetMessageJson] = spec_data['system_messages']
gen_enum_file7(game_enums)
self.gen_enum_file7()
gen_match_file7('game', game_messages)
gen_match_file7('system', system_messages)
for msg in game_messages:

View file

@ -4,11 +4,12 @@ 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
import twnet_parser.enum7 as enum7
class MsgClEmoticon(PrettyPrint):
def __init__(
self,
emoticon: int = 0
emoticon: int = enum7.EMOTICON_OOP
) -> None:
self.message_name = 'cl_emoticon'
self.system_message = False
@ -21,7 +22,7 @@ class MsgClEmoticon(PrettyPrint):
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.emoticon = unpacker.get_int() # TODO: this is a enum
self.emoticon = unpacker.get_int() # enum EMOTICON
return True
def pack(self) -> bytes:

View file

@ -4,11 +4,12 @@ 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
import twnet_parser.enum7 as enum7
class MsgClSay(PrettyPrint):
def __init__(
self,
mode: int = 0,
mode: int = enum7.CHAT_NONE,
target: int = 0,
message: str = 'default'
) -> None:
@ -25,7 +26,7 @@ class MsgClSay(PrettyPrint):
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.mode = unpacker.get_int() # TODO: this is a enum
self.mode = unpacker.get_int() # enum CHAT
self.target = unpacker.get_int()
self.message = unpacker.get_str(SANITIZE_CC)
return True

View file

@ -4,11 +4,12 @@ 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
import twnet_parser.enum7 as enum7
class MsgClSetSpectatorMode(PrettyPrint):
def __init__(
self,
spec_mode: int = 0,
spec_mode: int = enum7.SPEC_FREEVIEW,
spectator_id: int = 0
) -> None:
self.message_name = 'cl_set_spectator_mode'
@ -23,7 +24,7 @@ class MsgClSetSpectatorMode(PrettyPrint):
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.spec_mode = unpacker.get_int() # TODO: this is a enum
self.spec_mode = unpacker.get_int() # enum SPEC
self.spectator_id = unpacker.get_int()
return True

View file

@ -4,11 +4,12 @@ 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
import twnet_parser.enum7 as enum7
class MsgClSetTeam(PrettyPrint):
def __init__(
self,
team: int = 0
team: int = enum7.TEAM_SPECTATORS
) -> None:
self.message_name = 'cl_set_team'
self.system_message = False
@ -21,7 +22,7 @@ class MsgClSetTeam(PrettyPrint):
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.team = unpacker.get_int() # TODO: this is a enum
self.team = unpacker.get_int() # enum TEAM
return True
def pack(self) -> bytes:

View file

@ -4,13 +4,14 @@ 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
import twnet_parser.enum7 as enum7
class MsgDeClientEnter(PrettyPrint):
def __init__(
self,
name: str = 'default',
client_id: int = 0,
team: int = 0
team: int = enum7.TEAM_SPECTATORS
) -> None:
self.message_name = 'de_client_enter'
self.system_message = False
@ -27,7 +28,7 @@ class MsgDeClientEnter(PrettyPrint):
unpacker = Unpacker(data)
self.name = unpacker.get_str(SANITIZE_CC)
self.client_id = unpacker.get_int()
self.team = unpacker.get_int() # TODO: this is a enum
self.team = unpacker.get_int() # enum TEAM
return True
def pack(self) -> bytes:

View file

@ -4,11 +4,12 @@ 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
import twnet_parser.enum7 as enum7
class MsgSvChat(PrettyPrint):
def __init__(
self,
mode: int = 0,
mode: int = enum7.CHAT_NONE,
client_id: int = 0,
target_id: int = 0,
message: str = 'default'
@ -27,7 +28,7 @@ class MsgSvChat(PrettyPrint):
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.mode = unpacker.get_int() # TODO: this is a enum
self.mode = unpacker.get_int() # enum CHAT
self.client_id = unpacker.get_int()
self.target_id = unpacker.get_int()
self.message = unpacker.get_str(SANITIZE_CC)

View file

@ -5,13 +5,14 @@ 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
import twnet_parser.enum7 as enum7
class MsgSvClientInfo(PrettyPrint):
def __init__(
self,
client_id: int = 0,
local: bool = False,
team: int = 0,
team: int = enum7.TEAM_SPECTATORS,
name: str = 'default',
clan: str = 'default',
country: int = 0,
@ -45,7 +46,7 @@ class MsgSvClientInfo(PrettyPrint):
unpacker = Unpacker(data)
self.client_id = unpacker.get_int()
self.local = unpacker.get_int() == 1
self.team = unpacker.get_int() # TODO: this is a enum
self.team = unpacker.get_int() # enum TEAM
self.name = unpacker.get_str(SANITIZE_CC)
self.clan = unpacker.get_str(SANITIZE_CC)
self.country = unpacker.get_int()

View file

@ -4,12 +4,13 @@ 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
import twnet_parser.enum7 as enum7
class MsgSvEmoticon(PrettyPrint):
def __init__(
self,
client_id: int = 0,
emoticon: int = 0
emoticon: int = enum7.EMOTICON_OOP
) -> None:
self.message_name = 'sv_emoticon'
self.system_message = False
@ -24,7 +25,7 @@ class MsgSvEmoticon(PrettyPrint):
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.client_id = unpacker.get_int()
self.emoticon = unpacker.get_int() # TODO: this is a enum
self.emoticon = unpacker.get_int() # enum EMOTICON
return True
def pack(self) -> bytes:

View file

@ -4,12 +4,13 @@ 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
import twnet_parser.enum7 as enum7
class MsgSvTeam(PrettyPrint):
def __init__(
self,
client_id: int = 0,
team: int = 0,
team: int = enum7.TEAM_SPECTATORS,
silent: bool = False,
cooldown_tick: int = 0
) -> None:
@ -28,7 +29,7 @@ class MsgSvTeam(PrettyPrint):
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.client_id = unpacker.get_int()
self.team = unpacker.get_int() # TODO: this is a enum
self.team = unpacker.get_int() # enum TEAM
self.silent = unpacker.get_int() == 1
self.cooldown_tick = unpacker.get_int()
return True

View file

@ -4,12 +4,13 @@ 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
import twnet_parser.enum7 as enum7
class MsgSvVoteSet(PrettyPrint):
def __init__(
self,
client_id: int = 0,
type: int = 0,
type: int = enum7.VOTE_UNKNOWN,
timeout: int = 0,
description: str = 'default',
reason: str = 'default'
@ -30,7 +31,7 @@ class MsgSvVoteSet(PrettyPrint):
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.client_id = unpacker.get_int()
self.type = unpacker.get_int() # TODO: this is a enum
self.type = unpacker.get_int() # enum VOTE
self.timeout = unpacker.get_int()
self.description = unpacker.get_str(SANITIZE_CC)
self.reason = unpacker.get_str(SANITIZE_CC)

View file

@ -4,11 +4,12 @@ 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
import twnet_parser.enum7 as enum7
class MsgSvWeaponPickup(PrettyPrint):
def __init__(
self,
weapon: int = 0
weapon: int = enum7.WEAPON_HAMMER
) -> None:
self.message_name = 'sv_weapon_pickup'
self.system_message = False
@ -21,7 +22,7 @@ class MsgSvWeaponPickup(PrettyPrint):
# NOT the chunk header and NOT the message id
def unpack(self, data: bytes) -> bool:
unpacker = Unpacker(data)
self.weapon = unpacker.get_int() # TODO: this is a enum
self.weapon = unpacker.get_int() # enum WEAPON
return True
def pack(self) -> bytes: