Skip to content

Commit 8d0d130

Browse files
Boondorlmadame-rachelle
authored andcommitted
New API for assigning unique network ids to objects
1 parent 111fd48 commit 8d0d130

File tree

11 files changed

+233
-1
lines changed

11 files changed

+233
-1
lines changed

src/common/objects/dobject.cpp

+79-1
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ CCMD (dumpclasses)
218218
//
219219
//==========================================================================
220220

221+
#include "d_net.h"
222+
221223
void DObject::InPlaceConstructor (void *mem)
222224
{
223225
new ((EInPlace *)mem) DObject;
@@ -317,6 +319,8 @@ void DObject::Release()
317319

318320
void DObject::Destroy ()
319321
{
322+
NetworkEntityManager::RemoveNetworkEntity(this);
323+
320324
// We cannot call the VM during shutdown because all the needed data has been or is in the process of being deleted.
321325
if (PClass::bVMOperational)
322326
{
@@ -569,8 +573,15 @@ void DObject::Serialize(FSerializer &arc)
569573

570574
SerializeFlag("justspawned", OF_JustSpawned);
571575
SerializeFlag("spawned", OF_Spawned);
572-
576+
SerializeFlag("networked", OF_Networked);
577+
573578
ObjectFlags |= OF_SerialSuccess;
579+
580+
if (arc.isReading() && (ObjectFlags & OF_Networked))
581+
{
582+
ClearNetworkID();
583+
EnableNetworking(true);
584+
}
574585
}
575586

576587
void DObject::CheckIfSerialized () const
@@ -585,6 +596,73 @@ void DObject::CheckIfSerialized () const
585596
}
586597
}
587598

599+
//==========================================================================
600+
//
601+
//
602+
//
603+
//==========================================================================
604+
605+
void DObject::SetNetworkID(const uint32_t id)
606+
{
607+
if (!IsNetworked())
608+
{
609+
ObjectFlags |= OF_Networked;
610+
_networkID = id;
611+
}
612+
}
613+
614+
void DObject::ClearNetworkID()
615+
{
616+
ObjectFlags &= ~OF_Networked;
617+
_networkID = NetworkEntityManager::WorldNetID;
618+
}
619+
620+
void DObject::EnableNetworking(const bool enable)
621+
{
622+
if (enable)
623+
NetworkEntityManager::AddNetworkEntity(this);
624+
else
625+
NetworkEntityManager::RemoveNetworkEntity(this);
626+
}
627+
628+
static unsigned int GetNetworkID(DObject* const self)
629+
{
630+
return self->GetNetworkID();
631+
}
632+
633+
DEFINE_ACTION_FUNCTION_NATIVE(DObject, GetNetworkID, GetNetworkID)
634+
{
635+
PARAM_SELF_PROLOGUE(DObject);
636+
637+
ACTION_RETURN_INT(self->GetNetworkID());
638+
}
639+
640+
static void EnableNetworking(DObject* const self, const bool enable)
641+
{
642+
self->EnableNetworking(enable);
643+
}
644+
645+
DEFINE_ACTION_FUNCTION_NATIVE(DObject, EnableNetworking, EnableNetworking)
646+
{
647+
PARAM_SELF_PROLOGUE(DObject);
648+
PARAM_BOOL(enable);
649+
650+
self->EnableNetworking(enable);
651+
return 0;
652+
}
653+
654+
static DObject* GetNetworkEntity(const unsigned int id)
655+
{
656+
return NetworkEntityManager::GetNetworkEntity(id);
657+
}
658+
659+
DEFINE_ACTION_FUNCTION_NATIVE(DObject, GetNetworkEntity, GetNetworkEntity)
660+
{
661+
PARAM_PROLOGUE;
662+
PARAM_UINT(id);
663+
664+
ACTION_RETURN_OBJECT(NetworkEntityManager::GetNetworkEntity(id));
665+
}
588666

589667
DEFINE_ACTION_FUNCTION(DObject, MSTime)
590668
{

src/common/objects/dobject.h

+11
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,17 @@ class DObject
351351
friend T* Create(Args&&... args);
352352

353353
friend class JitCompiler;
354+
355+
private:
356+
// This is intentionally left unserialized.
357+
uint32_t _networkID;
358+
359+
public:
360+
inline bool IsNetworked() const { return (ObjectFlags & OF_Networked); }
361+
inline uint32_t GetNetworkID() const { return _networkID; }
362+
void SetNetworkID(const uint32_t id);
363+
void ClearNetworkID();
364+
virtual void EnableNetworking(const bool enable);
354365
};
355366

356367
// This is the only method aside from calling CreateNew that should be used for creating DObjects

src/common/objects/dobjgc.h

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum EObjectFlags
2626
OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk)
2727
OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning)
2828
OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function
29+
OF_Networked = 1 << 14, // Object has a unique network identifier that makes it synchronizable between all clients.
2930
};
3031

3132
template<class T> class TObjPtr;

src/d_main.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -3101,6 +3101,8 @@ static int FileSystemPrintf(FSMessageLevel level, const char* fmt, ...)
31013101

31023102
static int D_InitGame(const FIWADInfo* iwad_info, std::vector<std::string>& allwads, std::vector<std::string>& pwads)
31033103
{
3104+
NetworkEntityManager::InitializeNetworkEntities();
3105+
31043106
if (!restart)
31053107
{
31063108
V_InitScreenSize();

src/d_net.cpp

+83
Original file line numberDiff line numberDiff line change
@@ -2965,6 +2965,89 @@ int Net_GetLatency(int *ld, int *ad)
29652965
return severity;
29662966
}
29672967

2968+
void NetworkEntityManager::InitializeNetworkEntities()
2969+
{
2970+
if (!s_netEntities.Size())
2971+
s_netEntities.AppendFill(nullptr, NetIDStart); // Allocate the first 0-8 slots for the world and clients.
2972+
}
2973+
2974+
// Clients need special handling since they always go in slots 1 - MAXPLAYERS.
2975+
void NetworkEntityManager::SetClientNetworkEntity(player_t* const client)
2976+
{
2977+
AActor* const mo = client->mo;
2978+
const uint32_t id = ClientNetIDStart + mo->Level->PlayerNum(client);
2979+
2980+
// If resurrecting, we need to swap the corpse's position with the new pawn's
2981+
// position so it's no longer considered the client's body.
2982+
DObject* const oldBody = s_netEntities[id];
2983+
if (oldBody != nullptr)
2984+
{
2985+
if (oldBody == mo)
2986+
return;
2987+
2988+
const uint32_t curID = mo->GetNetworkID();
2989+
2990+
s_netEntities[curID] = oldBody;
2991+
oldBody->ClearNetworkID();
2992+
oldBody->SetNetworkID(curID);
2993+
2994+
mo->ClearNetworkID();
2995+
}
2996+
else
2997+
{
2998+
RemoveNetworkEntity(mo); // Free up its current id.
2999+
}
3000+
3001+
s_netEntities[id] = mo;
3002+
mo->SetNetworkID(id);
3003+
}
3004+
3005+
void NetworkEntityManager::AddNetworkEntity(DObject* const ent)
3006+
{
3007+
if (ent->IsNetworked())
3008+
return;
3009+
3010+
// Slot 0 is reserved for the world.
3011+
// Clients go in the first 1 - MAXPLAYERS slots
3012+
// Everything else is first come first serve.
3013+
uint32_t id = WorldNetID;
3014+
if (s_openNetIDs.Size())
3015+
{
3016+
s_openNetIDs.Pop(id);
3017+
s_netEntities[id] = ent;
3018+
}
3019+
else
3020+
{
3021+
id = s_netEntities.Push(ent);
3022+
}
3023+
3024+
ent->SetNetworkID(id);
3025+
}
3026+
3027+
void NetworkEntityManager::RemoveNetworkEntity(DObject* const ent)
3028+
{
3029+
if (!ent->IsNetworked())
3030+
return;
3031+
3032+
const uint32_t id = ent->GetNetworkID();
3033+
if (id == WorldNetID)
3034+
return;
3035+
3036+
assert(s_netEntities[id] == ent);
3037+
if (id >= NetIDStart)
3038+
s_openNetIDs.Push(id);
3039+
s_netEntities[id] = nullptr;
3040+
ent->ClearNetworkID();
3041+
}
3042+
3043+
DObject* NetworkEntityManager::GetNetworkEntity(const uint32_t id)
3044+
{
3045+
if (id == WorldNetID || id >= s_netEntities.Size())
3046+
return nullptr;
3047+
3048+
return s_netEntities[id];
3049+
}
3050+
29683051
// [RH] List "ping" times
29693052
CCMD (pings)
29703053
{

src/d_net.h

+23
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,29 @@ extern int nodeforplayer[MAXPLAYERS];
9595
extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
9696
extern int ticdup;
9797

98+
class player_t;
99+
class DObject;
100+
101+
class NetworkEntityManager
102+
{
103+
private:
104+
inline static TArray<DObject*> s_netEntities = {};
105+
inline static TArray<uint32_t> s_openNetIDs = {};
106+
107+
public:
108+
NetworkEntityManager() = delete;
109+
110+
inline static uint32_t WorldNetID = 0u;
111+
inline static uint32_t ClientNetIDStart = 1u;
112+
inline static uint32_t NetIDStart = MAXPLAYERS + 1u;
113+
114+
static void InitializeNetworkEntities();
115+
static void SetClientNetworkEntity(player_t* const client);
116+
static void AddNetworkEntity(DObject* const ent);
117+
static void RemoveNetworkEntity(DObject* const ent);
118+
static DObject* GetNetworkEntity(const uint32_t id);
119+
};
120+
98121
// [RH]
99122
// New generic packet structure:
100123
//

src/g_game.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,7 @@ void FLevelLocals::PlayerReborn (int player)
14251425
p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately
14261426
p->original_oldbuttons = ~0;
14271427
p->playerstate = PST_LIVE;
1428+
NetworkEntityManager::SetClientNetworkEntity(p);
14281429

14291430
if (gamestate != GS_TITLELEVEL)
14301431
{

src/p_saveg.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
#include "fragglescript/t_script.h"
6363
#include "s_music.h"
6464
#include "model.h"
65+
#include "d_net.h"
6566

6667
EXTERN_CVAR(Bool, save_formatted)
6768

@@ -653,6 +654,15 @@ void FLevelLocals::SerializePlayers(FSerializer &arc, bool skipload)
653654
ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload);
654655
}
655656
arc.EndArray();
657+
658+
if (!skipload)
659+
{
660+
for (unsigned int i = 0u; i < MAXPLAYERS; ++i)
661+
{
662+
if (PlayerInGame(i) && Players[i]->mo != nullptr)
663+
NetworkEntityManager::SetClientNetworkEntity(Players[i]);
664+
}
665+
}
656666
}
657667
if (!skipload && numPlayersNow > numPlayers)
658668
{

src/playsim/actor.h

+1
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,7 @@ class AActor final : public DThinker
804804
virtual void PostSerialize() override;
805805
virtual void PostBeginPlay() override; // Called immediately before the actor's first tick
806806
virtual void Tick() override;
807+
void EnableNetworking(const bool enable) override;
807808

808809
static AActor *StaticSpawn (FLevelLocals *Level, PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing = false);
809810

src/playsim/p_mobj.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
#include "fragglescript/t_fs.h"
102102
#include "shadowinlines.h"
103103
#include "model.h"
104+
#include "d_net.h"
104105

105106
// MACROS ------------------------------------------------------------------
106107

@@ -179,6 +180,23 @@ IMPLEMENT_POINTERS_START(AActor)
179180
IMPLEMENT_POINTER(boneComponentData)
180181
IMPLEMENT_POINTERS_END
181182

183+
//==========================================================================
184+
//
185+
// Make sure Actors can never have their networking disabled.
186+
//
187+
//==========================================================================
188+
189+
void AActor::EnableNetworking(const bool enable)
190+
{
191+
if (!enable)
192+
{
193+
ThrowAbortException(X_OTHER, "Cannot disable networking on Actors. Consider a Thinker instead.");
194+
return;
195+
}
196+
197+
Super::EnableNetworking(true);
198+
}
199+
182200
//==========================================================================
183201
//
184202
// AActor :: Serialize
@@ -4830,6 +4848,7 @@ AActor *AActor::StaticSpawn(FLevelLocals *Level, PClassActor *type, const DVecto
48304848
AActor *actor;
48314849

48324850
actor = static_cast<AActor *>(Level->CreateThinker(type));
4851+
actor->EnableNetworking(true);
48334852

48344853
ConstructActor(actor, pos, SpawningMapThing);
48354854
return actor;

wadsrc/static/zscript/doombase.zs

+3
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ extend class Object
137137
native static void MarkSound(Sound snd);
138138
native static uint BAM(double angle);
139139
native static void SetMusicVolume(float vol);
140+
native clearscope static Object GetNetworkEntity(uint id);
141+
native play void EnableNetworking(bool enable);
142+
native clearscope uint GetNetworkID() const;
140143
}
141144

142145
class Thinker : Object native play

0 commit comments

Comments
 (0)