mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-02-16 17:21:10 +00:00
New API for assigning unique network ids to objects
This commit is contained in:
parent
111fd48348
commit
8d0d130dc9
11 changed files with 233 additions and 1 deletions
|
@ -218,6 +218,8 @@ CCMD (dumpclasses)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
|
#include "d_net.h"
|
||||||
|
|
||||||
void DObject::InPlaceConstructor (void *mem)
|
void DObject::InPlaceConstructor (void *mem)
|
||||||
{
|
{
|
||||||
new ((EInPlace *)mem) DObject;
|
new ((EInPlace *)mem) DObject;
|
||||||
|
@ -317,6 +319,8 @@ void DObject::Release()
|
||||||
|
|
||||||
void DObject::Destroy ()
|
void DObject::Destroy ()
|
||||||
{
|
{
|
||||||
|
NetworkEntityManager::RemoveNetworkEntity(this);
|
||||||
|
|
||||||
// We cannot call the VM during shutdown because all the needed data has been or is in the process of being deleted.
|
// We cannot call the VM during shutdown because all the needed data has been or is in the process of being deleted.
|
||||||
if (PClass::bVMOperational)
|
if (PClass::bVMOperational)
|
||||||
{
|
{
|
||||||
|
@ -569,8 +573,15 @@ void DObject::Serialize(FSerializer &arc)
|
||||||
|
|
||||||
SerializeFlag("justspawned", OF_JustSpawned);
|
SerializeFlag("justspawned", OF_JustSpawned);
|
||||||
SerializeFlag("spawned", OF_Spawned);
|
SerializeFlag("spawned", OF_Spawned);
|
||||||
|
SerializeFlag("networked", OF_Networked);
|
||||||
|
|
||||||
ObjectFlags |= OF_SerialSuccess;
|
ObjectFlags |= OF_SerialSuccess;
|
||||||
|
|
||||||
|
if (arc.isReading() && (ObjectFlags & OF_Networked))
|
||||||
|
{
|
||||||
|
ClearNetworkID();
|
||||||
|
EnableNetworking(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DObject::CheckIfSerialized () const
|
void DObject::CheckIfSerialized () const
|
||||||
|
@ -585,6 +596,73 @@ void DObject::CheckIfSerialized () const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void DObject::SetNetworkID(const uint32_t id)
|
||||||
|
{
|
||||||
|
if (!IsNetworked())
|
||||||
|
{
|
||||||
|
ObjectFlags |= OF_Networked;
|
||||||
|
_networkID = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DObject::ClearNetworkID()
|
||||||
|
{
|
||||||
|
ObjectFlags &= ~OF_Networked;
|
||||||
|
_networkID = NetworkEntityManager::WorldNetID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DObject::EnableNetworking(const bool enable)
|
||||||
|
{
|
||||||
|
if (enable)
|
||||||
|
NetworkEntityManager::AddNetworkEntity(this);
|
||||||
|
else
|
||||||
|
NetworkEntityManager::RemoveNetworkEntity(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int GetNetworkID(DObject* const self)
|
||||||
|
{
|
||||||
|
return self->GetNetworkID();
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_ACTION_FUNCTION_NATIVE(DObject, GetNetworkID, GetNetworkID)
|
||||||
|
{
|
||||||
|
PARAM_SELF_PROLOGUE(DObject);
|
||||||
|
|
||||||
|
ACTION_RETURN_INT(self->GetNetworkID());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EnableNetworking(DObject* const self, const bool enable)
|
||||||
|
{
|
||||||
|
self->EnableNetworking(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_ACTION_FUNCTION_NATIVE(DObject, EnableNetworking, EnableNetworking)
|
||||||
|
{
|
||||||
|
PARAM_SELF_PROLOGUE(DObject);
|
||||||
|
PARAM_BOOL(enable);
|
||||||
|
|
||||||
|
self->EnableNetworking(enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static DObject* GetNetworkEntity(const unsigned int id)
|
||||||
|
{
|
||||||
|
return NetworkEntityManager::GetNetworkEntity(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_ACTION_FUNCTION_NATIVE(DObject, GetNetworkEntity, GetNetworkEntity)
|
||||||
|
{
|
||||||
|
PARAM_PROLOGUE;
|
||||||
|
PARAM_UINT(id);
|
||||||
|
|
||||||
|
ACTION_RETURN_OBJECT(NetworkEntityManager::GetNetworkEntity(id));
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_ACTION_FUNCTION(DObject, MSTime)
|
DEFINE_ACTION_FUNCTION(DObject, MSTime)
|
||||||
{
|
{
|
||||||
|
|
|
@ -351,6 +351,17 @@ protected:
|
||||||
friend T* Create(Args&&... args);
|
friend T* Create(Args&&... args);
|
||||||
|
|
||||||
friend class JitCompiler;
|
friend class JitCompiler;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is intentionally left unserialized.
|
||||||
|
uint32_t _networkID;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline bool IsNetworked() const { return (ObjectFlags & OF_Networked); }
|
||||||
|
inline uint32_t GetNetworkID() const { return _networkID; }
|
||||||
|
void SetNetworkID(const uint32_t id);
|
||||||
|
void ClearNetworkID();
|
||||||
|
virtual void EnableNetworking(const bool enable);
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is the only method aside from calling CreateNew that should be used for creating DObjects
|
// This is the only method aside from calling CreateNew that should be used for creating DObjects
|
||||||
|
|
|
@ -26,6 +26,7 @@ enum EObjectFlags
|
||||||
OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk)
|
OF_Transient = 1 << 11, // Object should not be archived (references to it will be nulled on disk)
|
||||||
OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning)
|
OF_Spawned = 1 << 12, // Thinker was spawned at all (some thinkers get deleted before spawning)
|
||||||
OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function
|
OF_Released = 1 << 13, // Object was released from the GC system and should not be processed by GC function
|
||||||
|
OF_Networked = 1 << 14, // Object has a unique network identifier that makes it synchronizable between all clients.
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T> class TObjPtr;
|
template<class T> class TObjPtr;
|
||||||
|
|
|
@ -3101,6 +3101,8 @@ static int FileSystemPrintf(FSMessageLevel level, const char* fmt, ...)
|
||||||
|
|
||||||
static int D_InitGame(const FIWADInfo* iwad_info, std::vector<std::string>& allwads, std::vector<std::string>& pwads)
|
static int D_InitGame(const FIWADInfo* iwad_info, std::vector<std::string>& allwads, std::vector<std::string>& pwads)
|
||||||
{
|
{
|
||||||
|
NetworkEntityManager::InitializeNetworkEntities();
|
||||||
|
|
||||||
if (!restart)
|
if (!restart)
|
||||||
{
|
{
|
||||||
V_InitScreenSize();
|
V_InitScreenSize();
|
||||||
|
|
|
@ -2965,6 +2965,89 @@ int Net_GetLatency(int *ld, int *ad)
|
||||||
return severity;
|
return severity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkEntityManager::InitializeNetworkEntities()
|
||||||
|
{
|
||||||
|
if (!s_netEntities.Size())
|
||||||
|
s_netEntities.AppendFill(nullptr, NetIDStart); // Allocate the first 0-8 slots for the world and clients.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clients need special handling since they always go in slots 1 - MAXPLAYERS.
|
||||||
|
void NetworkEntityManager::SetClientNetworkEntity(player_t* const client)
|
||||||
|
{
|
||||||
|
AActor* const mo = client->mo;
|
||||||
|
const uint32_t id = ClientNetIDStart + mo->Level->PlayerNum(client);
|
||||||
|
|
||||||
|
// If resurrecting, we need to swap the corpse's position with the new pawn's
|
||||||
|
// position so it's no longer considered the client's body.
|
||||||
|
DObject* const oldBody = s_netEntities[id];
|
||||||
|
if (oldBody != nullptr)
|
||||||
|
{
|
||||||
|
if (oldBody == mo)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint32_t curID = mo->GetNetworkID();
|
||||||
|
|
||||||
|
s_netEntities[curID] = oldBody;
|
||||||
|
oldBody->ClearNetworkID();
|
||||||
|
oldBody->SetNetworkID(curID);
|
||||||
|
|
||||||
|
mo->ClearNetworkID();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemoveNetworkEntity(mo); // Free up its current id.
|
||||||
|
}
|
||||||
|
|
||||||
|
s_netEntities[id] = mo;
|
||||||
|
mo->SetNetworkID(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkEntityManager::AddNetworkEntity(DObject* const ent)
|
||||||
|
{
|
||||||
|
if (ent->IsNetworked())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Slot 0 is reserved for the world.
|
||||||
|
// Clients go in the first 1 - MAXPLAYERS slots
|
||||||
|
// Everything else is first come first serve.
|
||||||
|
uint32_t id = WorldNetID;
|
||||||
|
if (s_openNetIDs.Size())
|
||||||
|
{
|
||||||
|
s_openNetIDs.Pop(id);
|
||||||
|
s_netEntities[id] = ent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
id = s_netEntities.Push(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->SetNetworkID(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkEntityManager::RemoveNetworkEntity(DObject* const ent)
|
||||||
|
{
|
||||||
|
if (!ent->IsNetworked())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const uint32_t id = ent->GetNetworkID();
|
||||||
|
if (id == WorldNetID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert(s_netEntities[id] == ent);
|
||||||
|
if (id >= NetIDStart)
|
||||||
|
s_openNetIDs.Push(id);
|
||||||
|
s_netEntities[id] = nullptr;
|
||||||
|
ent->ClearNetworkID();
|
||||||
|
}
|
||||||
|
|
||||||
|
DObject* NetworkEntityManager::GetNetworkEntity(const uint32_t id)
|
||||||
|
{
|
||||||
|
if (id == WorldNetID || id >= s_netEntities.Size())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return s_netEntities[id];
|
||||||
|
}
|
||||||
|
|
||||||
// [RH] List "ping" times
|
// [RH] List "ping" times
|
||||||
CCMD (pings)
|
CCMD (pings)
|
||||||
{
|
{
|
||||||
|
|
23
src/d_net.h
23
src/d_net.h
|
@ -95,6 +95,29 @@ extern int nodeforplayer[MAXPLAYERS];
|
||||||
extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
|
extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
|
||||||
extern int ticdup;
|
extern int ticdup;
|
||||||
|
|
||||||
|
class player_t;
|
||||||
|
class DObject;
|
||||||
|
|
||||||
|
class NetworkEntityManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
inline static TArray<DObject*> s_netEntities = {};
|
||||||
|
inline static TArray<uint32_t> s_openNetIDs = {};
|
||||||
|
|
||||||
|
public:
|
||||||
|
NetworkEntityManager() = delete;
|
||||||
|
|
||||||
|
inline static uint32_t WorldNetID = 0u;
|
||||||
|
inline static uint32_t ClientNetIDStart = 1u;
|
||||||
|
inline static uint32_t NetIDStart = MAXPLAYERS + 1u;
|
||||||
|
|
||||||
|
static void InitializeNetworkEntities();
|
||||||
|
static void SetClientNetworkEntity(player_t* const client);
|
||||||
|
static void AddNetworkEntity(DObject* const ent);
|
||||||
|
static void RemoveNetworkEntity(DObject* const ent);
|
||||||
|
static DObject* GetNetworkEntity(const uint32_t id);
|
||||||
|
};
|
||||||
|
|
||||||
// [RH]
|
// [RH]
|
||||||
// New generic packet structure:
|
// New generic packet structure:
|
||||||
//
|
//
|
||||||
|
|
|
@ -1425,6 +1425,7 @@ void FLevelLocals::PlayerReborn (int player)
|
||||||
p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately
|
p->oldbuttons = ~0, p->attackdown = true; p->usedown = true; // don't do anything immediately
|
||||||
p->original_oldbuttons = ~0;
|
p->original_oldbuttons = ~0;
|
||||||
p->playerstate = PST_LIVE;
|
p->playerstate = PST_LIVE;
|
||||||
|
NetworkEntityManager::SetClientNetworkEntity(p);
|
||||||
|
|
||||||
if (gamestate != GS_TITLELEVEL)
|
if (gamestate != GS_TITLELEVEL)
|
||||||
{
|
{
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
#include "fragglescript/t_script.h"
|
#include "fragglescript/t_script.h"
|
||||||
#include "s_music.h"
|
#include "s_music.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
|
#include "d_net.h"
|
||||||
|
|
||||||
EXTERN_CVAR(Bool, save_formatted)
|
EXTERN_CVAR(Bool, save_formatted)
|
||||||
|
|
||||||
|
@ -653,6 +654,15 @@ void FLevelLocals::SerializePlayers(FSerializer &arc, bool skipload)
|
||||||
ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload);
|
ReadMultiplePlayers(arc, numPlayers, numPlayersNow, skipload);
|
||||||
}
|
}
|
||||||
arc.EndArray();
|
arc.EndArray();
|
||||||
|
|
||||||
|
if (!skipload)
|
||||||
|
{
|
||||||
|
for (unsigned int i = 0u; i < MAXPLAYERS; ++i)
|
||||||
|
{
|
||||||
|
if (PlayerInGame(i) && Players[i]->mo != nullptr)
|
||||||
|
NetworkEntityManager::SetClientNetworkEntity(Players[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!skipload && numPlayersNow > numPlayers)
|
if (!skipload && numPlayersNow > numPlayers)
|
||||||
{
|
{
|
||||||
|
|
|
@ -804,6 +804,7 @@ public:
|
||||||
virtual void PostSerialize() override;
|
virtual void PostSerialize() override;
|
||||||
virtual void PostBeginPlay() override; // Called immediately before the actor's first tick
|
virtual void PostBeginPlay() override; // Called immediately before the actor's first tick
|
||||||
virtual void Tick() override;
|
virtual void Tick() override;
|
||||||
|
void EnableNetworking(const bool enable) override;
|
||||||
|
|
||||||
static AActor *StaticSpawn (FLevelLocals *Level, PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing = false);
|
static AActor *StaticSpawn (FLevelLocals *Level, PClassActor *type, const DVector3 &pos, replace_t allowreplacement, bool SpawningMapThing = false);
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,7 @@
|
||||||
#include "fragglescript/t_fs.h"
|
#include "fragglescript/t_fs.h"
|
||||||
#include "shadowinlines.h"
|
#include "shadowinlines.h"
|
||||||
#include "model.h"
|
#include "model.h"
|
||||||
|
#include "d_net.h"
|
||||||
|
|
||||||
// MACROS ------------------------------------------------------------------
|
// MACROS ------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -179,6 +180,23 @@ IMPLEMENT_POINTERS_START(AActor)
|
||||||
IMPLEMENT_POINTER(boneComponentData)
|
IMPLEMENT_POINTER(boneComponentData)
|
||||||
IMPLEMENT_POINTERS_END
|
IMPLEMENT_POINTERS_END
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Make sure Actors can never have their networking disabled.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void AActor::EnableNetworking(const bool enable)
|
||||||
|
{
|
||||||
|
if (!enable)
|
||||||
|
{
|
||||||
|
ThrowAbortException(X_OTHER, "Cannot disable networking on Actors. Consider a Thinker instead.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Super::EnableNetworking(true);
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// AActor :: Serialize
|
// AActor :: Serialize
|
||||||
|
@ -4830,6 +4848,7 @@ AActor *AActor::StaticSpawn(FLevelLocals *Level, PClassActor *type, const DVecto
|
||||||
AActor *actor;
|
AActor *actor;
|
||||||
|
|
||||||
actor = static_cast<AActor *>(Level->CreateThinker(type));
|
actor = static_cast<AActor *>(Level->CreateThinker(type));
|
||||||
|
actor->EnableNetworking(true);
|
||||||
|
|
||||||
ConstructActor(actor, pos, SpawningMapThing);
|
ConstructActor(actor, pos, SpawningMapThing);
|
||||||
return actor;
|
return actor;
|
||||||
|
|
|
@ -137,6 +137,9 @@ extend class Object
|
||||||
native static void MarkSound(Sound snd);
|
native static void MarkSound(Sound snd);
|
||||||
native static uint BAM(double angle);
|
native static uint BAM(double angle);
|
||||||
native static void SetMusicVolume(float vol);
|
native static void SetMusicVolume(float vol);
|
||||||
|
native clearscope static Object GetNetworkEntity(uint id);
|
||||||
|
native play void EnableNetworking(bool enable);
|
||||||
|
native clearscope uint GetNetworkID() const;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Thinker : Object native play
|
class Thinker : Object native play
|
||||||
|
|
Loading…
Reference in a new issue