ddnet/datasrc/compile.py
heinrich5991 5ae37e6c72 Add protocol extension with UUIDs
This system can easily be extended by independent authors without
collisions, something the old system with plain increasing integers did
not allow.

Do this by utilizing the previously unused message code `NETMSG_NULL`
which has a value of 0.

This works for engine and game messages, snapshot items and events.
2017-05-25 00:52:43 +02:00

339 lines
9.8 KiB
Python

import os, imp, sys
from datatypes import *
import content
import network
def create_enum_table(names, num):
lines = []
lines += ["enum", "{"]
for name in names:
lines += ["\t%s,"%name]
lines += ["\t%s" % num, "};"]
return lines
def create_flags_table(names):
lines = []
lines += ["enum", "{"]
i = 0
for name in names:
lines += ["\t%s = 1<<%d," % (name,i)]
i += 1
lines += ["};"]
return lines
def EmitEnum(names, num):
print("enum")
print("{")
print("\t%s=0," % names[0])
for name in names[1:]:
print("\t%s," % name)
print("\t%s" % num)
print("};")
def EmitFlags(names, num):
print("enum")
print("{")
i = 0
for name in names:
print("\t%s = 1<<%d," % (name,i))
i += 1
print("};")
gen_network_header = False
gen_network_source = False
gen_client_content_header = False
gen_client_content_source = False
gen_server_content_header = False
gen_server_content_source = False
if "network_header" in sys.argv: gen_network_header = True
if "network_source" in sys.argv: gen_network_source = True
if "client_content_header" in sys.argv: gen_client_content_header = True
if "client_content_source" in sys.argv: gen_client_content_source = True
if "server_content_header" in sys.argv: gen_server_content_header = True
if "server_content_source" in sys.argv: gen_server_content_source = True
if gen_client_content_header:
print("#ifndef CLIENT_CONTENT_HEADER")
print("#define CLIENT_CONTENT_HEADER")
if gen_server_content_header:
print("#ifndef SERVER_CONTENT_HEADER")
print("#define SERVER_CONTENT_HEADER")
if gen_client_content_header or gen_server_content_header:
# emit the type declarations
contentlines = open("datasrc/content.py", "rb").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(["IMAGE_%s"%i.name.value.upper() for i in content.container.images.items], "NUM_IMAGES")
EmitEnum(["ANIM_%s"%i.name.value.upper() for i in content.container.animations.items], "NUM_ANIMS")
EmitEnum(["SPRITE_%s"%i.name.value.upper() for i in content.container.sprites.items], "NUM_SPRITES")
if gen_client_content_source or gen_server_content_source:
if gen_client_content_source:
print('#include "client_data.h"')
if gen_server_content_source:
print('#include "server_data.h"')
EmitDefinition(content.container, "datacontainer")
print('CDataContainer *g_pData = &datacontainer;')
# NETWORK
if gen_network_header:
print("#ifndef GAME_GENERATED_PROTOCOL_H")
print("#define GAME_GENERATED_PROTOCOL_H")
print(network.RawHeader)
for e in network.Enums:
for l in create_enum_table(["%s_%s"%(e.name, v) for v in e.values], 'NUM_%sS'%e.name): print(l)
print("")
for e in network.Flags:
for l in create_flags_table(["%s_%s" % (e.name, v) for v in e.values]): print(l)
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 l in create_enum_table(["NETOBJTYPE_EX"]+[o.enum_name for o in non_extended], "NUM_NETOBJTYPES"): print(l)
for l in create_enum_table(["__NETOBJTYPE_UUID_HELPER=OFFSET_GAME_UUID-1"]+[o.enum_name for o in extended], "OFFSET_NETMSGTYPE_UUID"): print(l)
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 l in create_enum_table(["NETMSGTYPE_EX"]+[o.enum_name for o in non_extended], "NUM_NETMSGTYPES"): print(l)
print("")
for l in create_enum_table(["__NETMSGTYPE_UUID_HELPER=OFFSET_NETMSGTYPE_UUID-1"]+[o.enum_name for o in extended], "END_NETMSGTYPE_UUID"): print(l)
print("")
for item in network.Objects + network.Messages:
for line in item.emit_declaration():
print(line)
print("")
EmitEnum(["SOUND_%s"%i.name.value.upper() for i in content.container.sounds.items], "NUM_SOUNDS")
EmitEnum(["WEAPON_%s"%i.name.value.upper() for i in content.container.weapons.id.items], "NUM_WEAPONS")
print("""
class CNetObjHandler
{
const char *m_pMsgFailedOn;
const char *m_pObjCorrectedOn;
char m_aMsgData[1024];
int m_NumObjCorrections;
int ClampInt(const char *pErrorMsg, int Value, int Min, int Max);
static const char *ms_apObjNames[];
static int ms_aObjSizes[];
static const char *ms_apMsgNames[];
public:
CNetObjHandler();
int ValidateObj(int Type, void *pData, int Size);
const char *GetObjName(int Type);
int GetObjSize(int Type);
int NumObjCorrections();
const char *CorrectedObjOn();
const char *GetMsgName(int Type);
void *SecureUnpackMsg(int Type, CUnpacker *pUnpacker);
const char *FailedMsgOn();
};
""")
print("#endif // GAME_GENERATED_PROTOCOL_H")
if gen_network_source:
# create names
lines = []
lines += ['#include <engine/shared/protocol.h>']
lines += ['#include <engine/message.h>']
lines += ['#include "protocol.h"']
lines += ['CNetObjHandler::CNetObjHandler()']
lines += ['{']
lines += ['\tm_pMsgFailedOn = "";']
lines += ['\tm_pObjCorrectedOn = "";']
lines += ['\tm_NumObjCorrections = 0;']
lines += ['}']
lines += ['']
lines += ['int CNetObjHandler::NumObjCorrections() { return m_NumObjCorrections; }']
lines += ['const char *CNetObjHandler::CorrectedObjOn() { return m_pObjCorrectedOn; }']
lines += ['const char *CNetObjHandler::FailedMsgOn() { return m_pMsgFailedOn; }']
lines += ['']
lines += ['']
lines += ['']
lines += ['']
lines += ['']
lines += ['static const int max_int = 0x7fffffff;']
lines += ['static const int min_int = 0x80000000;']
lines += ['int CNetObjHandler::ClampInt(const char *pErrorMsg, int Value, int Min, int Max)']
lines += ['{']
lines += ['\tif(Value < Min) { m_pObjCorrectedOn = pErrorMsg; m_NumObjCorrections++; return Min; }']
lines += ['\tif(Value > Max) { m_pObjCorrectedOn = pErrorMsg; m_NumObjCorrections++; return Max; }']
lines += ['\treturn Value;']
lines += ['}']
lines += ["const char *CNetObjHandler::ms_apObjNames[] = {"]
lines += ['\t"invalid",']
lines += ['\t"%s",' % o.name for o in network.Objects]
lines += ['\t""', "};", ""]
lines += ["int CNetObjHandler::ms_aObjSizes[] = {"]
lines += ['\t0,']
lines += ['\tsizeof(%s),' % o.struct_name for o in network.Objects]
lines += ['\t0', "};", ""]
lines += ['const char *CNetObjHandler::ms_apMsgNames[] = {']
lines += ['\t"invalid",']
for msg in network.Messages:
lines += ['\t"%s",' % msg.name]
lines += ['\t""']
lines += ['};']
lines += ['']
lines += ['const char *CNetObjHandler::GetObjName(int Type)']
lines += ['{']
lines += ['\tif(Type < 0 || Type >= NUM_NETOBJTYPES) return "(out of range)";']
lines += ['\treturn ms_apObjNames[Type];']
lines += ['};']
lines += ['']
lines += ['int CNetObjHandler::GetObjSize(int Type)']
lines += ['{']
lines += ['\tif(Type < 0 || Type >= NUM_NETOBJTYPES) return 0;']
lines += ['\treturn ms_aObjSizes[Type];']
lines += ['};']
lines += ['']
lines += ['const char *CNetObjHandler::GetMsgName(int Type)']
lines += ['{']
lines += ['\tif(Type < 0 || Type >= NUM_NETMSGTYPES) return "(out of range)";']
lines += ['\treturn ms_apMsgNames[Type];']
lines += ['};']
lines += ['']
for l in lines:
print(l)
if 0:
for item in network.Objects:
for line in item.emit_validate():
print(line)
print("")
# create validate tables
lines = []
lines += ['static int validate_invalid(void *data, int size) { return -1; }']
lines += ["typedef int(*VALIDATEFUNC)(void *data, int size);"]
lines += ["static VALIDATEFUNC validate_funcs[] = {"]
lines += ['\tvalidate_invalid,']
lines += ['\tvalidate_%s,' % o.name for o in network.Objects]
lines += ["\t0x0", "};", ""]
lines += ["int netobj_validate(int type, void *data, int size)"]
lines += ["{"]
lines += ["\tif(type < 0 || type >= NUM_NETOBJTYPES) return -1;"]
lines += ["\treturn validate_funcs[type](data, size);"]
lines += ["};", ""]
lines = []
lines += ['int CNetObjHandler::ValidateObj(int Type, void *pData, int Size)']
lines += ['{']
lines += ['\tswitch(Type)']
lines += ['\t{']
lines += ['\tcase NETOBJTYPE_EX:']
lines += ['\t{']
lines += ['\t\treturn 0;']
lines += ['\t}']
for item in network.Objects:
for line in item.emit_validate():
lines += ["\t" + line]
lines += ['\t']
lines += ['\t}']
lines += ['\treturn -1;']
lines += ['};']
lines += ['']
#int Validate(int Type, void *pData, int Size);
if 0:
for item in network.Messages:
for line in item.emit_unpack():
print(line)
print("")
lines += ['static void *secure_unpack_invalid(CUnpacker *pUnpacker) { return 0; }']
lines += ['typedef void *(*SECUREUNPACKFUNC)(CUnpacker *pUnpacker);']
lines += ['static SECUREUNPACKFUNC secure_unpack_funcs[] = {']
lines += ['\tsecure_unpack_invalid,']
for msg in network.Messages:
lines += ['\tsecure_unpack_%s,' % msg.name]
lines += ['\t0x0']
lines += ['};']
#
lines += ['void *CNetObjHandler::SecureUnpackMsg(int Type, CUnpacker *pUnpacker)']
lines += ['{']
lines += ['\tm_pMsgFailedOn = 0;']
lines += ['\tswitch(Type)']
lines += ['\t{']
for item in network.Messages:
for line in item.emit_unpack():
lines += ["\t" + line]
lines += ['\t']
lines += ['\tdefault:']
lines += ['\t\tm_pMsgFailedOn = "(type out of range)";']
lines += ['\t\tbreak;']
lines += ['\t}']
lines += ['\t']
lines += ['\tif(pUnpacker->Error())']
lines += ['\t\tm_pMsgFailedOn = "(unpack error)";']
lines += ['\t']
lines += ['\tif(m_pMsgFailedOn)']
lines += ['\t\treturn 0;']
lines += ['\tm_pMsgFailedOn = "";']
lines += ['\treturn m_aMsgData;']
lines += ['};']
lines += ['']
lines += ['void RegisterGameUuids(CUuidManager *pManager)']
lines += ['{']
for item in network.Objects + network.Messages:
if item.ex is not None:
lines += ['\tpManager->RegisterName(%s, "%s");' % (item.enum_name, item.ex)]
lines += ['}']
for l in lines:
print(l)
if gen_client_content_header or gen_server_content_header:
print("#endif")