6560: Replace EntityEx with new netobjs r=edg-l a=trml

This is a proposal for replacing EntityEx with new netobjects, like suggested in #5860.
- Adds a new DDNetPickup that includes switch number
- a new DDNetProjectile that replaces the old one (which is renamed to DDRaceProjectile)
  - includes switch number, tunezone and velocity/speed (plus a flag that can be used to send velocitiy of player projectiles with full precision)
- switch number and subtype is added to DDNetLaser (to distinguish e.g. draggers with different strength)

The pr removes EntityEx from the server, but keeps compatibility in the client.

<!-- What is the motivation for the changes of this pull request? -->

<!-- Note that builds and other checks will be run for your change. Don't feel intimidated by failures in some of the checks. If you can't resolve them yourself, experienced devs can also resolve them before merging your pull request. -->

## Checklist

- [ ] Tested the change ingame
- [ ] Provided screenshots if it is a visual change
- [ ] Tested in combination with possibly related configuration options
- [ ] Written a unit test (especially base/) or added coverage to integration test
- [ ] Considered possible null pointers and out of bounds array indexing
- [ ] Changed no physics that affect existing maps
- [ ] Tested the change with [ASan+UBSan or valgrind's memcheck](https://github.com/ddnet/ddnet/#using-addresssanitizer--undefinedbehavioursanitizer-or-valgrinds-memcheck) (optional)


Co-authored-by: trml <trml@users.noreply.github.com>
This commit is contained in:
bors[bot] 2023-05-14 08:43:02 +00:00 committed by GitHub
commit e9d08a15fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 453 additions and 277 deletions

View file

@ -2256,6 +2256,8 @@ if(CLIENT)
laser_data.h
lineinput.cpp
lineinput.h
pickup_data.cpp
pickup_data.h
prediction/entities/character.cpp
prediction/entities/character.h
prediction/entities/laser.cpp

View file

@ -29,12 +29,17 @@ GameInfoFlags2 = [
"HUD_DDRACE", "NO_WEAK_HOOK"
]
ExPlayerFlags = ["AFK", "PAUSED", "SPEC"]
ProjectileFlags = [f"CLIENTID_BIT{i}" for i in range(8)] + [
LegacyProjectileFlags = [f"CLIENTID_BIT{i}" for i in range(8)] + [
"NO_OWNER", "IS_DDNET", "BOUNCE_HORIZONTAL", "BOUNCE_VERTICAL",
"EXPLOSIVE", "FREEZE",
]
ProjectileFlags = [
"BOUNCE_HORIZONTAL", "BOUNCE_VERTICAL", "EXPLOSIVE", "FREEZE", "NORMALIZE_VEL",
]
LaserTypes = ["RIFLE", "SHOTGUN", "DOOR", "FREEZE"]
LaserTypes = ["RIFLE", "SHOTGUN", "DOOR", "FREEZE", "DRAGGER", "GUN", "PLASMA"]
DraggerTypes = ["WEAK", "WEAK_NW", "NORMAL", "NORMAL_NW", "STRONG", "STRONG_NW"]
GunTypes = ["UNFREEZE", "EXPLOSIVE", "FREEZE", "EXPFREEZE"]
Emoticons = ["OOP", "EXCLAMATION", "HEARTS", "DROP", "DOTDOT", "MUSIC", "SORRY", "GHOST", "SUSHI", "SPLATTEE", "DEVILTEE", "ZOMG", "ZZZ", "WTF", "EYES", "QUESTION"]
@ -81,6 +86,8 @@ Enums = [
Enum("AUTHED", Authed),
Enum("ENTITYCLASS", EntityClasses),
Enum("LASERTYPE", LaserTypes),
Enum("LASERDRAGGERTYPE", DraggerTypes),
Enum("LASERGUNTYPE", GunTypes),
]
Flags = [
@ -91,6 +98,7 @@ Flags = [
Flags("GAMEINFOFLAG", GameInfoFlags),
Flags("GAMEINFOFLAG2", GameInfoFlags2),
Flags("EXPLAYERFLAG", ExPlayerFlags),
Flags("LEGACYPROJECTILEFLAG", LegacyProjectileFlags),
Flags("PROJECTILEFLAG", ProjectileFlags),
]
@ -267,7 +275,7 @@ Objects = [
# The code assumes that this has the same in-memory representation as
# the Projectile net object.
NetObjectEx("DDNetProjectile", "projectile@netobj.ddnet.tw", [
NetObjectEx("DDRaceProjectile", "projectile@netobj.ddnet.tw", [
NetIntAny("m_X"),
NetIntAny("m_Y"),
NetIntAny("m_Angle"),
@ -284,6 +292,29 @@ Objects = [
NetTick("m_StartTick"),
NetIntRange("m_Owner", -1, 'MAX_CLIENTS-1'),
NetIntAny("m_Type"),
NetIntAny("m_SwitchNumber", -1),
NetIntAny("m_Subtype", -1),
]),
NetObjectEx("DDNetProjectile", "ddnet-projectile@netobj.ddnet.tw", [
NetIntAny("m_X"),
NetIntAny("m_Y"),
NetIntAny("m_VelX"),
NetIntAny("m_VelY"),
NetIntRange("m_Type", 0, 'NUM_WEAPONS-1'),
NetTick("m_StartTick"),
NetIntRange("m_Owner", -1, 'MAX_CLIENTS-1'),
NetIntAny("m_SwitchNumber"),
NetIntAny("m_TuneZone"),
NetIntAny("m_Flags"),
]),
NetObjectEx("DDNetPickup", "pickup@netobj.ddnet.tw", [
NetIntAny("m_X"),
NetIntAny("m_Y"),
NetIntRange("m_Type", 0, 'max_int'),
NetIntRange("m_Subtype", 0, 'max_int'),
NetIntAny("m_SwitchNumber"),
]),
## Events

View file

@ -123,6 +123,7 @@ enum
VERSION_DDNET_WEAPON_SHIELDS = 16010,
VERSION_DDNET_NEW_HUD = 16020,
VERSION_DDNET_MULTI_LASER = 16040,
VERSION_DDNET_ENTITY_NETOBJS = 16200,
};
typedef std::bitset<MAX_CLIENTS> CClientMask;

View file

@ -11,6 +11,7 @@
#include <game/client/gameclient.h>
#include <game/client/laser_data.h>
#include <game/client/pickup_data.h>
#include <game/client/projectile_data.h>
#include <game/client/render.h>
@ -257,6 +258,7 @@ void CItems::RenderLaser(const CLaserData *pCurrent, bool IsPredicted)
ColorOut = g_Config.m_ClLaserShotgunOutlineColor;
ColorIn = g_Config.m_ClLaserShotgunInnerColor;
break;
case LASERTYPE_DRAGGER:
case LASERTYPE_DOOR:
ColorOut = g_Config.m_ClLaserDoorOutlineColor;
ColorIn = g_Config.m_ClLaserDoorInnerColor;
@ -265,6 +267,19 @@ void CItems::RenderLaser(const CLaserData *pCurrent, bool IsPredicted)
ColorOut = g_Config.m_ClLaserFreezeOutlineColor;
ColorIn = g_Config.m_ClLaserFreezeInnerColor;
break;
case LASERTYPE_GUN:
case LASERTYPE_PLASMA:
if(pCurrent->m_Subtype == LASERGUNTYPE_FREEZE || pCurrent->m_Subtype == LASERGUNTYPE_EXPFREEZE)
{
ColorOut = g_Config.m_ClLaserFreezeOutlineColor;
ColorIn = g_Config.m_ClLaserFreezeInnerColor;
}
else
{
ColorOut = g_Config.m_ClLaserRifleOutlineColor;
ColorIn = g_Config.m_ClLaserRifleInnerColor;
}
break;
default:
ColorOut = g_Config.m_ClLaserRifleOutlineColor;
ColorIn = g_Config.m_ClLaserRifleInnerColor;
@ -402,22 +417,14 @@ void CItems::OnRender()
const void *pData = Ent.m_pData;
const CNetObj_EntityEx *pEntEx = Ent.m_pDataEx;
bool Inactive = false;
if(pEntEx)
Inactive = !IsSuper && pEntEx->m_SwitchNumber > 0 && pEntEx->m_SwitchNumber < (int)aSwitchers.size() && !aSwitchers[pEntEx->m_SwitchNumber].m_aStatus[SwitcherTeam];
if(Item.m_Type == NETOBJTYPE_PROJECTILE || Item.m_Type == NETOBJTYPE_DDNETPROJECTILE)
if(Item.m_Type == NETOBJTYPE_PROJECTILE || Item.m_Type == NETOBJTYPE_DDRACEPROJECTILE || Item.m_Type == NETOBJTYPE_DDNETPROJECTILE)
{
CProjectileData Data;
if(Item.m_Type == NETOBJTYPE_PROJECTILE)
{
Data = ExtractProjectileInfo((const CNetObj_Projectile *)pData, &GameClient()->m_GameWorld);
}
else
{
Data = ExtractProjectileInfoDDNet((const CNetObj_DDNetProjectile *)pData, &GameClient()->m_GameWorld);
}
CProjectileData Data = ExtractProjectileInfo(Item.m_Type, pData, &GameClient()->m_GameWorld, pEntEx);
bool Inactive = !IsSuper && Data.m_SwitchNumber > 0 && Data.m_SwitchNumber < (int)aSwitchers.size() && !aSwitchers[Data.m_SwitchNumber].m_aStatus[SwitcherTeam];
if(Inactive && (Data.m_Explosive ? BlinkingProjEx : BlinkingProj))
continue;
if(UsePredicted)
{
if(auto *pProj = (CProjectile *)GameClient()->m_GameWorld.FindMatch(Item.m_ID, Item.m_Type, pData))
{
@ -434,12 +441,13 @@ void CItems::OnRender()
continue;
}
}
if(Inactive && (Data.m_Explosive ? BlinkingProjEx : BlinkingProj))
continue;
RenderProjectile(&Data, Item.m_ID);
}
else if(Item.m_Type == NETOBJTYPE_PICKUP)
else if(Item.m_Type == NETOBJTYPE_PICKUP || Item.m_Type == NETOBJTYPE_DDNETPICKUP)
{
CPickupData Data = ExtractPickupInfo(Item.m_Type, pData, pEntEx);
bool Inactive = !IsSuper && Data.m_SwitchNumber > 0 && Data.m_SwitchNumber < (int)aSwitchers.size() && !aSwitchers[Data.m_SwitchNumber].m_aStatus[SwitcherTeam];
if(Inactive && BlinkingPickup)
continue;
if(UsePredicted)
@ -461,46 +469,43 @@ void CItems::OnRender()
continue;
}
CLaserData Data;
if(Item.m_Type == NETOBJTYPE_LASER)
CLaserData Data = ExtractLaserInfo(Item.m_Type, pData, &GameClient()->m_GameWorld, pEntEx);
bool Inactive = !IsSuper && Data.m_SwitchNumber > 0 && Data.m_SwitchNumber < (int)aSwitchers.size() && !aSwitchers[Data.m_SwitchNumber].m_aStatus[SwitcherTeam];
if(Data.m_Type == LASERTYPE_FREEZE)
{
Data = ExtractLaserInfo((const CNetObj_Laser *)pData, &GameClient()->m_GameWorld);
if(Inactive && BlinkingLight)
continue;
Data.m_StartTick = DraggerStartTick;
}
else
else if(Data.m_Type == LASERTYPE_GUN)
{
Data = ExtractLaserInfoDDNet((const CNetObj_DDNetLaser *)pData, &GameClient()->m_GameWorld);
if(Inactive && BlinkingGun)
continue;
Data.m_StartTick = GunStartTick;
}
else if(Data.m_Type == LASERTYPE_DRAGGER)
{
if(Inactive && BlinkingDragger)
continue;
Data.m_StartTick = DraggerStartTick;
}
else if(Data.m_Type == LASERTYPE_DOOR)
{
if(Inactive || IsSuper)
{
Data.m_From.x = Data.m_To.x;
Data.m_From.y = Data.m_To.y;
}
Data.m_StartTick = Client()->GameTick(g_Config.m_ClDummy);
}
else if(Data.m_Type >= NUM_LASERTYPES)
{
if(Inactive && BlinkingDragger)
continue;
Data.m_StartTick = Client()->GameTick(g_Config.m_ClDummy);
}
if(pEntEx)
{
if(pEntEx->m_EntityClass == ENTITYCLASS_LIGHT)
{
if(Inactive && BlinkingLight)
continue;
Data.m_StartTick = DraggerStartTick;
}
if(pEntEx->m_EntityClass >= ENTITYCLASS_GUN_NORMAL && pEntEx->m_EntityClass <= ENTITYCLASS_GUN_UNFREEZE)
{
if(Inactive && BlinkingGun)
continue;
Data.m_StartTick = GunStartTick;
}
if(pEntEx->m_EntityClass >= ENTITYCLASS_DRAGGER_WEAK && pEntEx->m_EntityClass <= ENTITYCLASS_DRAGGER_STRONG)
{
if(Inactive && BlinkingDragger)
continue;
Data.m_StartTick = DraggerStartTick;
}
if(pEntEx->m_EntityClass == ENTITYCLASS_DOOR)
{
if(Inactive || IsSuper)
{
Data.m_From.x = Data.m_To.x;
Data.m_From.y = Data.m_To.y;
}
Data.m_StartTick = Client()->GameTick(g_Config.m_ClDummy);
}
}
RenderLaser(&Data);
}
}

View file

@ -3316,7 +3316,7 @@ void CGameClient::SnapCollectEntities()
const void *pData = Client()->SnapGetItem(IClient::SNAP_CURRENT, Index, &Item);
if(Item.m_Type == NETOBJTYPE_ENTITYEX)
vItemEx.push_back({Item, pData, 0});
else if(Item.m_Type == NETOBJTYPE_PICKUP || Item.m_Type == NETOBJTYPE_LASER || Item.m_Type == NETOBJTYPE_DDNETLASER || Item.m_Type == NETOBJTYPE_PROJECTILE || Item.m_Type == NETOBJTYPE_DDNETPROJECTILE)
else if(Item.m_Type == NETOBJTYPE_PICKUP || Item.m_Type == NETOBJTYPE_DDNETPICKUP || Item.m_Type == NETOBJTYPE_LASER || Item.m_Type == NETOBJTYPE_DDNETLASER || Item.m_Type == NETOBJTYPE_PROJECTILE || Item.m_Type == NETOBJTYPE_DDRACEPROJECTILE || Item.m_Type == NETOBJTYPE_DDNETPROJECTILE)
vItemData.push_back({Item, pData, 0});
}

View file

@ -9,18 +9,52 @@
#include <game/collision.h>
CLaserData ExtractLaserInfo(const CNetObj_Laser *pLaser, CGameWorld *pGameWorld)
CLaserData ExtractLaserInfo(int NetObjType, const void *pData, CGameWorld *pGameWorld, const CNetObj_EntityEx *pEntEx)
{
CLaserData Result = {vec2(0, 0)};
Result.m_From.x = pLaser->m_FromX;
Result.m_From.y = pLaser->m_FromY;
Result.m_To.x = pLaser->m_X;
Result.m_To.y = pLaser->m_Y;
Result.m_StartTick = pLaser->m_StartTick;
Result.m_ExtraInfo = false;
Result.m_Owner = -1;
Result.m_Type = -1;
Result.m_TuneZone = pGameWorld && pGameWorld->m_WorldConfig.m_UseTuneZones ? pGameWorld->Collision()->IsTune(pGameWorld->Collision()->GetMapIndex(Result.m_From)) : 0;
if(NetObjType == NETOBJTYPE_DDNETLASER)
{
Result = ExtractLaserInfoDDNet((const CNetObj_DDNetLaser *)pData, pGameWorld);
}
else
{
CNetObj_Laser *pLaser = (CNetObj_Laser *)pData;
Result.m_From.x = pLaser->m_FromX;
Result.m_From.y = pLaser->m_FromY;
Result.m_To.x = pLaser->m_X;
Result.m_To.y = pLaser->m_Y;
Result.m_StartTick = pLaser->m_StartTick;
Result.m_ExtraInfo = false;
Result.m_Owner = -1;
Result.m_Type = -1;
Result.m_SwitchNumber = 0;
Result.m_Subtype = -1;
Result.m_TuneZone = pGameWorld && pGameWorld->m_WorldConfig.m_UseTuneZones ? pGameWorld->Collision()->IsTune(pGameWorld->Collision()->GetMapIndex(Result.m_From)) : 0;
}
if(pEntEx && !(NetObjType == NETOBJTYPE_DDNETLASER && Result.m_SwitchNumber >= 0))
{
Result.m_SwitchNumber = pEntEx->m_SwitchNumber;
if(pEntEx->m_EntityClass == ENTITYCLASS_LIGHT)
{
Result.m_Type = LASERTYPE_FREEZE;
}
else if(pEntEx->m_EntityClass >= ENTITYCLASS_GUN_NORMAL && pEntEx->m_EntityClass <= ENTITYCLASS_GUN_UNFREEZE)
{
Result.m_Type = LASERTYPE_GUN;
}
else if(pEntEx->m_EntityClass >= ENTITYCLASS_DRAGGER_WEAK && pEntEx->m_EntityClass <= ENTITYCLASS_DRAGGER_STRONG)
{
Result.m_Type = LASERTYPE_DRAGGER;
}
else if(pEntEx->m_EntityClass == ENTITYCLASS_DOOR)
{
Result.m_Type = LASERTYPE_DOOR;
}
}
return Result;
}
@ -35,6 +69,8 @@ CLaserData ExtractLaserInfoDDNet(const CNetObj_DDNetLaser *pLaser, CGameWorld *p
Result.m_ExtraInfo = true;
Result.m_Owner = pLaser->m_Owner;
Result.m_Type = pLaser->m_Type;
Result.m_SwitchNumber = pLaser->m_SwitchNumber;
Result.m_Subtype = pLaser->m_Subtype;
Result.m_TuneZone = pGameWorld && pGameWorld->m_WorldConfig.m_UseTuneZones ? pGameWorld->Collision()->IsTune(pGameWorld->Collision()->GetMapIndex(Result.m_From)) : 0;
return Result;
}

View file

@ -7,6 +7,7 @@
struct CNetObj_Laser;
struct CNetObj_DDNetLaser;
struct CNetObj_EntityEx;
class CLaserData
{
@ -18,11 +19,13 @@ public:
// The rest is only set if m_ExtraInfo is true.
int m_Owner;
int m_Type;
int m_SwitchNumber;
int m_Subtype;
// TuneZone is introduced locally
int m_TuneZone;
};
CLaserData ExtractLaserInfo(const CNetObj_Laser *pLaser, class CGameWorld *pGameWorld);
CLaserData ExtractLaserInfo(int NetObjType, const void *pData, class CGameWorld *pGameWorld, const CNetObj_EntityEx *pEntEx);
CLaserData ExtractLaserInfoDDNet(const CNetObj_DDNetLaser *pLaser, class CGameWorld *pGameWorld);
#endif // GAME_CLIENT_LASER_DATA_H

View file

@ -0,0 +1,41 @@
/* (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 "pickup_data.h"
#include <engine/shared/snapshot.h>
#include <game/collision.h>
#include <game/generated/protocol.h>
CPickupData ExtractPickupInfo(int NetObjType, const void *pData, const CNetObj_EntityEx *pEntEx)
{
if(NetObjType == NETOBJTYPE_DDNETPICKUP)
{
return ExtractPickupInfoDDNet((CNetObj_DDNetPickup *)pData);
}
CNetObj_Pickup *pPickup = (CNetObj_Pickup *)pData;
CPickupData Result = {vec2(0, 0)};
Result.m_Pos.x = pPickup->m_X;
Result.m_Pos.y = pPickup->m_Y;
Result.m_Type = pPickup->m_Type;
Result.m_Subtype = pPickup->m_Subtype;
Result.m_SwitchNumber = pEntEx ? pEntEx->m_SwitchNumber : 0;
return Result;
}
CPickupData ExtractPickupInfoDDNet(const CNetObj_DDNetPickup *pPickup)
{
CPickupData Result = {vec2(0, 0)};
Result.m_Pos.x = pPickup->m_X;
Result.m_Pos.y = pPickup->m_Y;
Result.m_Type = pPickup->m_Type;
Result.m_Subtype = pPickup->m_Subtype;
Result.m_SwitchNumber = pPickup->m_SwitchNumber;
return Result;
}

View file

@ -0,0 +1,24 @@
/* (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_PICKUP_DATA_H
#define GAME_CLIENT_PICKUP_DATA_H
#include <base/vmath.h>
struct CNetObj_Pickup;
struct CNetObj_DDNetPickup;
struct CNetObj_EntityEx;
class CPickupData
{
public:
vec2 m_Pos;
int m_Type;
int m_Subtype;
int m_SwitchNumber;
};
CPickupData ExtractPickupInfo(int NetObjType, const void *pData, const CNetObj_EntityEx *pEntEx);
CPickupData ExtractPickupInfoDDNet(const CNetObj_DDNetPickup *pPickup);
#endif // GAME_CLIENT_PICKUP_DATA_H

View file

@ -208,15 +208,6 @@ CLaser::CLaser(CGameWorld *pGameWorld, int ID, CLaserData *pLaser) :
m_ID = ID;
}
void CLaser::FillInfo(CNetObj_Laser *pLaser)
{
pLaser->m_X = (int)m_Pos.x;
pLaser->m_Y = (int)m_Pos.y;
pLaser->m_FromX = (int)m_From.x;
pLaser->m_FromY = (int)m_From.y;
pLaser->m_StartTick = m_EvalTick;
}
bool CLaser::Match(CLaser *pLaser)
{
if(pLaser->m_EvalTick != m_EvalTick)
@ -240,6 +231,8 @@ CLaserData CLaser::GetData() const
Result.m_ExtraInfo = true;
Result.m_Owner = m_Owner;
Result.m_Type = m_Type == WEAPON_SHOTGUN ? LASERTYPE_SHOTGUN : LASERTYPE_RIFLE;
Result.m_Subtype = -1;
Result.m_TuneZone = m_TuneZone;
Result.m_SwitchNumber = m_Number;
return Result;
}

View file

@ -20,7 +20,6 @@ public:
const int &GetOwner() { return m_Owner; }
const int &GetEvalTick() { return m_EvalTick; }
CLaser(CGameWorld *pGameWorld, int ID, CLaserData *pLaser);
void FillInfo(CNetObj_Laser *pLaser);
bool Match(CLaser *pLaser);
CLaserData GetData() const;

View file

@ -2,6 +2,7 @@
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include "pickup.h"
#include "character.h"
#include <game/client/pickup_data.h>
#include <game/collision.h>
#include <game/generated/protocol.h>
#include <game/mapitems.h>
@ -141,24 +142,17 @@ void CPickup::Move()
}
}
CPickup::CPickup(CGameWorld *pGameWorld, int ID, CNetObj_Pickup *pPickup, const CNetObj_EntityEx *pEntEx) :
CPickup::CPickup(CGameWorld *pGameWorld, int ID, const CPickupData *pPickup) :
CEntity(pGameWorld, CGameWorld::ENTTYPE_PICKUP)
{
m_Pos.x = pPickup->m_X;
m_Pos.y = pPickup->m_Y;
m_Pos = pPickup->m_Pos;
m_Type = pPickup->m_Type;
m_Subtype = pPickup->m_Subtype;
m_Core = vec2(0.f, 0.f);
m_IsCoreActive = false;
m_ID = ID;
m_Layer = LAYER_GAME;
m_Number = 0;
if(pEntEx)
{
m_Layer = pEntEx->m_Layer;
m_Number = pEntEx->m_SwitchNumber;
}
m_Number = pPickup->m_SwitchNumber;
m_Layer = m_Number > 0 ? LAYER_SWITCH : LAYER_GAME;
}
void CPickup::FillInfo(CNetObj_Pickup *pPickup)

View file

@ -5,6 +5,8 @@
#include <game/client/prediction/entity.h>
class CPickupData;
class CPickup : public CEntity
{
public:
@ -12,7 +14,7 @@ public:
void Tick() override;
CPickup(CGameWorld *pGameWorld, int ID, CNetObj_Pickup *pPickup, const CNetObj_EntityEx *pEntEx = 0);
CPickup(CGameWorld *pGameWorld, int ID, const CPickupData *pPickup);
void FillInfo(CNetObj_Pickup *pPickup);
bool Match(CPickup *pPickup);
bool InDDNetTile() { return m_IsCoreActive; }

View file

@ -155,7 +155,7 @@ void CProjectile::SetBouncing(int Value)
m_Bouncing = Value;
}
CProjectile::CProjectile(CGameWorld *pGameWorld, int ID, CProjectileData *pProj, const CNetObj_EntityEx *pEntEx) :
CProjectile::CProjectile(CGameWorld *pGameWorld, int ID, const CProjectileData *pProj) :
CEntity(pGameWorld, CGameWorld::ENTTYPE_PROJECTILE)
{
m_Pos = pProj->m_StartPos;
@ -191,14 +191,8 @@ CProjectile::CProjectile(CGameWorld *pGameWorld, int ID, CProjectileData *pProj,
Lifetime = GetTuning(m_TuneZone)->m_ShotgunLifetime * GameWorld()->GameTickSpeed();
m_LifeSpan = Lifetime - (pGameWorld->GameTick() - m_StartTick);
m_ID = ID;
m_Layer = LAYER_GAME;
m_Number = 0;
if(pEntEx)
{
m_Layer = LAYER_SWITCH;
m_Number = pEntEx->m_SwitchNumber;
}
m_Number = pProj->m_SwitchNumber;
m_Layer = m_Number > 0 ? LAYER_SWITCH : LAYER_GAME;
}
CProjectileData CProjectile::GetData() const
@ -214,6 +208,7 @@ CProjectileData CProjectile::GetData() const
Result.m_Bouncing = m_Bouncing;
Result.m_Freeze = m_Freeze;
Result.m_TuneZone = m_TuneZone;
Result.m_SwitchNumber = m_Number;
return Result;
}

View file

@ -37,7 +37,7 @@ public:
const vec2 &GetDirection() { return m_Direction; }
const int &GetOwner() { return m_Owner; }
const int &GetStartTick() { return m_StartTick; }
CProjectile(CGameWorld *pGameWorld, int ID, CProjectileData *pProj, const CNetObj_EntityEx *pEntEx = 0);
CProjectile(CGameWorld *pGameWorld, int ID, const CProjectileData *pProj);
private:
vec2 m_Direction;

View file

@ -10,6 +10,7 @@
#include <algorithm>
#include <engine/shared/config.h>
#include <game/client/laser_data.h>
#include <game/client/pickup_data.h>
#include <game/client/projectile_data.h>
#include <game/mapitems.h>
#include <utility>
@ -408,18 +409,10 @@ void CGameWorld::NetCharAdd(int ObjID, CNetObj_Character *pCharObj, CNetObj_DDNe
void CGameWorld::NetObjAdd(int ObjID, int ObjType, const void *pObjData, const CNetObj_EntityEx *pDataEx)
{
if((ObjType == NETOBJTYPE_PROJECTILE || ObjType == NETOBJTYPE_DDNETPROJECTILE) && m_WorldConfig.m_PredictWeapons)
if((ObjType == NETOBJTYPE_PROJECTILE || ObjType == NETOBJTYPE_DDRACEPROJECTILE || ObjType == NETOBJTYPE_DDNETPROJECTILE) && m_WorldConfig.m_PredictWeapons)
{
CProjectileData Data;
if(ObjType == NETOBJTYPE_PROJECTILE)
{
Data = ExtractProjectileInfo((const CNetObj_Projectile *)pObjData, this);
}
else
{
Data = ExtractProjectileInfoDDNet((const CNetObj_DDNetProjectile *)pObjData, this);
}
CProjectile NetProj = CProjectile(this, ObjID, &Data, pDataEx);
CProjectileData Data = ExtractProjectileInfo(ObjType, pObjData, this, pDataEx);
CProjectile NetProj = CProjectile(this, ObjID, &Data);
if(NetProj.m_Type != WEAPON_SHOTGUN && absolute(length(NetProj.m_Direction) - 1.f) > 0.02f) // workaround to skip grenades on ball mod
return;
@ -471,9 +464,10 @@ void CGameWorld::NetObjAdd(int ObjID, int ObjType, const void *pObjData, const C
CProjectile *pProj = new CProjectile(NetProj);
InsertEntity(pProj);
}
else if(ObjType == NETOBJTYPE_PICKUP && m_WorldConfig.m_PredictWeapons)
else if((ObjType == NETOBJTYPE_PICKUP || ObjType == NETOBJTYPE_DDNETPICKUP) && m_WorldConfig.m_PredictWeapons)
{
CPickup NetPickup = CPickup(this, ObjID, (CNetObj_Pickup *)pObjData, pDataEx);
CPickupData Data = ExtractPickupInfo(ObjType, pObjData, pDataEx);
CPickup NetPickup = CPickup(this, ObjID, &Data);
if(CPickup *pPickup = (CPickup *)GetEntity(ObjID, ENTTYPE_PICKUP))
{
if(NetPickup.Match(pPickup))
@ -488,15 +482,12 @@ void CGameWorld::NetObjAdd(int ObjID, int ObjType, const void *pObjData, const C
}
else if((ObjType == NETOBJTYPE_LASER || ObjType == NETOBJTYPE_DDNETLASER) && m_WorldConfig.m_PredictWeapons)
{
CLaserData Data;
if(ObjType == NETOBJTYPE_LASER)
CLaserData Data = ExtractLaserInfo(ObjType, pObjData, this, pDataEx);
if(Data.m_Type >= 0 && Data.m_Type != LASERTYPE_RIFLE && Data.m_Type != LASERTYPE_SHOTGUN)
{
Data = ExtractLaserInfo((const CNetObj_Laser *)pObjData, this);
}
else
{
Data = ExtractLaserInfoDDNet((const CNetObj_DDNetLaser *)pObjData, this);
return;
}
CLaser NetLaser = CLaser(this, ObjID, &Data);
CLaser *pMatching = 0;
if(CLaser *pLaser = dynamic_cast<CLaser *>(GetEntity(ObjID, ENTTYPE_LASER)))
@ -618,28 +609,22 @@ void CGameWorld::CopyWorld(CGameWorld *pFrom)
CEntity *CGameWorld::FindMatch(int ObjID, int ObjType, const void *pObjData)
{
#define FindType(EntType, EntClass, ObjClass) \
{ \
CEntity *pEnt = GetEntity(ObjID, EntType); \
if(pEnt && EntClass(this, ObjID, (ObjClass *)pObjData).Match((EntClass *)pEnt)) \
return pEnt; \
return 0; \
}
switch(ObjType)
{
case NETOBJTYPE_CHARACTER: FindType(ENTTYPE_CHARACTER, CCharacter, CNetObj_Character);
case NETOBJTYPE_CHARACTER:
{
CCharacter *pEnt = (CCharacter *)GetEntity(ObjID, ENTTYPE_CHARACTER);
if(pEnt && CCharacter(this, ObjID, (CNetObj_Character *)pObjData).Match((CCharacter *)pEnt))
{
return pEnt;
}
return 0;
}
case NETOBJTYPE_PROJECTILE:
case NETOBJTYPE_DDRACEPROJECTILE:
case NETOBJTYPE_DDNETPROJECTILE:
{
CProjectileData Data;
if(ObjType == NETOBJTYPE_PROJECTILE)
{
Data = ExtractProjectileInfo((const CNetObj_Projectile *)pObjData, this);
}
else
{
Data = ExtractProjectileInfoDDNet((const CNetObj_DDNetProjectile *)pObjData, this);
}
CProjectileData Data = ExtractProjectileInfo(ObjType, pObjData, this, nullptr);
CProjectile *pEnt = (CProjectile *)GetEntity(ObjID, ENTTYPE_PROJECTILE);
if(pEnt && CProjectile(this, ObjID, &Data).Match(pEnt))
{
@ -650,15 +635,7 @@ CEntity *CGameWorld::FindMatch(int ObjID, int ObjType, const void *pObjData)
case NETOBJTYPE_LASER:
case NETOBJTYPE_DDNETLASER:
{
CLaserData Data;
if(ObjType == NETOBJTYPE_LASER)
{
Data = ExtractLaserInfo((const CNetObj_Laser *)pObjData, this);
}
else
{
Data = ExtractLaserInfoDDNet((const CNetObj_DDNetLaser *)pObjData, this);
}
CLaserData Data = ExtractLaserInfo(ObjType, pObjData, this, nullptr);
CLaser *pEnt = (CLaser *)GetEntity(ObjID, ENTTYPE_LASER);
if(pEnt && CLaser(this, ObjID, &Data).Match(pEnt))
{
@ -666,7 +643,17 @@ CEntity *CGameWorld::FindMatch(int ObjID, int ObjType, const void *pObjData)
}
return 0;
}
case NETOBJTYPE_PICKUP: FindType(ENTTYPE_PICKUP, CPickup, CNetObj_Pickup);
case NETOBJTYPE_PICKUP:
case NETOBJTYPE_DDNETPICKUP:
{
CPickupData Data = ExtractPickupInfo(ObjType, pObjData, nullptr);
CPickup *pEnt = (CPickup *)GetEntity(ObjID, ENTTYPE_PICKUP);
if(pEnt && CPickup(this, ObjID, &Data).Match(pEnt))
{
return pEnt;
}
return 0;
}
}
return 0;
}

View file

@ -11,16 +11,20 @@
bool UseProjectileExtraInfo(const CNetObj_Projectile *pProj)
{
return pProj->m_VelY >= 0 && (pProj->m_VelY & PROJECTILEFLAG_IS_DDNET) != 0;
return pProj->m_VelY >= 0 && (pProj->m_VelY & LEGACYPROJECTILEFLAG_IS_DDNET) != 0;
}
CProjectileData ExtractProjectileInfo(const CNetObj_Projectile *pProj, CGameWorld *pGameWorld)
CProjectileData ExtractProjectileInfo(int NetObjType, const void *pData, CGameWorld *pGameWorld, const CNetObj_EntityEx *pEntEx)
{
if(UseProjectileExtraInfo(pProj))
CNetObj_Projectile *pProj = (CNetObj_Projectile *)pData;
if(NetObjType == NETOBJTYPE_DDNETPROJECTILE)
{
CNetObj_DDNetProjectile Proj;
mem_copy(&Proj, pProj, sizeof(Proj));
return ExtractProjectileInfoDDNet(&Proj, pGameWorld);
return ExtractProjectileInfoDDNet((CNetObj_DDNetProjectile *)pData);
}
else if(NetObjType == NETOBJTYPE_DDRACEPROJECTILE || (NetObjType == NETOBJTYPE_PROJECTILE && UseProjectileExtraInfo(pProj)))
{
return ExtractProjectileInfoDDRace((CNetObj_DDRaceProjectile *)pData, pGameWorld, pEntEx);
}
CProjectileData Result = {vec2(0, 0)};
@ -33,10 +37,11 @@ CProjectileData ExtractProjectileInfo(const CNetObj_Projectile *pProj, CGameWorl
Result.m_ExtraInfo = false;
Result.m_Owner = -1;
Result.m_TuneZone = pGameWorld && pGameWorld->m_WorldConfig.m_UseTuneZones ? pGameWorld->Collision()->IsTune(pGameWorld->Collision()->GetMapIndex(Result.m_StartPos)) : 0;
Result.m_SwitchNumber = pEntEx ? pEntEx->m_SwitchNumber : 0;
return Result;
}
CProjectileData ExtractProjectileInfoDDNet(const CNetObj_DDNetProjectile *pProj, CGameWorld *pGameWorld)
CProjectileData ExtractProjectileInfoDDRace(const CNetObj_DDRaceProjectile *pProj, CGameWorld *pGameWorld, const CNetObj_EntityEx *pEntEx)
{
CProjectileData Result = {vec2(0, 0)};
@ -50,15 +55,52 @@ CProjectileData ExtractProjectileInfoDDNet(const CNetObj_DDNetProjectile *pProj,
Result.m_ExtraInfo = true;
Result.m_Owner = pProj->m_Data & 255;
if(pProj->m_Data & PROJECTILEFLAG_NO_OWNER || Result.m_Owner < 0 || Result.m_Owner >= MAX_CLIENTS)
if(pProj->m_Data & LEGACYPROJECTILEFLAG_NO_OWNER || Result.m_Owner < 0 || Result.m_Owner >= MAX_CLIENTS)
{
Result.m_Owner = -1;
}
// PROJECTILEFLAG_BOUNCE_HORIZONTAL, PROJECTILEFLAG_BOUNCE_VERTICAL
// LEGACYPROJECTILEFLAG_BOUNCE_HORIZONTAL, LEGACYPROJECTILEFLAG_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;
Result.m_Explosive = pProj->m_Data & LEGACYPROJECTILEFLAG_EXPLOSIVE;
Result.m_Freeze = pProj->m_Data & LEGACYPROJECTILEFLAG_FREEZE;
Result.m_TuneZone = pGameWorld && pGameWorld->m_WorldConfig.m_UseTuneZones ? pGameWorld->Collision()->IsTune(pGameWorld->Collision()->GetMapIndex(Result.m_StartPos)) : 0;
Result.m_SwitchNumber = pEntEx ? pEntEx->m_SwitchNumber : 0;
return Result;
}
CProjectileData ExtractProjectileInfoDDNet(const CNetObj_DDNetProjectile *pProj)
{
CProjectileData Result = {vec2(0, 0)};
Result.m_StartPos = vec2(pProj->m_X / 100.0f, pProj->m_Y / 100.0f);
Result.m_StartVel = vec2(pProj->m_VelX / 1e6f, pProj->m_VelY / 1e6f);
if(pProj->m_Flags & PROJECTILEFLAG_NORMALIZE_VEL)
{
Result.m_StartVel = normalize(Result.m_StartVel);
}
Result.m_Type = pProj->m_Type;
Result.m_StartTick = pProj->m_StartTick;
Result.m_ExtraInfo = true;
Result.m_Owner = pProj->m_Owner;
Result.m_SwitchNumber = pProj->m_SwitchNumber;
Result.m_TuneZone = pProj->m_TuneZone;
Result.m_Bouncing = 0;
if(pProj->m_Flags & PROJECTILEFLAG_BOUNCE_HORIZONTAL)
{
Result.m_Bouncing |= 1;
}
if(pProj->m_Flags & PROJECTILEFLAG_BOUNCE_VERTICAL)
{
Result.m_Bouncing |= 2;
}
Result.m_Explosive = pProj->m_Flags & PROJECTILEFLAG_EXPLOSIVE;
Result.m_Freeze = pProj->m_Flags & PROJECTILEFLAG_FREEZE;
return Result;
}
@ -73,7 +115,7 @@ void SnapshotRemoveExtraProjectileInfo(unsigned char *pData)
CNetObj_Projectile *pProj = (CNetObj_Projectile *)((void *)pItem->Data());
if(UseProjectileExtraInfo(pProj))
{
CProjectileData Data = ExtractProjectileInfo(pProj, nullptr);
CProjectileData Data = ExtractProjectileInfo(NETOBJTYPE_PROJECTILE, pProj, nullptr, nullptr);
pProj->m_X = Data.m_StartPos.x;
pProj->m_Y = Data.m_StartPos.y;
pProj->m_VelX = (int)(Data.m_StartVel.x * 100.0f);

View file

@ -6,7 +6,9 @@
#include <base/vmath.h>
struct CNetObj_Projectile;
struct CNetObj_DDRaceProjectile;
struct CNetObj_DDNetProjectile;
struct CNetObj_EntityEx;
class CProjectileData
{
@ -21,11 +23,13 @@ public:
bool m_Explosive;
int m_Bouncing;
bool m_Freeze;
int m_SwitchNumber;
// TuneZone is introduced locally
int m_TuneZone;
};
CProjectileData ExtractProjectileInfo(const CNetObj_Projectile *pProj, class CGameWorld *pGameWorld);
CProjectileData ExtractProjectileInfoDDNet(const CNetObj_DDNetProjectile *pProj, class CGameWorld *pGameWorld);
CProjectileData ExtractProjectileInfo(int NetObjType, const void *pData, class CGameWorld *pGameWorld, const CNetObj_EntityEx *pEntEx);
CProjectileData ExtractProjectileInfoDDRace(const CNetObj_DDRaceProjectile *pProj, class CGameWorld *pGameWorld, const CNetObj_EntityEx *pEntEx);
CProjectileData ExtractProjectileInfoDDNet(const CNetObj_DDNetProjectile *pProj);
#endif // GAME_CLIENT_PROJECTILE_DATA_H

View file

@ -378,7 +378,8 @@ void CCharacter::FireWeapon()
}
DoWeaponSwitch();
vec2 Direction = normalize(vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY));
vec2 MouseTarget = vec2(m_LatestInput.m_TargetX, m_LatestInput.m_TargetY);
vec2 Direction = normalize(MouseTarget);
bool FullAuto = false;
if(m_Core.m_ActiveWeapon == WEAPON_GRENADE || m_Core.m_ActiveWeapon == WEAPON_SHOTGUN || m_Core.m_ActiveWeapon == WEAPON_LASER)
@ -513,7 +514,8 @@ void CCharacter::FireWeapon()
Lifetime, //Span
false, //Freeze
false, //Explosive
-1 //SoundImpact
-1, //SoundImpact
MouseTarget //InitDir
);
GameServer()->CreateSound(m_Pos, SOUND_GUN_FIRE, TeamMask());
@ -551,8 +553,9 @@ void CCharacter::FireWeapon()
Lifetime, //Span
false, //Freeze
true, //Explosive
SOUND_GRENADE_EXPLODE //SoundImpact
); //SoundImpact
SOUND_GRENADE_EXPLODE, //SoundImpact
MouseTarget // MouseTarget
);
GameServer()->CreateSound(m_Pos, SOUND_GRENADE_FIRE, TeamMask());
}

View file

@ -52,19 +52,11 @@ void CDoor::Snap(int SnappingClient)
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
CNetObj_EntityEx *pEntData = 0;
if(SnappingClientVersion >= VERSION_DDNET_SWITCH)
pEntData = Server()->SnapNewItem<CNetObj_EntityEx>(GetID());
vec2 From;
int StartTick;
if(pEntData)
if(SnappingClientVersion >= VERSION_DDNET_ENTITY_NETOBJS)
{
pEntData->m_SwitchNumber = m_Number;
pEntData->m_Layer = m_Layer;
pEntData->m_EntityClass = ENTITYCLASS_DOOR;
From = m_To;
StartTick = 0;
}
@ -87,5 +79,5 @@ void CDoor::Snap(int SnappingClient)
}
GameServer()->SnapLaserObject(CSnapContext(SnappingClientVersion), GetID(),
m_Pos, From, StartTick, -1, LASERTYPE_DOOR);
m_Pos, From, StartTick, -1, LASERTYPE_DOOR, 0, m_Number);
}

View file

@ -186,17 +186,12 @@ void CDragger::Snap(int SnappingClient)
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
CNetObj_EntityEx *pEntData = 0;
if(SnappingClientVersion >= VERSION_DDNET_SWITCH)
int Subtype = (m_IgnoreWalls ? 1 : 0) | (clamp(round_to_int(m_Strength - 1.f), 0, 2) << 1);
int StartTick;
if(SnappingClientVersion >= VERSION_DDNET_ENTITY_NETOBJS)
{
pEntData = Server()->SnapNewItem<CNetObj_EntityEx>(GetID());
if(pEntData)
{
pEntData->m_SwitchNumber = m_Number;
pEntData->m_Layer = m_Layer;
pEntData->m_EntityClass = clamp(ENTITYCLASS_DRAGGER_WEAK + round_to_int(m_Strength) - 1,
(int)ENTITYCLASS_DRAGGER_WEAK, (int)ENTITYCLASS_DRAGGER_STRONG);
}
StartTick = 0;
}
else
{
@ -212,11 +207,7 @@ void CDragger::Snap(int SnappingClient)
if(pChar && m_Layer == LAYER_SWITCH && m_Number > 0 &&
!Switchers()[m_Number].m_aStatus[pChar->Team()] && !Tick)
return;
}
int StartTick = 0;
if(!pEntData)
{
StartTick = m_EvalTick;
if(StartTick < Server()->Tick() - 4)
StartTick = Server()->Tick() - 4;
@ -225,5 +216,5 @@ void CDragger::Snap(int SnappingClient)
}
GameServer()->SnapLaserObject(CSnapContext(SnappingClientVersion), GetID(),
m_Pos, m_Pos, StartTick, -1, LASERTYPE_DOOR);
m_Pos, m_Pos, StartTick, -1, LASERTYPE_DRAGGER, Subtype, m_Number);
}

View file

@ -109,6 +109,8 @@ void CDraggerBeam::Snap(int SnappingClient)
return;
}
int Subtype = (m_IgnoreWalls ? 1 : 0) | (clamp(round_to_int(m_Strength - 1.f), 0, 2) << 1);
int StartTick = m_EvalTick;
if(StartTick < Server()->Tick() - 4)
{
@ -121,7 +123,7 @@ void CDraggerBeam::Snap(int SnappingClient)
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
GameServer()->SnapLaserObject(CSnapContext(SnappingClientVersion), GetID(),
m_Pos, TargetPos, StartTick, -1, LASERTYPE_DOOR);
m_Pos, TargetPos, StartTick, -1, LASERTYPE_DRAGGER, Subtype, m_Number);
}
void CDraggerBeam::SwapClients(int Client1, int Client2)

View file

@ -149,25 +149,12 @@ void CGun::Snap(int SnappingClient)
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
CNetObj_EntityEx *pEntData = 0;
if(SnappingClientVersion >= VERSION_DDNET_SWITCH)
{
pEntData = static_cast<CNetObj_EntityEx *>(Server()->SnapNewItem(NETOBJTYPE_ENTITYEX, GetID(),
sizeof(CNetObj_EntityEx)));
if(pEntData)
{
pEntData->m_SwitchNumber = m_Number;
pEntData->m_Layer = m_Layer;
int Subtype = (m_Explosive ? 1 : 0) | (m_Freeze ? 2 : 0);
if(m_Explosive && !m_Freeze)
pEntData->m_EntityClass = ENTITYCLASS_GUN_NORMAL;
else if(m_Explosive && m_Freeze)
pEntData->m_EntityClass = ENTITYCLASS_GUN_EXPLOSIVE;
else if(!m_Explosive && m_Freeze)
pEntData->m_EntityClass = ENTITYCLASS_GUN_FREEZE;
else
pEntData->m_EntityClass = ENTITYCLASS_GUN_UNFREEZE;
}
int StartTick;
if(SnappingClientVersion >= VERSION_DDNET_ENTITY_NETOBJS)
{
StartTick = 0;
}
else
{
@ -184,10 +171,10 @@ void CGun::Snap(int SnappingClient)
if(pChar && m_Layer == LAYER_SWITCH && m_Number > 0 &&
!Switchers()[m_Number].m_aStatus[pChar->Team()] && (!Tick))
return;
StartTick = m_EvalTick;
}
int StartTick = pEntData ? 0 : m_EvalTick;
GameServer()->SnapLaserObject(CSnapContext(SnappingClientVersion), GetID(),
m_Pos, m_Pos, StartTick, -1, m_Freeze ? LASERTYPE_FREEZE : LASERTYPE_RIFLE);
m_Pos, m_Pos, StartTick, -1, LASERTYPE_GUN, Subtype, m_Number);
}

View file

@ -316,8 +316,9 @@ void CLaser::Snap(int SnappingClient)
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
int LaserType = m_Type == WEAPON_LASER ? LASERTYPE_RIFLE : m_Type == WEAPON_SHOTGUN ? LASERTYPE_SHOTGUN : -1;
GameServer()->SnapLaserObject(CSnapContext(SnappingClientVersion), GetID(),
m_Pos, m_From, m_EvalTick, m_Owner, LaserType);
m_Pos, m_From, m_EvalTick, m_Owner, LaserType, 0, m_Number);
}
void CLaser::SwapClients(int Client1, int Client2)

View file

@ -107,28 +107,11 @@ void CLight::Snap(int SnappingClient)
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
CNetObj_EntityEx *pEntData = 0;
if(SnappingClientVersion >= VERSION_DDNET_SWITCH && (m_Layer == LAYER_SWITCH || length(m_Core) > 0))
pEntData = static_cast<CNetObj_EntityEx *>(Server()->SnapNewItem(NETOBJTYPE_ENTITYEX, GetID(), sizeof(CNetObj_EntityEx)));
CCharacter *pChr = GameServer()->GetPlayerChar(SnappingClient);
if(SnappingClient != SERVER_DEMO_CLIENT && (GameServer()->m_apPlayers[SnappingClient]->GetTeam() == TEAM_SPECTATORS || GameServer()->m_apPlayers[SnappingClient]->IsPaused()) && GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID != SPEC_FREEVIEW)
pChr = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID);
if(pEntData)
{
pEntData->m_SwitchNumber = m_Number;
pEntData->m_Layer = m_Layer;
pEntData->m_EntityClass = ENTITYCLASS_LIGHT;
}
else
{
int Tick = (Server()->Tick() % Server()->TickSpeed()) % 6;
if(pChr && pChr->IsAlive() && m_Layer == LAYER_SWITCH && m_Number > 0 && !Switchers()[m_Number].m_aStatus[pChr->Team()] && Tick)
return;
}
vec2 From = m_Pos;
int StartTick = 0;
@ -145,8 +128,12 @@ void CLight::Snap(int SnappingClient)
From = m_To;
}
if(!pEntData)
if(SnappingClientVersion < VERSION_DDNET_ENTITY_NETOBJS)
{
int Tick = (Server()->Tick() % Server()->TickSpeed()) % 6;
if(pChr && pChr->IsAlive() && m_Layer == LAYER_SWITCH && m_Number > 0 && !Switchers()[m_Number].m_aStatus[pChr->Team()] && Tick)
return;
StartTick = m_EvalTick;
if(StartTick < Server()->Tick() - 4)
StartTick = Server()->Tick() - 4;
@ -155,5 +142,5 @@ void CLight::Snap(int SnappingClient)
}
GameServer()->SnapLaserObject(CSnapContext(SnappingClientVersion), GetID(),
m_Pos, From, StartTick, -1, LASERTYPE_FREEZE);
m_Pos, From, StartTick, -1, LASERTYPE_FREEZE, 0, m_Number);
}

View file

@ -167,32 +167,22 @@ void CPickup::Snap(int SnappingClient)
if(NetworkClipped(SnappingClient))
return;
CCharacter *pChar = GameServer()->GetPlayerChar(SnappingClient);
if(SnappingClient != SERVER_DEMO_CLIENT && (GameServer()->m_apPlayers[SnappingClient]->GetTeam() == TEAM_SPECTATORS || GameServer()->m_apPlayers[SnappingClient]->IsPaused()) && GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID != SPEC_FREEVIEW)
pChar = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID);
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
bool Sixup = Server()->IsSixup(SnappingClient);
CNetObj_EntityEx *pEntData = 0;
if(SnappingClientVersion >= VERSION_DDNET_SWITCH && (m_Layer == LAYER_SWITCH || length(m_Core) > 0))
pEntData = Server()->SnapNewItem<CNetObj_EntityEx>(GetID());
if(SnappingClientVersion < VERSION_DDNET_ENTITY_NETOBJS)
{
CCharacter *pChar = GameServer()->GetPlayerChar(SnappingClient);
if(SnappingClient != SERVER_DEMO_CLIENT && (GameServer()->m_apPlayers[SnappingClient]->GetTeam() == TEAM_SPECTATORS || GameServer()->m_apPlayers[SnappingClient]->IsPaused()) && GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID != SPEC_FREEVIEW)
pChar = GameServer()->GetPlayerChar(GameServer()->m_apPlayers[SnappingClient]->m_SpectatorID);
if(pEntData)
{
pEntData->m_SwitchNumber = m_Number;
pEntData->m_Layer = m_Layer;
pEntData->m_EntityClass = ENTITYCLASS_PICKUP;
}
else
{
int Tick = (Server()->Tick() % Server()->TickSpeed()) % 11;
if(pChar && pChar->IsAlive() && m_Layer == LAYER_SWITCH && m_Number > 0 && !Switchers()[m_Number].m_aStatus[pChar->Team()] && !Tick)
return;
}
GameServer()->SnapPickup(CSnapContext(SnappingClientVersion, Sixup), GetID(), m_Pos, m_Type, m_Subtype);
GameServer()->SnapPickup(CSnapContext(SnappingClientVersion, Sixup), GetID(), m_Pos, m_Type, m_Subtype, m_Number);
}
void CPickup::Move()

View file

@ -119,8 +119,10 @@ void CPlasma::Snap(int SnappingClient)
return;
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
int Subtype = (m_Explosive ? 1 : 0) | (m_Freeze ? 2 : 0);
GameServer()->SnapLaserObject(CSnapContext(SnappingClientVersion), GetID(),
m_Pos, m_Pos, m_EvalTick, -1, m_Freeze ? LASERTYPE_FREEZE : LASERTYPE_RIFLE);
m_Pos, m_Pos, m_EvalTick, -1, LASERTYPE_PLASMA, Subtype, m_Number);
}
void CPlasma::SwapClients(int Client1, int Client2)

View file

@ -21,6 +21,7 @@ CProjectile::CProjectile(
bool Freeze,
bool Explosive,
int SoundImpact,
vec2 InitDir,
int Layer,
int Number) :
CEntity(pGameWorld, CGameWorld::ENTTYPE_PROJECTILE)
@ -39,6 +40,7 @@ CProjectile::CProjectile(
m_Number = Number;
m_Freeze = Freeze;
m_InitDir = InitDir;
m_TuneZone = GameServer()->Collision()->IsTune(GameServer()->Collision()->GetMapIndex(m_Pos));
CCharacter *pOwnerChar = GameServer()->GetPlayerChar(m_Owner);
@ -305,19 +307,8 @@ void CProjectile::Snap(int SnappingClient)
if(NetworkClipped(SnappingClient, GetPos(Ct)))
return;
if(m_LifeSpan == -2)
{
CNetObj_EntityEx *pEntData = Server()->SnapNewItem<CNetObj_EntityEx>(GetID());
if(!pEntData)
return;
pEntData->m_SwitchNumber = m_Number;
pEntData->m_Layer = m_Layer;
pEntData->m_EntityClass = ENTITYCLASS_PROJECTILE;
}
int SnappingClientVersion = GameServer()->GetClientVersion(SnappingClient);
if(SnappingClientVersion < VERSION_DDNET_SWITCH)
if(SnappingClientVersion < VERSION_DDNET_ENTITY_NETOBJS)
{
CCharacter *pSnapChar = GameServer()->GetPlayerChar(SnappingClient);
int Tick = (Server()->Tick() % Server()->TickSpeed()) % ((m_Explosive) ? 6 : 20);
@ -337,16 +328,26 @@ void CProjectile::Snap(int SnappingClient)
if(SnappingClient != SERVER_DEMO_CLIENT && m_Owner != -1 && !TeamMask.test(SnappingClient))
return;
CNetObj_DDNetProjectile DDNetProjectile;
if(SnappingClientVersion >= VERSION_DDNET_ANTIPING_PROJECTILE && FillExtraInfo(&DDNetProjectile))
CNetObj_DDRaceProjectile DDRaceProjectile;
if(SnappingClientVersion >= VERSION_DDNET_ENTITY_NETOBJS)
{
int Type = SnappingClientVersion < VERSION_DDNET_MSG_LEGACY ? (int)NETOBJTYPE_PROJECTILE : NETOBJTYPE_DDNETPROJECTILE;
void *pProj = Server()->SnapNewItem(Type, GetID(), sizeof(DDNetProjectile));
CNetObj_DDNetProjectile *pDDNetProjectile = static_cast<CNetObj_DDNetProjectile *>(Server()->SnapNewItem(NETOBJTYPE_DDNETPROJECTILE, GetID(), sizeof(CNetObj_DDNetProjectile)));
if(!pDDNetProjectile)
{
return;
}
FillExtraInfo(pDDNetProjectile);
}
else if(SnappingClientVersion >= VERSION_DDNET_ANTIPING_PROJECTILE && FillExtraInfoLegacy(&DDRaceProjectile))
{
int Type = SnappingClientVersion < VERSION_DDNET_MSG_LEGACY ? (int)NETOBJTYPE_PROJECTILE : NETOBJTYPE_DDRACEPROJECTILE;
void *pProj = Server()->SnapNewItem(Type, GetID(), sizeof(DDRaceProjectile));
if(!pProj)
{
return;
}
mem_copy(pProj, &DDNetProjectile, sizeof(DDNetProjectile));
mem_copy(pProj, &DDRaceProjectile, sizeof(DDRaceProjectile));
}
else
{
@ -371,7 +372,7 @@ void CProjectile::SetBouncing(int Value)
m_Bouncing = Value;
}
bool CProjectile::FillExtraInfo(CNetObj_DDNetProjectile *pProj)
bool CProjectile::FillExtraInfoLegacy(CNetObj_DDRaceProjectile *pProj)
{
const int MaxPos = 0x7fffffff / 100;
if(absolute((int)m_Pos.y) + 1 >= MaxPos || absolute((int)m_Pos.x) + 1 >= MaxPos)
@ -385,15 +386,15 @@ bool CProjectile::FillExtraInfo(CNetObj_DDNetProjectile *pProj)
int Data = 0;
Data |= (absolute(m_Owner) & 255) << 0;
if(m_Owner < 0)
Data |= PROJECTILEFLAG_NO_OWNER;
Data |= LEGACYPROJECTILEFLAG_NO_OWNER;
//This bit tells the client to use the extra info
Data |= PROJECTILEFLAG_IS_DDNET;
// PROJECTILEFLAG_BOUNCE_HORIZONTAL, PROJECTILEFLAG_BOUNCE_VERTICAL
Data |= LEGACYPROJECTILEFLAG_IS_DDNET;
// LEGACYPROJECTILEFLAG_BOUNCE_HORIZONTAL, LEGACYPROJECTILEFLAG_BOUNCE_VERTICAL
Data |= (m_Bouncing & 3) << 10;
if(m_Explosive)
Data |= PROJECTILEFLAG_EXPLOSIVE;
Data |= LEGACYPROJECTILEFLAG_EXPLOSIVE;
if(m_Freeze)
Data |= PROJECTILEFLAG_FREEZE;
Data |= LEGACYPROJECTILEFLAG_FREEZE;
pProj->m_X = (int)(m_Pos.x * 100.0f);
pProj->m_Y = (int)(m_Pos.y * 100.0f);
@ -403,3 +404,45 @@ bool CProjectile::FillExtraInfo(CNetObj_DDNetProjectile *pProj)
pProj->m_Type = m_Type;
return true;
}
void CProjectile::FillExtraInfo(CNetObj_DDNetProjectile *pProj)
{
int Flags = 0;
if(m_Bouncing & 1)
{
Flags |= PROJECTILEFLAG_BOUNCE_HORIZONTAL;
}
if(m_Bouncing & 2)
{
Flags |= PROJECTILEFLAG_BOUNCE_VERTICAL;
}
if(m_Explosive)
{
Flags |= PROJECTILEFLAG_EXPLOSIVE;
}
if(m_Freeze)
{
Flags |= PROJECTILEFLAG_FREEZE;
}
if(m_Owner < 0)
{
pProj->m_VelX = round_to_int(m_Direction.x * 1e6f);
pProj->m_VelY = round_to_int(m_Direction.y * 1e6f);
}
else
{
pProj->m_VelX = round_to_int(m_InitDir.x);
pProj->m_VelY = round_to_int(m_InitDir.y);
Flags |= PROJECTILEFLAG_NORMALIZE_VEL;
}
pProj->m_X = round_to_int(m_Pos.x * 100.0f);
pProj->m_Y = round_to_int(m_Pos.y * 100.0f);
pProj->m_Type = m_Type;
pProj->m_StartTick = m_StartTick;
pProj->m_Owner = m_Owner;
pProj->m_Flags = Flags;
pProj->m_SwitchNumber = m_Number;
pProj->m_TuneZone = m_TuneZone;
}

View file

@ -18,6 +18,7 @@ public:
bool Freeze,
bool Explosive,
int SoundImpact,
vec2 InitDir,
int Layer = 0,
int Number = 0);
@ -46,10 +47,12 @@ private:
bool m_Freeze;
int m_TuneZone;
bool m_BelongsToPracticeTeam;
vec2 m_InitDir;
public:
void SetBouncing(int Value);
bool FillExtraInfo(CNetObj_DDNetProjectile *pProj);
bool FillExtraInfoLegacy(CNetObj_DDRaceProjectile *pProj);
void FillExtraInfo(CNetObj_DDNetProjectile *pProj);
virtual int GetOwnerID() const override { return m_Owner; }
};

View file

@ -373,7 +373,7 @@ void CGameContext::CreateSoundGlobal(int Sound, int Target)
}
}
bool CGameContext::SnapLaserObject(const CSnapContext &Context, int SnapID, const vec2 &To, const vec2 &From, int StartTick, int Owner, int LaserType)
bool CGameContext::SnapLaserObject(const CSnapContext &Context, int SnapID, const vec2 &To, const vec2 &From, int StartTick, int Owner, int LaserType, int Subtype, int SwitchNumber)
{
if(Context.GetClientVersion() >= VERSION_DDNET_MULTI_LASER)
{
@ -388,6 +388,8 @@ bool CGameContext::SnapLaserObject(const CSnapContext &Context, int SnapID, cons
pObj->m_StartTick = StartTick;
pObj->m_Owner = Owner;
pObj->m_Type = LaserType;
pObj->m_Subtype = Subtype;
pObj->m_SwitchNumber = SwitchNumber;
}
else
{
@ -405,7 +407,7 @@ bool CGameContext::SnapLaserObject(const CSnapContext &Context, int SnapID, cons
return true;
}
bool CGameContext::SnapPickup(const CSnapContext &Context, int SnapID, const vec2 &Pos, int Type, int SubType)
bool CGameContext::SnapPickup(const CSnapContext &Context, int SnapID, const vec2 &Pos, int Type, int SubType, int SwitchNumber)
{
if(Context.IsSixup())
{
@ -421,6 +423,18 @@ bool CGameContext::SnapPickup(const CSnapContext &Context, int SnapID, const vec
else if(Type == POWERUP_NINJA)
pPickup->m_Type = protocol7::PICKUP_NINJA;
}
else if(Context.GetClientVersion() >= VERSION_DDNET_ENTITY_NETOBJS)
{
CNetObj_DDNetPickup *pPickup = Server()->SnapNewItem<CNetObj_DDNetPickup>(SnapID);
if(!pPickup)
return false;
pPickup->m_X = (int)Pos.x;
pPickup->m_Y = (int)Pos.y;
pPickup->m_Type = Type;
pPickup->m_Subtype = SubType;
pPickup->m_SwitchNumber = SwitchNumber;
}
else
{
CNetObj_Pickup *pPickup = Server()->SnapNewItem<CNetObj_Pickup>(SnapID);

View file

@ -228,8 +228,8 @@ public:
void CreateSound(vec2 Pos, int Sound, CClientMask Mask = CClientMask().set());
void CreateSoundGlobal(int Sound, int Target = -1);
bool SnapLaserObject(const CSnapContext &Context, int SnapID, const vec2 &To, const vec2 &From, int StartTick, int Owner = -1, int LaserType = -1);
bool SnapPickup(const CSnapContext &Context, int SnapID, const vec2 &Pos, int Type, int SubType);
bool SnapLaserObject(const CSnapContext &Context, int SnapID, const vec2 &To, const vec2 &From, int StartTick, int Owner = -1, int LaserType = -1, int Subtype = -1, int SwitchNumber = -1);
bool SnapPickup(const CSnapContext &Context, int SnapID, const vec2 &Pos, int Type, int SubType, int SwitchNumber);
enum
{

View file

@ -232,6 +232,7 @@ bool IGameController::OnEntity(int Index, int x, int y, int Layer, int Flags, bo
true, //Freeze
true, //Explosive
(g_Config.m_SvShotgunBulletSound) ? SOUND_GRENADE_EXPLODE : -1, //SoundImpact
vec2(std::sin(Deg), std::cos(Deg)), // InitDir
Layer,
Number);
pBullet->SetBouncing(2 - (Dir % 2));
@ -258,6 +259,7 @@ bool IGameController::OnEntity(int Index, int x, int y, int Layer, int Flags, bo
true, //Freeze
false, //Explosive
SOUND_GRENADE_EXPLODE,
vec2(std::sin(Deg), std::cos(Deg)), // InitDir
Layer,
Number);
pBullet->SetBouncing(2 - (Dir % 2));