Files
GTASource/rage/contrib/samples/sample_datasync/app.h
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

299 lines
8.2 KiB
C++

//
// sample_datasync/app.h
//
// Copyright (C) 1999-2008 Rockstar Games. All Rights Reserved.
//
#ifndef SAMPLE_DATASYNC_APP_H
#define SAMPLE_DATASYNC_APP_H
#include "datasync/synchobject.h"
#include "datasync/synchmsg.h"
#include "datasync/netpresent.h"
#include "net/timesync.h"
#include "sample_snet/app.h"
#include "vector/vector3.h"
namespace rage
{
class bkBank;
}
//////////////////////////////////////////////////////////////////////////
// TemplateNetSyncObject - THIS BASE CLASS IS GENERICALLY USEFUL
template <typename T> class TemplateNetSyncObject : public rage::NSOObjectBase //Currently all objects derive from NSOObjectBase, an empty class with a virtual interface. This can be designed out if needed.
{
public:
virtual rage::NSOType *GetNSOType() const
{
return &sm_AutoSyncData;
}
TemplateNetSyncObject() : m_ObjectId(0) { }
TemplateNetSyncObject(int objectid) : m_ObjectId(objectid) { }
#if __BANK
static void AddWidgets(rage::bkBank &bk) { sm_AutoSyncData.AddWidgets(bk); }
#endif
static rage::NSOInstance* CreateTransmitter(T& rObj, u64 iRemoteTarget)
{
return rage::NSOInstance::CreateTransmitter(*rObj.GetNSOType(), rObj, iRemoteTarget);
}
static rage::NSOInstance* CreateReceiver(T& obj)
{
return rage::NSOInstance::CreateReceiver(*obj.GetNSOType(), obj);
}
int m_ObjectId;
protected:
static rage::NSOType sm_AutoSyncData;
static T &sm_OffsetFromZero;
};
// TemplateNetSyncObject
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// GenericTransportableNSDMsg - this is where the SNU interface would live
class GenericTransportableNSDMsg : public NetSyncDataMsg
{
public:
struct NetSessionTransport
{
NetSessionTransport() : mp_Cxn(0), mp_PeerMgr(0)
{
}
NetSessionTransport(rage::netConnectionManager &cxn, rage::PeerManager &pmgr) : mp_Cxn(&cxn), mp_PeerMgr(&pmgr)
{
}
rage::netConnectionManager* mp_Cxn;
rage::PeerManager* mp_PeerMgr;
};
NetSessionTransport m_Transport;
int m_CxnId;
GenericTransportableNSDMsg(): m_CxnId(0)
{}
GenericTransportableNSDMsg(NetSessionTransport trans, int cxnId, u32 iObjectId, float fNetTime): NetSyncDataMsg(iObjectId, fNetTime)
, m_Transport(trans)
, m_CxnId(cxnId)
{
}
template<typename T>
bool SendToCxn( const int cxnId,
const T& msg,
const unsigned flags,
rage::netSequence* frameSeq = 0 )
{
if( AssertVerify( cxnId >= 0 ) )
{
if (m_Transport.mp_Cxn->IsOpen(cxnId) || m_Transport.mp_Cxn->IsPendingOpen(cxnId))
{
return m_Transport.mp_Cxn->Send( cxnId, msg, flags, frameSeq ); // can't put this in a base class because the "this" is a template of the type passed in, could have msg as a param
}
}
return false;
}
template<typename T>
bool SendToAllPeers(
const T& msg,
const unsigned flags,
rage::netSequence* frameSeq = 0 )
{
// just send to all other peers for now
bool b = false;
Peer* peers[PeerManager::MAX_PEERS];
const unsigned numPeers = m_Transport.mp_PeerMgr->GetPeers(peers, COUNTOF(peers));
for(unsigned i = 0; i < numPeers; ++i)
{
if(!peers[i]->IsLocal())
{
b |= SendToCxn( peers[i]->GetCxnId(), msg, flags, frameSeq );
}
}
return b;
}
};
// GenericTransportableNSDMsg
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SampleNetSyncObject - Primary sync-able object for this tester
class SampleNetSyncObject : public TemplateNetSyncObject<SampleNetSyncObject>
{
public:
rage::Vector3 m_vPos;
Color32 m_cColor;
SampleNetSyncObject()
:m_vPos(rage::Vector3::ZeroType)
{
m_cColor.Set(0);
}
SampleNetSyncObject(rage::u32 objectid, float x, float y, float z)
: TemplateNetSyncObject<SampleNetSyncObject>(objectid)
, m_vPos(x,y,z)
, m_cColor(0)
{
}
//sm_AutoSyncData holds information for synchronising the NetGOHActor class. It also provided us with an API to create and destroy per client view data.
//When we create views we need to create an NSOInstance to go with this type. Also each remote object is currently required to have an NSOInstance to receive data.
static void CreateAutoSyncData()
{
//The NetPresenter is an object used to gather information about the data structure. It has a load of
//helper functions wrapping templated functionality in NSEPresenter to simplify common rules.
rage::NetPresenter presenter;
//Groups are handy for visualisation and stats tracking. They also add a guard flag to the data.
//If nothing in the group or its children gets serialized then the flag isn't set and the section
//is skipped. We'll see a variation on the theme a little later.
if (presenter.StartGroup("ContainedData"))
{
//We store data as offsets from 0 so we can reapply to different instances of the class
presenter.HeartbeatAndChange("AnyChangeVector3", sm_OffsetFromZero.m_vPos, 10.0f);
presenter.OnChange("AnyChangeColor32", *(int*)(void*)&sm_OffsetFromZero.m_cColor);
//All StartGroup calls require an EndGroup.
presenter.EndGroup();
}
//Now we give the data to the NSOType. This is used later to create instance data (NSOInstance) for each view)
sm_AutoSyncData.SetupFromPresenter(presenter);
}
};
// SampleNetSyncObject
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SampleAppDataSyncMsg - message for syncing NSOInstances (using a globally unique net_id system - (only rudimentarily supplied)
class SampleAppDataSyncMsg : public GenericTransportableNSDMsg
{
NSOInstance *OnGetReciever(u32 net_id) const;
public:
virtual void OnSendMsg()
{
SendToCxn( m_CxnId, *this, 0); //TMS: This used to be reliable or not but these days we handle that under the hood
}
SampleAppDataSyncMsg() {}
SampleAppDataSyncMsg(NetSessionTransport trans, int cxnId, u32 iObjectId, float fNetTime)
: GenericTransportableNSDMsg(trans, cxnId, iObjectId, fNetTime)
{}
NET_MESSAGE_DECL( SampleAppDataSyncMsg );
NET_MESSAGE_SER( bb, msg )
{
return NetSyncDataMsg::SerMsgPayload( bb, msg );
}
};
// SampleAppDataSyncMsg
//////////////////////////////////////////////////////////////////////////
// main tester plugin
class SampleApp3dWorld {
public:
SampleApp3dWorld();
static SampleSessionPlugin sm_Plugin;
static SampleApp3dWorld* sm_Instance;
enum Channels
{
TIME_SYNC_CHANNEL_ID = 0,
};
struct nsoinst_node
{
nsoinst_node() : mp_Data(NULL), mp_Next(NULL), m_CxnId(0)
{
}
int m_CxnId;
SampleNetSyncObject* mp_Object;
NSOInstance* mp_Data;
nsoinst_node* mp_Next;
};
void AddRemoteViewOfLocalAvatar(int cxnid);
nsoinst_node* GetRemoteObject(int net_id); // will create if not found
#if __BANK
void AddWidgets();
#endif
void UpdateTimeSync(LiveManager *livemgr);
void JoinedSession(snSession *session);
void AddRemoteViewOfWorld(int cxnId);
bool HandleMessages(unsigned msgId, const netEventFrameReceived* fr);
void AddGamer(Gamer *gamer);
void Update(LiveManager *livemgr, PeerManager &peermgr); // top level update function
void UpdateObjects();
void UpdateNet(netConnectionManager &rMgr, PeerManager &rPeerMgr);
void Draw();
void Disconnect();
// sample_session plugin delegates
static void PluginUpdate(LiveManager *livemgr, PeerManager &peermgr)
{
sm_Instance->Update(livemgr, peermgr);
}
static void PluginDraw()
{
sm_Instance->Draw();
}
static void PluginJoinedSession(snSession *session)
{
sm_Instance->JoinedSession(session);
}
static bool PluginHandleMessages(unsigned msgId, const netEventFrameReceived* fr)
{
return sm_Instance->HandleMessages(msgId, fr);
}
static void PluginAddGamer(Gamer *gamer)
{
sm_Instance->AddGamer(gamer);
}
static void PluginInit()
{
sm_Instance = rage_new SampleApp3dWorld;
}
protected:
enum {
MIN_X=-5, MAX_X=5,
MIN_Y=-5, MAX_Y=5,
MIN_Z=-5, MAX_Z=5,
NUM_WORLD_OBJECTS = (MAX_X-MIN_X)*(MAX_Y-MIN_Y)*(MAX_Z-MIN_Z)
};
SampleNetSyncObject* mp_LocalAvatar;
unsigned m_LastTime;
rage::netTimeSync* m_TimeSync;
nsoinst_node* m_RemoteViewsOfLocalObjects;
nsoinst_node* m_RemoteObjects;
bool m_UseLocalWorldObjects;
SampleNetSyncObject m_LocalWorldObjects[NUM_WORLD_OBJECTS];
};
#endif // SAMPLE_DATASYNC_APP_H