diff --git a/datasrc/compile.py b/datasrc/compile.py index fa8522aa3..2306fc744 100644 --- a/datasrc/compile.py +++ b/datasrc/compile.py @@ -115,6 +115,8 @@ public: const char *FailedObjOn() const; const char *GetMsgName(int Type) const; + void DebugDumpSnapshot(const class CSnapshot *pSnap) const; + int DumpObj(int Type, const void *pData, int Size) const; void *SecureUnpackMsg(int Type, CUnpacker *pUnpacker); bool TeeHistorianRecordMsg(int Type); const char *FailedMsgOn() const; @@ -133,7 +135,9 @@ def gen_network_source(): #include #include #include +#include +#include #include CNetObjHandler::CNetObjHandler() @@ -245,8 +249,47 @@ const char *CNetObjHandler::GetMsgName(int Type) const } return "(out of range)"; } + +void CNetObjHandler::DebugDumpSnapshot(const CSnapshot *pSnap) const +{ + dbg_msg("snapshot", "data_size=%d num_items=%d", pSnap->DataSize(), pSnap->NumItems()); + for(int i = 0; i < pSnap->NumItems(); i++) + { + const CSnapshotItem *pItem = pSnap->GetItem(i); + int Size = pSnap->GetItemSize(i); + int Type = pSnap->GetItemType(i); + const char *pName = GetObjName(pItem->Type()); + if(Type > OFFSET_UUID && Type < g_UuidManager.NumUuids() + OFFSET_UUID) + pName = g_UuidManager.GetName(Type); + dbg_msg("snapshot", "\\t%s type=%d id=%d size=%d", pName, pItem->Type(), pItem->Id(), Size); + if(!DumpObj(Type, pItem->Data(), Size)) + continue; + + for(size_t b = 0; b < Size / sizeof(int32_t); b++) + dbg_msg("snapshot", "\\t\\t%3d %12d\\t%08x", (int)b, pItem->Data()[b], pItem->Data()[b]); + } +} + """) + lines = [] + lines += ['int CNetObjHandler::DumpObj(int Type, const void *pData, int Size) const'] + lines += ['{'] + lines += ["\tchar aRawData[512];"] + lines += ['\tswitch(Type)'] + lines += ['\t{'] + + for item in network.Objects: + for line in item.emit_dump(network.Objects): + lines += ["\t" + line] + lines += ['\t'] + lines += ['\t}'] + lines += ['\treturn -1;'] + lines += ['};'] + lines += [''] + for line in lines: + print(line) + lines = [] lines += ["""\ void *CNetObjHandler::SecureUnpackObj(int Type, CUnpacker *pUnpacker) @@ -275,10 +318,10 @@ void *CNetObjHandler::SecureUnpackObj(int Type, CUnpacker *pUnpacker) m_pObjFailedOn = "(type out of range)"; break; } - + if(pUnpacker->Error()) m_pObjFailedOn = "(unpack error)"; - + if(m_pObjFailedOn) return 0; m_pObjFailedOn = ""; @@ -308,10 +351,10 @@ void *CNetObjHandler::SecureUnpackMsg(int Type, CUnpacker *pUnpacker) m_pMsgFailedOn = "(type out of range)"; break; } - + if(pUnpacker->Error()) m_pMsgFailedOn = "(unpack error)"; - + if(m_pMsgFailedOn) return 0; m_pMsgFailedOn = ""; diff --git a/datasrc/datatypes.py b/datasrc/datatypes.py index 8d3ce023b..98e50fdf6 100644 --- a/datasrc/datatypes.py +++ b/datasrc/datatypes.py @@ -233,6 +233,15 @@ class NetObject: lines += ["};"] return lines + def members_from_this_and_parents(self, objects): + variables = self.variables + next_base_name = self.base + while next_base_name is not None: + base_item = only([i for i in objects if i.name == next_base_name]) + variables = base_item.variables + variables + next_base_name = base_item.base + return variables + def emit_uncompressed_unpack_and_validate(self, objects): lines = [] lines += [f"case {self.enum_name}:"] @@ -240,12 +249,7 @@ class NetObject: lines += [f"\t{self.struct_name} *pData = ({self.struct_name} *)m_aUnpackedData;"] unpack_lines = [] - variables = self.variables - next_base_name = self.base - while next_base_name is not None: - base_item = only([i for i in objects if i.name == next_base_name]) - variables = base_item.variables + variables - next_base_name = base_item.base + variables = self.members_from_this_and_parents(objects) for v in variables: if not self.validate_size and v.default is None: raise ValueError(f"{v.name} in {self.name} has no default value. Member variables that do not have a default value cannot be used in a structure whose size is not validated.") @@ -260,6 +264,26 @@ class NetObject: lines += ["} break;"] return lines + def emit_dump(self, objects): + lines = [] + lines += [f"case {self.enum_name}:"] + lines += ["{"] + lines += [f"\t{self.struct_name} *pObj = ({self.struct_name} *)pData;"] + unpack_lines = [] + variables = self.members_from_this_and_parents(objects) + offset = 0 + for v in variables: + unpack_lines += ["\t"+line for line in v.emit_dump(offset)] + offset += 1 + + if len(unpack_lines) > 0: + lines += unpack_lines + else: + lines += ["\t(void)pData;"] + lines += ["return 0;"] + lines += ["};"] + return lines + class NetEvent(NetObject): def __init__(self, name, variables, ex=None): NetObject.__init__(self, name, variables, ex=ex) @@ -340,6 +364,8 @@ class NetVariable: return [] def emit_unpack_msg_check(self): return [] + def emit_dump(self, offset): + return [f"str_format(aRawData, sizeof(aRawData), \"\\t\\t%3d %12d\\t%08x\", {offset}, ((const int *)pData)[{offset}], ((const int *)pData)[{offset}]);"] class NetString(NetVariable): def emit_declaration(self): @@ -384,6 +410,16 @@ class NetIntAny(NetVariable): return [f"pData->{self.name} = pUnpacker->GetIntOrDefault({self.default});"] def emit_pack(self): return [f"pPacker->AddInt({self.name});"] + def emit_dump(self, offset): + return NetVariable(self.name).emit_dump(offset) + \ + [f"dbg_msg(\"snapshot\", \"%s\\t{self.name}=%d\", aRawData, pObj->{self.name});"] + +class NetTwIntString(NetIntAny): + def emit_dump(self, offset): + return NetVariable(self.name).emit_dump(offset) + \ + [f"aInts[0] = pObj->{self.name};"] + \ + ["IntsToStr(aInts, std::size(aInts), aStr, std::size(aStr));"] + \ + [f"dbg_msg(\"snapshot\", \"%s\\t{self.name}=%d\\tIntToStr: %s\", aRawData, pObj->{self.name}, aStr);"] class NetIntRange(NetIntAny): def __init__(self, name, min_val, max_val, default=None): @@ -394,6 +430,23 @@ class NetIntRange(NetIntAny): return [f"pData->{self.name} = ClampInt(\"{self.name}\", pData->{self.name}, {self.min}, {self.max});"] def emit_unpack_msg_check(self): return [f"if(pData->{self.name} < {self.min} || pData->{self.name} > {self.max}) {{ m_pMsgFailedOn = \"{self.name}\"; break; }}"] + def emit_dump(self, offset): + min_fmt=f"min={self.min}" + min_arg = '' + try: + int(self.min) + except ValueError: + min_fmt = f"min={self.min}(%d)" + min_arg = f", (int){self.min}" + max_fmt=f"max={self.max}" + max_arg = '' + try: + int(self.max) + except ValueError: + max_fmt = f"max={self.max}(%d)" + max_arg = f", (int){self.max}" + return NetVariable(self.name).emit_dump(offset) + \ + [f"dbg_msg(\"snapshot\", \"%s\\t{self.name}=%d ({min_fmt} {max_fmt})\", aRawData, pObj->{self.name}{min_arg}{max_arg});"] class NetBool(NetIntRange): def __init__(self, name, default=None): @@ -403,6 +456,9 @@ class NetBool(NetIntRange): class NetTick(NetIntAny): def __init__(self, name, default=None): NetIntAny.__init__(self,name,default=default) + def emit_dump(self, offset): + return NetVariable(self.name).emit_dump(offset) + \ + [f"dbg_msg(\"snapshot\", \"%s\\t{self.name}=%d (NetTick)\", aRawData, pObj->{self.name});"] class NetArray(NetVariable): def __init__(self, var, size): diff --git a/datasrc/network.py b/datasrc/network.py index 04c02748b..1c8fcd911 100644 --- a/datasrc/network.py +++ b/datasrc/network.py @@ -1,7 +1,8 @@ # pylint: skip-file # See https://github.com/ddnet/ddnet/issues/3507 -from datatypes import Enum, Flags, NetArray, NetBool, NetEvent, NetEventEx, NetIntAny, NetIntRange, NetMessage, NetMessageEx, NetObject, NetObjectEx, NetString, NetStringHalfStrict, NetStringStrict, NetTick +from datatypes import Enum, Flags, NetArray, NetBool, NetEvent, NetEventEx, NetIntAny, NetTwIntString, NetIntRange +from datatypes import NetMessage, NetMessageEx, NetObject, NetObjectEx, NetString, NetStringHalfStrict, NetStringStrict, NetTick Emotes = ["NORMAL", "PAIN", "HAPPY", "SURPRISE", "ANGRY", "BLINK"] PlayerFlags = ["PLAYING", "IN_MENU", "CHATTING", "SCOREBOARD", "AIM"] @@ -215,17 +216,17 @@ Objects = [ NetObject("ClientInfo", [ # 4*4 = 16 characters - NetIntAny("m_Name0"), NetIntAny("m_Name1"), NetIntAny("m_Name2"), - NetIntAny("m_Name3"), + NetTwIntString("m_Name0"), NetTwIntString("m_Name1"), NetTwIntString("m_Name2"), + NetTwIntString("m_Name3"), # 4*3 = 12 characters - NetIntAny("m_Clan0"), NetIntAny("m_Clan1"), NetIntAny("m_Clan2"), + NetTwIntString("m_Clan0"), NetTwIntString("m_Clan1"), NetTwIntString("m_Clan2"), NetIntAny("m_Country"), # 4*6 = 24 characters - NetIntAny("m_Skin0"), NetIntAny("m_Skin1"), NetIntAny("m_Skin2"), - NetIntAny("m_Skin3"), NetIntAny("m_Skin4"), NetIntAny("m_Skin5"), + NetTwIntString("m_Skin0"), NetTwIntString("m_Skin1"), NetTwIntString("m_Skin2"), + NetTwIntString("m_Skin3"), NetTwIntString("m_Skin4"), NetTwIntString("m_Skin5"), NetIntRange("m_UseCustomColor", 0, 1), @@ -552,7 +553,7 @@ Messages = [ NetIntAny("m_ServerTimeBest"), NetIntAny("m_PlayerTimeBest"), ]), - + NetMessageEx("Sv_KillMsgTeam", "killmsgteam@netmsg.ddnet.tw", [ NetIntRange("m_Team", 0, 'MAX_CLIENTS-1'), NetIntRange("m_First", -1, 'MAX_CLIENTS-1'), diff --git a/datasrc/seven/compile.py b/datasrc/seven/compile.py index 75616ca60..3d2c2119f 100644 --- a/datasrc/seven/compile.py +++ b/datasrc/seven/compile.py @@ -87,6 +87,7 @@ def main(): print("#ifndef GAME_GENERATED_PROTOCOL7_H") print("#define GAME_GENERATED_PROTOCOL7_H") print("class CUnpacker;") + print("class CSnapshot;") print("#include ") print("namespace protocol7 {") print(network.RawHeader) @@ -150,6 +151,8 @@ def main(): CNetObjHandler(); int ValidateObj(int Type, const void *pData, int Size); + void DebugDumpSnapshot(const CSnapshot *pSnap) const; + int DumpObj(int Type, const void *pData, int Size) const; const char *GetObjName(int Type) const; int GetObjSize(int Type) const; const char *FailedObjOn() const; @@ -174,6 +177,7 @@ def main(): lines += ['#include '] lines += ['#include '] lines += ['#include '] + lines += ['#include '] lines += ['namespace protocol7 {'] @@ -269,6 +273,46 @@ def main(): lines += ['};'] lines += [''] + for l in lines: + print(l) + + print("""\ +void CNetObjHandler::DebugDumpSnapshot(const ::CSnapshot *pSnap) const +{ + dbg_msg("snapshot", "data_size=%d num_items=%d", pSnap->DataSize(), pSnap->NumItems()); + for(int i = 0; i < pSnap->NumItems(); i++) + { + const CSnapshotItem *pItem = pSnap->GetItem(i); + int Size = pSnap->GetItemSize(i); + int Type = pSnap->GetItemType(i); + const char *pName = GetObjName(pItem->Type()); + if(Type > OFFSET_UUID && Type < g_UuidManager.NumUuids() + OFFSET_UUID) + pName = g_UuidManager.GetName(Type); + dbg_msg("snapshot", "\\t%s type=%d id=%d size=%d", pName, pItem->Type(), pItem->Id(), Size); + if(!DumpObj(Type, pItem->Data(), Size)) + continue; + + for(size_t b = 0; b < Size / sizeof(int32_t); b++) + dbg_msg("snapshot", "\\t\\t%3d %12d\\t%08x", (int)b, pItem->Data()[b], pItem->Data()[b]); + } +}\n""") + + lines = [] + lines += ['int CNetObjHandler::DumpObj(int Type, const void *pData, int Size) const'] + lines += ['{'] + lines += ["\tchar aRawData[512];"] + lines += ['\tswitch(Type)'] + lines += ['\t{'] + + for item in network.Objects: + for line in item.emit_dump(network.Objects): + lines += ["\t" + line] + lines += ['\t'] + lines += ['\t}'] + lines += ['\treturn -1;'] + lines += ['};'] + lines += [''] + lines += ['void *CNetObjHandler::SecureUnpackMsg(int Type, CUnpacker *pUnpacker)'] lines += ['{'] lines += ['\tm_pMsgFailedOn = 0;'] diff --git a/datasrc/seven/datatypes.py b/datasrc/seven/datatypes.py index bb3b75c33..f42da2b3d 100644 --- a/datasrc/seven/datatypes.py +++ b/datasrc/seven/datatypes.py @@ -234,24 +234,38 @@ class NetObject: lines += ["\t"+line for line in v.emit_declaration()] lines += ["};"] return lines - def emit_validate(self, objects): - lines = [f"case {self.enum_name}:"] - lines += ["{"] - lines += [f"\t{self.struct_name} *pObj = ({self.struct_name} *)pData;"] - lines += ["\tif(sizeof(*pObj) != Size) return -1;"] - + def members_from_this_and_parents(self, objects): variables = self.variables next_base_name = self.base while next_base_name is not None: base_item = only([i for i in objects if i.name == next_base_name]) variables = base_item.variables + variables next_base_name = base_item.base - + return variables + def emit_validate(self, objects): + lines = [f"case {self.enum_name}:"] + lines += ["{"] + lines += [f"\t{self.struct_name} *pObj = ({self.struct_name} *)pData;"] + lines += ["\tif(sizeof(*pObj) != Size) return -1;"] + variables = self.members_from_this_and_parents(objects) for v in variables: lines += ["\t"+line for line in v.emit_validate()] lines += ["\treturn 0;"] lines += ["}"] return lines + def emit_dump(self, objects): + lines = [f"case {self.enum_name}:"] + lines += ["{"] + lines += [f"\t{self.struct_name} *pObj = ({self.struct_name} *)pData;"] + lines += ["\tif(sizeof(*pObj) != Size) return -1;"] + variables = self.members_from_this_and_parents(objects) + offset = 0 + for v in variables: + lines += ["\t"+line for line in v.emit_dump(offset)] + offset += 1 + lines += ["\treturn 0;"] + lines += ["}"] + return lines class NetEvent(NetObject): @@ -312,6 +326,8 @@ class NetVariable: return [] def emit_unpack_check(self): return [] + def emit_dump(self, offset): + return [f"str_format(aRawData, sizeof(aRawData), \"\\t\\t%3d %12d\\t%08x\", {offset}, ((const int *)pData)[{offset}], ((const int *)pData)[{offset}]);"] class NetString(NetVariable): def emit_declaration(self): @@ -338,6 +354,9 @@ class NetIntAny(NetVariable): return [f"pMsg->{self.name} = pUnpacker->GetIntOrDefault({self.default});"] def emit_pack(self): return [f"pPacker->AddInt({self.name});"] + def emit_dump(self, offset): + return NetVariable(self.name).emit_dump(offset) + \ + [f"dbg_msg(\"snapshot\", \"%s\\t{self.name}=%d\", aRawData, pObj->{self.name});"] class NetIntRange(NetIntAny): def __init__(self, name, min_val, max_val, default=None): @@ -348,6 +367,23 @@ class NetIntRange(NetIntAny): return [f"if(!CheckInt(\"{self.name}\", pObj->{self.name}, {self.min}, {self.max})) return -1;"] def emit_unpack_check(self): return [f"if(!CheckInt(\"{self.name}\", pMsg->{self.name}, {self.min}, {self.max})) break;"] + def emit_dump(self, offset): + min_fmt=f"min={self.min}" + min_arg = '' + try: + int(self.min) + except ValueError: + min_fmt = f"min={self.min}(%d)" + min_arg = f", (int){self.min}" + max_fmt=f"max={self.max}" + max_arg = '' + try: + int(self.max) + except ValueError: + max_fmt = f"max={self.max}(%d)" + max_arg = f", (int){self.max}" + return NetVariable(self.name).emit_dump(offset) + \ + [f"dbg_msg(\"snapshot\", \"%s\\t{self.name}=%d ({min_fmt} {max_fmt})\", aRawData, pObj->{self.name}{min_arg}{max_arg});"] class NetEnum(NetIntRange): def __init__(self, name, enum): @@ -366,6 +402,9 @@ class NetFlag(NetIntAny): return [f"if(!CheckFlag(\"{self.name}\", pObj->{self.name}, {self.mask})) return -1;"] def emit_unpack_check(self): return [f"if(!CheckFlag(\"{self.name}\", pMsg->{self.name}, {self.mask})) break;"] + def emit_dump(self, offset): + return NetVariable(self.name).emit_dump(offset) + \ + [f"dbg_msg(\"snapshot\", \"%s\\t{self.name}=%d (mask=%d)\", aRawData, pObj->{self.name}, {self.mask});"] class NetBool(NetIntRange): def __init__(self, name, default=None): diff --git a/src/engine/server.h b/src/engine/server.h index 462afbb97..148590721 100644 --- a/src/engine/server.h +++ b/src/engine/server.h @@ -347,6 +347,9 @@ public: virtual const char *Version() const = 0; virtual const char *NetVersion() const = 0; + virtual CNetObjHandler *GetNetObjHandler() = 0; + virtual protocol7::CNetObjHandler *GetNetObjHandler7() = 0; + // DDRace virtual void OnPreTickTeehistorian() = 0; diff --git a/src/engine/shared/snapshot.h b/src/engine/shared/snapshot.h index 24aeaafa9..d7d44fe99 100644 --- a/src/engine/shared/snapshot.h +++ b/src/engine/shared/snapshot.h @@ -53,6 +53,7 @@ public: }; int NumItems() const { return m_NumItems; } + int DataSize() const { return m_DataSize; } const CSnapshotItem *GetItem(int Index) const; int GetItemSize(int Index) const; int GetItemIndex(int Key) const; @@ -62,6 +63,9 @@ public: const void *FindItem(int Type, int Id) const; unsigned Crc() const; + // Prints the raw snapshot data showing item and int boundaries. + // See also `CNetObjHandler::DebugDumpSnapshot(const CSnapshot *pSnap)` + // For more detailed annotations of the data. void DebugDump() const; bool IsValid(size_t ActualSize) const; diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index dd5868d07..4388fed40 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -169,6 +169,8 @@ public: IAntibot *Antibot() { return m_pAntibot; } CTeeHistorian *TeeHistorian() { return &m_TeeHistorian; } bool TeeHistorianActive() const { return m_TeeHistorianActive; } + CNetObjHandler *GetNetObjHandler() override { return &m_NetObjHandler; } + protocol7::CNetObjHandler *GetNetObjHandler7() override { return &m_NetObjHandler7; } CGameContext(); CGameContext(int Reset);