From 8903f4971645c24756e7a0a917512a7b76fe25de Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Wed, 3 Apr 2024 16:00:16 +0800 Subject: [PATCH] Render skins in 0.7 demos --- CMakeLists.txt | 1 + src/engine/client.h | 8 +- src/engine/client/client.cpp | 87 ++- src/engine/server/server.cpp | 45 +- src/engine/shared/demo.cpp | 2 + src/engine/shared/demo.h | 2 +- .../shared/sixup_translate_snapshot.cpp | 524 +--------------- src/engine/shared/snapshot.h | 12 +- src/engine/shared/translation_context.h | 4 + src/game/client/gameclient.cpp | 13 +- src/game/client/gameclient.h | 21 +- src/game/client/sixup_translate_game.cpp | 39 +- src/game/client/sixup_translate_snapshot.cpp | 564 ++++++++++++++++++ 13 files changed, 747 insertions(+), 575 deletions(-) create mode 100644 src/game/client/sixup_translate_snapshot.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 10afbcc61..7c85dd345 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2427,6 +2427,7 @@ if(CLIENT) render.h render_map.cpp sixup_translate_game.cpp + sixup_translate_snapshot.cpp skin.h ui.cpp ui.h diff --git a/src/engine/client.h b/src/engine/client.h index 69eafbdef..8c0f501ac 100644 --- a/src/engine/client.h +++ b/src/engine/client.h @@ -14,7 +14,6 @@ #include #include -#include #include #include @@ -374,6 +373,7 @@ public: virtual const char *GetItemName(int Type) const = 0; virtual const char *Version() const = 0; virtual const char *NetVersion() const = 0; + virtual const char *NetVersion7() const = 0; virtual int DDNetVersion() const = 0; virtual const char *DDNetVersionStr() const = 0; @@ -388,9 +388,13 @@ public: virtual protocol7::CNetObjHandler *GetNetObjHandler7() = 0; virtual int ClientVersion7() const = 0; + + virtual void ApplySkin7InfoFromSnapObj(const protocol7::CNetObj_De_ClientInfo *pObj, int ClientId) = 0; + virtual int OnDemoRecSnap7(class CSnapshot *pFrom, class CSnapshot *pTo, int Conn) = 0; + virtual int TranslateSnap(class CSnapshot *pSnapDstSix, class CSnapshot *pSnapSrcSeven, int Conn, bool Dummy) = 0; }; -void SnapshotRemoveExtraProjectileInfo(CSnapshot *pSnap); +void SnapshotRemoveExtraProjectileInfo(class CSnapshot *pSnap); extern IGameClient *CreateGameClient(); #endif diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index b637fa2b1..860f42e77 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -1988,15 +1988,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy) unsigned char aTmpTransSnapBuffer[CSnapshot::MAX_SIZE]; CSnapshot *pTmpTransSnapBuffer = (CSnapshot *)aTmpTransSnapBuffer; mem_copy(pTmpTransSnapBuffer, pTmpBuffer3, CSnapshot::MAX_SIZE); - AltSnapSize = pTmpTransSnapBuffer->TranslateSevenToSix( - pAltSnapBuffer, - m_TranslationContext, - LocalTime(), - CClient::GameTick(g_Config.m_ClDummy), - Conn, - Dummy, - GameClient()->GetNetObjHandler7(), - GameClient()->GetNetObjHandler()); + AltSnapSize = GameClient()->TranslateSnap(pAltSnapBuffer, pTmpTransSnapBuffer, Conn, Dummy); } else { @@ -2017,13 +2009,26 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy) // for antiping: if the projectile netobjects from the server contains extra data, this is removed and the original content restored before recording demo SnapshotRemoveExtraProjectileInfo(pTmpBuffer3); + unsigned char aSnapSeven[CSnapshot::MAX_SIZE]; + CSnapshot *pSnapSeven = (CSnapshot *)aSnapSeven; + int DemoSnapSize = SnapSize; + if(IsSixup()) + { + DemoSnapSize = GameClient()->OnDemoRecSnap7(pTmpBuffer3, pSnapSeven, Conn); + if(DemoSnapSize < 0) + { + dbg_msg("sixup", "demo snapshot failed. error=%d", DemoSnapSize); + return; + } + } + // add snapshot to demo for(auto &DemoRecorder : m_aDemoRecorder) { if(DemoRecorder.IsRecording()) { // write snapshot - DemoRecorder.RecordSnapshot(GameTick, pTmpBuffer3, SnapSize); + DemoRecorder.RecordSnapshot(GameTick, IsSixup() ? pSnapSeven : pTmpBuffer3, DemoSnapSize); } } } @@ -2524,25 +2529,24 @@ void CClient::OnDemoPlayerSnapshot(void *pData, int Size) // create a verified and unpacked snapshot unsigned char aAltSnapBuffer[CSnapshot::MAX_SIZE]; CSnapshot *pAltSnapBuffer = (CSnapshot *)aAltSnapBuffer; - const int AltSnapSize = UnpackAndValidateSnapshot((CSnapshot *)pData, pAltSnapBuffer); - if(AltSnapSize < 0) - { - dbg_msg("client", "unpack snapshot and validate failed. error=%d", AltSnapSize); - return; - } + int AltSnapSize; - unsigned char aTmpTranslateBuffer[CSnapshot::MAX_SIZE]; - CSnapshot *pTmpTranslateBuffer = nullptr; - int TranslatedSize = 0; if(IsSixup()) { - pTmpTranslateBuffer = (CSnapshot *)aTmpTranslateBuffer; - TranslatedSize = ((CSnapshot *)pData)->TranslateSevenToSix(pTmpTranslateBuffer, m_TranslationContext, LocalTime(), GameTick(g_Config.m_ClDummy), CONN_MAIN, false, GameClient()->GetNetObjHandler7(), GameClient()->GetNetObjHandler()); - if(TranslatedSize < 0) + AltSnapSize = GameClient()->TranslateSnap(pAltSnapBuffer, (CSnapshot *)pData, CONN_MAIN, false); + if(AltSnapSize < 0) { - dbg_msg("sixup", "failed to translate snapshot. error=%d", TranslatedSize); - pTmpTranslateBuffer = nullptr; - TranslatedSize = 0; + dbg_msg("sixup", "failed to translate snapshot. error=%d", AltSnapSize); + return; + } + } + else + { + AltSnapSize = UnpackAndValidateSnapshot((CSnapshot *)pData, pAltSnapBuffer); + if(AltSnapSize < 0) + { + dbg_msg("client", "unpack snapshot and validate failed. error=%d", AltSnapSize); + return; } } @@ -2551,9 +2555,6 @@ void CClient::OnDemoPlayerSnapshot(void *pData, int Size) mem_copy(m_aapSnapshots[0][SNAP_CURRENT]->m_pSnap, pData, Size); mem_copy(m_aapSnapshots[0][SNAP_CURRENT]->m_pAltSnap, pAltSnapBuffer, AltSnapSize); - if(pTmpTranslateBuffer && TranslatedSize > 0) - mem_copy(m_aapSnapshots[0][SNAP_CURRENT]->m_pAltSnap, pTmpTranslateBuffer, TranslatedSize); - GameClient()->OnNewSnapshot(); } @@ -3916,7 +3917,20 @@ void CClient::DemoRecorder_Start(const char *pFilename, bool WithTimestamp, int str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pFilename); } - m_aDemoRecorder[Recorder].Start(Storage(), m_pConsole, aFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Sha256(), m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File()); + m_aDemoRecorder[Recorder].Start( + Storage(), + m_pConsole, + aFilename, + IsSixup() ? GameClient()->NetVersion7() : GameClient()->NetVersion(), + m_aCurrentMap, + m_pMap->Sha256(), + m_pMap->Crc(), + "client", + m_pMap->MapSize(), + 0, + m_pMap->File(), + nullptr, + nullptr); } } @@ -4880,7 +4894,20 @@ void CClient::RaceRecord_Start(const char *pFilename) if(State() != IClient::STATE_ONLINE) m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online"); else - m_aDemoRecorder[RECORDER_RACE].Start(Storage(), m_pConsole, pFilename, GameClient()->NetVersion(), m_aCurrentMap, m_pMap->Sha256(), m_pMap->Crc(), "client", m_pMap->MapSize(), 0, m_pMap->File()); + m_aDemoRecorder[RECORDER_RACE].Start( + Storage(), + m_pConsole, + pFilename, + IsSixup() ? GameClient()->NetVersion7() : GameClient()->NetVersion(), + m_aCurrentMap, + m_pMap->Sha256(), + m_pMap->Crc(), + "client", + m_pMap->MapSize(), + 0, + m_pMap->File(), + nullptr, + nullptr); } void CClient::RaceRecord_Stop() diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index bbaa93406..58db0157e 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -3375,7 +3375,20 @@ void CServer::DemoRecorder_HandleAutoStart() str_timestamp(aTimestamp, sizeof(aTimestamp)); char aFilename[IO_MAX_PATH_LENGTH]; str_format(aFilename, sizeof(aFilename), "demos/auto/server/%s_%s.demo", m_aCurrentMap, aTimestamp); - m_aDemoRecorder[RECORDER_AUTO].Start(Storage(), m_pConsole, aFilename, GameServer()->NetVersion(), m_aCurrentMap, m_aCurrentMapSha256[MAP_TYPE_SIX], m_aCurrentMapCrc[MAP_TYPE_SIX], "server", m_aCurrentMapSize[MAP_TYPE_SIX], m_apCurrentMapData[MAP_TYPE_SIX]); + m_aDemoRecorder[RECORDER_AUTO].Start( + Storage(), + m_pConsole, + aFilename, + GameServer()->NetVersion(), + m_aCurrentMap, + m_aCurrentMapSha256[MAP_TYPE_SIX], + m_aCurrentMapCrc[MAP_TYPE_SIX], + "server", + m_aCurrentMapSize[MAP_TYPE_SIX], + m_apCurrentMapData[MAP_TYPE_SIX], + nullptr, + nullptr, + nullptr); if(Config()->m_SvAutoDemoMax) { @@ -3402,7 +3415,20 @@ void CServer::StartRecord(int ClientId) { char aFilename[IO_MAX_PATH_LENGTH]; str_format(aFilename, sizeof(aFilename), "demos/%s_%d_%d_tmp.demo", m_aCurrentMap, m_NetServer.Address().port, ClientId); - m_aDemoRecorder[ClientId].Start(Storage(), Console(), aFilename, GameServer()->NetVersion(), m_aCurrentMap, m_aCurrentMapSha256[MAP_TYPE_SIX], m_aCurrentMapCrc[MAP_TYPE_SIX], "server", m_aCurrentMapSize[MAP_TYPE_SIX], m_apCurrentMapData[MAP_TYPE_SIX]); + m_aDemoRecorder[ClientId].Start( + Storage(), + Console(), + aFilename, + GameServer()->NetVersion(), + m_aCurrentMap, + m_aCurrentMapSha256[MAP_TYPE_SIX], + m_aCurrentMapCrc[MAP_TYPE_SIX], + "server", + m_aCurrentMapSize[MAP_TYPE_SIX], + m_apCurrentMapData[MAP_TYPE_SIX], + nullptr, + nullptr, + nullptr); } } @@ -3451,7 +3477,20 @@ void CServer::ConRecord(IConsole::IResult *pResult, void *pUser) str_timestamp(aTimestamp, sizeof(aTimestamp)); str_format(aFilename, sizeof(aFilename), "demos/demo_%s.demo", aTimestamp); } - pServer->m_aDemoRecorder[RECORDER_MANUAL].Start(pServer->Storage(), pServer->Console(), aFilename, pServer->GameServer()->NetVersion(), pServer->m_aCurrentMap, pServer->m_aCurrentMapSha256[MAP_TYPE_SIX], pServer->m_aCurrentMapCrc[MAP_TYPE_SIX], "server", pServer->m_aCurrentMapSize[MAP_TYPE_SIX], pServer->m_apCurrentMapData[MAP_TYPE_SIX]); + pServer->m_aDemoRecorder[RECORDER_MANUAL].Start( + pServer->Storage(), + pServer->Console(), + aFilename, + pServer->GameServer()->NetVersion(), + pServer->m_aCurrentMap, + pServer->m_aCurrentMapSha256[MAP_TYPE_SIX], + pServer->m_aCurrentMapCrc[MAP_TYPE_SIX], + "server", + pServer->m_aCurrentMapSize[MAP_TYPE_SIX], + pServer->m_apCurrentMapData[MAP_TYPE_SIX], + nullptr, + nullptr, + nullptr); } void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser) diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp index 21678de73..34ddbc3c3 100644 --- a/src/engine/shared/demo.cpp +++ b/src/engine/shared/demo.cpp @@ -316,6 +316,8 @@ void CDemoRecorder::RecordSnapshot(int Tick, const void *pData, int Size) // create delta char aDeltaData[CSnapshot::MAX_SIZE + sizeof(int)]; + m_pSnapshotDelta->SetStaticsize(protocol7::NETEVENTTYPE_SOUNDWORLD, true); + m_pSnapshotDelta->SetStaticsize(protocol7::NETEVENTTYPE_DAMAGE, true); const int DeltaSize = m_pSnapshotDelta->CreateDelta((CSnapshot *)m_aLastSnapshotData, (CSnapshot *)pData, &aDeltaData); if(DeltaSize) { diff --git a/src/engine/shared/demo.h b/src/engine/shared/demo.h index 5792429f1..3cb83d184 100644 --- a/src/engine/shared/demo.h +++ b/src/engine/shared/demo.h @@ -45,7 +45,7 @@ public: CDemoRecorder() {} ~CDemoRecorder() override; - int Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetversion, const char *pMap, const SHA256_DIGEST &Sha256, unsigned MapCrc, const char *pType, unsigned MapSize, unsigned char *pMapData, IOHANDLE MapFile = nullptr, DEMOFUNC_FILTER pfnFilter = nullptr, void *pUser = nullptr); + int Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetversion, const char *pMap, const SHA256_DIGEST &Sha256, unsigned MapCrc, const char *pType, unsigned MapSize, unsigned char *pMapData, IOHANDLE MapFile, DEMOFUNC_FILTER pfnFilter, void *pUser); int Stop(IDemoRecorder::EStopMode Mode, const char *pTargetFilename = "") override; void AddDemoMarker(); diff --git a/src/engine/shared/sixup_translate_snapshot.cpp b/src/engine/shared/sixup_translate_snapshot.cpp index d90925d1c..f693b45c8 100644 --- a/src/engine/shared/sixup_translate_snapshot.cpp +++ b/src/engine/shared/sixup_translate_snapshot.cpp @@ -1,516 +1,26 @@ -#include "compression.h" -#include "snapshot.h" -#include "uuid_manager.h" - -#include -#include - -#include #include -#include -#include +#include "snapshot.h" -#include -#include -#include - -#include - -int CSnapshot::TranslateSevenToSix( - CSnapshot *pSixSnapDest, - CTranslationContext &TranslationContext, - float LocalTime, - int GameTick, - int Conn, - bool Dummy, - protocol7::CNetObjHandler *pNetObjHandler, - CNetObjHandler *pNetObjHandler6) +void CSnapshotBuilder::Init7(const CSnapshot *pSnapshot) { - CSnapshotBuilder Builder; - Builder.Init(); + // the method is called Init7 because it is only used for 0.7 support + // but the snap we are building is a 0.6 snap + m_Sixup = false; - for(auto &PlayerInfosRace : TranslationContext.m_apPlayerInfosRace) - PlayerInfosRace = nullptr; - - int SpectatorId = -3; - - for(int i = 0; i < m_NumItems; i++) + if(pSnapshot->m_DataSize + sizeof(CSnapshot) + pSnapshot->m_NumItems * sizeof(int) * 2 > CSnapshot::MAX_SIZE || pSnapshot->m_NumItems > CSnapshot::MAX_ITEMS) { - const CSnapshotItem *pItem7 = (CSnapshotItem *)GetItem(i); - const int Size = GetItemSize(i); - const int ItemType = GetItemType(i); - - // ddnet ex snap items - if((ItemType > __NETOBJTYPE_UUID_HELPER && ItemType < OFFSET_NETMSGTYPE_UUID) || pItem7->Type() == NETOBJTYPE_EX) - { - CUnpacker Unpacker; - Unpacker.Reset(pItem7->Data(), Size); - - void *pRawObj = pNetObjHandler6->SecureUnpackObj(ItemType, &Unpacker); - if(!pRawObj) - { - if(ItemType != UUID_UNKNOWN) - dbg_msg("sixup", "dropped weird ddnet ex object '%s' (%d), failed on '%s'", pNetObjHandler6->GetObjName(ItemType), ItemType, pNetObjHandler6->FailedObjOn()); - continue; - } - const int ItemSize = pNetObjHandler6->GetUnpackedObjSize(ItemType); - - void *pObj = Builder.NewItem(pItem7->Type(), pItem7->Id(), ItemSize); - if(!pObj) - return -17; - - mem_copy(pObj, pRawObj, ItemSize); - continue; - } - - if(pNetObjHandler->ValidateObj(pItem7->Type(), pItem7->Data(), Size) != 0) - { - if(pItem7->Type() > 0 && pItem7->Type() < OFFSET_UUID_TYPE) - { - dbg_msg( - "sixup", - "invalidated index=%d type=%d (%s) size=%d id=%d", - i, - pItem7->Type(), - pNetObjHandler->GetObjName(pItem7->Type()), - Size, - pItem7->Id()); - } - InvalidateItem(i); - } - - if(pItem7->Type() == protocol7::NETOBJTYPE_PLAYERINFORACE) - { - const protocol7::CNetObj_PlayerInfoRace *pInfo = (const protocol7::CNetObj_PlayerInfoRace *)pItem7->Data(); - int ClientId = pItem7->Id(); - if(ClientId < MAX_CLIENTS && TranslationContext.m_aClients[ClientId].m_Active) - { - TranslationContext.m_apPlayerInfosRace[ClientId] = pInfo; - } - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_SPECTATORINFO) - { - const protocol7::CNetObj_SpectatorInfo *pSpec7 = (const protocol7::CNetObj_SpectatorInfo *)pItem7->Data(); - SpectatorId = pSpec7->m_SpectatorId; - if(pSpec7->m_SpecMode == protocol7::SPEC_FREEVIEW) - SpectatorId = SPEC_FREEVIEW; - } + // key and offset per item + dbg_assert(m_DataSize + sizeof(CSnapshot) + m_NumItems * sizeof(int) * 2 < CSnapshot::MAX_SIZE, "too much data"); + dbg_assert(m_NumItems < CSnapshot::MAX_ITEMS, "too many items"); + dbg_msg("sixup", "demo recording failed on invalid snapshot"); + m_DataSize = 0; + m_NumItems = 0; + return; } - // hack to put game info in the snap - // even though in 0.7 we get it as a game message - // this message has to be on the top - // the client looks at the items in order and needs the gameinfo at the top - // otherwise it will not render skins with team colors - if(TranslationContext.m_ShouldSendGameInfo) - { - void *pObj = Builder.NewItem(NETOBJTYPE_GAMEINFO, 0, sizeof(CNetObj_GameInfo)); - if(!pObj) - return -1; - - int GameStateFlagsSix = 0; - if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_GAMEOVER) - GameStateFlagsSix |= GAMESTATEFLAG_GAMEOVER; - if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_SUDDENDEATH) - GameStateFlagsSix |= GAMESTATEFLAG_SUDDENDEATH; - if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_PAUSED) - GameStateFlagsSix |= GAMESTATEFLAG_PAUSED; - - /* - This is a 0.7 only flag that we just ignore for now - - GAMESTATEFLAG_ROUNDOVER - */ - - CNetObj_GameInfo Info6 = {}; - Info6.m_GameFlags = TranslationContext.m_GameFlags; - Info6.m_GameStateFlags = GameStateFlagsSix; - Info6.m_RoundStartTick = TranslationContext.m_GameStartTick7; - Info6.m_WarmupTimer = 0; // removed in 0.7 - if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_WARMUP) - Info6.m_WarmupTimer = TranslationContext.m_GameStateEndTick7 - GameTick; - if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_STARTCOUNTDOWN) - Info6.m_WarmupTimer = TranslationContext.m_GameStateEndTick7 - GameTick; - - // hack to port 0.7 race timer to ddnet warmup gametimer hack - int TimerClientId = clamp(TranslationContext.m_aLocalClientId[Conn], 0, (int)MAX_CLIENTS); - if(SpectatorId >= 0) - TimerClientId = SpectatorId; - const protocol7::CNetObj_PlayerInfoRace *pRaceInfo = TranslationContext.m_apPlayerInfosRace[TimerClientId]; - if(pRaceInfo) - { - Info6.m_WarmupTimer = -pRaceInfo->m_RaceStartTick; - Info6.m_GameStateFlags |= GAMESTATEFLAG_RACETIME; - } - - Info6.m_ScoreLimit = TranslationContext.m_ScoreLimit; - Info6.m_TimeLimit = TranslationContext.m_TimeLimit; - Info6.m_RoundNum = TranslationContext.m_MatchNum; - Info6.m_RoundCurrent = TranslationContext.m_MatchCurrent; - - mem_copy(pObj, &Info6, sizeof(CNetObj_GameInfo)); - } - - for(int i = 0; i < MAX_CLIENTS; i++) - { - const CTranslationContext::CClientData &Client = TranslationContext.m_aClients[i]; - if(!Client.m_Active) - continue; - - void *pObj = Builder.NewItem(NETOBJTYPE_CLIENTINFO, i, sizeof(CNetObj_ClientInfo)); - if(!pObj) - return -2; - - CNetObj_ClientInfo Info6 = {}; - StrToInts(&Info6.m_Name0, 4, Client.m_aName); - StrToInts(&Info6.m_Clan0, 3, Client.m_aClan); - Info6.m_Country = Client.m_Country; - StrToInts(&Info6.m_Skin0, 6, Client.m_aSkinName); - Info6.m_UseCustomColor = Client.m_UseCustomColor; - Info6.m_ColorBody = Client.m_ColorBody; - Info6.m_ColorFeet = Client.m_ColorFeet; - mem_copy(pObj, &Info6, sizeof(CNetObj_ClientInfo)); - } - - bool NewGameData = false; - - for(int i = 0; i < m_NumItems; i++) - { - const CSnapshotItem *pItem7 = GetItem(i); - const int Size = GetItemSize(i); - // the first few items are a full match - // no translation needed - if(pItem7->Type() == protocol7::NETOBJTYPE_PROJECTILE || - pItem7->Type() == protocol7::NETOBJTYPE_LASER || - pItem7->Type() == protocol7::NETOBJTYPE_FLAG) - { - void *pObj = Builder.NewItem(pItem7->Type(), pItem7->Id(), Size); - if(!pObj) - return -4; - - mem_copy(pObj, pItem7->Data(), Size); - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_PICKUP) - { - void *pObj = Builder.NewItem(NETOBJTYPE_PICKUP, pItem7->Id(), sizeof(CNetObj_Pickup)); - if(!pObj) - return -5; - - const protocol7::CNetObj_Pickup *pPickup7 = (const protocol7::CNetObj_Pickup *)pItem7->Data(); - CNetObj_Pickup Pickup6 = {}; - Pickup6.m_X = pPickup7->m_X; - Pickup6.m_Y = pPickup7->m_Y; - PickupType_SevenToSix(pPickup7->m_Type, Pickup6.m_Type, Pickup6.m_Subtype); - - mem_copy(pObj, &Pickup6, sizeof(CNetObj_Pickup)); - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_GAMEDATA) - { - const protocol7::CNetObj_GameData *pGameData = (const protocol7::CNetObj_GameData *)pItem7->Data(); - TranslationContext.m_GameStateFlags7 = pGameData->m_GameStateFlags; - TranslationContext.m_GameStartTick7 = pGameData->m_GameStartTick; - TranslationContext.m_GameStateEndTick7 = pGameData->m_GameStateEndTick; - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_GAMEDATATEAM) - { - // 0.7 added GameDataTeam and GameDataFlag - // both items merged together have all fields of the 0.6 GameData - // so if we get either one of them we store the details in the translation context - // and build one GameData snap item after this loop - const protocol7::CNetObj_GameDataTeam *pTeam7 = (const protocol7::CNetObj_GameDataTeam *)pItem7->Data(); - - TranslationContext.m_TeamscoreRed = pTeam7->m_TeamscoreRed; - TranslationContext.m_TeamscoreBlue = pTeam7->m_TeamscoreBlue; - NewGameData = true; - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_GAMEDATAFLAG) - { - const protocol7::CNetObj_GameDataFlag *pFlag7 = (const protocol7::CNetObj_GameDataFlag *)pItem7->Data(); - - TranslationContext.m_FlagCarrierRed = pFlag7->m_FlagCarrierRed; - TranslationContext.m_FlagCarrierBlue = pFlag7->m_FlagCarrierBlue; - NewGameData = true; - - // used for blinking the flags in the hud - // but that already works the 0.6 way - // and is covered by the NETOBJTYPE_GAMEDATA translation - // pFlag7->m_FlagDropTickRed; - // pFlag7->m_FlagDropTickBlue; - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_CHARACTER) - { - void *pObj = Builder.NewItem(NETOBJTYPE_CHARACTER, pItem7->Id(), sizeof(CNetObj_Character)); - if(!pObj) - return -6; - - const protocol7::CNetObj_Character *pChar7 = (const protocol7::CNetObj_Character *)pItem7->Data(); - - CNetObj_Character Char6 = {}; - // character core is unchanged - mem_copy(&Char6, pItem7->Data(), sizeof(CNetObj_CharacterCore)); - - Char6.m_PlayerFlags = 0; - if(pItem7->Id() >= 0 && pItem7->Id() < MAX_CLIENTS) - Char6.m_PlayerFlags = PlayerFlags_SevenToSix(TranslationContext.m_aClients[pItem7->Id()].m_PlayerFlags7); - Char6.m_Health = pChar7->m_Health; - Char6.m_Armor = pChar7->m_Armor; - Char6.m_AmmoCount = pChar7->m_AmmoCount; - Char6.m_Weapon = pChar7->m_Weapon; - Char6.m_Emote = pChar7->m_Emote; - Char6.m_AttackTick = pChar7->m_AttackTick; - - if(TranslationContext.m_aLocalClientId[Conn] != pItem7->Id()) - { - if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_GROUND_JUMP) - { - void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); - if(!pEvent) - return -7; - - CNetEvent_SoundWorld Sound = {}; - Sound.m_X = pChar7->m_X; - Sound.m_Y = pChar7->m_Y; - Sound.m_SoundId = SOUND_PLAYER_JUMP; - mem_copy(pEvent, &Sound, sizeof(CNetEvent_SoundWorld)); - } - if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_HOOK_ATTACH_PLAYER) - { - void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); - if(!pEvent) - return -7; - - CNetEvent_SoundWorld Sound = {}; - Sound.m_X = pChar7->m_X; - Sound.m_Y = pChar7->m_Y; - Sound.m_SoundId = SOUND_HOOK_ATTACH_PLAYER; - mem_copy(pEvent, &Sound, sizeof(CNetEvent_SoundWorld)); - } - if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_HOOK_ATTACH_GROUND) - { - void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); - if(!pEvent) - return -7; - - CNetEvent_SoundWorld Sound = {}; - Sound.m_X = pChar7->m_X; - Sound.m_Y = pChar7->m_Y; - Sound.m_SoundId = SOUND_HOOK_ATTACH_GROUND; - mem_copy(pEvent, &Sound, sizeof(CNetEvent_SoundWorld)); - } - if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_HOOK_HIT_NOHOOK) - { - void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); - if(!pEvent) - return -7; - - CNetEvent_SoundWorld Sound = {}; - Sound.m_X = pChar7->m_X; - Sound.m_Y = pChar7->m_Y; - Sound.m_SoundId = SOUND_HOOK_NOATTACH; - mem_copy(pEvent, &Sound, sizeof(CNetEvent_SoundWorld)); - } - } - - mem_copy(pObj, &Char6, sizeof(CNetObj_Character)); - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_PLAYERINFO) - { - void *pObj = Builder.NewItem(NETOBJTYPE_PLAYERINFO, pItem7->Id(), sizeof(CNetObj_PlayerInfo)); - if(!pObj) - return -8; - - const protocol7::CNetObj_PlayerInfo *pInfo7 = (const protocol7::CNetObj_PlayerInfo *)pItem7->Data(); - CNetObj_PlayerInfo Info6 = {}; - Info6.m_Local = TranslationContext.m_aLocalClientId[Conn] == pItem7->Id(); - Info6.m_ClientId = pItem7->Id(); - Info6.m_Team = 0; - if(pItem7->Id() >= 0 && pItem7->Id() < MAX_CLIENTS) - { - Info6.m_Team = TranslationContext.m_aClients[pItem7->Id()].m_Team; - TranslationContext.m_aClients[pItem7->Id()].m_PlayerFlags7 = pInfo7->m_PlayerFlags; - } - Info6.m_Score = pInfo7->m_Score; - Info6.m_Latency = pInfo7->m_Latency; - mem_copy(pObj, &Info6, sizeof(CNetObj_PlayerInfo)); - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_SPECTATORINFO) - { - void *pObj = Builder.NewItem(NETOBJTYPE_SPECTATORINFO, pItem7->Id(), sizeof(CNetObj_SpectatorInfo)); - if(!pObj) - return -9; - - const protocol7::CNetObj_SpectatorInfo *pSpec7 = (const protocol7::CNetObj_SpectatorInfo *)pItem7->Data(); - CNetObj_SpectatorInfo Spec6 = {}; - Spec6.m_SpectatorId = pSpec7->m_SpectatorId; - if(pSpec7->m_SpecMode == protocol7::SPEC_FREEVIEW) - Spec6.m_SpectatorId = SPEC_FREEVIEW; - Spec6.m_X = pSpec7->m_X; - Spec6.m_Y = pSpec7->m_Y; - mem_copy(pObj, &Spec6, sizeof(CNetObj_SpectatorInfo)); - } - else if(pItem7->Type() == protocol7::NETEVENTTYPE_EXPLOSION) - { - void *pEvent = Builder.NewItem(NETEVENTTYPE_EXPLOSION, pItem7->Id(), sizeof(CNetEvent_Explosion)); - if(!pEvent) - return -10; - - const protocol7::CNetEvent_Explosion *pExplosion7 = (const protocol7::CNetEvent_Explosion *)pItem7->Data(); - CNetEvent_Explosion Explosion6 = {}; - Explosion6.m_X = pExplosion7->m_X; - Explosion6.m_Y = pExplosion7->m_Y; - mem_copy(pEvent, &Explosion6, sizeof(CNetEvent_Explosion)); - } - else if(pItem7->Type() == protocol7::NETEVENTTYPE_SPAWN) - { - void *pEvent = Builder.NewItem(NETEVENTTYPE_SPAWN, pItem7->Id(), sizeof(CNetEvent_Spawn)); - if(!pEvent) - return -11; - - const protocol7::CNetEvent_Spawn *pSpawn7 = (const protocol7::CNetEvent_Spawn *)pItem7->Data(); - CNetEvent_Spawn Spawn6 = {}; - Spawn6.m_X = pSpawn7->m_X; - Spawn6.m_Y = pSpawn7->m_Y; - mem_copy(pEvent, &Spawn6, sizeof(CNetEvent_Spawn)); - } - else if(pItem7->Type() == protocol7::NETEVENTTYPE_HAMMERHIT) - { - void *pEvent = Builder.NewItem(NETEVENTTYPE_HAMMERHIT, pItem7->Id(), sizeof(CNetEvent_HammerHit)); - if(!pEvent) - return -12; - - const protocol7::CNetEvent_HammerHit *pHammerHit7 = (const protocol7::CNetEvent_HammerHit *)pItem7->Data(); - CNetEvent_HammerHit HammerHit6 = {}; - HammerHit6.m_X = pHammerHit7->m_X; - HammerHit6.m_Y = pHammerHit7->m_Y; - mem_copy(pEvent, &HammerHit6, sizeof(CNetEvent_HammerHit)); - } - else if(pItem7->Type() == protocol7::NETEVENTTYPE_DEATH) - { - void *pEvent = Builder.NewItem(NETEVENTTYPE_DEATH, pItem7->Id(), sizeof(CNetEvent_Death)); - if(!pEvent) - return -13; - - const protocol7::CNetEvent_Death *pDeath7 = (const protocol7::CNetEvent_Death *)pItem7->Data(); - CNetEvent_Death Death6 = {}; - Death6.m_X = pDeath7->m_X; - Death6.m_Y = pDeath7->m_Y; - Death6.m_ClientId = pDeath7->m_ClientId; - mem_copy(pEvent, &Death6, sizeof(CNetEvent_Death)); - } - else if(pItem7->Type() == protocol7::NETEVENTTYPE_SOUNDWORLD) - { - void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); - if(!pEvent) - return -14; - - const protocol7::CNetEvent_SoundWorld *pSoundWorld7 = (const protocol7::CNetEvent_SoundWorld *)pItem7->Data(); - CNetEvent_SoundWorld SoundWorld6 = {}; - SoundWorld6.m_X = pSoundWorld7->m_X; - SoundWorld6.m_Y = pSoundWorld7->m_Y; - SoundWorld6.m_SoundId = pSoundWorld7->m_SoundId; - mem_copy(pEvent, &SoundWorld6, sizeof(CNetEvent_SoundWorld)); - } - else if(pItem7->Type() == protocol7::NETEVENTTYPE_DAMAGE) - { - // 0.7 introduced amount for damage indicators - // so for one 0.7 item we might create multiple 0.6 ones - const protocol7::CNetEvent_Damage *pDmg7 = (const protocol7::CNetEvent_Damage *)pItem7->Data(); - - int Amount = pDmg7->m_HealthAmount + pDmg7->m_ArmorAmount; - if(Amount < 1) - continue; - - int ClientId = pDmg7->m_ClientId; - TranslationContext.m_aDamageTaken[ClientId]++; - - float Angle; - // create healthmod indicator - if(LocalTime < TranslationContext.m_aDamageTakenTick[ClientId] + 0.5f) - { - // make sure that the damage indicators don't group together - Angle = TranslationContext.m_aDamageTaken[ClientId] * 0.25f; - } - else - { - TranslationContext.m_aDamageTaken[ClientId] = 0; - Angle = 0; - } - - TranslationContext.m_aDamageTakenTick[ClientId] = LocalTime; - - float a = 3 * pi / 2 + Angle; - float s = a - pi / 3; - float e = a + pi / 3; - for(int k = 0; k < Amount; k++) - { - // pItem7->Id() is reused that is technically wrong - // but the client implementation does not look at the ids - // and renders the damage indicators just fine - void *pEvent = Builder.NewItem(NETEVENTTYPE_DAMAGEIND, pItem7->Id(), sizeof(CNetEvent_DamageInd)); - if(!pEvent) - return -16; - - CNetEvent_DamageInd Dmg6 = {}; - Dmg6.m_X = pDmg7->m_X; - Dmg6.m_Y = pDmg7->m_Y; - float f = mix(s, e, float(k + 1) / float(Amount + 2)); - Dmg6.m_Angle = (int)(f * 256.0f); - mem_copy(pEvent, &Dmg6, sizeof(CNetEvent_DamageInd)); - } - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_DE_CLIENTINFO) - { - const protocol7::CNetObj_De_ClientInfo *pInfo = (const protocol7::CNetObj_De_ClientInfo *)pItem7->Data(); - - const int ClientId = pItem7->Id(); - - if(ClientId < 0 || ClientId >= MAX_CLIENTS) - { - dbg_msg("sixup", "De_ClientInfo got invalid ClientId: %d", ClientId); - return -17; - } - - if(pInfo->m_Local) - { - TranslationContext.m_aLocalClientId[Conn] = ClientId; - } - CTranslationContext::CClientData &Client = TranslationContext.m_aClients[ClientId]; - Client.m_Active = true; - Client.m_Team = pInfo->m_Team; - IntsToStr(pInfo->m_aName, 4, Client.m_aName, std::size(Client.m_aName)); - IntsToStr(pInfo->m_aClan, 3, Client.m_aClan, std::size(Client.m_aClan)); - Client.m_Country = pInfo->m_Country; - - // TODO: get this method into scope - // ApplySkin7InfoFromGameMsg(pInfo, ClientId); - } - else if(pItem7->Type() == protocol7::NETOBJTYPE_DE_GAMEINFO) - { - const protocol7::CNetObj_De_GameInfo *pInfo = (const protocol7::CNetObj_De_GameInfo *)pItem7->Data(); - - TranslationContext.m_GameFlags = pInfo->m_GameFlags; - TranslationContext.m_ScoreLimit = pInfo->m_ScoreLimit; - TranslationContext.m_TimeLimit = pInfo->m_TimeLimit; - TranslationContext.m_MatchNum = pInfo->m_MatchNum; - TranslationContext.m_MatchCurrent = pInfo->m_MatchCurrent; - TranslationContext.m_ShouldSendGameInfo = true; - } - } - - if(NewGameData) - { - void *pObj = Builder.NewItem(NETOBJTYPE_GAMEDATA, 0, sizeof(CNetObj_GameData)); - if(!pObj) - return -17; - - CNetObj_GameData GameData = {}; - GameData.m_TeamscoreRed = TranslationContext.m_TeamscoreRed; - GameData.m_TeamscoreBlue = TranslationContext.m_TeamscoreBlue; - GameData.m_FlagCarrierRed = TranslationContext.m_FlagCarrierRed; - GameData.m_FlagCarrierBlue = TranslationContext.m_FlagCarrierBlue; - mem_copy(pObj, &GameData, sizeof(CNetObj_GameData)); - } - - return Builder.Finish(pSixSnapDest); + m_DataSize = pSnapshot->m_DataSize; + m_NumItems = pSnapshot->m_NumItems; + mem_copy(m_aOffsets, pSnapshot->Offsets(), sizeof(int) * m_NumItems); + mem_copy(m_aData, pSnapshot->DataStart(), m_DataSize); } diff --git a/src/engine/shared/snapshot.h b/src/engine/shared/snapshot.h index 4060b9938..24aeaafa9 100644 --- a/src/engine/shared/snapshot.h +++ b/src/engine/shared/snapshot.h @@ -63,15 +63,6 @@ public: unsigned Crc() const; void DebugDump() const; - int TranslateSevenToSix( - CSnapshot *pSixSnapDest, - class CTranslationContext &TranslationContext, - float LocalTime, - int GameTick, - int Conn, - bool Dummy, - protocol7::CNetObjHandler *pNetObjHandler, - CNetObjHandler *pNetObjHandler6); bool IsValid(size_t ActualSize) const; static const CSnapshot *EmptySnapshot() { return &ms_EmptySnapshot; } @@ -171,12 +162,13 @@ class CSnapshotBuilder int GetExtendedItemTypeIndex(int TypeId); int GetTypeFromIndex(int Index) const; - bool m_Sixup; + bool m_Sixup = false; public: CSnapshotBuilder(); void Init(bool Sixup = false); + void Init7(const CSnapshot *pSnapshot); void *NewItem(int Type, int Id, int Size); diff --git a/src/engine/shared/translation_context.h b/src/engine/shared/translation_context.h index 60cae63c9..53d9cdbb9 100644 --- a/src/engine/shared/translation_context.h +++ b/src/engine/shared/translation_context.h @@ -1,6 +1,7 @@ #ifndef ENGINE_SHARED_TRANSLATION_CONTEXT_H #define ENGINE_SHARED_TRANSLATION_CONTEXT_H +#include #include #include @@ -98,6 +99,9 @@ public: bool m_ShouldSendGameInfo; int m_GameStateFlags7; + // 0.7 game flags + // use in combination with protocol7::GAMEFLAG_* + // for example protocol7::GAMEFLAG_TEAMS int m_GameFlags; int m_ScoreLimit; int m_TimeLimit; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index ba4aa2113..881ef4f44 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -81,6 +81,7 @@ using namespace std::chrono_literals; const char *CGameClient::Version() const { return GAME_VERSION; } const char *CGameClient::NetVersion() const { return GAME_NETVERSION; } +const char *CGameClient::NetVersion7() const { return GAME_NETVERSION7; } int CGameClient::DDNetVersion() const { return DDNET_VERSION_NUMBER; } const char *CGameClient::DDNetVersionStr() const { return m_aDDNetVersionStr; } int CGameClient::ClientVersion7() const { return CLIENT_VERSION7; } @@ -2336,9 +2337,9 @@ void CGameClient::CClientData::Reset() m_SkinColor = 0; for(int i = 0; i < protocol7::NUM_SKINPARTS; ++i) { - m_aaSkinPartNames[i][0] = '\0'; - m_aUseCustomColors[i] = 0; - m_aSkinPartColors[i] = 0; + m_Sixup.m_aaSkinPartNames[i][0] = '\0'; + m_Sixup.m_aUseCustomColors[i] = 0; + m_Sixup.m_aSkinPartColors[i] = 0; } m_Team = 0; m_Emoticon = 0; @@ -2464,11 +2465,11 @@ bool CGameClient::GotWantedSkin7(bool Dummy) for(int SkinPart = 0; SkinPart < protocol7::NUM_SKINPARTS; SkinPart++) { - if(str_comp(m_aClients[m_aLocalIds[(int)Dummy]].m_aaSkinPartNames[SkinPart], apSkinPartsPtr[SkinPart])) + if(str_comp(m_aClients[m_aLocalIds[(int)Dummy]].m_Sixup.m_aaSkinPartNames[SkinPart], apSkinPartsPtr[SkinPart])) return false; - if(m_aClients[m_aLocalIds[(int)Dummy]].m_aUseCustomColors[SkinPart] != aUCCVars[SkinPart]) + if(m_aClients[m_aLocalIds[(int)Dummy]].m_Sixup.m_aUseCustomColors[SkinPart] != aUCCVars[SkinPart]) return false; - if(m_aClients[m_aLocalIds[(int)Dummy]].m_aSkinPartColors[SkinPart] != aColorVars[SkinPart]) + if(m_aClients[m_aLocalIds[(int)Dummy]].m_Sixup.m_aSkinPartColors[SkinPart] != aColorVars[SkinPart]) return false; } diff --git a/src/game/client/gameclient.h b/src/game/client/gameclient.h index 2ec09e4c5..60aa7f4e0 100644 --- a/src/game/client/gameclient.h +++ b/src/game/client/gameclient.h @@ -365,12 +365,6 @@ public: int m_Country; char m_aSkinName[24]; int m_SkinColor; - - // 0.7 Skin - char m_aaSkinPartNames[protocol7::NUM_SKINPARTS][protocol7::MAX_SKIN_LENGTH]; - int m_aUseCustomColors[protocol7::NUM_SKINPARTS]; - int m_aSkinPartColors[protocol7::NUM_SKINPARTS]; - int m_Team; int m_Emoticon; float m_EmoticonStartFraction; @@ -433,6 +427,17 @@ public: void UpdateRenderInfo(bool IsTeamPlay); void Reset(); + + class CSixup + { + public: + char m_aaSkinPartNames[protocol7::NUM_SKINPARTS][protocol7::MAX_SKIN_LENGTH]; + int m_aUseCustomColors[protocol7::NUM_SKINPARTS]; + int m_aSkinPartColors[protocol7::NUM_SKINPARTS]; + }; + + // 0.7 Skin + CSixup m_Sixup; }; CClientData m_aClients[MAX_CLIENTS]; @@ -493,7 +498,10 @@ public: void OnStateChange(int NewState, int OldState) override; template void ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId); + void ApplySkin7InfoFromSnapObj(const protocol7::CNetObj_De_ClientInfo *pObj, int ClientId) override; + int OnDemoRecSnap7(class CSnapshot *pFrom, class CSnapshot *pTo, int Conn) override; void *TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn); + int TranslateSnap(CSnapshot *pSnapDstSix, CSnapshot *pSnapSrcSeven, int Conn, bool Dummy) override; void OnMessage(int MsgId, CUnpacker *pUnpacker, int Conn, bool Dummy) override; void InvalidateSnapshot() override; void OnNewSnapshot() override; @@ -522,6 +530,7 @@ public: const char *GetItemName(int Type) const override; const char *Version() const override; const char *NetVersion() const override; + const char *NetVersion7() const override; int DDNetVersion() const override; const char *DDNetVersionStr() const override; virtual int ClientVersion7() const override; diff --git a/src/game/client/sixup_translate_game.cpp b/src/game/client/sixup_translate_game.cpp index a93ef0730..c11100ca3 100644 --- a/src/game/client/sixup_translate_game.cpp +++ b/src/game/client/sixup_translate_game.cpp @@ -1,3 +1,7 @@ +#include +#include +#include +#include #include #include @@ -81,12 +85,12 @@ void CGameClient::ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId) char *apSkinPartsPtr[protocol7::NUM_SKINPARTS]; for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++) { - str_utf8_copy_num(pClient->m_aaSkinPartNames[Part], pMsg->m_apSkinPartNames[Part], sizeof(pClient->m_aaSkinPartNames[Part]), protocol7::MAX_SKIN_LENGTH); - apSkinPartsPtr[Part] = pClient->m_aaSkinPartNames[Part]; - pClient->m_aUseCustomColors[Part] = pMsg->m_aUseCustomColors[Part]; - pClient->m_aSkinPartColors[Part] = pMsg->m_aSkinPartColors[Part]; + str_utf8_copy_num(pClient->m_Sixup.m_aaSkinPartNames[Part], pMsg->m_apSkinPartNames[Part], sizeof(pClient->m_Sixup.m_aaSkinPartNames[Part]), protocol7::MAX_SKIN_LENGTH); + apSkinPartsPtr[Part] = pClient->m_Sixup.m_aaSkinPartNames[Part]; + pClient->m_Sixup.m_aUseCustomColors[Part] = pMsg->m_aUseCustomColors[Part]; + pClient->m_Sixup.m_aSkinPartColors[Part] = pMsg->m_aSkinPartColors[Part]; } - m_Skins7.ValidateSkinParts(apSkinPartsPtr, pClient->m_aUseCustomColors, pClient->m_aSkinPartColors, m_pClient->m_TranslationContext.m_GameFlags); + m_Skins7.ValidateSkinParts(apSkinPartsPtr, pClient->m_Sixup.m_aUseCustomColors, pClient->m_Sixup.m_aSkinPartColors, m_pClient->m_TranslationContext.m_GameFlags); if(time_season() == SEASON_XMAS) { @@ -98,12 +102,12 @@ void CGameClient::ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId) for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++) { - int Id = m_Skins7.FindSkinPart(Part, pClient->m_aaSkinPartNames[Part], false); + int Id = m_Skins7.FindSkinPart(Part, pClient->m_Sixup.m_aaSkinPartNames[Part], false); const CSkins7::CSkinPart *pSkinPart = m_Skins7.GetSkinPart(Part, Id); - if(pClient->m_aUseCustomColors[Part]) + if(pClient->m_Sixup.m_aUseCustomColors[Part]) { pClient->m_SkinInfo.m_Sixup.m_aTextures[Part] = pSkinPart->m_ColorTexture; - pClient->m_SkinInfo.m_Sixup.m_aColors[Part] = m_Skins7.GetColor(pClient->m_aSkinPartColors[Part], Part == protocol7::SKINPART_MARKING); + pClient->m_SkinInfo.m_Sixup.m_aColors[Part] = m_Skins7.GetColor(pMsg->m_aSkinPartColors[Part], Part == protocol7::SKINPART_MARKING); } else { @@ -112,14 +116,29 @@ void CGameClient::ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId) } if(pClient->m_SkinInfo.m_Sixup.m_HatTexture.IsValid()) { - if(Part == protocol7::SKINPART_BODY && str_comp(pClient->m_aaSkinPartNames[Part], "standard")) + if(Part == protocol7::SKINPART_BODY && str_comp(pClient->m_Sixup.m_aaSkinPartNames[Part], "standard")) pClient->m_SkinInfo.m_Sixup.m_HatSpriteIndex = CSkins7::HAT_OFFSET_SIDE + (ClientId % CSkins7::HAT_NUM); - if(Part == protocol7::SKINPART_DECORATION && !str_comp(pClient->m_aaSkinPartNames[Part], "twinbopp")) + if(Part == protocol7::SKINPART_DECORATION && !str_comp(pClient->m_Sixup.m_aaSkinPartNames[Part], "twinbopp")) pClient->m_SkinInfo.m_Sixup.m_HatSpriteIndex = CSkins7::HAT_OFFSET_SIDE + (ClientId % CSkins7::HAT_NUM); } } } +void CGameClient::ApplySkin7InfoFromSnapObj(const protocol7::CNetObj_De_ClientInfo *pObj, int ClientId) +{ + char aSkinPartNames[protocol7::NUM_SKINPARTS][protocol7::MAX_SKIN_ARRAY_SIZE]; + protocol7::CNetMsg_Sv_SkinChange Msg; + Msg.m_ClientId = ClientId; + for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++) + { + IntsToStr(pObj->m_aaSkinPartNames[Part], 6, aSkinPartNames[Part], std::size(aSkinPartNames[Part])); + Msg.m_apSkinPartNames[Part] = aSkinPartNames[Part]; + Msg.m_aUseCustomColors[Part] = pObj->m_aUseCustomColors[Part]; + Msg.m_aSkinPartColors[Part] = pObj->m_aSkinPartColors[Part]; + } + ApplySkin7InfoFromGameMsg(&Msg, ClientId); +} + void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn) { if(!m_pClient->IsSixup()) diff --git a/src/game/client/sixup_translate_snapshot.cpp b/src/game/client/sixup_translate_snapshot.cpp new file mode 100644 index 000000000..1cbb03f18 --- /dev/null +++ b/src/game/client/sixup_translate_snapshot.cpp @@ -0,0 +1,564 @@ +#include +#include +#include +#include +#include + +int CGameClient::TranslateSnap(CSnapshot *pSnapDstSix, CSnapshot *pSnapSrcSeven, int Conn, bool Dummy) +{ + CSnapshotBuilder Builder; + Builder.Init(); + + float LocalTime = Client()->LocalTime(); + int GameTick = Client()->GameTick(g_Config.m_ClDummy); + CTranslationContext &TranslationContext = Client()->m_TranslationContext; + + for(auto &PlayerInfosRace : TranslationContext.m_apPlayerInfosRace) + PlayerInfosRace = nullptr; + + int SpectatorId = -3; + + for(int i = 0; i < pSnapSrcSeven->NumItems(); i++) + { + const CSnapshotItem *pItem7 = (CSnapshotItem *)pSnapSrcSeven->GetItem(i); + const int Size = pSnapSrcSeven->GetItemSize(i); + const int ItemType = pSnapSrcSeven->GetItemType(i); + + // ddnet ex snap items + if((ItemType > __NETOBJTYPE_UUID_HELPER && ItemType < OFFSET_NETMSGTYPE_UUID) || pItem7->Type() == NETOBJTYPE_EX) + { + CUnpacker Unpacker; + Unpacker.Reset(pItem7->Data(), Size); + + void *pRawObj = GetNetObjHandler()->SecureUnpackObj(ItemType, &Unpacker); + if(!pRawObj) + { + if(ItemType != UUID_UNKNOWN) + dbg_msg("sixup", "dropped weird ddnet ex object '%s' (%d), failed on '%s'", GetNetObjHandler()->GetObjName(ItemType), ItemType, GetNetObjHandler()->FailedObjOn()); + continue; + } + const int ItemSize = GetNetObjHandler()->GetUnpackedObjSize(ItemType); + + void *pObj = Builder.NewItem(pItem7->Type(), pItem7->Id(), ItemSize); + if(!pObj) + return -17; + + mem_copy(pObj, pRawObj, ItemSize); + continue; + } + + if(GetNetObjHandler7()->ValidateObj(pItem7->Type(), pItem7->Data(), Size) != 0) + { + if(pItem7->Type() > 0 && pItem7->Type() < CSnapshot::OFFSET_UUID_TYPE) + { + dbg_msg( + "sixup", + "invalidated index=%d type=%d (%s) size=%d id=%d", + i, + pItem7->Type(), + GetNetObjHandler7()->GetObjName(pItem7->Type()), + Size, + pItem7->Id()); + } + pSnapSrcSeven->InvalidateItem(i); + } + + if(pItem7->Type() == protocol7::NETOBJTYPE_PLAYERINFORACE) + { + const protocol7::CNetObj_PlayerInfoRace *pInfo = (const protocol7::CNetObj_PlayerInfoRace *)pItem7->Data(); + int ClientId = pItem7->Id(); + if(ClientId < MAX_CLIENTS && TranslationContext.m_aClients[ClientId].m_Active) + { + TranslationContext.m_apPlayerInfosRace[ClientId] = pInfo; + } + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_SPECTATORINFO) + { + const protocol7::CNetObj_SpectatorInfo *pSpec7 = (const protocol7::CNetObj_SpectatorInfo *)pItem7->Data(); + SpectatorId = pSpec7->m_SpectatorId; + if(pSpec7->m_SpecMode == protocol7::SPEC_FREEVIEW) + SpectatorId = SPEC_FREEVIEW; + } + } + + // hack to put game info in the snap + // even though in 0.7 we get it as a game message + // this message has to be on the top + // the client looks at the items in order and needs the gameinfo at the top + // otherwise it will not render skins with team colors + if(TranslationContext.m_ShouldSendGameInfo) + { + void *pObj = Builder.NewItem(NETOBJTYPE_GAMEINFO, 0, sizeof(CNetObj_GameInfo)); + if(!pObj) + return -1; + + int GameStateFlagsSix = 0; + if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_GAMEOVER) + GameStateFlagsSix |= GAMESTATEFLAG_GAMEOVER; + if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_SUDDENDEATH) + GameStateFlagsSix |= GAMESTATEFLAG_SUDDENDEATH; + if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_PAUSED) + GameStateFlagsSix |= GAMESTATEFLAG_PAUSED; + + /* + This is a 0.7 only flag that we just ignore for now + + GAMESTATEFLAG_ROUNDOVER + */ + + CNetObj_GameInfo Info6 = {}; + Info6.m_GameFlags = TranslationContext.m_GameFlags; + Info6.m_GameStateFlags = GameStateFlagsSix; + Info6.m_RoundStartTick = TranslationContext.m_GameStartTick7; + Info6.m_WarmupTimer = 0; // removed in 0.7 + if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_WARMUP) + Info6.m_WarmupTimer = TranslationContext.m_GameStateEndTick7 - GameTick; + if(TranslationContext.m_GameStateFlags7 & protocol7::GAMESTATEFLAG_STARTCOUNTDOWN) + Info6.m_WarmupTimer = TranslationContext.m_GameStateEndTick7 - GameTick; + + // hack to port 0.7 race timer to ddnet warmup gametimer hack + int TimerClientId = clamp(TranslationContext.m_aLocalClientId[Conn], 0, (int)MAX_CLIENTS); + if(SpectatorId >= 0) + TimerClientId = SpectatorId; + const protocol7::CNetObj_PlayerInfoRace *pRaceInfo = TranslationContext.m_apPlayerInfosRace[TimerClientId]; + if(pRaceInfo) + { + Info6.m_WarmupTimer = -pRaceInfo->m_RaceStartTick; + Info6.m_GameStateFlags |= GAMESTATEFLAG_RACETIME; + } + + Info6.m_ScoreLimit = TranslationContext.m_ScoreLimit; + Info6.m_TimeLimit = TranslationContext.m_TimeLimit; + Info6.m_RoundNum = TranslationContext.m_MatchNum; + Info6.m_RoundCurrent = TranslationContext.m_MatchCurrent; + + mem_copy(pObj, &Info6, sizeof(CNetObj_GameInfo)); + } + + for(int i = 0; i < MAX_CLIENTS; i++) + { + const CTranslationContext::CClientData &Client = TranslationContext.m_aClients[i]; + if(!Client.m_Active) + continue; + + void *pObj = Builder.NewItem(NETOBJTYPE_CLIENTINFO, i, sizeof(CNetObj_ClientInfo)); + if(!pObj) + return -2; + + CNetObj_ClientInfo Info6 = {}; + StrToInts(&Info6.m_Name0, 4, Client.m_aName); + StrToInts(&Info6.m_Clan0, 3, Client.m_aClan); + Info6.m_Country = Client.m_Country; + StrToInts(&Info6.m_Skin0, 6, Client.m_aSkinName); + Info6.m_UseCustomColor = Client.m_UseCustomColor; + Info6.m_ColorBody = Client.m_ColorBody; + Info6.m_ColorFeet = Client.m_ColorFeet; + mem_copy(pObj, &Info6, sizeof(CNetObj_ClientInfo)); + } + + bool NewGameData = false; + + for(int i = 0; i < pSnapSrcSeven->NumItems(); i++) + { + const CSnapshotItem *pItem7 = pSnapSrcSeven->GetItem(i); + const int Size = pSnapSrcSeven->GetItemSize(i); + // the first few items are a full match + // no translation needed + if(pItem7->Type() == protocol7::NETOBJTYPE_PROJECTILE || + pItem7->Type() == protocol7::NETOBJTYPE_LASER || + pItem7->Type() == protocol7::NETOBJTYPE_FLAG) + { + void *pObj = Builder.NewItem(pItem7->Type(), pItem7->Id(), Size); + if(!pObj) + return -4; + + mem_copy(pObj, pItem7->Data(), Size); + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_PICKUP) + { + void *pObj = Builder.NewItem(NETOBJTYPE_PICKUP, pItem7->Id(), sizeof(CNetObj_Pickup)); + if(!pObj) + return -5; + + const protocol7::CNetObj_Pickup *pPickup7 = (const protocol7::CNetObj_Pickup *)pItem7->Data(); + CNetObj_Pickup Pickup6 = {}; + Pickup6.m_X = pPickup7->m_X; + Pickup6.m_Y = pPickup7->m_Y; + PickupType_SevenToSix(pPickup7->m_Type, Pickup6.m_Type, Pickup6.m_Subtype); + + mem_copy(pObj, &Pickup6, sizeof(CNetObj_Pickup)); + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_GAMEDATA) + { + const protocol7::CNetObj_GameData *pGameData = (const protocol7::CNetObj_GameData *)pItem7->Data(); + TranslationContext.m_GameStateFlags7 = pGameData->m_GameStateFlags; + TranslationContext.m_GameStartTick7 = pGameData->m_GameStartTick; + TranslationContext.m_GameStateEndTick7 = pGameData->m_GameStateEndTick; + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_GAMEDATATEAM) + { + // 0.7 added GameDataTeam and GameDataFlag + // both items merged together have all fields of the 0.6 GameData + // so if we get either one of them we store the details in the translation context + // and build one GameData snap item after this loop + const protocol7::CNetObj_GameDataTeam *pTeam7 = (const protocol7::CNetObj_GameDataTeam *)pItem7->Data(); + + TranslationContext.m_TeamscoreRed = pTeam7->m_TeamscoreRed; + TranslationContext.m_TeamscoreBlue = pTeam7->m_TeamscoreBlue; + NewGameData = true; + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_GAMEDATAFLAG) + { + const protocol7::CNetObj_GameDataFlag *pFlag7 = (const protocol7::CNetObj_GameDataFlag *)pItem7->Data(); + + TranslationContext.m_FlagCarrierRed = pFlag7->m_FlagCarrierRed; + TranslationContext.m_FlagCarrierBlue = pFlag7->m_FlagCarrierBlue; + NewGameData = true; + + // used for blinking the flags in the hud + // but that already works the 0.6 way + // and is covered by the NETOBJTYPE_GAMEDATA translation + // pFlag7->m_FlagDropTickRed; + // pFlag7->m_FlagDropTickBlue; + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_CHARACTER) + { + void *pObj = Builder.NewItem(NETOBJTYPE_CHARACTER, pItem7->Id(), sizeof(CNetObj_Character)); + if(!pObj) + return -6; + + const protocol7::CNetObj_Character *pChar7 = (const protocol7::CNetObj_Character *)pItem7->Data(); + + CNetObj_Character Char6 = {}; + // character core is unchanged + mem_copy(&Char6, pItem7->Data(), sizeof(CNetObj_CharacterCore)); + + Char6.m_PlayerFlags = 0; + if(pItem7->Id() >= 0 && pItem7->Id() < MAX_CLIENTS) + Char6.m_PlayerFlags = PlayerFlags_SevenToSix(TranslationContext.m_aClients[pItem7->Id()].m_PlayerFlags7); + Char6.m_Health = pChar7->m_Health; + Char6.m_Armor = pChar7->m_Armor; + Char6.m_AmmoCount = pChar7->m_AmmoCount; + Char6.m_Weapon = pChar7->m_Weapon; + Char6.m_Emote = pChar7->m_Emote; + Char6.m_AttackTick = pChar7->m_AttackTick; + + if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_HOOK_ATTACH_PLAYER) + { + void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); + if(!pEvent) + return -7; + + CNetEvent_SoundWorld Sound = {}; + Sound.m_X = pChar7->m_X; + Sound.m_Y = pChar7->m_Y; + Sound.m_SoundId = SOUND_HOOK_ATTACH_PLAYER; + mem_copy(pEvent, &Sound, sizeof(CNetEvent_SoundWorld)); + } + + if(TranslationContext.m_aLocalClientId[Conn] != pItem7->Id()) + { + if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_GROUND_JUMP) + { + void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); + if(!pEvent) + return -7; + + CNetEvent_SoundWorld Sound = {}; + Sound.m_X = pChar7->m_X; + Sound.m_Y = pChar7->m_Y; + Sound.m_SoundId = SOUND_PLAYER_JUMP; + mem_copy(pEvent, &Sound, sizeof(CNetEvent_SoundWorld)); + } + if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_HOOK_ATTACH_GROUND) + { + void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); + if(!pEvent) + return -7; + + CNetEvent_SoundWorld Sound = {}; + Sound.m_X = pChar7->m_X; + Sound.m_Y = pChar7->m_Y; + Sound.m_SoundId = SOUND_HOOK_ATTACH_GROUND; + mem_copy(pEvent, &Sound, sizeof(CNetEvent_SoundWorld)); + } + if(pChar7->m_TriggeredEvents & protocol7::COREEVENTFLAG_HOOK_HIT_NOHOOK) + { + void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); + if(!pEvent) + return -7; + + CNetEvent_SoundWorld Sound = {}; + Sound.m_X = pChar7->m_X; + Sound.m_Y = pChar7->m_Y; + Sound.m_SoundId = SOUND_HOOK_NOATTACH; + mem_copy(pEvent, &Sound, sizeof(CNetEvent_SoundWorld)); + } + } + + mem_copy(pObj, &Char6, sizeof(CNetObj_Character)); + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_PLAYERINFO) + { + void *pObj = Builder.NewItem(NETOBJTYPE_PLAYERINFO, pItem7->Id(), sizeof(CNetObj_PlayerInfo)); + if(!pObj) + return -8; + + const protocol7::CNetObj_PlayerInfo *pInfo7 = (const protocol7::CNetObj_PlayerInfo *)pItem7->Data(); + CNetObj_PlayerInfo Info6 = {}; + Info6.m_Local = TranslationContext.m_aLocalClientId[Conn] == pItem7->Id(); + Info6.m_ClientId = pItem7->Id(); + Info6.m_Team = 0; + if(pItem7->Id() >= 0 && pItem7->Id() < MAX_CLIENTS) + { + Info6.m_Team = TranslationContext.m_aClients[pItem7->Id()].m_Team; + TranslationContext.m_aClients[pItem7->Id()].m_PlayerFlags7 = pInfo7->m_PlayerFlags; + } + Info6.m_Score = pInfo7->m_Score; + Info6.m_Latency = pInfo7->m_Latency; + mem_copy(pObj, &Info6, sizeof(CNetObj_PlayerInfo)); + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_SPECTATORINFO) + { + void *pObj = Builder.NewItem(NETOBJTYPE_SPECTATORINFO, pItem7->Id(), sizeof(CNetObj_SpectatorInfo)); + if(!pObj) + return -9; + + const protocol7::CNetObj_SpectatorInfo *pSpec7 = (const protocol7::CNetObj_SpectatorInfo *)pItem7->Data(); + CNetObj_SpectatorInfo Spec6 = {}; + Spec6.m_SpectatorId = pSpec7->m_SpectatorId; + if(pSpec7->m_SpecMode == protocol7::SPEC_FREEVIEW) + Spec6.m_SpectatorId = SPEC_FREEVIEW; + Spec6.m_X = pSpec7->m_X; + Spec6.m_Y = pSpec7->m_Y; + mem_copy(pObj, &Spec6, sizeof(CNetObj_SpectatorInfo)); + } + else if(pItem7->Type() == protocol7::NETEVENTTYPE_EXPLOSION) + { + void *pEvent = Builder.NewItem(NETEVENTTYPE_EXPLOSION, pItem7->Id(), sizeof(CNetEvent_Explosion)); + if(!pEvent) + return -10; + + const protocol7::CNetEvent_Explosion *pExplosion7 = (const protocol7::CNetEvent_Explosion *)pItem7->Data(); + CNetEvent_Explosion Explosion6 = {}; + Explosion6.m_X = pExplosion7->m_X; + Explosion6.m_Y = pExplosion7->m_Y; + mem_copy(pEvent, &Explosion6, sizeof(CNetEvent_Explosion)); + } + else if(pItem7->Type() == protocol7::NETEVENTTYPE_SPAWN) + { + void *pEvent = Builder.NewItem(NETEVENTTYPE_SPAWN, pItem7->Id(), sizeof(CNetEvent_Spawn)); + if(!pEvent) + return -11; + + const protocol7::CNetEvent_Spawn *pSpawn7 = (const protocol7::CNetEvent_Spawn *)pItem7->Data(); + CNetEvent_Spawn Spawn6 = {}; + Spawn6.m_X = pSpawn7->m_X; + Spawn6.m_Y = pSpawn7->m_Y; + mem_copy(pEvent, &Spawn6, sizeof(CNetEvent_Spawn)); + } + else if(pItem7->Type() == protocol7::NETEVENTTYPE_HAMMERHIT) + { + void *pEvent = Builder.NewItem(NETEVENTTYPE_HAMMERHIT, pItem7->Id(), sizeof(CNetEvent_HammerHit)); + if(!pEvent) + return -12; + + const protocol7::CNetEvent_HammerHit *pHammerHit7 = (const protocol7::CNetEvent_HammerHit *)pItem7->Data(); + CNetEvent_HammerHit HammerHit6 = {}; + HammerHit6.m_X = pHammerHit7->m_X; + HammerHit6.m_Y = pHammerHit7->m_Y; + mem_copy(pEvent, &HammerHit6, sizeof(CNetEvent_HammerHit)); + } + else if(pItem7->Type() == protocol7::NETEVENTTYPE_DEATH) + { + void *pEvent = Builder.NewItem(NETEVENTTYPE_DEATH, pItem7->Id(), sizeof(CNetEvent_Death)); + if(!pEvent) + return -13; + + const protocol7::CNetEvent_Death *pDeath7 = (const protocol7::CNetEvent_Death *)pItem7->Data(); + CNetEvent_Death Death6 = {}; + Death6.m_X = pDeath7->m_X; + Death6.m_Y = pDeath7->m_Y; + Death6.m_ClientId = pDeath7->m_ClientId; + mem_copy(pEvent, &Death6, sizeof(CNetEvent_Death)); + } + else if(pItem7->Type() == protocol7::NETEVENTTYPE_SOUNDWORLD) + { + void *pEvent = Builder.NewItem(NETEVENTTYPE_SOUNDWORLD, pItem7->Id(), sizeof(CNetEvent_SoundWorld)); + if(!pEvent) + return -14; + + const protocol7::CNetEvent_SoundWorld *pSoundWorld7 = (const protocol7::CNetEvent_SoundWorld *)pItem7->Data(); + CNetEvent_SoundWorld SoundWorld6 = {}; + SoundWorld6.m_X = pSoundWorld7->m_X; + SoundWorld6.m_Y = pSoundWorld7->m_Y; + SoundWorld6.m_SoundId = pSoundWorld7->m_SoundId; + mem_copy(pEvent, &SoundWorld6, sizeof(CNetEvent_SoundWorld)); + } + else if(pItem7->Type() == protocol7::NETEVENTTYPE_DAMAGE) + { + // 0.7 introduced amount for damage indicators + // so for one 0.7 item we might create multiple 0.6 ones + const protocol7::CNetEvent_Damage *pDmg7 = (const protocol7::CNetEvent_Damage *)pItem7->Data(); + + int Amount = pDmg7->m_HealthAmount + pDmg7->m_ArmorAmount; + if(Amount < 1) + continue; + + int ClientId = pDmg7->m_ClientId; + TranslationContext.m_aDamageTaken[ClientId]++; + + float Angle; + // create healthmod indicator + if(LocalTime < TranslationContext.m_aDamageTakenTick[ClientId] + 0.5f) + { + // make sure that the damage indicators don't group together + Angle = TranslationContext.m_aDamageTaken[ClientId] * 0.25f; + } + else + { + TranslationContext.m_aDamageTaken[ClientId] = 0; + Angle = 0; + } + + TranslationContext.m_aDamageTakenTick[ClientId] = LocalTime; + + float a = 3 * pi / 2 + Angle; + float s = a - pi / 3; + float e = a + pi / 3; + for(int k = 0; k < Amount; k++) + { + // pItem7->Id() is reused that is technically wrong + // but the client implementation does not look at the ids + // and renders the damage indicators just fine + void *pEvent = Builder.NewItem(NETEVENTTYPE_DAMAGEIND, pItem7->Id(), sizeof(CNetEvent_DamageInd)); + if(!pEvent) + return -16; + + CNetEvent_DamageInd Dmg6 = {}; + Dmg6.m_X = pDmg7->m_X; + Dmg6.m_Y = pDmg7->m_Y; + float f = mix(s, e, float(k + 1) / float(Amount + 2)); + Dmg6.m_Angle = (int)(f * 256.0f); + mem_copy(pEvent, &Dmg6, sizeof(CNetEvent_DamageInd)); + } + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_DE_CLIENTINFO) + { + const protocol7::CNetObj_De_ClientInfo *pInfo = (const protocol7::CNetObj_De_ClientInfo *)pItem7->Data(); + + const int ClientId = pItem7->Id(); + + if(ClientId < 0 || ClientId >= MAX_CLIENTS) + { + dbg_msg("sixup", "De_ClientInfo got invalid ClientId: %d", ClientId); + return -17; + } + + if(pInfo->m_Local) + { + TranslationContext.m_aLocalClientId[Conn] = ClientId; + } + CTranslationContext::CClientData &Client = TranslationContext.m_aClients[ClientId]; + Client.m_Active = true; + Client.m_Team = pInfo->m_Team; + IntsToStr(pInfo->m_aName, 4, Client.m_aName, std::size(Client.m_aName)); + IntsToStr(pInfo->m_aClan, 3, Client.m_aClan, std::size(Client.m_aClan)); + Client.m_Country = pInfo->m_Country; + + ApplySkin7InfoFromSnapObj(pInfo, ClientId); + } + else if(pItem7->Type() == protocol7::NETOBJTYPE_DE_GAMEINFO) + { + const protocol7::CNetObj_De_GameInfo *pInfo = (const protocol7::CNetObj_De_GameInfo *)pItem7->Data(); + + TranslationContext.m_GameFlags = pInfo->m_GameFlags; + TranslationContext.m_ScoreLimit = pInfo->m_ScoreLimit; + TranslationContext.m_TimeLimit = pInfo->m_TimeLimit; + TranslationContext.m_MatchNum = pInfo->m_MatchNum; + TranslationContext.m_MatchCurrent = pInfo->m_MatchCurrent; + TranslationContext.m_ShouldSendGameInfo = true; + } + } + + if(NewGameData) + { + void *pObj = Builder.NewItem(NETOBJTYPE_GAMEDATA, 0, sizeof(CNetObj_GameData)); + if(!pObj) + return -17; + + CNetObj_GameData GameData = {}; + GameData.m_TeamscoreRed = TranslationContext.m_TeamscoreRed; + GameData.m_TeamscoreBlue = TranslationContext.m_TeamscoreBlue; + GameData.m_FlagCarrierRed = TranslationContext.m_FlagCarrierRed; + GameData.m_FlagCarrierBlue = TranslationContext.m_FlagCarrierBlue; + mem_copy(pObj, &GameData, sizeof(CNetObj_GameData)); + } + + return Builder.Finish(pSnapDstSix); +} + +int CGameClient::OnDemoRecSnap7(CSnapshot *pFrom, CSnapshot *pTo, int Conn) +{ + CSnapshotBuilder Builder; + Builder.Init7(pFrom); + + // add client info + for(int i = 0; i < MAX_CLIENTS; i++) + { + if(!m_aClients[i].m_Active) + continue; + + void *pItem = Builder.NewItem(protocol7::NETOBJTYPE_DE_CLIENTINFO, i, sizeof(protocol7::CNetObj_De_ClientInfo)); + if(!pItem) + return -1; + + CTranslationContext::CClientData &ClientData = Client()->m_TranslationContext.m_aClients[i]; + + protocol7::CNetObj_De_ClientInfo ClientInfoObj; + ClientInfoObj.m_Local = i == Client()->m_TranslationContext.m_aLocalClientId[Conn]; + ClientInfoObj.m_Team = ClientData.m_Team; + StrToInts(ClientInfoObj.m_aName, 4, m_aClients[i].m_aName); + StrToInts(ClientInfoObj.m_aClan, 3, m_aClients[i].m_aClan); + ClientInfoObj.m_Country = ClientData.m_Country; + + for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++) + { + StrToInts(ClientInfoObj.m_aaSkinPartNames[Part], 6, m_aClients[i].m_Sixup.m_aaSkinPartNames[Part]); + ClientInfoObj.m_aUseCustomColors[Part] = m_aClients[i].m_Sixup.m_aUseCustomColors[Part]; + ClientInfoObj.m_aSkinPartColors[Part] = m_aClients[i].m_Sixup.m_aSkinPartColors[Part]; + } + + mem_copy(pItem, &ClientInfoObj, sizeof(protocol7::CNetObj_De_ClientInfo)); + } + + // add tuning + CTuningParams StandardTuning; + if(mem_comp(&StandardTuning, &m_aTuning[Conn], sizeof(CTuningParams)) != 0) + { + void *pItem = Builder.NewItem(protocol7::NETOBJTYPE_DE_TUNEPARAMS, 0, sizeof(protocol7::CNetObj_De_TuneParams)); + if(!pItem) + return -2; + + protocol7::CNetObj_De_TuneParams TuneParams; + mem_copy(&TuneParams.m_aTuneParams, &m_aTuning[Conn], sizeof(TuneParams)); + mem_copy(pItem, &TuneParams, sizeof(protocol7::CNetObj_De_TuneParams)); + } + + // add game info + void *pItem = Builder.NewItem(protocol7::NETOBJTYPE_DE_GAMEINFO, 0, sizeof(protocol7::CNetObj_De_GameInfo)); + if(!pItem) + return -3; + + protocol7::CNetObj_De_GameInfo GameInfo; + + GameInfo.m_GameFlags = Client()->m_TranslationContext.m_GameFlags; + GameInfo.m_ScoreLimit = Client()->m_TranslationContext.m_ScoreLimit; + GameInfo.m_TimeLimit = Client()->m_TranslationContext.m_TimeLimit; + GameInfo.m_MatchNum = Client()->m_TranslationContext.m_MatchNum; + GameInfo.m_MatchCurrent = Client()->m_TranslationContext.m_MatchCurrent; + + mem_copy(pItem, &GameInfo, sizeof(protocol7::CNetObj_De_GameInfo)); + + return Builder.Finish(pTo); +}