Render skins in 0.7 demos

This commit is contained in:
ChillerDragon 2024-04-03 16:00:16 +08:00 committed by Dennis Felsing
parent be67622544
commit 8903f49716
13 changed files with 747 additions and 575 deletions

View file

@ -2427,6 +2427,7 @@ if(CLIENT)
render.h render.h
render_map.cpp render_map.cpp
sixup_translate_game.cpp sixup_translate_game.cpp
sixup_translate_snapshot.cpp
skin.h skin.h
ui.cpp ui.cpp
ui.h ui.h

View file

@ -14,7 +14,6 @@
#include <game/generated/protocol7.h> #include <game/generated/protocol7.h>
#include <engine/friends.h> #include <engine/friends.h>
#include <engine/shared/snapshot.h>
#include <functional> #include <functional>
#include <engine/client/enums.h> #include <engine/client/enums.h>
@ -374,6 +373,7 @@ public:
virtual const char *GetItemName(int Type) const = 0; virtual const char *GetItemName(int Type) const = 0;
virtual const char *Version() const = 0; virtual const char *Version() const = 0;
virtual const char *NetVersion() const = 0; virtual const char *NetVersion() const = 0;
virtual const char *NetVersion7() const = 0;
virtual int DDNetVersion() const = 0; virtual int DDNetVersion() const = 0;
virtual const char *DDNetVersionStr() const = 0; virtual const char *DDNetVersionStr() const = 0;
@ -388,9 +388,13 @@ public:
virtual protocol7::CNetObjHandler *GetNetObjHandler7() = 0; virtual protocol7::CNetObjHandler *GetNetObjHandler7() = 0;
virtual int ClientVersion7() const = 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(); extern IGameClient *CreateGameClient();
#endif #endif

View file

@ -1988,15 +1988,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket, int Conn, bool Dummy)
unsigned char aTmpTransSnapBuffer[CSnapshot::MAX_SIZE]; unsigned char aTmpTransSnapBuffer[CSnapshot::MAX_SIZE];
CSnapshot *pTmpTransSnapBuffer = (CSnapshot *)aTmpTransSnapBuffer; CSnapshot *pTmpTransSnapBuffer = (CSnapshot *)aTmpTransSnapBuffer;
mem_copy(pTmpTransSnapBuffer, pTmpBuffer3, CSnapshot::MAX_SIZE); mem_copy(pTmpTransSnapBuffer, pTmpBuffer3, CSnapshot::MAX_SIZE);
AltSnapSize = pTmpTransSnapBuffer->TranslateSevenToSix( AltSnapSize = GameClient()->TranslateSnap(pAltSnapBuffer, pTmpTransSnapBuffer, Conn, Dummy);
pAltSnapBuffer,
m_TranslationContext,
LocalTime(),
CClient::GameTick(g_Config.m_ClDummy),
Conn,
Dummy,
GameClient()->GetNetObjHandler7(),
GameClient()->GetNetObjHandler());
} }
else 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 // 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); 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 // add snapshot to demo
for(auto &DemoRecorder : m_aDemoRecorder) for(auto &DemoRecorder : m_aDemoRecorder)
{ {
if(DemoRecorder.IsRecording()) if(DemoRecorder.IsRecording())
{ {
// write snapshot // 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 // create a verified and unpacked snapshot
unsigned char aAltSnapBuffer[CSnapshot::MAX_SIZE]; unsigned char aAltSnapBuffer[CSnapshot::MAX_SIZE];
CSnapshot *pAltSnapBuffer = (CSnapshot *)aAltSnapBuffer; CSnapshot *pAltSnapBuffer = (CSnapshot *)aAltSnapBuffer;
const int AltSnapSize = UnpackAndValidateSnapshot((CSnapshot *)pData, pAltSnapBuffer); int AltSnapSize;
if(AltSnapSize < 0)
{
dbg_msg("client", "unpack snapshot and validate failed. error=%d", AltSnapSize);
return;
}
unsigned char aTmpTranslateBuffer[CSnapshot::MAX_SIZE];
CSnapshot *pTmpTranslateBuffer = nullptr;
int TranslatedSize = 0;
if(IsSixup()) if(IsSixup())
{ {
pTmpTranslateBuffer = (CSnapshot *)aTmpTranslateBuffer; AltSnapSize = GameClient()->TranslateSnap(pAltSnapBuffer, (CSnapshot *)pData, CONN_MAIN, false);
TranslatedSize = ((CSnapshot *)pData)->TranslateSevenToSix(pTmpTranslateBuffer, m_TranslationContext, LocalTime(), GameTick(g_Config.m_ClDummy), CONN_MAIN, false, GameClient()->GetNetObjHandler7(), GameClient()->GetNetObjHandler()); if(AltSnapSize < 0)
if(TranslatedSize < 0)
{ {
dbg_msg("sixup", "failed to translate snapshot. error=%d", TranslatedSize); dbg_msg("sixup", "failed to translate snapshot. error=%d", AltSnapSize);
pTmpTranslateBuffer = nullptr; return;
TranslatedSize = 0; }
}
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_pSnap, pData, Size);
mem_copy(m_aapSnapshots[0][SNAP_CURRENT]->m_pAltSnap, pAltSnapBuffer, AltSnapSize); 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(); 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); 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) if(State() != IClient::STATE_ONLINE)
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online"); m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "demorec/record", "client is not online");
else 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() void CClient::RaceRecord_Stop()

View file

@ -3375,7 +3375,20 @@ void CServer::DemoRecorder_HandleAutoStart()
str_timestamp(aTimestamp, sizeof(aTimestamp)); str_timestamp(aTimestamp, sizeof(aTimestamp));
char aFilename[IO_MAX_PATH_LENGTH]; char aFilename[IO_MAX_PATH_LENGTH];
str_format(aFilename, sizeof(aFilename), "demos/auto/server/%s_%s.demo", m_aCurrentMap, aTimestamp); 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) if(Config()->m_SvAutoDemoMax)
{ {
@ -3402,7 +3415,20 @@ void CServer::StartRecord(int ClientId)
{ {
char aFilename[IO_MAX_PATH_LENGTH]; char aFilename[IO_MAX_PATH_LENGTH];
str_format(aFilename, sizeof(aFilename), "demos/%s_%d_%d_tmp.demo", m_aCurrentMap, m_NetServer.Address().port, ClientId); 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_timestamp(aTimestamp, sizeof(aTimestamp));
str_format(aFilename, sizeof(aFilename), "demos/demo_%s.demo", 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) void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser)

View file

@ -316,6 +316,8 @@ void CDemoRecorder::RecordSnapshot(int Tick, const void *pData, int Size)
// create delta // create delta
char aDeltaData[CSnapshot::MAX_SIZE + sizeof(int)]; 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); const int DeltaSize = m_pSnapshotDelta->CreateDelta((CSnapshot *)m_aLastSnapshotData, (CSnapshot *)pData, &aDeltaData);
if(DeltaSize) if(DeltaSize)
{ {

View file

@ -45,7 +45,7 @@ public:
CDemoRecorder() {} CDemoRecorder() {}
~CDemoRecorder() override; ~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; int Stop(IDemoRecorder::EStopMode Mode, const char *pTargetFilename = "") override;
void AddDemoMarker(); void AddDemoMarker();

View file

@ -1,516 +1,26 @@
#include "compression.h"
#include "snapshot.h"
#include "uuid_manager.h"
#include <climits>
#include <cstdlib>
#include <base/math.h>
#include <base/system.h> #include <base/system.h>
#include <engine/shared/protocolglue.h> #include "snapshot.h"
#include <engine/shared/translation_context.h>
#include <game/generated/protocol.h> void CSnapshotBuilder::Init7(const CSnapshot *pSnapshot)
#include <game/generated/protocol7.h>
#include <game/generated/protocolglue.h>
#include <game/gamecore.h>
int CSnapshot::TranslateSevenToSix(
CSnapshot *pSixSnapDest,
CTranslationContext &TranslationContext,
float LocalTime,
int GameTick,
int Conn,
bool Dummy,
protocol7::CNetObjHandler *pNetObjHandler,
CNetObjHandler *pNetObjHandler6)
{ {
CSnapshotBuilder Builder; // the method is called Init7 because it is only used for 0.7 support
Builder.Init(); // but the snap we are building is a 0.6 snap
m_Sixup = false;
for(auto &PlayerInfosRace : TranslationContext.m_apPlayerInfosRace) if(pSnapshot->m_DataSize + sizeof(CSnapshot) + pSnapshot->m_NumItems * sizeof(int) * 2 > CSnapshot::MAX_SIZE || pSnapshot->m_NumItems > CSnapshot::MAX_ITEMS)
PlayerInfosRace = nullptr;
int SpectatorId = -3;
for(int i = 0; i < m_NumItems; i++)
{ {
const CSnapshotItem *pItem7 = (CSnapshotItem *)GetItem(i); // key and offset per item
const int Size = GetItemSize(i); dbg_assert(m_DataSize + sizeof(CSnapshot) + m_NumItems * sizeof(int) * 2 < CSnapshot::MAX_SIZE, "too much data");
const int ItemType = GetItemType(i); dbg_assert(m_NumItems < CSnapshot::MAX_ITEMS, "too many items");
dbg_msg("sixup", "demo recording failed on invalid snapshot");
// ddnet ex snap items m_DataSize = 0;
if((ItemType > __NETOBJTYPE_UUID_HELPER && ItemType < OFFSET_NETMSGTYPE_UUID) || pItem7->Type() == NETOBJTYPE_EX) m_NumItems = 0;
{ return;
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;
}
} }
// hack to put game info in the snap m_DataSize = pSnapshot->m_DataSize;
// even though in 0.7 we get it as a game message m_NumItems = pSnapshot->m_NumItems;
// this message has to be on the top mem_copy(m_aOffsets, pSnapshot->Offsets(), sizeof(int) * m_NumItems);
// the client looks at the items in order and needs the gameinfo at the top mem_copy(m_aData, pSnapshot->DataStart(), m_DataSize);
// 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);
} }

View file

@ -63,15 +63,6 @@ public:
unsigned Crc() const; unsigned Crc() const;
void DebugDump() 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; bool IsValid(size_t ActualSize) const;
static const CSnapshot *EmptySnapshot() { return &ms_EmptySnapshot; } static const CSnapshot *EmptySnapshot() { return &ms_EmptySnapshot; }
@ -171,12 +162,13 @@ class CSnapshotBuilder
int GetExtendedItemTypeIndex(int TypeId); int GetExtendedItemTypeIndex(int TypeId);
int GetTypeFromIndex(int Index) const; int GetTypeFromIndex(int Index) const;
bool m_Sixup; bool m_Sixup = false;
public: public:
CSnapshotBuilder(); CSnapshotBuilder();
void Init(bool Sixup = false); void Init(bool Sixup = false);
void Init7(const CSnapshot *pSnapshot);
void *NewItem(int Type, int Id, int Size); void *NewItem(int Type, int Id, int Size);

View file

@ -1,6 +1,7 @@
#ifndef ENGINE_SHARED_TRANSLATION_CONTEXT_H #ifndef ENGINE_SHARED_TRANSLATION_CONTEXT_H
#define ENGINE_SHARED_TRANSLATION_CONTEXT_H #define ENGINE_SHARED_TRANSLATION_CONTEXT_H
#include <base/system.h>
#include <engine/shared/protocol.h> #include <engine/shared/protocol.h>
#include <game/generated/protocol7.h> #include <game/generated/protocol7.h>
@ -98,6 +99,9 @@ public:
bool m_ShouldSendGameInfo; bool m_ShouldSendGameInfo;
int m_GameStateFlags7; int m_GameStateFlags7;
// 0.7 game flags
// use in combination with protocol7::GAMEFLAG_*
// for example protocol7::GAMEFLAG_TEAMS
int m_GameFlags; int m_GameFlags;
int m_ScoreLimit; int m_ScoreLimit;
int m_TimeLimit; int m_TimeLimit;

View file

@ -81,6 +81,7 @@ using namespace std::chrono_literals;
const char *CGameClient::Version() const { return GAME_VERSION; } const char *CGameClient::Version() const { return GAME_VERSION; }
const char *CGameClient::NetVersion() const { return GAME_NETVERSION; } const char *CGameClient::NetVersion() const { return GAME_NETVERSION; }
const char *CGameClient::NetVersion7() const { return GAME_NETVERSION7; }
int CGameClient::DDNetVersion() const { return DDNET_VERSION_NUMBER; } int CGameClient::DDNetVersion() const { return DDNET_VERSION_NUMBER; }
const char *CGameClient::DDNetVersionStr() const { return m_aDDNetVersionStr; } const char *CGameClient::DDNetVersionStr() const { return m_aDDNetVersionStr; }
int CGameClient::ClientVersion7() const { return CLIENT_VERSION7; } int CGameClient::ClientVersion7() const { return CLIENT_VERSION7; }
@ -2336,9 +2337,9 @@ void CGameClient::CClientData::Reset()
m_SkinColor = 0; m_SkinColor = 0;
for(int i = 0; i < protocol7::NUM_SKINPARTS; ++i) for(int i = 0; i < protocol7::NUM_SKINPARTS; ++i)
{ {
m_aaSkinPartNames[i][0] = '\0'; m_Sixup.m_aaSkinPartNames[i][0] = '\0';
m_aUseCustomColors[i] = 0; m_Sixup.m_aUseCustomColors[i] = 0;
m_aSkinPartColors[i] = 0; m_Sixup.m_aSkinPartColors[i] = 0;
} }
m_Team = 0; m_Team = 0;
m_Emoticon = 0; m_Emoticon = 0;
@ -2464,11 +2465,11 @@ bool CGameClient::GotWantedSkin7(bool Dummy)
for(int SkinPart = 0; SkinPart < protocol7::NUM_SKINPARTS; SkinPart++) 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; 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; 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; return false;
} }

View file

@ -365,12 +365,6 @@ public:
int m_Country; int m_Country;
char m_aSkinName[24]; char m_aSkinName[24];
int m_SkinColor; 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_Team;
int m_Emoticon; int m_Emoticon;
float m_EmoticonStartFraction; float m_EmoticonStartFraction;
@ -433,6 +427,17 @@ public:
void UpdateRenderInfo(bool IsTeamPlay); void UpdateRenderInfo(bool IsTeamPlay);
void Reset(); 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]; CClientData m_aClients[MAX_CLIENTS];
@ -493,7 +498,10 @@ public:
void OnStateChange(int NewState, int OldState) override; void OnStateChange(int NewState, int OldState) override;
template<typename T> template<typename T>
void ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId); 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); 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 OnMessage(int MsgId, CUnpacker *pUnpacker, int Conn, bool Dummy) override;
void InvalidateSnapshot() override; void InvalidateSnapshot() override;
void OnNewSnapshot() override; void OnNewSnapshot() override;
@ -522,6 +530,7 @@ public:
const char *GetItemName(int Type) const override; const char *GetItemName(int Type) const override;
const char *Version() const override; const char *Version() const override;
const char *NetVersion() const override; const char *NetVersion() const override;
const char *NetVersion7() const override;
int DDNetVersion() const override; int DDNetVersion() const override;
const char *DDNetVersionStr() const override; const char *DDNetVersionStr() const override;
virtual int ClientVersion7() const override; virtual int ClientVersion7() const override;

View file

@ -1,3 +1,7 @@
#include <base/system.h>
#include <engine/shared/protocol7.h>
#include <game/gamecore.h>
#include <game/generated/protocol7.h>
#include <game/localization.h> #include <game/localization.h>
#include <game/client/gameclient.h> #include <game/client/gameclient.h>
@ -81,12 +85,12 @@ void CGameClient::ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId)
char *apSkinPartsPtr[protocol7::NUM_SKINPARTS]; char *apSkinPartsPtr[protocol7::NUM_SKINPARTS];
for(int Part = 0; Part < protocol7::NUM_SKINPARTS; Part++) 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); 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_aaSkinPartNames[Part]; apSkinPartsPtr[Part] = pClient->m_Sixup.m_aaSkinPartNames[Part];
pClient->m_aUseCustomColors[Part] = pMsg->m_aUseCustomColors[Part]; pClient->m_Sixup.m_aUseCustomColors[Part] = pMsg->m_aUseCustomColors[Part];
pClient->m_aSkinPartColors[Part] = pMsg->m_aSkinPartColors[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) 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++) 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); 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_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 else
{ {
@ -112,14 +116,29 @@ void CGameClient::ApplySkin7InfoFromGameMsg(const T *pMsg, int ClientId)
} }
if(pClient->m_SkinInfo.m_Sixup.m_HatTexture.IsValid()) 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); 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); 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) void *CGameClient::TranslateGameMsg(int *pMsgId, CUnpacker *pUnpacker, int Conn)
{ {
if(!m_pClient->IsSixup()) if(!m_pClient->IsSixup())

View file

@ -0,0 +1,564 @@
#include <engine/shared/protocolglue.h>
#include <engine/shared/snapshot.h>
#include <engine/shared/translation_context.h>
#include <game/client/gameclient.h>
#include <game/generated/protocol7.h>
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);
}