438 lines
11 KiB
Python
Executable File
438 lines
11 KiB
Python
Executable File
import argparse
|
|
import content
|
|
import network
|
|
|
|
from datatypes import EmitDefinition, EmitTypeDeclaration
|
|
|
|
def create_enum_table(names, num):
|
|
lines = []
|
|
lines += ["enum", "{"]
|
|
for name in names:
|
|
lines += [f"\t{name},"]
|
|
lines += [f"\t{num}", "};"]
|
|
return lines
|
|
|
|
|
|
def create_flags_table(names):
|
|
lines = []
|
|
lines += ["enum", "{"]
|
|
for i, name in enumerate(names):
|
|
lines += [f"\t{name} = 1<<{int(i)},"]
|
|
lines += ["};"]
|
|
return lines
|
|
|
|
|
|
def EmitEnum(names, num):
|
|
print("enum")
|
|
print("{")
|
|
print(f"\t{names[0]}=0,")
|
|
for name in names[1:]:
|
|
print(f"\t{name},")
|
|
print(f"\t{num}")
|
|
print("};")
|
|
|
|
|
|
def EmitFlags(names):
|
|
print("enum")
|
|
print("{")
|
|
for i, name in enumerate(names):
|
|
print(f"\t{name} = 1<<{int(i)},")
|
|
print("};")
|
|
|
|
|
|
def gen_network_header():
|
|
print("#ifndef GAME_GENERATED_PROTOCOL_H")
|
|
print("#define GAME_GENERATED_PROTOCOL_H")
|
|
print("class CUnpacker;")
|
|
print("#include <engine/message.h>")
|
|
print(network.RawHeader)
|
|
|
|
for e in network.Enums:
|
|
for line in create_enum_table([f"{e.name}_{v}" for v in e.values], f'NUM_{e.name}S'): # pylint: disable=no-member
|
|
print(line)
|
|
print("")
|
|
|
|
for e in network.Flags:
|
|
for line in create_flags_table([f"{e.name}_{v}" for v in e.values]):
|
|
print(line)
|
|
print("")
|
|
|
|
non_extended = [o for o in network.Objects if o.ex is None]
|
|
extended = [o for o in network.Objects if o.ex is not None]
|
|
for line in create_enum_table(["NETOBJTYPE_EX"]+[o.enum_name for o in non_extended], "NUM_NETOBJTYPES"):
|
|
print(line)
|
|
for line in create_enum_table(["__NETOBJTYPE_UUID_HELPER=OFFSET_GAME_UUID-1"]+[o.enum_name for o in extended], "OFFSET_NETMSGTYPE_UUID"):
|
|
print(line)
|
|
print("")
|
|
|
|
non_extended = [o for o in network.Messages if o.ex is None]
|
|
extended = [o for o in network.Messages if o.ex is not None]
|
|
for line in create_enum_table(["NETMSGTYPE_EX"]+[o.enum_name for o in non_extended], "NUM_NETMSGTYPES"):
|
|
print(line)
|
|
print("")
|
|
for line in create_enum_table(["__NETMSGTYPE_UUID_HELPER=OFFSET_NETMSGTYPE_UUID-1"]+[o.enum_name for o in extended], "OFFSET_MAPITEMTYPE_UUID"):
|
|
print(line)
|
|
print("")
|
|
|
|
for item in network.Objects + network.Messages:
|
|
for line in item.emit_declaration():
|
|
print(line)
|
|
print("")
|
|
|
|
EmitEnum([f"SOUND_{i.name.value.upper()}" for i in content.container.sounds.items], "NUM_SOUNDS")
|
|
EmitEnum([f"WEAPON_{i.name.value.upper()}" for i in content.container.weapons.id.items], "NUM_WEAPONS")
|
|
|
|
print("""
|
|
class CNetObjHandler
|
|
{
|
|
const char *m_pMsgFailedOn;
|
|
const char *m_pObjFailedOn;
|
|
const char *m_pObjCorrectedOn;
|
|
char m_aUnpackedData[1024 * 2];
|
|
int m_NumObjCorrections;
|
|
int ClampInt(const char *pErrorMsg, int Value, int Min, int Max);
|
|
|
|
static const char *ms_apObjNames[];
|
|
static const char *ms_apExObjNames[];
|
|
static int ms_aObjSizes[];
|
|
static int ms_aUnpackedObjSizes[];
|
|
static int ms_aUnpackedExObjSizes[];
|
|
static const char *ms_apMsgNames[];
|
|
static const char *ms_apExMsgNames[];
|
|
|
|
public:
|
|
CNetObjHandler();
|
|
|
|
void *SecureUnpackObj(int Type, CUnpacker *pUnpacker);
|
|
const char *GetObjName(int Type) const;
|
|
int GetObjSize(int Type) const;
|
|
int GetUnpackedObjSize(int Type) const;
|
|
int NumObjCorrections() const;
|
|
const char *CorrectedObjOn() const;
|
|
const char *FailedObjOn() const;
|
|
|
|
const char *GetMsgName(int Type) const;
|
|
void *SecureUnpackMsg(int Type, CUnpacker *pUnpacker);
|
|
bool TeeHistorianRecordMsg(int Type);
|
|
const char *FailedMsgOn() const;
|
|
};
|
|
""")
|
|
|
|
print("#endif // GAME_GENERATED_PROTOCOL_H")
|
|
|
|
|
|
def gen_network_source():
|
|
|
|
print("""\
|
|
#include "protocol.h"
|
|
|
|
#include <engine/shared/packer.h>
|
|
#include <engine/shared/protocol.h>
|
|
#include <engine/shared/uuid_manager.h>
|
|
|
|
#include <game/mapitems_ex.h>
|
|
|
|
CNetObjHandler::CNetObjHandler()
|
|
{
|
|
m_pMsgFailedOn = "";
|
|
m_pObjFailedOn = "";
|
|
m_pObjCorrectedOn = "";
|
|
m_NumObjCorrections = 0;
|
|
}
|
|
|
|
int CNetObjHandler::NumObjCorrections() const { return m_NumObjCorrections; }
|
|
const char *CNetObjHandler::CorrectedObjOn() const { return m_pObjCorrectedOn; }
|
|
const char *CNetObjHandler::FailedObjOn() const { return m_pObjFailedOn; }
|
|
const char *CNetObjHandler::FailedMsgOn() const { return m_pMsgFailedOn; }
|
|
|
|
static const int max_int = 0x7fffffff;
|
|
static const int min_int = 0x80000000;
|
|
|
|
int CNetObjHandler::ClampInt(const char *pErrorMsg, int Value, int Min, int Max)
|
|
{
|
|
if(Value < Min) { m_pObjCorrectedOn = pErrorMsg; m_NumObjCorrections++; return Min; }
|
|
if(Value > Max) { m_pObjCorrectedOn = pErrorMsg; m_NumObjCorrections++; return Max; }
|
|
return Value;
|
|
}
|
|
""")
|
|
|
|
|
|
lines = []
|
|
lines += ["const char *CNetObjHandler::ms_apObjNames[] = {"]
|
|
lines += ['\t"EX/UUID",']
|
|
lines += [f'\t"{o.name}",' for o in network.Objects if o.ex is None]
|
|
lines += ['\t""', "};", ""]
|
|
|
|
lines += ["const char *CNetObjHandler::ms_apExObjNames[] = {"]
|
|
lines += ['\t"invalid",']
|
|
lines += [f'\t"{o.name}",' for o in network.Objects if o.ex is not None]
|
|
lines += ['\t""', "};", ""]
|
|
|
|
lines += ["int CNetObjHandler::ms_aObjSizes[] = {"]
|
|
lines += ['\t0,']
|
|
lines += [f'\tsizeof({o.struct_name}),' for o in network.Objects if o.ex is None]
|
|
lines += ['\t0', "};", ""]
|
|
|
|
lines += ["int CNetObjHandler::ms_aUnpackedObjSizes[] = {"]
|
|
lines += ['\t16,']
|
|
lines += [f'\tsizeof({o.struct_name}),' for o in network.Objects if o.ex is None]
|
|
lines += ["};", ""]
|
|
|
|
lines += ["int CNetObjHandler::ms_aUnpackedExObjSizes[] = {"]
|
|
lines += ['\t0,']
|
|
lines += [f'\tsizeof({o.struct_name}),' for o in network.Objects if o.ex is not None]
|
|
lines += ["};", ""]
|
|
|
|
lines += ['const char *CNetObjHandler::ms_apMsgNames[] = {']
|
|
lines += ['\t"invalid",']
|
|
lines += [f'\t"{msg.name}",' for msg in network.Messages if msg.ex is None]
|
|
lines += ['\t""', "};", ""]
|
|
|
|
lines += ['const char *CNetObjHandler::ms_apExMsgNames[] = {']
|
|
lines += ['\t"invalid",']
|
|
lines += [f'\t"{msg.name}",' for msg in network.Messages if msg.ex is not None]
|
|
lines += ['\t""', "};", ""]
|
|
|
|
for line in lines:
|
|
print(line)
|
|
|
|
print("""\
|
|
const char *CNetObjHandler::GetObjName(int Type) const
|
|
{
|
|
if(Type >= 0 && Type < NUM_NETOBJTYPES)
|
|
{
|
|
return ms_apObjNames[Type];
|
|
}
|
|
else if(Type > __NETOBJTYPE_UUID_HELPER && Type < OFFSET_NETMSGTYPE_UUID)
|
|
{
|
|
return ms_apExObjNames[Type - __NETOBJTYPE_UUID_HELPER];
|
|
}
|
|
return "(out of range)";
|
|
}
|
|
|
|
int CNetObjHandler::GetObjSize(int Type) const
|
|
{
|
|
if(Type < 0 || Type >= NUM_NETOBJTYPES) return 0;
|
|
return ms_aObjSizes[Type];
|
|
}
|
|
|
|
int CNetObjHandler::GetUnpackedObjSize(int Type) const
|
|
{
|
|
if(Type >= 0 && Type < NUM_NETOBJTYPES)
|
|
{
|
|
return ms_aUnpackedObjSizes[Type];
|
|
}
|
|
else if(Type > __NETOBJTYPE_UUID_HELPER && Type < OFFSET_NETMSGTYPE_UUID)
|
|
{
|
|
return ms_aUnpackedExObjSizes[Type - __NETOBJTYPE_UUID_HELPER];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const char *CNetObjHandler::GetMsgName(int Type) const
|
|
{
|
|
if(Type >= 0 && Type < NUM_NETMSGTYPES)
|
|
{
|
|
return ms_apMsgNames[Type];
|
|
}
|
|
else if(Type > __NETMSGTYPE_UUID_HELPER && Type < OFFSET_MAPITEMTYPE_UUID)
|
|
{
|
|
return ms_apExMsgNames[Type - __NETMSGTYPE_UUID_HELPER];
|
|
}
|
|
return "(out of range)";
|
|
}
|
|
""")
|
|
|
|
lines = []
|
|
lines += ["""\
|
|
void *CNetObjHandler::SecureUnpackObj(int Type, CUnpacker *pUnpacker)
|
|
{
|
|
m_pObjFailedOn = 0;
|
|
switch(Type)
|
|
{
|
|
case NETOBJTYPE_EX:
|
|
{
|
|
const unsigned char *pPtr = pUnpacker->GetRaw(sizeof(CUuid));
|
|
if(pPtr != 0)
|
|
{
|
|
mem_copy(m_aUnpackedData, pPtr, sizeof(CUuid));
|
|
}
|
|
break;
|
|
}
|
|
"""]
|
|
|
|
for item in network.Objects:
|
|
base_item = None
|
|
if item.base:
|
|
base_item = next(i for i in network.Objects if i.name == item.base)
|
|
for line in item.emit_uncompressed_unpack_and_validate(base_item):
|
|
lines += ["\t" + line]
|
|
lines += ['\t']
|
|
|
|
lines += ["""\
|
|
default:
|
|
m_pObjFailedOn = "(type out of range)";
|
|
break;
|
|
}
|
|
|
|
if(pUnpacker->Error())
|
|
m_pObjFailedOn = "(unpack error)";
|
|
|
|
if(m_pObjFailedOn)
|
|
return 0;
|
|
m_pObjFailedOn = "";
|
|
return m_aUnpackedData;
|
|
}
|
|
"""]
|
|
for line in lines:
|
|
print(line)
|
|
|
|
|
|
lines = []
|
|
lines += ["""\
|
|
void *CNetObjHandler::SecureUnpackMsg(int Type, CUnpacker *pUnpacker)
|
|
{
|
|
m_pMsgFailedOn = 0;
|
|
switch(Type)
|
|
{
|
|
"""]
|
|
|
|
for item in network.Messages:
|
|
for line in item.emit_unpack_msg():
|
|
lines += ["\t" + line]
|
|
lines += ['\t']
|
|
|
|
lines += ["""\
|
|
default:
|
|
m_pMsgFailedOn = "(type out of range)";
|
|
break;
|
|
}
|
|
|
|
if(pUnpacker->Error())
|
|
m_pMsgFailedOn = "(unpack error)";
|
|
|
|
if(m_pMsgFailedOn)
|
|
return 0;
|
|
m_pMsgFailedOn = "";
|
|
return m_aUnpackedData;
|
|
}
|
|
"""]
|
|
for line in lines:
|
|
print(line)
|
|
|
|
|
|
lines = []
|
|
lines += ["""\
|
|
bool CNetObjHandler::TeeHistorianRecordMsg(int Type)
|
|
{
|
|
switch(Type)
|
|
{
|
|
"""]
|
|
|
|
empty = True
|
|
for msg in network.Messages:
|
|
if not msg.teehistorian:
|
|
lines += [f'\tcase {msg.enum_name}:']
|
|
empty = False
|
|
if not empty:
|
|
lines += ['\t\treturn false;']
|
|
|
|
lines += ["""\
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
"""]
|
|
for line in lines:
|
|
print(line)
|
|
|
|
|
|
lines = []
|
|
lines += ["""\
|
|
void RegisterGameUuids(CUuidManager *pManager)
|
|
{
|
|
"""]
|
|
|
|
for item in network.Objects + network.Messages:
|
|
if item.ex is not None:
|
|
lines += [f'\tpManager->RegisterName({item.enum_name}, "{item.ex}");']
|
|
|
|
lines += ["""
|
|
RegisterMapItemTypeUuids(pManager);
|
|
}
|
|
"""]
|
|
for line in lines:
|
|
print(line)
|
|
|
|
|
|
def gen_common_content_header():
|
|
# print some includes
|
|
print('#include <engine/graphics.h>')
|
|
|
|
# emit the type declarations
|
|
with open("datasrc/content.py", "rb") as content_file:
|
|
contentlines = content_file.readlines()
|
|
order = []
|
|
for line in contentlines:
|
|
line = line.strip()
|
|
if line[:6] == "class ".encode() and "(Struct)".encode() in line:
|
|
order += [line.split()[1].split("(".encode())[0].decode("ascii")]
|
|
for name in order:
|
|
EmitTypeDeclaration(content.__dict__[name])
|
|
|
|
# the container pointer
|
|
print('extern CDataContainer *g_pData;')
|
|
|
|
# enums
|
|
EmitEnum([f"IMAGE_{i.name.value.upper()}" for i in content.container.images.items], "NUM_IMAGES")
|
|
EmitEnum([f"ANIM_{i.name.value.upper()}" for i in content.container.animations.items], "NUM_ANIMS")
|
|
EmitEnum([f"SPRITE_{i.name.value.upper()}" for i in content.container.sprites.items], "NUM_SPRITES")
|
|
|
|
def gen_common_content_source():
|
|
EmitDefinition(content.container, "datacontainer")
|
|
print('CDataContainer *g_pData = &datacontainer;')
|
|
|
|
def gen_client_content_header():
|
|
print("#ifndef CLIENT_CONTENT_HEADER")
|
|
print("#define CLIENT_CONTENT_HEADER")
|
|
gen_common_content_header()
|
|
print("#endif")
|
|
|
|
|
|
def gen_client_content_source():
|
|
print('#include "client_data.h"')
|
|
gen_common_content_source()
|
|
|
|
|
|
def gen_server_content_header():
|
|
print("#ifndef SERVER_CONTENT_HEADER")
|
|
print("#define SERVER_CONTENT_HEADER")
|
|
gen_common_content_header()
|
|
print("#endif")
|
|
|
|
|
|
def gen_server_content_source():
|
|
print('#include "server_data.h"')
|
|
gen_common_content_source()
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description=('Generate C++ Source Files for the Teeworlds Network Protocol')
|
|
)
|
|
FUNCTION_MAP = {
|
|
'network_header': gen_network_header,
|
|
'network_source': gen_network_source,
|
|
'client_content_header': gen_client_content_header,
|
|
'client_content_source': gen_client_content_source,
|
|
'server_content_header': gen_server_content_header,
|
|
'server_content_source': gen_server_content_source,
|
|
}
|
|
|
|
parser.add_argument('file_to_generate', choices=FUNCTION_MAP.keys())
|
|
args = parser.parse_args()
|
|
FUNCTION_MAP[args.file_to_generate]()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|