Generate system messages

This commit is contained in:
ChillerDragon 2023-03-29 16:22:00 +02:00
parent 14fd5f2b40
commit b39f067222

View file

@ -20,22 +20,23 @@ class GameMessageMemberTypeJson(TypedDict):
kind: str kind: str
disallow_cc: bool disallow_cc: bool
class GameMessageMemberJson(TypedDict): class NetMessageMemberJson(TypedDict):
name: list[str] name: list[str]
type: GameMessageMemberTypeJson type: GameMessageMemberTypeJson
class GameMessageJson(TypedDict): class NetMessageJson(TypedDict):
id: int id: int
name: list[str] name: list[str]
members: list[GameMessageMemberJson] members: list[NetMessageMemberJson]
attributes: list[Literal['msg_encoding']] attributes: list[Literal['msg_encoding']]
class SpecJson(TypedDict): class SpecJson(TypedDict):
constants: list[ConstantJson] constants: list[ConstantJson]
game_enumerations: list[GameEnumJson] game_enumerations: list[GameEnumJson]
game_messages: list[GameMessageJson] game_messages: list[NetMessageJson]
system_messages: list[NetMessageJson]
def gen_match_game7(game_messages: list[GameMessageJson]): def gen_match_game7(game_messages: list[NetMessageJson]):
match_game7: str = """# generated by scripts/generate_messages.py match_game7: str = """# generated by scripts/generate_messages.py
from typing import Optional from typing import Optional
@ -44,7 +45,7 @@ from twnet_parser.net_message import NetMessage
""" """
msg: GameMessageJson msg: NetMessageJson
for msg in game_messages: for msg in game_messages:
name_snake = name_to_snake(msg['name']) name_snake = name_to_snake(msg['name'])
match_game7 += "import twnet_parser.messages7.game" \ match_game7 += "import twnet_parser.messages7.game" \
@ -100,13 +101,13 @@ def name_to_snake(name_list: list[str]) -> str:
name = '_'.join(name_list) name = '_'.join(name_list)
return fix_name_conflict(name) return fix_name_conflict(name)
def generate_msg(msg: GameMessageJson) -> None: def generate_msg(msg: NetMessageJson, game: str) -> None:
name_snake = name_to_snake(msg['name']) name_snake = name_to_snake(msg['name'])
name_camel = name_to_camel(msg['name']) name_camel = name_to_camel(msg['name'])
dirname = os.path.dirname(__file__) dirname = os.path.dirname(__file__)
file_path= os.path.join( file_path= os.path.join(
dirname, dirname,
'../twnet_parser/messages7/game/', f'../twnet_parser/messages7/{game}/',
f'{name_snake}.py') f'{name_snake}.py')
# if os.path.exists(file_path): # if os.path.exists(file_path):
# print(f"Warning: file already exists! {file_path}") # print(f"Warning: file already exists! {file_path}")
@ -131,7 +132,7 @@ def generate_msg(msg: GameMessageJson) -> None:
if member['type']['kind'] == 'string': # TODO: sanitize cc if member['type']['kind'] == 'string': # TODO: sanitize cc
ftype = 'str' ftype = 'str'
default = "'default'" default = "'default'"
elif member['type']['kind'] == 'raw': elif member['type']['kind'] in ('raw', 'sha256', 'rest', 'data'): # TODO: data has a size field
ftype = 'bytes' ftype = 'bytes'
default = "b'\\x00'" default = "b'\\x00'"
# {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}}, # {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}},
@ -158,15 +159,18 @@ def generate_msg(msg: GameMessageJson) -> None:
elif member['type']['kind'] == 'flags': # TODO: think about flags elif member['type']['kind'] == 'flags': # TODO: think about flags
ftype = 'int' ftype = 'int'
default = '0' default = '0'
elif member['type']['kind'] == 'optional': # TODO: think about optionals
ftype = 'int'
default = '0'
else: else:
print(f"Error: unknown type {member['type']}") raise ValueError(f"Error: unknown type {member['type']}")
exit(1)
name = name_to_snake(member["name"]) name = name_to_snake(member["name"])
args.append(f' {name}: {ftype} = {default}') args.append(f' {name}: {ftype} = {default}')
out_file.write(',\n'.join(args) + '\n') out_file.write(',\n'.join(args) + '\n')
out_file.write(' ) -> None:\n') out_file.write(' ) -> None:\n')
out_file.write(f" self.message_name = '{name_snake}'\n") out_file.write(f" self.message_name = '{name_snake}'\n")
out_file.write(" self.system_message = False\n") sys: str = 'True' if game == 'system' else 'False'
out_file.write(f" self.system_message = {sys}\n")
out_file.write(" self.header: ChunkHeader\n") out_file.write(" self.header: ChunkHeader\n")
out_file.write('\n') out_file.write('\n')
for member in msg['members']: for member in msg['members']:
@ -174,7 +178,7 @@ def generate_msg(msg: GameMessageJson) -> None:
ftype = 'int' ftype = 'int'
if member['type']['kind'] == 'string': if member['type']['kind'] == 'string':
ftype = 'str' ftype = 'str'
elif member['type']['kind'] == 'raw': elif member['type']['kind'] in ('raw', 'sha256', 'rest', 'data'): # TODO: data has a size field
ftype = 'bytes' ftype = 'bytes'
# {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}}, # {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}},
elif member['type']['kind'] == 'enum': elif member['type']['kind'] == 'enum':
@ -192,9 +196,10 @@ def generate_msg(msg: GameMessageJson) -> None:
ftype = 'int' ftype = 'int'
elif member['type']['kind'] == 'flags': # TODO: think about flags elif member['type']['kind'] == 'flags': # TODO: think about flags
ftype = 'int' ftype = 'int'
elif member['type']['kind'] == 'optional': # TODO: think about optionals
ftype = 'int'
else: else:
print(f"Error: unknown type {member['type']}") raise ValueError(f"Error: unknown type {member['type']}")
exit(1)
name = name_to_snake(member["name"]) name = name_to_snake(member["name"])
out_file.write(f" self.{name}: {ftype} = {name}\n") out_file.write(f" self.{name}: {ftype} = {name}\n")
out_file.write('\n') out_file.write('\n')
@ -208,7 +213,7 @@ def generate_msg(msg: GameMessageJson) -> None:
unpacker = 'int()' unpacker = 'int()'
if member['type']['kind'] == 'string': # TODO: sanitize cc if member['type']['kind'] == 'string': # TODO: sanitize cc
unpacker = 'str()' unpacker = 'str()'
elif member['type']['kind'] == 'raw': elif member['type']['kind'] in ('raw', 'sha256', 'rest', 'data'): # TODO: data has a size field
unpacker = 'raw()' unpacker = 'raw()'
# {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}}, # {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}},
elif member['type']['kind'] == 'enum': elif member['type']['kind'] == 'enum':
@ -226,9 +231,10 @@ def generate_msg(msg: GameMessageJson) -> None:
unpacker = 'int() # TODO: this is an array' unpacker = 'int() # TODO: this is an array'
elif member['type']['kind'] == 'flags': # TODO: think about flags elif member['type']['kind'] == 'flags': # TODO: think about flags
unpacker = 'int() # TODO: this is a flag' unpacker = 'int() # TODO: this is a flag'
elif member['type']['kind'] == 'optional': # TODO: think about optional
unpacker = 'int() # TODO: this is a optional of type any'
else: else:
print(f"Error: unknown type {member['type']}") raise ValueError(f"Error: unknown type {member['type']}")
exit(1)
name = name_to_snake(member["name"]) name = name_to_snake(member["name"])
out_file.write(f' self.{name} = unpacker.get_{unpacker}\n') out_file.write(f' self.{name} = unpacker.get_{unpacker}\n')
out_file.write(' return True\n') out_file.write(' return True\n')
@ -236,12 +242,12 @@ def generate_msg(msg: GameMessageJson) -> None:
out_file.write(' def pack(self) -> bytes:\n') out_file.write(' def pack(self) -> bytes:\n')
out_file.write(gen_pack_return(msg)) out_file.write(gen_pack_return(msg))
def get_dependencies(msg: GameMessageJson) -> str: def get_dependencies(msg: NetMessageJson) -> str:
packer_deps: list[str] = [] packer_deps: list[str] = []
for member in msg['members']: for member in msg['members']:
if member['type']['kind'] == 'string': # TODO: sanitize cc if member['type']['kind'] == 'string': # TODO: sanitize cc
packer_deps.append('pack_str') packer_deps.append('pack_str')
elif member['type']['kind'] == 'raw': elif member['type']['kind'] in ('raw', 'sha256', 'rest', 'data'): # TODO: data has a size field
pass pass
# {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}}, # {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}},
elif member['type']['kind'] == 'enum': elif member['type']['kind'] == 'enum':
@ -259,20 +265,21 @@ def get_dependencies(msg: GameMessageJson) -> str:
packer_deps.append('pack_int') packer_deps.append('pack_int')
elif member['type']['kind'] == 'flags': # TODO: think about flags elif member['type']['kind'] == 'flags': # TODO: think about flags
packer_deps.append('pack_int') packer_deps.append('pack_int')
elif member['type']['kind'] == 'optional': # TODO: think about optional
packer_deps.append('pack_int')
else: else:
print(f"Error: unknown type {member['type']}") raise ValueError(f"Error: unknown type {member['type']}")
exit(1)
if len(packer_deps) == 0: if len(packer_deps) == 0:
return '' return ''
return 'from twnet_parser.packer import ' + \ return 'from twnet_parser.packer import ' + \
', '.join(set(packer_deps)) + '\n' ', '.join(set(packer_deps)) + '\n'
def pack_field(member: GameMessageMemberJson) -> str: def pack_field(member: NetMessageMemberJson) -> str:
name: str = name_to_snake(member["name"]) name: str = name_to_snake(member["name"])
packer = 'int' packer = 'int'
if member['type']['kind'] == 'string': # TODO: sanitize cc if member['type']['kind'] == 'string': # TODO: sanitize cc
packer = 'str' packer = 'str'
elif member['type']['kind'] == 'raw': elif member['type']['kind'] in ('raw', 'sha256', 'rest', 'data'): # TODO: data has a size field
return f'self.{name}' return f'self.{name}'
# {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}}, # {"name": ["mode"], "type": {"kind": "enum", "enum": ["chat"]}},
elif member['type']['kind'] == 'enum': elif member['type']['kind'] == 'enum':
@ -290,13 +297,14 @@ def pack_field(member: GameMessageMemberJson) -> str:
packer = 'int' packer = 'int'
elif member['type']['kind'] == 'flags': # TODO: think about flags elif member['type']['kind'] == 'flags': # TODO: think about flags
packer = 'int' packer = 'int'
elif member['type']['kind'] == 'optional': # TODO: think about optional
packer = 'int'
else: else:
print(f"Error: unknown type {member['type']}") raise ValueError(f"Error: unknown type {member['type']}")
exit(1)
return f'pack_{packer}(self.{name})' return f'pack_{packer}(self.{name})'
def gen_pack_return(msg: GameMessageJson) -> str: def gen_pack_return(msg: NetMessageJson) -> str:
members: list[GameMessageMemberJson] = msg['members'] members: list[NetMessageMemberJson] = msg['members']
if len(members) == 0: if len(members) == 0:
return " return b''" return " return b''"
if len(members) == 1: if len(members) == 1:
@ -311,12 +319,13 @@ def generate(spec: str) -> None:
with open(spec) as spec_io: with open(spec) as spec_io:
spec_data: SpecJson = json.load(spec_io) spec_data: SpecJson = json.load(spec_io)
# for msg in [spec_data['game_messages'][1]]: # for msg in [spec_data['game_messages'][1]]:
game_messages: list[GameMessageJson] = spec_data['game_messages'] game_messages: list[NetMessageJson] = spec_data['game_messages']
system_messages: list[NetMessageJson] = spec_data['system_messages']
gen_match_game7(game_messages) gen_match_game7(game_messages)
exit(0)
for msg in game_messages: for msg in game_messages:
generate_msg(msg) generate_msg(msg, 'game')
for msg in system_messages:
generate_msg(msg, 'system')
def main() -> None: def main() -> None:
dirname = os.path.dirname(__file__) dirname = os.path.dirname(__file__)