Replace projectile hack with a new extended snapshot object

Still send the old hack to old clients, still accept the old hack from
old servers. This is so F-Client can support DDNet servers without such
hacks.
This commit is contained in:
heinrich5991 2021-01-17 17:18:08 +01:00
parent 9784726683
commit 43998ce41a
17 changed files with 258 additions and 208 deletions

View file

@ -1561,8 +1561,6 @@ set_src(GAME_SHARED GLOB src/game
collision.cpp collision.cpp
collision.h collision.h
ddracecommands.h ddracecommands.h
extrainfo.cpp
extrainfo.h
gamecore.cpp gamecore.cpp
gamecore.h gamecore.h
layers.cpp layers.cpp
@ -1779,6 +1777,8 @@ if(CLIENT)
prediction/entity.h prediction/entity.h
prediction/gameworld.cpp prediction/gameworld.cpp
prediction/gameworld.h prediction/gameworld.h
projectile_data.cpp
projectile_data.h
race.cpp race.cpp
race.h race.h
render.cpp render.cpp

View file

@ -24,6 +24,10 @@ GameInfoFlags2 = [
"ALLOW_X_SKINS", "GAMETYPE_CITY", "GAMETYPE_FDDRACE", "ENTITIES_FDDRACE", "ALLOW_X_SKINS", "GAMETYPE_CITY", "GAMETYPE_FDDRACE", "ENTITIES_FDDRACE",
] ]
ExPlayerFlags = ["AFK", "PAUSED", "SPEC"] ExPlayerFlags = ["AFK", "PAUSED", "SPEC"]
ProjectileFlags = ["CLIENTID_BIT{}".format(i) for i in range(8)] + [
"NO_OWNER", "IS_DDNET", "BOUNCE_HORIZONTAL", "BOUNCE_VERTICAL",
"EXPLOSIVE", "FREEZE",
]
Emoticons = ["OOP", "EXCLAMATION", "HEARTS", "DROP", "DOTDOT", "MUSIC", "SORRY", "GHOST", "SUSHI", "SPLATTEE", "DEVILTEE", "ZOMG", "ZZZ", "WTF", "EYES", "QUESTION"] Emoticons = ["OOP", "EXCLAMATION", "HEARTS", "DROP", "DOTDOT", "MUSIC", "SORRY", "GHOST", "SUSHI", "SPLATTEE", "DEVILTEE", "ZOMG", "ZZZ", "WTF", "EYES", "QUESTION"]
@ -80,6 +84,7 @@ Flags = [
Flags("GAMEINFOFLAG", GameInfoFlags), Flags("GAMEINFOFLAG", GameInfoFlags),
Flags("GAMEINFOFLAG2", GameInfoFlags2), Flags("GAMEINFOFLAG2", GameInfoFlags2),
Flags("EXPLAYERFLAG", ExPlayerFlags), Flags("EXPLAYERFLAG", ExPlayerFlags),
Flags("PROJECTILEFLAG", ProjectileFlags),
] ]
Objects = [ Objects = [
@ -244,6 +249,17 @@ Objects = [
NetIntAny("m_Flags2"), NetIntAny("m_Flags2"),
], validate_size=False), ], validate_size=False),
# The code assumes that this has the same in-memory representation as
# the Projectile net object.
NetObjectEx("DDNetProjectile", "projectile@netobj.ddnet.tw", [
NetIntAny("m_X"),
NetIntAny("m_Y"),
NetIntAny("m_Angle"),
NetIntAny("m_Data"),
NetIntRange("m_Type", 0, 'NUM_WEAPONS-1'),
NetTick("m_StartTick"),
]),
## Events ## Events
NetEvent("Common", [ NetEvent("Common", [

View file

@ -261,5 +261,7 @@ public:
virtual bool IsDisplayingWarning() = 0; virtual bool IsDisplayingWarning() = 0;
}; };
void SnapshotRemoveExtraProjectileInfo(unsigned char *pData);
extern IGameClient *CreateGameClient(); extern IGameClient *CreateGameClient();
#endif #endif

View file

@ -50,7 +50,6 @@
#include <engine/shared/snapshot.h> #include <engine/shared/snapshot.h>
#include <engine/shared/uuid_manager.h> #include <engine/shared/uuid_manager.h>
#include <game/extrainfo.h>
#include <game/version.h> #include <game/version.h>
#include <mastersrv/mastersrv.h> #include <mastersrv/mastersrv.h>
@ -2025,7 +2024,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket)
// 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
unsigned char aExtraInfoRemoved[CSnapshot::MAX_SIZE]; unsigned char aExtraInfoRemoved[CSnapshot::MAX_SIZE];
mem_copy(aExtraInfoRemoved, pTmpBuffer3, SnapSize); mem_copy(aExtraInfoRemoved, pTmpBuffer3, SnapSize);
SnapshotRemoveExtraInfo(aExtraInfoRemoved); SnapshotRemoveExtraProjectileInfo(aExtraInfoRemoved);
// add snapshot to demo // add snapshot to demo
for(auto &DemoRecorder : m_DemoRecorder) for(auto &DemoRecorder : m_DemoRecorder)

View file

@ -32,7 +32,6 @@
// DDRace // DDRace
#include <engine/shared/linereader.h> #include <engine/shared/linereader.h>
#include <game/extrainfo.h>
#include <vector> #include <vector>
#include <zlib.h> #include <zlib.h>
@ -842,12 +841,8 @@ void CServer::DoSnapshot()
GameServer()->OnSnap(-1); GameServer()->OnSnap(-1);
SnapshotSize = m_SnapshotBuilder.Finish(aData); SnapshotSize = m_SnapshotBuilder.Finish(aData);
// for antiping: if the projectile netobjects contains extra data, this is removed and the original content restored before recording demo
unsigned char aExtraInfoRemoved[CSnapshot::MAX_SIZE];
mem_copy(aExtraInfoRemoved, aData, SnapshotSize);
SnapshotRemoveExtraInfo(aExtraInfoRemoved);
// write snapshot // write snapshot
m_aDemoRecorder[MAX_CLIENTS].RecordSnapshot(Tick(), aExtraInfoRemoved, SnapshotSize); m_aDemoRecorder[MAX_CLIENTS].RecordSnapshot(Tick(), aData, SnapshotSize);
} }
// create snapshots for all clients // create snapshots for all clients
@ -887,12 +882,8 @@ void CServer::DoSnapshot()
if(m_aDemoRecorder[i].IsRecording()) if(m_aDemoRecorder[i].IsRecording())
{ {
// for antiping: if the projectile netobjects contains extra data, this is removed and the original content restored before recording demo
unsigned char aExtraInfoRemoved[CSnapshot::MAX_SIZE];
mem_copy(aExtraInfoRemoved, aData, SnapshotSize);
SnapshotRemoveExtraInfo(aExtraInfoRemoved);
// write snapshot // write snapshot
m_aDemoRecorder[i].RecordSnapshot(Tick(), aExtraInfoRemoved, SnapshotSize); m_aDemoRecorder[i].RecordSnapshot(Tick(), aData, SnapshotSize);
} }
Crc = pData->Crc(); Crc = pData->Crc();

View file

@ -8,9 +8,9 @@
#include <game/generated/protocol.h> #include <game/generated/protocol.h>
#include <game/client/gameclient.h> #include <game/client/gameclient.h>
#include <game/client/projectile_data.h>
#include <game/client/render.h> #include <game/client/render.h>
#include <game/client/ui.h> #include <game/client/ui.h>
#include <game/extrainfo.h>
#include <game/client/components/effects.h> #include <game/client/components/effects.h>
#include <game/client/components/flow.h> #include <game/client/components/flow.h>
@ -22,7 +22,7 @@ void CItems::OnReset()
m_NumExtraProjectiles = 0; m_NumExtraProjectiles = 0;
} }
void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID) void CItems::RenderProjectile(const CProjectileData *pCurrent, int ItemID)
{ {
int CurWeapon = clamp(pCurrent->m_Type, 0, NUM_WEAPONS - 1); int CurWeapon = clamp(pCurrent->m_Type, 0, NUM_WEAPONS - 1);
@ -62,20 +62,12 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID)
if(Ct < 0) if(Ct < 0)
return; // projectile haven't been shot yet return; // projectile haven't been shot yet
vec2 StartPos; vec2 Pos = CalcPos(pCurrent->m_StartPos, pCurrent->m_StartVel, Curvature, Speed, Ct);
vec2 StartVel; vec2 PrevPos = CalcPos(pCurrent->m_StartPos, pCurrent->m_StartVel, Curvature, Speed, Ct - 0.001f);
ExtractInfo(pCurrent, &StartPos, &StartVel);
vec2 Pos = CalcPos(StartPos, StartVel, Curvature, Speed, Ct);
vec2 PrevPos = CalcPos(StartPos, StartVel, Curvature, Speed, Ct - 0.001f);
float Alpha = 1.f; float Alpha = 1.f;
if(UseExtraInfo(pCurrent)) if(pCurrent->m_ExtraInfo && pCurrent->m_Owner >= 0 && m_pClient->IsOtherTeam(pCurrent->m_Owner))
{ {
int Owner;
ExtractExtraInfo(pCurrent, &Owner, 0, 0, 0);
if(Owner >= 0 && m_pClient->IsOtherTeam(Owner))
Alpha = g_Config.m_ClShowOthersAlpha / 100.0f; Alpha = g_Config.m_ClShowOthersAlpha / 100.0f;
} }
@ -310,11 +302,7 @@ void CItems::OnRender()
{ {
for(auto *pProj = (CProjectile *)GameClient()->m_PredictedWorld.FindFirst(CGameWorld::ENTTYPE_PROJECTILE); pProj; pProj = (CProjectile *)pProj->NextEntity()) for(auto *pProj = (CProjectile *)GameClient()->m_PredictedWorld.FindFirst(CGameWorld::ENTTYPE_PROJECTILE); pProj; pProj = (CProjectile *)pProj->NextEntity())
{ {
CNetObj_Projectile Data; CProjectileData Data = pProj->GetData();
if(pProj->m_Type != WEAPON_SHOTGUN || pProj->m_Explosive || pProj->m_Freeze)
pProj->FillExtraInfo(&Data);
else
pProj->FillInfo(&Data);
RenderProjectile(&Data, pProj->ID()); RenderProjectile(&Data, pProj->ID());
} }
for(auto *pLaser = (CLaser *)GameClient()->m_PredictedWorld.FindFirst(CGameWorld::ENTTYPE_LASER); pLaser; pLaser = (CLaser *)pLaser->NextEntity()) for(auto *pLaser = (CLaser *)GameClient()->m_PredictedWorld.FindFirst(CGameWorld::ENTTYPE_LASER); pLaser; pLaser = (CLaser *)pLaser->NextEntity())
@ -343,8 +331,17 @@ void CItems::OnRender()
IClient::CSnapItem Item; IClient::CSnapItem Item;
const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item); const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, i, &Item);
if(Item.m_Type == NETOBJTYPE_PROJECTILE || Item.m_Type == NETOBJTYPE_DDNETPROJECTILE)
{
CProjectileData Data;
if(Item.m_Type == NETOBJTYPE_PROJECTILE) if(Item.m_Type == NETOBJTYPE_PROJECTILE)
{ {
Data = ExtractProjectileInfo((const CNetObj_Projectile *)pData);
}
else
{
Data = ExtractProjectileInfoDDNet((const CNetObj_DDNetProjectile *)pData);
}
if(UsePredicted) if(UsePredicted)
{ {
if(auto *pProj = (CProjectile *)GameClient()->m_GameWorld.FindMatch(Item.m_ID, Item.m_Type, pData)) if(auto *pProj = (CProjectile *)GameClient()->m_GameWorld.FindMatch(Item.m_ID, Item.m_Type, pData))
@ -354,13 +351,13 @@ void CItems::OnRender()
&& (pProj->GetOwner() < 0 || !GameClient()->m_aClients[pProj->GetOwner()].m_IsPredictedLocal) // skip locally predicted projectiles && (pProj->GetOwner() < 0 || !GameClient()->m_aClients[pProj->GetOwner()].m_IsPredictedLocal) // skip locally predicted projectiles
&& !Client()->SnapFindItem(IClient::SNAP_PREV, Item.m_Type, Item.m_ID)) && !Client()->SnapFindItem(IClient::SNAP_PREV, Item.m_Type, Item.m_ID))
{ {
ReconstructSmokeTrail((const CNetObj_Projectile *)pData, Item.m_ID, pProj->m_DestroyTick); ReconstructSmokeTrail(&Data, pProj->m_DestroyTick);
} }
pProj->m_LastRenderTick = Client()->GameTick(g_Config.m_ClDummy); pProj->m_LastRenderTick = Client()->GameTick(g_Config.m_ClDummy);
continue; continue;
} }
} }
RenderProjectile((const CNetObj_Projectile *)pData, Item.m_ID); RenderProjectile(&Data, Item.m_ID);
} }
else if(Item.m_Type == NETOBJTYPE_PICKUP) else if(Item.m_Type == NETOBJTYPE_PICKUP)
{ {
@ -409,7 +406,10 @@ void CItems::OnRender()
m_NumExtraProjectiles--; m_NumExtraProjectiles--;
} }
else if(!UsePredicted) else if(!UsePredicted)
RenderProjectile(&m_aExtraProjectiles[i], 0); {
CProjectileData Data = ExtractProjectileInfo(&m_aExtraProjectiles[i]);
RenderProjectile(&Data, 0);
}
} }
Graphics()->QuadsSetRotation(0); Graphics()->QuadsSetRotation(0);
@ -484,7 +484,7 @@ void CItems::AddExtraProjectile(CNetObj_Projectile *pProj)
} }
} }
void CItems::ReconstructSmokeTrail(const CNetObj_Projectile *pCurrent, int ItemID, int DestroyTick) void CItems::ReconstructSmokeTrail(const CProjectileData *pCurrent, int DestroyTick)
{ {
bool LocalPlayerInGame = false; bool LocalPlayerInGame = false;
@ -520,17 +520,9 @@ void CItems::ReconstructSmokeTrail(const CNetObj_Projectile *pCurrent, int ItemI
float Gt = (Client()->PrevGameTick(g_Config.m_ClDummy) - pCurrent->m_StartTick) / (float)SERVER_TICK_SPEED + Client()->GameTickTime(g_Config.m_ClDummy); float Gt = (Client()->PrevGameTick(g_Config.m_ClDummy) - pCurrent->m_StartTick) / (float)SERVER_TICK_SPEED + Client()->GameTickTime(g_Config.m_ClDummy);
vec2 StartPos;
vec2 StartVel;
ExtractInfo(pCurrent, &StartPos, &StartVel);
float Alpha = 1.f; float Alpha = 1.f;
if(UseExtraInfo(pCurrent)) if(pCurrent->m_ExtraInfo && pCurrent->m_Owner >= 0 && m_pClient->IsOtherTeam(pCurrent->m_Owner))
{ {
int Owner;
ExtractExtraInfo(pCurrent, &Owner, 0, 0, 0);
if(Owner >= 0 && m_pClient->IsOtherTeam(Owner))
Alpha = g_Config.m_ClShowOthersAlpha / 100.0f; Alpha = g_Config.m_ClShowOthersAlpha / 100.0f;
} }
@ -543,8 +535,8 @@ void CItems::ReconstructSmokeTrail(const CNetObj_Projectile *pCurrent, int ItemI
for(int i = 1 + (int)(Gt / Step); i < (int)(T / Step); i++) for(int i = 1 + (int)(Gt / Step); i < (int)(T / Step); i++)
{ {
float t = Step * (float)i + 0.4f * Step * (frandom() - 0.5f); float t = Step * (float)i + 0.4f * Step * (frandom() - 0.5f);
vec2 Pos = CalcPos(StartPos, StartVel, Curvature, Speed, t); vec2 Pos = CalcPos(pCurrent->m_StartPos, pCurrent->m_StartVel, Curvature, Speed, t);
vec2 PrevPos = CalcPos(StartPos, StartVel, Curvature, Speed, t - 0.001f); vec2 PrevPos = CalcPos(pCurrent->m_StartPos, pCurrent->m_StartVel, Curvature, Speed, t - 0.001f);
vec2 Vel = Pos - PrevPos; vec2 Vel = Pos - PrevPos;
float TimePassed = Pt - t; float TimePassed = Pt - t;
if(Pt - MinTrailSpan > 0.01f) if(Pt - MinTrailSpan > 0.01f)

View file

@ -4,6 +4,8 @@
#define GAME_CLIENT_COMPONENTS_ITEMS_H #define GAME_CLIENT_COMPONENTS_ITEMS_H
#include <game/client/component.h> #include <game/client/component.h>
class CProjectileData;
class CItems : public CComponent class CItems : public CComponent
{ {
enum enum
@ -14,7 +16,7 @@ class CItems : public CComponent
CNetObj_Projectile m_aExtraProjectiles[MAX_EXTRA_PROJECTILES]; CNetObj_Projectile m_aExtraProjectiles[MAX_EXTRA_PROJECTILES];
int m_NumExtraProjectiles; int m_NumExtraProjectiles;
void RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID); void RenderProjectile(const CProjectileData *pCurrent, int ItemID);
void RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent, bool IsPredicted = false); void RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCurrent, bool IsPredicted = false);
void RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent, const CNetObj_GameData *pPrevGameData, const CNetObj_GameData *pCurGameData); void RenderFlag(const CNetObj_Flag *pPrev, const CNetObj_Flag *pCurrent, const CNetObj_GameData *pPrevGameData, const CNetObj_GameData *pCurGameData);
void RenderLaser(const struct CNetObj_Laser *pCurrent, bool IsPredicted = false); void RenderLaser(const struct CNetObj_Laser *pCurrent, bool IsPredicted = false);
@ -28,7 +30,7 @@ public:
void AddExtraProjectile(CNetObj_Projectile *pProj); void AddExtraProjectile(CNetObj_Projectile *pProj);
void ReconstructSmokeTrail(const CNetObj_Projectile *pCurrent, int ItemID, int DestroyTick); void ReconstructSmokeTrail(const CProjectileData *pCurrent, int DestroyTick);
}; };
#endif #endif

View file

@ -26,7 +26,6 @@
#include "race.h" #include "race.h"
#include "render.h" #include "render.h"
#include <game/extrainfo.h>
#include <game/localization.h> #include <game/localization.h>
#include <game/version.h> #include <game/version.h>

View file

@ -1,6 +1,7 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "projectile.h" #include "projectile.h"
#include <game/client/projectile_data.h>
#include <game/generated/protocol.h> #include <game/generated/protocol.h>
#include <engine/shared/config.h> #include <engine/shared/config.h>
@ -150,16 +151,23 @@ void CProjectile::SetBouncing(int Value)
m_Bouncing = Value; m_Bouncing = Value;
} }
CProjectile::CProjectile(CGameWorld *pGameWorld, int ID, CNetObj_Projectile *pProj) : CProjectile::CProjectile(CGameWorld *pGameWorld, int ID, CProjectileData *pProj) :
CEntity(pGameWorld, CGameWorld::ENTTYPE_PROJECTILE) CEntity(pGameWorld, CGameWorld::ENTTYPE_PROJECTILE)
{ {
ExtractInfo(pProj, &m_Pos, &m_Direction); m_Pos = pProj->m_StartPos;
if(UseExtraInfo(pProj)) m_Direction = pProj->m_StartVel;
ExtractExtraInfo(pProj, &m_Owner, &m_Explosive, &m_Bouncing, &m_Freeze); if(pProj->m_ExtraInfo)
{
m_Owner = pProj->m_Owner;
m_Explosive = pProj->m_Explosive;
m_Bouncing = pProj->m_Bouncing;
m_Freeze = pProj->m_Freeze;
}
else else
{ {
m_Owner = -1; m_Owner = -1;
m_Bouncing = m_Freeze = 0; m_Bouncing = 0;
m_Freeze = 0;
m_Explosive = (pProj->m_Type == WEAPON_GRENADE) && (fabs(1.0f - length(m_Direction)) < 0.015f); m_Explosive = (pProj->m_Type == WEAPON_GRENADE) && (fabs(1.0f - length(m_Direction)) < 0.015f);
} }
m_Type = pProj->m_Type; m_Type = pProj->m_Type;
@ -181,45 +189,19 @@ CProjectile::CProjectile(CGameWorld *pGameWorld, int ID, CNetObj_Projectile *pPr
m_ID = ID; m_ID = ID;
} }
void CProjectile::FillInfo(CNetObj_Projectile *pProj) CProjectileData CProjectile::GetData() const
{ {
pProj->m_X = (int)m_Pos.x; CProjectileData Result;
pProj->m_Y = (int)m_Pos.y; Result.m_StartPos = m_Pos;
pProj->m_VelX = (int)(m_Direction.x * 100.0f); Result.m_StartVel = m_Direction;
pProj->m_VelY = (int)(m_Direction.y * 100.0f); Result.m_Type = m_Type;
pProj->m_StartTick = m_StartTick; Result.m_StartTick = m_StartTick;
pProj->m_Type = m_Type; Result.m_ExtraInfo = true;
} Result.m_Owner = m_Owner;
Result.m_Explosive = m_Explosive;
void CProjectile::FillExtraInfo(CNetObj_Projectile *pProj) Result.m_Bouncing = m_Bouncing;
{ Result.m_Freeze = m_Freeze;
const int MaxPos = 0x7fffffff / 100; return Result;
if(abs((int)m_Pos.y) + 1 >= MaxPos || abs((int)m_Pos.x) + 1 >= MaxPos)
{
//If the modified data would be too large to fit in an integer, send normal data instead
FillInfo(pProj);
return;
}
//Send additional/modified info, by modifiying the fields of the netobj
float Angle = -atan2f(m_Direction.x, m_Direction.y);
int Data = 0;
Data |= (abs(m_Owner) & 255) << 0;
if(m_Owner < 0)
Data |= 1 << 8;
Data |= 1 << 9; //This bit tells the client to use the extra info
Data |= (m_Bouncing & 3) << 10;
if(m_Explosive)
Data |= 1 << 12;
if(m_Freeze)
Data |= 1 << 13;
pProj->m_X = (int)(m_Pos.x * 100.0f);
pProj->m_Y = (int)(m_Pos.y * 100.0f);
pProj->m_VelX = (int)(Angle * 1000000.0f);
pProj->m_VelY = Data;
pProj->m_StartTick = m_StartTick;
pProj->m_Type = m_Type;
} }
bool CProjectile::Match(CProjectile *pProj) bool CProjectile::Match(CProjectile *pProj)

View file

@ -5,7 +5,8 @@
#include "character.h" #include "character.h"
#include <game/client/prediction/entity.h> #include <game/client/prediction/entity.h>
#include <game/extrainfo.h>
class CProjectileData;
class CProjectile : public CEntity class CProjectile : public CEntity
{ {
@ -28,18 +29,17 @@ public:
int Number = 0); int Number = 0);
vec2 GetPos(float Time); vec2 GetPos(float Time);
void FillInfo(CNetObj_Projectile *pProj); CProjectileData GetData() const;
virtual void Tick(); virtual void Tick();
bool Match(CProjectile *pProj); bool Match(CProjectile *pProj);
void SetBouncing(int Value); void SetBouncing(int Value);
void FillExtraInfo(CNetObj_Projectile *pProj);
const vec2 &GetDirection() { return m_Direction; } const vec2 &GetDirection() { return m_Direction; }
const int &GetOwner() { return m_Owner; } const int &GetOwner() { return m_Owner; }
const int &GetStartTick() { return m_StartTick; } const int &GetStartTick() { return m_StartTick; }
CProjectile(CGameWorld *pGameWorld, int ID, CNetObj_Projectile *pProj); CProjectile(CGameWorld *pGameWorld, int ID, CProjectileData *pProj);
private: private:
vec2 m_Direction; vec2 m_Direction;

View file

@ -9,6 +9,7 @@
#include "entity.h" #include "entity.h"
#include <algorithm> #include <algorithm>
#include <engine/shared/config.h> #include <engine/shared/config.h>
#include <game/client/projectile_data.h>
#include <utility> #include <utility>
////////////////////////////////////////////////// //////////////////////////////////////////////////
@ -368,9 +369,18 @@ void CGameWorld::NetCharAdd(int ObjID, CNetObj_Character *pCharObj, CNetObj_DDNe
void CGameWorld::NetObjAdd(int ObjID, int ObjType, const void *pObjData) void CGameWorld::NetObjAdd(int ObjID, int ObjType, const void *pObjData)
{ {
if(ObjType == NETOBJTYPE_PROJECTILE && m_WorldConfig.m_PredictWeapons) if((ObjType == NETOBJTYPE_PROJECTILE || ObjType == NETOBJTYPE_DDNETPROJECTILE) && m_WorldConfig.m_PredictWeapons)
{ {
CProjectile NetProj = CProjectile(this, ObjID, (CNetObj_Projectile *)pObjData); CProjectileData Data;
if(ObjType == NETOBJTYPE_PROJECTILE)
{
Data = ExtractProjectileInfo((const CNetObj_Projectile *)pObjData);
}
else
{
Data = ExtractProjectileInfoDDNet((const CNetObj_DDNetProjectile *)pObjData);
}
CProjectile NetProj = CProjectile(this, ObjID, &Data);
if(NetProj.m_Type != WEAPON_SHOTGUN && fabs(length(NetProj.m_Direction) - 1.f) > 0.02f) // workaround to skip grenades on ball mod if(NetProj.m_Type != WEAPON_SHOTGUN && fabs(length(NetProj.m_Direction) - 1.f) > 0.02f) // workaround to skip grenades on ball mod
return; return;
@ -385,7 +395,7 @@ void CGameWorld::NetObjAdd(int ObjID, int ObjType, const void *pObjData)
return; return;
} }
} }
if(!UseExtraInfo((CNetObj_Projectile *)pObjData)) if(!Data.m_ExtraInfo)
{ {
// try to match the newly received (unrecognized) projectile with a locally fired one // try to match the newly received (unrecognized) projectile with a locally fired one
for(CProjectile *pProj = (CProjectile *)FindFirst(CGameWorld::ENTTYPE_PROJECTILE); pProj; pProj = (CProjectile *)pProj->TypeNext()) for(CProjectile *pProj = (CProjectile *)FindFirst(CGameWorld::ENTTYPE_PROJECTILE); pProj; pProj = (CProjectile *)pProj->TypeNext())
@ -570,7 +580,25 @@ CEntity *CGameWorld::FindMatch(int ObjID, int ObjType, const void *pObjData)
switch(ObjType) switch(ObjType)
{ {
case NETOBJTYPE_CHARACTER: FindType(ENTTYPE_CHARACTER, CCharacter, CNetObj_Character); case NETOBJTYPE_CHARACTER: FindType(ENTTYPE_CHARACTER, CCharacter, CNetObj_Character);
case NETOBJTYPE_PROJECTILE: FindType(ENTTYPE_PROJECTILE, CProjectile, CNetObj_Projectile); case NETOBJTYPE_PROJECTILE:
case NETOBJTYPE_DDNETPROJECTILE:
{
CProjectileData Data;
if(ObjType == NETOBJTYPE_PROJECTILE)
{
Data = ExtractProjectileInfo((const CNetObj_Projectile *)pObjData);
}
else
{
Data = ExtractProjectileInfoDDNet((const CNetObj_DDNetProjectile *)pObjData);
}
CProjectile *pEnt = (CProjectile *)GetEntity(ObjID, ENTTYPE_PROJECTILE);
if(pEnt && CProjectile(this, ObjID, &Data).Match(pEnt))
{
return pEnt;
}
return 0;
}
case NETOBJTYPE_LASER: FindType(ENTTYPE_LASER, CLaser, CNetObj_Laser); case NETOBJTYPE_LASER: FindType(ENTTYPE_LASER, CLaser, CNetObj_Laser);
case NETOBJTYPE_PICKUP: FindType(ENTTYPE_PICKUP, CPickup, CNetObj_Pickup); case NETOBJTYPE_PICKUP: FindType(ENTTYPE_PICKUP, CPickup, CNetObj_Pickup);
} }

View file

@ -0,0 +1,80 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "projectile_data.h"
#include <base/math.h>
#include <engine/shared/snapshot.h>
#include <game/generated/protocol.h>
bool UseProjectileExtraInfo(const CNetObj_Projectile *pProj)
{
return pProj->m_VelY >= 0 && (pProj->m_VelY & PROJECTILEFLAG_IS_DDNET) != 0;
}
CProjectileData ExtractProjectileInfo(const CNetObj_Projectile *pProj)
{
if(UseProjectileExtraInfo(pProj))
{
CNetObj_DDNetProjectile Proj;
mem_copy(&Proj, pProj, sizeof(Proj));
return ExtractProjectileInfoDDNet(&Proj);
}
CProjectileData Result = {vec2(0, 0)};
Result.m_StartPos.x = pProj->m_X;
Result.m_StartPos.y = pProj->m_Y;
Result.m_StartVel.x = pProj->m_VelX / 100.0f;
Result.m_StartVel.y = pProj->m_VelY / 100.0f;
Result.m_Type = pProj->m_Type;
Result.m_StartTick = pProj->m_StartTick;
Result.m_ExtraInfo = false;
Result.m_Owner = -1;
return Result;
}
CProjectileData ExtractProjectileInfoDDNet(const CNetObj_DDNetProjectile *pProj)
{
CProjectileData Result = {vec2(0, 0)};
Result.m_StartPos.x = pProj->m_X / 100.0f;
Result.m_StartPos.y = pProj->m_Y / 100.0f;
float Angle = pProj->m_Angle / 1000000.0f;
Result.m_StartVel.x = sin(-Angle);
Result.m_StartVel.y = cos(-Angle);
Result.m_Type = pProj->m_Type;
Result.m_StartTick = pProj->m_StartTick;
Result.m_ExtraInfo = true;
Result.m_Owner = pProj->m_Data & 255;
if((pProj->m_Data & PROJECTILEFLAG_NO_OWNER) & 1)
{
Result.m_Owner = -1;
}
// PROJECTILEFLAG_BOUNCE_HORIZONTAL, PROJECTILEFLAG_BOUNCE_VERTICAL
Result.m_Bouncing = (pProj->m_Data >> 10) & 3;
Result.m_Explosive = pProj->m_Data & PROJECTILEFLAG_EXPLOSIVE;
Result.m_Freeze = pProj->m_Data & PROJECTILEFLAG_FREEZE;
return Result;
}
void SnapshotRemoveExtraProjectileInfo(unsigned char *pData)
{
CSnapshot *pSnap = (CSnapshot *)pData;
for(int Index = 0; Index < pSnap->NumItems(); Index++)
{
CSnapshotItem *pItem = pSnap->GetItem(Index);
if(pItem->Type() == NETOBJTYPE_PROJECTILE)
{
CNetObj_Projectile *pProj = (CNetObj_Projectile *)((void *)pItem->Data());
if(UseProjectileExtraInfo(pProj))
{
CProjectileData Data = ExtractProjectileInfo(pProj);
pProj->m_X = Data.m_StartPos.x;
pProj->m_Y = Data.m_StartPos.y;
pProj->m_VelX = (int)(Data.m_StartVel.x * 100.0f);
pProj->m_VelY = (int)(Data.m_StartVel.y * 100.0f);
}
}
}
}

View file

@ -0,0 +1,29 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_CLIENT_PROJECTILE_DATA_H
#define GAME_CLIENT_PROJECTILE_DATA_H
#include <base/vmath.h>
struct CNetObj_Projectile;
struct CNetObj_DDNetProjectile;
class CProjectileData
{
public:
vec2 m_StartPos;
vec2 m_StartVel;
int m_Type;
int m_StartTick;
bool m_ExtraInfo;
// The rest is only set if m_ExtraInfo is true.
int m_Owner;
bool m_Explosive;
int m_Bouncing;
bool m_Freeze;
};
CProjectileData ExtractProjectileInfo(const CNetObj_Projectile *pProj);
CProjectileData ExtractProjectileInfoDDNet(const CNetObj_DDNetProjectile *pProj);
#endif // GAME_CLIENT_PROJECTILE_DATA_H

View file

@ -1,72 +0,0 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "extrainfo.h"
#include <base/math.h>
#include <engine/shared/snapshot.h>
#include <game/generated/protocol.h>
bool UseExtraInfo(const CNetObj_Projectile *pProj)
{
bool ExtraInfoFlag = ((abs(pProj->m_VelY) & (1 << 9)) != 0);
return ExtraInfoFlag;
}
void ExtractInfo(const CNetObj_Projectile *pProj, vec2 *StartPos, vec2 *StartVel)
{
if(!UseExtraInfo(pProj))
{
StartPos->x = pProj->m_X;
StartPos->y = pProj->m_Y;
StartVel->x = pProj->m_VelX / 100.0f;
StartVel->y = pProj->m_VelY / 100.0f;
}
else
{
StartPos->x = pProj->m_X / 100.0f;
StartPos->y = pProj->m_Y / 100.0f;
float Angle = pProj->m_VelX / 1000000.0f;
StartVel->x = sin(-Angle);
StartVel->y = cos(-Angle);
}
}
void ExtractExtraInfo(const CNetObj_Projectile *pProj, int *Owner, bool *Explosive, int *Bouncing, bool *Freeze)
{
int Data = pProj->m_VelY;
if(Owner)
{
*Owner = Data & 255;
if((Data >> 8) & 1)
*Owner = -(*Owner);
}
if(Bouncing)
*Bouncing = (Data >> 10) & 3;
if(Explosive)
*Explosive = (Data >> 12) & 1;
if(Freeze)
*Freeze = (Data >> 13) & 1;
}
void SnapshotRemoveExtraInfo(unsigned char *pData)
{
CSnapshot *pSnap = (CSnapshot *)pData;
for(int Index = 0; Index < pSnap->NumItems(); Index++)
{
CSnapshotItem *pItem = pSnap->GetItem(Index);
if(pItem->Type() == NETOBJTYPE_PROJECTILE)
{
CNetObj_Projectile *pProj = (CNetObj_Projectile *)((void *)pItem->Data());
if(UseExtraInfo(pProj))
{
vec2 Pos;
vec2 Vel;
ExtractInfo(pProj, &Pos, &Vel);
pProj->m_X = Pos.x;
pProj->m_Y = Pos.y;
pProj->m_VelX = (int)(Vel.x * 100.0f);
pProj->m_VelY = (int)(Vel.y * 100.0f);
}
}
}
}

View file

@ -1,15 +0,0 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#ifndef GAME_EXTRAINFO_H
#define GAME_EXTRAINFO_H
#include <game/generated/protocol.h>
#include <base/vmath.h>
bool UseExtraInfo(const CNetObj_Projectile *pProj);
void ExtractInfo(const CNetObj_Projectile *pProj, vec2 *StartPos, vec2 *StartVel);
void ExtractExtraInfo(const CNetObj_Projectile *pProj, int *Owner, bool *Explosive, int *Bouncing, bool *Freeze);
void SnapshotRemoveExtraInfo(unsigned char *pData);
#endif

View file

@ -5,6 +5,7 @@
#include <game/server/gamecontext.h> #include <game/server/gamecontext.h>
#include <game/server/gamemodes/DDRace.h> #include <game/server/gamemodes/DDRace.h>
#include <game/server/player.h> #include <game/server/player.h>
#include <game/version.h>
#include <engine/shared/config.h> #include <engine/shared/config.h>
#include <game/server/teams.h> #include <game/server/teams.h>
@ -319,12 +320,26 @@ void CProjectile::Snap(int SnappingClient)
if(m_Owner != -1 && !CmaskIsSet(TeamMask, SnappingClient)) if(m_Owner != -1 && !CmaskIsSet(TeamMask, SnappingClient))
return; return;
CNetObj_Projectile *pProj = static_cast<CNetObj_Projectile *>(Server()->SnapNewItem(NETOBJTYPE_PROJECTILE, GetID(), sizeof(CNetObj_Projectile))); int SnappingClientVersion = SnappingClient >= 0 ? GameServer()->GetClientVersion(SnappingClient) : CLIENT_VERSIONNR;
if(pProj)
CNetObj_DDNetProjectile DDNetProjectile;
if(SnappingClientVersion >= VERSION_DDNET_ANTIPING_PROJECTILE && FillExtraInfo(&DDNetProjectile))
{ {
if(SnappingClient > -1 && GameServer()->m_apPlayers[SnappingClient] && GameServer()->m_apPlayers[SnappingClient]->GetClientVersion() >= VERSION_DDNET_ANTIPING_PROJECTILE) int Type = SnappingClientVersion <= VERSION_DDNET_MSG_LEGACY ? (int)NETOBJTYPE_PROJECTILE : NETOBJTYPE_DDNETPROJECTILE;
FillExtraInfo(pProj); void *pProj = Server()->SnapNewItem(Type, GetID(), sizeof(DDNetProjectile));
if(!pProj)
{
return;
}
mem_copy(pProj, &DDNetProjectile, sizeof(DDNetProjectile));
}
else else
{
CNetObj_Projectile *pProj = static_cast<CNetObj_Projectile *>(Server()->SnapNewItem(NETOBJTYPE_PROJECTILE, GetID(), sizeof(CNetObj_Projectile)));
if(!pProj)
{
return;
}
FillInfo(pProj); FillInfo(pProj);
} }
} }
@ -336,14 +351,13 @@ void CProjectile::SetBouncing(int Value)
m_Bouncing = Value; m_Bouncing = Value;
} }
void CProjectile::FillExtraInfo(CNetObj_Projectile *pProj) bool CProjectile::FillExtraInfo(CNetObj_DDNetProjectile *pProj)
{ {
const int MaxPos = 0x7fffffff / 100; const int MaxPos = 0x7fffffff / 100;
if(abs((int)m_Pos.y) + 1 >= MaxPos || abs((int)m_Pos.x) + 1 >= MaxPos) if(abs((int)m_Pos.y) + 1 >= MaxPos || abs((int)m_Pos.x) + 1 >= MaxPos)
{ {
//If the modified data would be too large to fit in an integer, send normal data instead //If the modified data would be too large to fit in an integer, send normal data instead
FillInfo(pProj); return false;
return;
} }
//Send additional/modified info, by modifiying the fields of the netobj //Send additional/modified info, by modifiying the fields of the netobj
float Angle = -atan2f(m_Direction.x, m_Direction.y); float Angle = -atan2f(m_Direction.x, m_Direction.y);
@ -351,18 +365,21 @@ void CProjectile::FillExtraInfo(CNetObj_Projectile *pProj)
int Data = 0; int Data = 0;
Data |= (abs(m_Owner) & 255) << 0; Data |= (abs(m_Owner) & 255) << 0;
if(m_Owner < 0) if(m_Owner < 0)
Data |= 1 << 8; Data |= PROJECTILEFLAG_NO_OWNER;
Data |= 1 << 9; //This bit tells the client to use the extra info //This bit tells the client to use the extra info
Data |= PROJECTILEFLAG_IS_DDNET;
// PROJECTILEFLAG_BOUNCE_HORIZONTAL, PROJECTILEFLAG_BOUNCE_VERTICAL
Data |= (m_Bouncing & 3) << 10; Data |= (m_Bouncing & 3) << 10;
if(m_Explosive) if(m_Explosive)
Data |= 1 << 12; Data |= PROJECTILEFLAG_EXPLOSIVE;
if(m_Freeze) if(m_Freeze)
Data |= 1 << 13; Data |= PROJECTILEFLAG_FREEZE;
pProj->m_X = (int)(m_Pos.x * 100.0f); pProj->m_X = (int)(m_Pos.x * 100.0f);
pProj->m_Y = (int)(m_Pos.y * 100.0f); pProj->m_Y = (int)(m_Pos.y * 100.0f);
pProj->m_VelX = (int)(Angle * 1000000.0f); pProj->m_Angle = (int)(Angle * 1000000.0f);
pProj->m_VelY = Data; pProj->m_Data = Data;
pProj->m_StartTick = m_StartTick; pProj->m_StartTick = m_StartTick;
pProj->m_Type = m_Type; pProj->m_Type = m_Type;
return true;
} }

View file

@ -49,7 +49,7 @@ private:
public: public:
void SetBouncing(int Value); void SetBouncing(int Value);
void FillExtraInfo(CNetObj_Projectile *pProj); bool FillExtraInfo(CNetObj_DDNetProjectile *pProj);
}; };
#endif #endif